HR Screener
An asynchronous interview intelligence and candidate screening platform that ingests raw meeting transcripts, deterministically scrubs Russian PII and conversational filler before external LLM dispatch, and produces grounded, evidence-backed competency evaluations with complete token accounting. Decision-support system. Final hiring decisions remain human.
What problem was framed
Technical and performance recruitment workflows suffer from two compounding bottlenecks:
- Recruiter Fatigue & Subjective Evaluation: Evaluating 45-minute unstructured call recordings or meeting transcripts requires 30 to 45 minutes of manual review per candidate. Under high hiring volume, recruiters develop cognitive fatigue, lose consistency across competency criteria, and default to superficial impressions rather than hard evidence.
- Data Sovereignty & PII Exposure: Passing raw conversational transcripts directly to external third-party LLMs (often hosted in cloud environments abroad) creates significant privacy, data-sovereignty and cross-border compliance risk. Transcripts routinely contain full names, direct contact details, current compensation packages, internal partner names, and confidential business terms.
The core architectural question: Can an asynchronous backend pipeline ingest raw meeting transcripts, deterministically strip all personally identifiable information (names, contacts, compensation) and linguistic filler locally before cloud transit, compress the context window by ~35%, and produce an evidence-backed candidate competency evaluation in under 8 seconds for less than a tenth of a cent per interview?
Guiding technical constraints
[PERSON_1]) on-premise prior to generating API payloads. Mapping tables remain strictly in the local database.End-to-End System Topology
The system separates the synchronous ingestion surface (Go / Echo v4) from the CPU-bound text sanitization and I/O-bound LLM evaluation workers (Asynq / Redis), backed by a relational ledger in PostgreSQL.
┌────────────────────────────────────────────────────────────────────────┐
│ INCOMING INTERVIEW SOURCE (MyMeet Webhook / Manual DOCX / UI Upload) │
└───────────────────────────────────┬────────────────────────────────────┘
│ HTTP POST (Raw Body + HMAC Signature)
▼
┌────────────────────────────────────────────────────────────────────────┐
│ GO ECHO API GATEWAY (`cmd/api`) │
│ - HMAC-SHA256 signature verification (`hmac.Equal` constant-time check)│
│ - Webhook payload idempotency validation (`Idempotency-Key` / unique) │
│ - Persist raw transcript & initialize interview record (`pending`) │
│ - Enqueue `tasks.TypeEvaluateTranscript` to Redis │
│ - Immediate HTTP 202 Accepted response │
└───────────────────────────────────┬────────────────────────────────────┘
│ Asynchronous Job Queue (Redis)
▼
┌────────────────────────────────────────────────────────────────────────┐
│ ASYNQ TASK WORKER ENGINE (`cmd/worker`) │
│ │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ STEP 1: DETERMINISTIC PRE-PROCESSING & PII SANITIZATION │ │
│ │ - Strip MyMeet timestamps: `[\d2:\d2(:\d2)?]` │ │
│ │ - Speaker normalization & Russian Cyrillic Name Extraction │ │
│ │ - Replace full names with tokens: `[PERSON_1]`, `[PERSON_2]` │ │
│ │ - Eliminate speech parasite words («ну», «типа», «как бы», etc.) │ │
│ │ - Collapse stutter loops & whitespace normalization │ │
│ │ - Structure into clean XML schema: `<dialogue><s name="...">...</s>`│ │
│ │ - Generate `ProcessingReport` (chars, words, % saved, PII map) │ │
│ └──────────────────────────────────┬─────────────────────────────────┘ │
│ │ Sanitized XML Transcript (~35% smaller)
│ ▼
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ STEP 2: LLM COMPETENCY MATCHING (DeepSeek-Chat V4 Flash) │ │
│ │ - Strict JSON Schema enforcement (no markdown fences) │ │
│ │ - Evaluate against vacancy requirements & competency rubric │ │
│ │ - Extract: overall_score (0–10), summary, recommendation │ │
│ │ (`strong_yes` | `yes` | `maybe` | `no` | `strong_no`) │ │
│ │ - Record `tokens_in`, `tokens_out`, latency & exact USD/RUB cost │ │
│ └──────────────────────────────────┬─────────────────────────────────┘ │
└────────────────────────────────────┼───────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────────────┐
│ POSTGRESQL PERSISTENCE & AUDIT TRAIL │
│ - `interviews`: candidate_name, position, vacancy_text, status │
│ - `transcripts`: raw_text, anonymized_text, pii_map (JSONB), report │
│ - `evaluations`: overall_score, recommendation, summary, tokens_in/out │
│ - Strict separation between sensitive raw text and scrubbed models │
└────────────────────────────────────────────────────────────────────────┘ Core Engineering Decisions
1. Deterministic Multi-Pass Anonymizer & Context Compressor
Sending raw meeting transcripts directly to third-party LLMs introduces severe privacy leaks and wastes prompt budget on conversational noise. Rather than relying on non-deterministic, slow LLM-based redaction, HR Screener implements a multi-pass lexical pipeline in pure Go (internal/service/anonymizer.go).
The pipeline executes sequentially in sub-millisecond time:
- Timestamp Scrubbing: Matches patterns like
[0:08],[12:30], and[1:12:30]from MyMeet audio transcript logs and removes them. - Speaker Normalization & PII Replacement: Normalizes speaker names (e.g. handling
Гость (1)$\rightarrow$Guest_1), detects Russian Cyrillic two-word names via boundary regex, and maps them to deterministic identifiers ([PERSON_1],[PERSON_2]). A reverse dictionarypii_mapis saved to PostgreSQL JSONB for internal recruiter auditing. - Linguistic Parasite Elimination: Scans for verbal fillers common in Russian conversational speech («ну», «э», «типа», «как бы», «короче», «значит») using word-boundary matching.
- Stutter Loop Collapsing: Speech-to-text engines often produce duplicate stutter strings (e.g. «мы мы мы начали проект»). The lexer performs adjacent token deduplication, collapsing repeated words into a single instance.
- Structured XML Dialogue Encoding: Converts messy line-breaks into a clean, compact XML dialogue tree:
<dialogue><s name="[PERSON_1]">...</s></dialogue>, which guides LLM attention to speaker transitions with minimal formatting overhead.
// Counted reductions and PII mapping generated deterministically
report := &ProcessingReport{
RawChars: len(rawText),
RawWords: countWords(rawText),
OutputFormat: "xml_dialogue",
StepsApplied: []string{
"Снятие таймкодов MyMeet",
"Нормализация спикеров и замена ФИО на токены [PERSON_N]",
"Удаление слов-паразитов (ну, э, типа, …)",
"Схлопывание повторов слов",
"Структурирование в XML <dialogue>",
},
}
// Resulting in ~35% token reduction before any LLM API call
report.CharsRemovedPct = round2((1 - float64(report.OutputChars)/float64(report.RawChars)) * 100)
report.TokensSavedEstimate = report.EstimatedTokensBefore - report.EstimatedTokensAfter 2. Asynchronous Queue Architecture & Webhook Reliability
Audio transcription services like MyMeet complete processing minutes after a call ends and deliver the payload via webhook. If the receiving HTTP endpoint performs LLM inference synchronously, external webhook dispatchers will time out (typically 5–10s limits), triggering cascading retries and duplicate evaluations.
HR Screener isolates ingestion from processing using Echo v4 and Asynq:
- HMAC-SHA256 Webhook Verification: Every inbound request from MyMeet is validated against a shared secret using
hmac.Equalin constant time to prevent timing attacks. - Idempotent Enqueueing: Webhook events carry unique delivery tokens. If an event is re-sent due to network blips, the database enforces a unique constraint, preventing double-processing.
- Redis Job Dispatch: The HTTP handler writes the raw record, enqueues an asynchronous
tasks.TypeEvaluateTranscripttask to Redis, and responds immediately withHTTP 202 Accepted. - State Machine Tracking: Worker processes advance the interview status through an explicit state machine:
pending$\rightarrow$processing$\rightarrow$evaluating$\rightarrow$completed(orfailedwith recorded error logs).
3. Grounded Two-Stage Inference & JSON Schema Enforcement
A common failure of automated candidate evaluation is hallucination: models inventing technologies or inflating experience that was never mentioned in the interview.
To prevent this, the evaluation pipeline enforces grounded reasoning:
- Vacancy Grounding: The prompt explicitly pairs the structured vacancy criteria (must-have technical stack, years of experience, operational scope) against the sanitized XML dialogue.
- Schema-Constrained Extraction: The model is constrained to return a strict JSON payload using DeepSeek's native JSON Mode:
overall_score: Continuous float (0.00 to 10.00) calibrated against the role requirements.recommendation: Categorical decision enum (strong_yes,yes,maybe,no,strong_no).summary: Comprehensive analytical justification detailing verified technical strengths, identified knowledge gaps, and specific conversational quotes.
- Auditability: Both the parsed JSON result and the raw LLM string response are saved in PostgreSQL JSONB, enabling instant inspection if a recruiter questions a score.
4. Strict Token Accounting & Unit Economics
Operationalizing AI in agency and enterprise contexts requires precise cost transparency. HR Screener embeds a real-time token ledger directly into the evaluation lifecycle (internal/service/llm_pricing.go).
Using DeepSeek-Chat (V4 Flash):
- Pricing: $0.14 per 1M input tokens (cache miss) and $0.28 per 1M output tokens.
- Cost per Screening: A typical 45-minute interview transcript (~5,000–8,000 words), once sanitized and compressed, requires ~2,500 input tokens and produces ~500 output tokens. Total inference cost is approximately $0.00049 to $0.001 per screening (less than 0.10 ₽).
- Economic Visibility: The UI displays exact tokens consumed, estimated tokens saved by pre-processing, and the exact cost in both USD and local currency for every candidate reviewed.
Failure Modes & Defensive Design
Designing high-stakes evaluation infrastructure requires handling noisy inputs and model edge-cases gracefully:
Mitigation: Unique event IDs and constant-time HMAC verification ensure subsequent deliveries are dropped idempotently at the HTTP gateway before reaching Redis.
Mitigation: DeepSeek JSON Mode enforces valid syntax. The worker unmarshals strictly into Go structs; on failure, the raw output is saved and the task retried with backoff.
Mitigation: Asynq exponential backoff retries tasks up to 5 times. An independent database reconciler flags stagnant interviews and alerts engineering.
Mitigation: Multi-tiered heuristic scans check speaker headers, initial greetings, and capitalized Cyrillic pairs, replacing all matched entities before generating the prompt body.