DEV Community

lamingsrb
lamingsrb

Posted on • Originally published at bizflowai.io

4 Files Into One Human-Reviewed Interview Brief

4 Files, One Interview Brief: The Bot That Preps the Human

Your hiring manager opens the CV in one tab, hunts through Gmail for the application thread in another, digs up the role scorecard in a shared drive, and joins the call three minutes late still not sure what to ask. That is 15 minutes of tab-switching before a 30-minute interview. This post walks through the exact bot I build for clients that turns those four scattered inputs into a single evidence-linked briefing packet — without letting the model touch a hiring decision.

The one rule that keeps this safe: the bot preps humans, it does not judge them

This bot prepares the interviewer. It does not score, rank, reject, advance, or message the candidate. That boundary is the entire reason the workflow is worth building first. Every automation vendor demo I have seen starts at the riskiest step — resume scoring — which is exactly where bias, EEOC exposure, and bad candidate experience live. Flip the order. Automate the boring information-gathering that your hiring manager is doing badly at 8:57 AM, and leave the judgment where it belongs.

Practically, that means the bot has three hard "no" rules baked into the system prompt and the workflow logic itself:

  • No numeric fit score, no "recommend hire / no hire" language, no ranked comparison across candidates.
  • No candidate-facing output. The bot writes to the interviewer only.
  • No stage changes in the ATS. If you use Greenhouse, Ashby, or a spreadsheet, the bot reads. A human writes.

If you skip this framing, the same code becomes a compliance problem the moment a rejected candidate asks how the decision was made.

The 4 inputs, and the boring identifier problem that breaks most builds

The four inputs are: calendar event, candidate CV, application form response, and role scorecard. The one people skip is the scorecard, and without it the model produces a fluent summary that has nothing to do with what you are hiring for.

Keep the scorecard to five fields so a hiring manager will actually maintain it:

Field Example (customer support role)
Role title Senior Support Specialist, SaaS
Must-have Fluent written English, 2+ yrs ticketing (Zendesk/Intercom), B2B SaaS exposure
Nice-to-have SQL basics, technical writing samples
Concerns to verify US timezone overlap, availability start date, examples of angry-customer handling
Interview areas Written comms sample, live triage exercise, escalation judgment

The second boring problem: one clean identifier per candidate. Email is the only reliable one. Names collide, LinkedIn URLs get edited, phone numbers are missing half the time. If your calendar event only has a name, matching becomes a guessing game the moment you interview two people named David Chen in the same quarter.

The fix is a title convention, not code:

Interview — Jane Doe — jane.doe@gmail.com — Support Specialist
Enter fullscreen mode Exit fullscreen mode

Or put the email in the event description field. Not glamorous. But every reliable automation I have shipped starts with a boring, consistent input format.

The trigger, the extractor, and the "do not guess" rule

The trigger is a scheduled job that runs every 10 minutes and looks for calendar events with an Interview label starting in the next 30-60 minutes. When it finds one, it pulls: candidate email, candidate name, interviewer name, role title, event start time, meeting link.

If any critical field is missing, the bot does not guess. It sends the interviewer a short exception notice and stops.

# pseudo-code, n8n / Make / Python all map to this shape
def handle_event(event):
    ctx = extract_calendar_fields(event)
    missing = [k for k in ("candidate_email", "role_title", "interviewer") if not ctx.get(k)]
    if missing:
        notify_interviewer(
            to=ctx.get("interviewer_email"),
            subject=f"Briefing skipped: {event['summary']}",
            body=f"Missing fields: {', '.join(missing)}. "
                 f"Fix the event and I will regenerate on next run.\n{event['htmlLink']}"
        )
        return
    build_briefing(ctx)
Enter fullscreen mode Exit fullscreen mode

Silent guessing is how automations create embarrassing failures — the wrong candidate summary sent to the wrong interviewer, or a briefing about "David Chen" that mixes two people's CVs. An exception notice takes 20 seconds for a human to fix. A hallucinated brief takes a week of trust to rebuild.

Fields the trigger must always confirm before proceeding

  • candidate_email — the join key for every downstream lookup
  • role_title — required to load the correct scorecard
  • interviewer — required to send the brief to the right person
  • event_start — required to schedule delivery 30 min before

Gmail search, CV extraction, and keeping the payload small

Once the trigger has the candidate email, the bot searches Gmail with a narrow query. Broad searches ("everything from this person, ever") are how you end up with a 40,000-token context window full of scheduling back-and-forth.

from:jane.doe@gmail.com OR to:jane.doe@gmail.com
label:Applications OR label:Recruiting OR label:Interviews
newer_than:120d
has:attachment OR subject:(application OR interview OR resume OR cv)
Enter fullscreen mode Exit fullscreen mode

Then apply three filters:

  1. Newest CV wins. If the candidate sent three versions, use the most recent. List older files as source links, do not blend them into the extracted text.
  2. Extract with page references. For PDFs, keep page_number metadata alongside every text chunk. pdfplumber or pypdf both work. This is what lets the final brief cite "CV page 2" instead of a vague summary.
  3. Pull only the fields the interviewer needs from the application form. Work history, key open-ended answers, portfolio links, stated availability. Skip the "how did you hear about us" field.
import pdfplumber

