HomeAboutProjectsCertificates

© 2026 Suraj Kiran Airi. Built with Next.js & Tailwind.

EMAILRESUMELINKEDIN
ALL PROJECTS
AIFULL STACK

SHERLOCK — REAL-TIME CANDIDATE IDENTIFIER

Jul 2026 - Jul 2026 • WEB APP / API

Architecture diagram

Architecture diagram

Real-time system identifying the actual interview candidate via Bayesian belief across 9 parallel live signals.

THE PROBLEM

Modern hiring platforms can't verify that the person on screen during a video interview is actually the invited candidate — they may join under a pseudonym, a device name, or a mismatched display name. A one-shot name match is trivially defeated; the system needs to reason continuously as new signals arrive throughout the call.

THE RESULT

Built a complete three-service system (simulator, Next.js dashboard, Python inference engine) that runs 9 independent signal extractors in parallel — 7 rule-based and 2 LLM-backed — and maintains a live Bayesian belief over candidate identity, surfacing a real-time verdict with confidence and a per-signal reasoning trail on the dashboard.

HOW IT WORKS

Sherlock treats candidate identification as a continuous probabilistic inference problem rather than a name lookup. The system is split into three services that communicate over WebSocket: a Python scenario simulator, a Next.js relay dashboard, and a Python FastAPI belief engine.

Key technical achievements include:

  • Belief Engine: Maintains two independent log-odds tracks per participant — logit_candidate (converted via softmax across all participants, so probabilities are zero-sum) and logit_not_candidate (converted via per-participant sigmoid, allowing multiple people to simultaneously be eliminated) — updated on every incoming event.
  • Per-Identifier Decay: Each identifier's contribution is stored as its own bucket with a configurable decay_half_life. Decayed values are summed lazily at read time (raw * 0.5^(elapsed / half_life)) so stale evidence fades without a background timer.
  • 9 Pluggable Identifiers: name_match, email_identity, host_organizer (rule-based, one-time); speaking_share, qa_pattern, screenshare_heuristic, silent_observer (rule-based, continuous); llm_name_role and llm_transcript_role (LLM-backed via LiteLLM, fail-open — the engine stays fully functional if the LLM provider is unreachable).
  • DAG-Ordered Processor Registry: Identifiers are registered in a dependency DAG (Kahn's algorithm); downstream processors are guaranteed to run only after their dependencies have produced output into a shared Feature Cache.
  • 5-State Detection Machine: exploring → searching → likely_candidate → stable_candidate → lost_candidate, with a warmup gate (≥ 20 s elapsed + ≥ 3 evidence pieces) to prevent premature mis-identification, and hysteresis (separate entry/exit thresholds + a streak requirement) to prevent flapping.
  • Scenario Simulator: Compiles YAML scenario files into a timed event stream (participant_join, transcript_segment, screenshare_start, etc.) over WebSocket at configurable playback speed; includes 9 authored scenarios covering edge cases like mid-call renames, silent observer farms, and webcam-off sessions.
  • Next.js Dashboard: Relays the event stream to the engine, displays live transcript, participant tiles, and an engine panel with real-time confidence scores and per-identifier reasoning lines; uses Zustand for session state and Zod for wire-format validation.

TECH STACK

Python 3.12+FastAPILiteLLMuvicornwebsocketspydanticpyyamlNext.js 16React 19TypeScriptTailwind CSS v4ZustandZodshadcn/uiTurborepopnpm

TAGS

Bayesian InferenceLog-Odds AccumulationExponential DecaySoftmaxWebSocketEvent-Driven ArchitectureDAG Dependency OrderingState MachineLLM IntegrationPluggable IdentifiersScenario SimulationYAML Scenario AuthoringZod Schema ValidationZustandServer-Sent Events

LINKS

GitHub
GitHub ↗