Skip to content

Repository files navigation

Interview Agent

A live, voice-first video interview with an AI interviewer that adapts to each candidate's learning journey.

Built for the "The Interview Agent" problem statement. It reads a candidate's progress through a 31-day AI curriculum and conducts a realistic interview: a speaking avatar, voice answers, adaptive follow-ups, maintained context, structured feedback, and a full integrity report. The engine stays fully deterministic offline as an automatic fallback, and an OpenCode Zen backend (AI) powers the conversation itself: questions derive from each answer, difficulty adapts to the role and performance, over-answers are steered back, and the whole transcript is in context so the interviewer can continue from any point. ElevenLabs voices the interviewer when a key is set; otherwise the browser's built-in voice is used.

Quickstart

cd backend
python -m venv .venv
# Windows: .venv\Scripts\activate   |   macOS/Linux: source .venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --host 0.0.0.0 --port 8000

Open http://localhost:8000, pick a candidate profile, and start the interview.

Run the test suite:

cd backend
pytest -q

Route map

Route Method Purpose
/ GET Video interview frontend
/api/interview POST Interview endpoint (contract below)
/api/interview/state GET Resume state for a session
/api/proctor POST / GET Integrity event log for a session
/api/plan GET Agentic study plan + practice questions
/api/tts POST ElevenLabs text-to-speech proxy (audio/mpeg)
/api/health GET Health check (AI backend status)
/data/candidates.json GET Candidate profiles
/data/curriculum.json GET 31-day curriculum

Live URL: https://<team-fills-after-deploy>.example.com

The video interview

  • Single screen, three transforms: check-in, live call, report. The live call is a centered stage: the interviewer avatar and his words sit in the middle of the screen. The moment he stops speaking, the microphone opens automatically and everything you say is captioned live so you know it is being captured.
  • Aarav's voice: ElevenLabs text-to-speech via POST /api/tts when ELEVENLABS_KEY is set (default voice Adam, free-plan friendly). Falls back to the browser's built-in voice automatically.
  • Conversational engine: with an OpenCode Zen key, every interviewer line is generated from the full conversation, role, and difficulty level. Questions derive from your answers, difficulty rises or falls with performance, and if you over-answer Aarav gently pulls the conversation back. The deterministic engine takes over instantly if AI is unavailable.
  • Check-in: pick a candidate profile; microphone, camera, and voice are checked automatically. You can still join with text only.
  • Live call: "Aarav", a real human 3D interviewer (Ready Player Me GLB with ARKit visemes), asks questions and speaks them aloud (browser text-to-speech). You answer by voice (browser speech-to-text) or text. Captions show every exchange. If the avatar host is unreachable, a built-in fallback avatar renders instead.
  • Avatar configuration: the model URL can be swapped with ?avatar=<glb-url> or by setting localStorage ia_avatar. Default is a Ready Player Me avatar (models.readyplayer.me) with ARKit morph targets.
  • Integrity monitoring: face and eye tracking (eyes closed, looking away, multiple faces), tab switches and window blur, audio silence, and screen share are logged with server timestamps and reported at the end. Every capability fails soft.
  • Resume and failsafe: sessions are snapshotted to disk after every turn. Refresh, close the tab, or lose the connection and you can resume exactly where you left off with one tap. Sends retry with backoff and reconcile against server state so no answer is lost or double-counted.
  • Feedback: structured feedback, an auto-generated study plan, practice questions, and the integrity report.

AI backend (OpenCode Zen)

The interview is powered by an AI backend with a deterministic fallback.

  • Provider: OpenCode Zen (OpenAI-compatible chat/completions), key via OPENCODE_KEY in .env.
  • Model routing by task weight ("how much weightage is needed"):
    • ZEN_FAST_MODEL (default deepseek-v4-flash-free, free) handles the low-weightage per-turn work: answer evaluation and follow-up generation.
    • ZEN_SMART_MODEL (default nemotron-3-ultra-free, free) handles the high-weightage work: final feedback, study plan, and practice questions.
    • Swap in any Zen model via env vars (e.g. deepseek-v4-flash) without code changes.
  • Agentic automations: when an interview completes, the backend automatically generates a prioritized study plan and practice questions for the candidate's weak topics (GET /api/plan). No human intervention.
  • Fallback: every AI call fails soft. If the backend is unreachable, unconfigured, or returns bad output, the engine silently falls back to the deterministic keyword engine and the interview still completes.
  • See .env.example for all settings.