def extract_cv(path):
    pages = []
    with pdfplumber.open(path) as pdf:
        for i, page in enumerate(pdf.pages, start=1):
            text = page.extract_text() or ""
            pages.append({"page": i, "text": text.strip()})
    return pages  # keep structure, do not join into one blob
Enter fullscreen mode Exit fullscreen mode

The payload going into the model should be small and labeled. For a typical candidate this is roughly:

Source Size
Calendar event ~200 tokens
CV (extracted, page-labeled) 1,500-3,000 tokens
Application form (filtered) 400-800 tokens
Selected email threads (max 3) 500-1,500 tokens
Role scorecard 200-400 tokens
Total input ~3,000-6,000 tokens

At current Claude Sonnet or GPT-4-class pricing, one briefing runs about $0.03-$0.08. Dumping every email would push that past $0.50 per brief and would make the citations unverifiable.

The prompt: labels, citations, and no opinions

Every input chunk gets a source label before it hits the model. This is the single change that separates a useful brief from an AI-generated opinion.

[SOURCE: cv_page_2] "Led a support team of 4 at Acme SaaS, 2022-2024..."
[SOURCE: application_q3_dated_2026-03-04] "Available to start within 3 weeks..."
[SOURCE: email_thread_2026-03-06] "Confirmed she is US Pacific time..."
[SOURCE: scorecard_v1.3_must_have] "US timezone overlap required"
Enter fullscreen mode Exit fullscreen mode

The system prompt then enforces the boundary:

You are preparing a hiring manager for an interview. You do NOT
evaluate the candidate. You do NOT recommend hire/no-hire. You do
NOT produce a score.

For every claim you make, cite the source label in brackets, e.g.
[cv_page_2]. If a scorecard requirement has no matching evidence in
the sources, say "no evidence found in provided sources" — do not
infer, do not fill in from general knowledge.

Output sections (in this order):
1. Snapshot (3 bullets, facts only)
2. Scorecard coverage (each must-have + nice-to-have, with evidence
   or "no evidence found")
3. Concerns to verify (from scorecard, phrased as questions)
4. Suggested questions (3-5, grounded in gaps or claims worth probing)
5. Source index (list of files/links used)
Enter fullscreen mode Exit fullscreen mode

That "no evidence found" clause is the anti-hallucination lever. Without it, the model will happily write "Jane has strong SQL skills" because SQL is on the scorecard, even though nothing in her CV mentions it.

Delivery, review, and what a good brief looks like at 8:30 AM

The brief lands in the interviewer's inbox (or Slack DM) 30 minutes before the call, with the meeting link at the top and the source index at the bottom. Every claim has a bracketed citation the interviewer can click.

A real one looks roughly like this:

INTERVIEW BRIEFING — Jane Doe — Senior Support Specialist
Interview: 2026-04-15 09:00 PT with Marcus | [Join call]

SNAPSHOT
• 2 yrs support lead at Acme SaaS (B2B) [cv_page_2]
• Available in 3 weeks, US Pacific [application_q3, email_2026-03-06]
• Portfolio: writing samples + one incident postmortem [application_q7]

SCORECARD COVERAGE
✓ Ticketing 2+ yrs — Zendesk at Acme [cv_page_2]
✓ B2B SaaS exposure — Acme is B2B [cv_page_1]
✓ US timezone — confirmed Pacific [email_2026-03-06]
? Angry-customer example — no evidence found in provided sources

CONCERNS TO VERIFY
• Ask for a specific escalation she handled end-to-end
• Confirm start date given current notice period

SUGGESTED QUESTIONS
1. Walk me through the incident in your postmortem — what did you own?
2. Tell me about a ticket you escalated. What was the trigger?
[...]
Enter fullscreen mode Exit fullscreen mode

The interviewer skims for 3 minutes, spots the "no evidence found" gap, and knows exactly what to probe. That is the whole product.

What breaks in the first two weeks of running this

  • Calendar events without the email in title or description — fix the convention, do not patch the code.
  • Gmail label drift ("Recruiting-2026" vs "Recruiting") — normalize labels once, monthly.
  • CVs sent as image-only PDFs — add an OCR fallback (Tesseract or a vision model) and flag the brief as "OCR used, verify accuracy."
  • Interviewer forwards the brief to the candidate by accident — put a visible INTERNAL — DO NOT FORWARD header on every brief.

Why bizflowai.io helps with this

This is exactly the class of workflow I ship for small teams at bizflowai.io — the boring, high-leverage pre-work that a human still owns the decision on. For hiring specifically, that means the calendar-triggered briefing bot above, plus adjacent pieces like a scorecard template your managers will actually fill in, exception routing when inputs are missing, and the source-labeled prompt structure that keeps the model from inventing evidence. Nothing in the stack scores or rejects candidates. Every decision stays with your hiring manager, and they walk into the call already prepped.


Want more like this?

I publish practical AI automation, GenAI engineering, and faceless content workflows on YouTube every week.

Subscribe to bizflowai.io on YouTube — never miss a new tutorial.

Planning an AI automation project or need a second opinion on your architecture?

Connect with me on LinkedIn — Lazar Milicevic, GenAI Engineer & bizflowai.io Founder.

Visit bizflowai.io for our services, case studies, and AI consulting.

Top comments (0)