API contract

Single endpoint, no authentication. State is maintained via sessionId.

Start a new interview:

POST /api/interview
{
  "sessionId": "abc-123",
  "candidate": { ...candidate object from candidates.json... }
}
{
  "reply": "Welcome, Sarah. I'm your interviewer for today...",
  "done": false,
  "feedback": null
}

Continue the conversation:

POST /api/interview
{
  "sessionId": "abc-123",
  "message": "An embedding is a vector that captures semantic meaning."
}
{
  "reply": "Good. Now why would we compare embeddings with cosine similarity...",
  "done": false,
  "feedback": null
}

Complete:

{
  "reply": "Thank you, Sarah. The interview is complete. Here is your feedback.",
  "done": true,
  "feedback": {
    "summary": "string",
    "strengths": ["string"],
    "gaps": ["string"],
    "next": ["string"]
  }
}

Errors:

Status Body detail When
400 provide candidate or message Neither field present
400 sessionId required Missing or empty sessionId
404 session not found Message for an unstarted session

Architecture

The request path is POST /api/interview -> SessionStore -> InterviewEngine -> QUESTION_BANK / AI backend, with the response (and final feedback) flowing back the same way.

browser (vanilla JS) --POST /api/interview--> SessionStore --get/create-->
  InterviewEngine (state machine, 8+ questions across 4+ days)
     |-- question text -------------------> QUESTION_BANK (17 curriculum days)
     |-- answer -> AI scoring (fast model) -> fallback keyword scoring
     |          -> follow-up or next question
     |-- done -> AI feedback (smart model) -> fallback deterministic
     `-- GET /api/plan -> AI study plan + practice (smart model) -> fallback

Four parts:

  • InterviewEngine (backend/app/interview.py): the state machine. Builds a personalized question plan from the candidate's completed missions, scores answers (AI first, keyword fallback), and responds with a scaffold follow-up (weak answers) or a deeper probe (medium answers). Produces structured feedback at the end.
  • AI backend (backend/app/ai.py, config.py): OpenCode Zen client with two-tier model routing and soft-fail behavior.
  • SessionStore (backend/app/state.py): in-memory map of sessionId to engine. No persistence, per the problem statement.
  • Frontend (frontend/): mobile-first chat UI with a candidate picker, a feedback panel, and the auto-generated study plan and practice questions. No build step.

How minimum requirements are met

Requirement Where
Conversational technical interview InterviewEngine, multi-turn state machine
Minimum 8 questions _build_plan() guarantees 8+ primaries
At least 4 curriculum days _build_plan() guarantees 4+ days; 17-day question bank
Follow-up questions based on responses respond(): AI scoring or keyword scoring selects scaffold or probe
Maintain conversation context transcript kept per session across turns
Structured feedback at end _build_feedback() / AI generate_feedback()
Required HTTP endpoint POST /api/interview in backend/app/main.py

Verified by backend/tests/test_interview.py, backend/tests/test_api.py, and backend/tests/test_ai.py (offline fakes for the AI layer).

Data

backend/app/data/curriculum.json and backend/app/data/candidates.json are synthetic hackathon data provided for this challenge. They are exact copies of the challenge resources.

Deployment

docker build -t interview-agent .
docker run -p 8000:8000 interview-agent

The Docker image includes backend/app/data (via COPY backend backend), so the API works standalone.

AI usage log

See AI_USAGE_LOG.md.

About

The Interview Agent - a live, voice-first video interview with an AI interviewer (Aarav) that adapts to each candidate's learning journey. Conversational AI, 3D avatar, ElevenLabs voice, integrity proctoring, and auto-resume failsafes.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages