Your AI-powered personal interview strategist β tailored to your resume, your target job, and your target company.
InterviewOS is a production-grade, multi-agent AI platform that helps job seekers prepare for interviews with company-specific research, resume-aligned answers, salary intelligence, and a structured 7-day prep plan. It is built on LangGraph.js, Next.js, NestJS, PostgreSQL + pgvector, and Redis + BullMQ.
- β¨ Features
- π§ Why InterviewOS β and Why Not Off-the-Shelf Agent Skills?
- ποΈ Architecture
- π€ LangGraph Agents
- π§ User Flows
- ποΈ Project Structure
- π Getting Started
- βοΈ Configuration
- π§ͺ Testing
- π‘οΈ Agent Safety Rules
- π€ Contribution Guidelines
- π License
- π Resume Parsing β Extract skills, experience, and impact statements from raw resume text.
- π Job Description Analysis β Identify hard skills, soft skills, signals, and disqualifiers.
- π’ Company Research β Live web research with citations (no hallucinated facts).
- π° Salary Insights β Range estimates with confidence levels and source URLs.
- π©Ί Pain Point Detection β Likely company/team pain points the role is meant to solve.
- β Interview Question Bank β Behavioral, technical, and role-specific questions per company and seniority.
- π Answer Coach β STAR-structured sample answers grounded in your resume only.
- ποΈ 7-Day Prep Plan β Prioritized topics and a day-by-day study plan.
- π§΅ Background Job Runs β Long-running multi-agent runs orchestrated via BullMQ with live progress.
- π Per-Agent Debug Logs β Inspect each LangGraph node's status, duration, errors, and outputs.
"The OpenAI Assistants API, Claude Skills, and similar high-level agent abstractions are powerful β but they were the wrong tool for this job."
I deliberately did not build InterviewOS on top of OpenAI Assistants/Agents SDK or Claude Skills (or similar managed agent runtimes). Here is why:
Off-the-shelf agent skills tightly couple your application to one vendor's runtime, tool spec, and pricing tier. InterviewOS is multi-provider by design β OpenAI, Anthropic, Groq, and Google Gemini are interchangeable behind a single LLMProvider interface, with automatic fallback and retry. A single environment variable swaps providers; tomorrow's cheaper model is a one-line change.
Managed "skills" frameworks model the world as a single agent + tools + memory. InterviewOS is a directed graph of ten specialized agents that share typed state, run in a deterministic order, and produce schema-validated outputs at every step. LangGraph.js fits this shape natively; assistant-style frameworks force you to fake it with prompts and hope.
Every agent output is a Zod-validated structure (ResumeProfile, JDAnalysis, CompanyResearchReport, β¦) that flows directly into Prisma. Generic "tool calling" is too loose β we need re-prompting, repair, and per-field confidence scores. Owning the loop lets us enforce the contract.
Every node's start time, duration, status, error, prompt, and raw response are logged and surfaced in the UI's Agent Runs view. Hosted skills hide the runtime β when something fails at 3 AM, you need to see the actual prompt and the raw model response, not a black-box trace.
Resume + company + JD analysis can easily fan out to 20+ LLM calls. We added a Gemini Flash free model path and a MockLLMProvider so contributors can develop and test without burning a single token. Hosted agent runtimes don't let you do this cleanly.
Interview prep runs take minutes. They need a queue, retries, progress reporting, and the ability to resume. BullMQ + Redis + LangGraph gives that out of the box. Assistants APIs are request/response β you'd end up rebuilding a queue on top anyway.
The LLMProvider and SearchProvider abstractions are mockable, so every node has a fast, deterministic unit test. Skills frameworks are hard to mock because the runtime is the framework.
Off-the-shelf agent skills optimize for demos. InterviewOS optimizes for production: typed state, schema-validated outputs, multi-provider failover, queueable runs, observable graphs, and zero vendor lock-in.
flowchart TB
subgraph Client["π₯οΈ Frontend β Next.js 14 (App Router)"]
UI[Dashboard<br/>Job Targets<br/>Agent Runs<br/>Reports]
end
subgraph API["βοΈ Backend API β NestJS"]
AUTH[Auth Module]
JT[Job Targets Module]
AR[Agent Runs Module]
REP[Reports Module]
SET[Settings Module]
end
subgraph Queue["π¦ Job Queue"]
BMQ[(BullMQ Worker)]
RDS[(Redis)]
end
subgraph Agents["π§ LangGraph.js Agent Layer"]
WF[Interview Prep Workflow<br/>StateGraph]
NODES[10 Agent Nodes]
WF --> NODES
end
subgraph Providers["π Provider Abstraction"]
LLM[LLM Provider<br/>OpenAI Β· Anthropic Β· Groq Β· Gemini]
SRCH[Search Provider<br/>Tavily Β· Exa Β· SerpAPI]
end
subgraph Data["πΎ Data Layer"]
PG[(PostgreSQL<br/>+ pgvector)]
PRS[Prisma ORM]
end
UI -->|REST| API
API -->|enqueue run| RDS
RDS --> BMQ
BMQ -->|invoke| WF
NODES -->|generate| LLM
NODES -->|research| SRCH
NODES -->|persist| PRS
PRS --> PG
API -->|read| PRS
Frontend (Next.js)
β
Backend API (NestJS)
β
Agent Layer (LangGraph.js)
β
Providers (LLM + Search)
β
Database (PostgreSQL + pgvector)
β
Queue (Redis + BullMQ)
We chose LangGraph.js over alternatives like LangChain Agents, OpenAI Assistants, AutoGen, and CrewAI because the interview-prep problem is fundamentally a graph-shaped, stateful, multi-step workflow β exactly what LangGraph was designed for.
| Requirement | LangGraph Capability |
|---|---|
| π§΅ Multi-step deterministic flow | First-class StateGraph with explicit nodes and edges |
| π¦ Shared typed state across agents | Annotation-based state schema with reducers |
| π Conditional routing (e.g., skip salary if cached) | addConditionalEdges |
| β³ Long-running background jobs | Pure JS β composes naturally with BullMQ workers |
| π Per-node retries and error isolation | Each node is a function β wrappable with retry/backoff |
| ποΈ Streaming progress to the UI | Reducers + per-node hooks β live progress + node statuses |
| π€ Future human-in-the-loop review | LangGraph supports interrupt/resume natively |
| π§ͺ Testable in isolation | Nodes are plain functions; mock providers in unit tests |
flowchart LR
START([βΆοΈ START]) --> A[πͺͺ intake]
A --> B[π resumeParser]
B --> C[π jdAnalysis]
C --> D[π’ companyResearch]
D --> E[π° salaryResearch]
E --> F[π©Ί painPoint]
F --> G[β interviewQuestion]
G --> H[π answerCoach]
H --> I[ποΈ prepPlan]
I --> J[β
finalize]
J --> END([βΉοΈ END])
| # | Agent | Responsibility |
|---|---|---|
| 1 | IntakeAgent | Validate inputs, normalize the project |
| 2 | ResumeParserAgent | Extract structured profile from resume text |
| 3 | JDAnalysisAgent | Hard/soft skills, signals, disqualifiers |
| 4 | CompanyResearchAgent | Live web research with citations |
| 5 | SalaryResearchAgent | Salary range + confidence + sources |
| 6 | PainPointAgent | Likely team/company pain points |
| 7 | InterviewQuestionAgent | Tailored question bank |
| 8 | AnswerCoachAgent | STAR answers grounded in user's resume |
| 9 | PrepPlanAgent | Priority topics + 7-day plan |
| 10 | FinalizeAgent | Aggregate, persist, report |
- π¦ Workflow Definition β packages/agents/src/workflows/interview-prep.workflow.ts
- 𧬠Shared State (Annotation + Reducers) β packages/agents/src/state/
- π§± Per-Node Implementations β packages/agents/src/nodes/
- π Provider Abstractions β packages/agents/src/providers/
- π Prompts β prompts/agents/ and prompts/system/
Each node is wrapped in createSafeWorkflowNode which adds:
- β±οΈ Per-node timing and status reporting
- π Graceful failure (errors do not crash the run)
- π Live progress percentage (10% β 100%) streamed back to BullMQ
- πͺ΅ Structured logging of inputs, outputs, and errors
sequenceDiagram
actor U as π€ User
participant W as π₯οΈ Web (Next.js)
participant A as βοΈ API (NestJS)
participant DB as πΎ Postgres
participant Q as π¦ BullMQ
participant G as π§ LangGraph Workflow
participant L as π LLM/Search Providers
U->>W: Paste resume + JD + target company
W->>A: POST /job-targets
A->>DB: Create JobTarget (status=pending)
A->>Q: enqueue("interview-prep", jobTargetId)
A-->>W: 201 { jobTargetId }
W-->>U: Redirect β /agent-runs/[id]
Q->>G: invoke(InterviewPrepState)
loop For each of 10 agents
G->>L: prompt + (optional) web search
L-->>G: structured response
G->>DB: persist node output
G->>A: progress + node status
A-->>W: SSE / poll β live UI update
end
G->>DB: status=completed
W-->>U: Final report rendered
flowchart LR
U[π€ User] --> D[π Dashboard]
D --> JT[π Job Target page]
JT --> R1[π Resume Match]
JT --> R2[π’ Company Report]
JT --> R3[π° Salary Insight]
JT --> R4[β Question Bank]
JT --> R5[π Answer Guides]
JT --> R6[ποΈ 7-Day Prep Plan]
JT --> AR[π§ͺ Agent Run Debug View]
AR --> NL[πͺ΅ Per-node logs<br/>prompts Β· responses Β· errors]
flowchart LR
U[π€ User] --> S[βοΈ Settings page]
S --> K1[π OpenAI key]
S --> K2[π Anthropic key]
S --> K3[π Groq key]
S --> K4[π Gemini key β free tier]
S --> SP[π Tavily / Exa / SerpAPI]
S --> SAVE[πΎ Persist in app_settings]
SAVE --> RUN[βΆοΈ Subsequent runs use new providers]
InterviewOS/
βββ apps/
β βββ web/ # Next.js 14 frontend (App Router + Tailwind)
β βββ api/ # NestJS backend (auth, job-targets, agent-runs, reports, settings)
βββ packages/
β βββ agents/ # LangGraph.js workflow + nodes + providers + prompts
β β βββ src/
β β βββ nodes/ # The 10 agent implementations
β β βββ workflows/ # StateGraph definition
β β βββ state/ # Typed shared state + reducers
β β βββ providers/ # LLM + Search abstractions
β β βββ prompts/ # Prompt loader
β β βββ queue/ # BullMQ worker glue
β βββ database/ # Prisma schema + client
β βββ shared/ # Zod schemas + shared types
βββ prompts/
β βββ agents/ # Per-agent prompts (versioned, file-based)
β βββ system/ # System-level prompt fragments
βββ docker/ # Postgres init scripts
βββ scripts/ # Dev scripts (e.g. create-test-user)
βββ docker-compose.yml # Postgres (pgvector) + Redis
βββ pnpm-workspace.yaml
βββ package.json
- π’ Node.js β₯ 20
- π¦ pnpm β₯ 9
- π³ Docker + Docker Compose (for Postgres and Redis)
- π At least one LLM provider key (Gemini Flash has a free tier)
git clone https://github.com/<your-org>/InterviewOS.git
cd InterviewOS
pnpm installdocker compose up -d # Postgres (pgvector) + RedisCopy .env.example files in apps/api, apps/web, and packages/database to .env and fill in:
DATABASE_URL=postgresql://interviewos:interviewos@localhost:5432/interviewos
REDIS_URL=redis://localhost:6379
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
GROQ_API_KEY=gsk_...
GEMINI_API_KEY=...
TAVILY_API_KEY=tvly-...pnpm --filter @interviewos/database prisma migrate dev
pnpm tsx scripts/create-test-user.tspnpm dev # runs web + api + agents in parallel- π Web: http://localhost:3000
- π οΈ API: http://localhost:3001
LLM and search provider keys can be set either via .env (for development) or via the in-app Settings page (persisted in app_settings). Settings page entries override env vars at runtime.
Supported providers out of the box:
| Type | Providers |
|---|---|
| π§ LLM | OpenAI Β· Anthropic Β· Groq Β· Google Gemini (Flash free tier) Β· Mock |
| π Search | Tavily Β· Exa Β· SerpAPI Β· Mock |
A FallbackLLMProvider and RetryingLLMProvider are composed automatically β if the primary provider fails or rate-limits, the next one is tried with exponential backoff.
pnpm test # all packages
pnpm --filter @interviewos/agents test # agent unit testsEvery agent node ships with a unit test that uses MockLLMProvider and MockSearchProvider, so the entire workflow can run end-to-end with zero API calls and zero cost.
These are enforced by prompts, schemas, and tests across every agent:
- π« Never invent company facts.
- π« Never invent salary data.
- π Use citations for all research-based claims.
- π§Ύ Clearly separate facts, estimates, and assumptions.
- π Resume-based answers must use only user-provided resume details.
- β Never promise interview success.
- β Never provide discriminatory advice.
- π Do not store unnecessary sensitive information.
- β Every agent output must follow a Zod schema.
- π Every external research output must include source URLs.
β οΈ If confidence is low, mark it as low confidence.
We welcome contributions! π
- Be respectful. This project follows a standard contributor code of conduct β be kind, be helpful.
- Open an issue first for anything larger than a small fix. It saves wasted work.
- Keep PRs focused. One concern per PR. Small, reviewable diffs > sprawling rewrites.
- Fork the repo and create a feature branch:
git checkout -b feat/your-feature
- Install + bootstrap with
pnpm installanddocker compose up -d. - Run the full pipeline locally before opening a PR:
pnpm typecheck pnpm lint pnpm test pnpm build - Write a test for any new node, provider, or schema. New agent nodes must include at least one unit test using mock providers.
- Update prompts in
/promptsβ never hard-code prompts inside.tsfiles. - Update the Zod schema in
packages/sharedif you change an agent's output shape.
- π¦ TypeScript strict mode, no
any. - πͺ΅ Structured logging only β no stray
console.log. - π Provider interfaces stay abstract β never import
openai/anthropicSDKs directly inside a node. - π§ͺ Dependency injection β nodes receive their providers as parameters, not via global imports.
- π± No hard-coded API keys. Ever. Use
.envandapp_settings. - π§Ή Prefer small, focused commits with conventional-commit-style messages (
feat:,fix:,chore:,docs:).
- Create the prompt in
prompts/agents/<your-agent>.md. - Create the Zod schema in
packages/shared/src/schemas/. - Implement the node in
packages/agents/src/nodes/<your-agent>.node.ts. - Register it in
packages/agents/src/nodes/index.tsand add it to the workflow graph inpackages/agents/src/workflows/interview-prep.workflow.ts. - Write a unit test in
packages/agents/src/nodes/__tests__/. - Update the Prisma schema if the output needs to be persisted.
- Update this README's agent table. βοΈ
-
pnpm typecheckpasses -
pnpm testpasses - New behavior is covered by tests
- Prompts live in
/prompts, schemas inpackages/shared - No new vendor SDK imports outside
packages/agents/src/providers - Docs/README updated where relevant
- PR description explains the why, not just the what
Please include:
- Reproduction steps
- Expected vs. actual behavior
- The affected agent run ID (so we can pull node-level logs)
- Provider used (OpenAI / Anthropic / Groq / Gemini / Mock)
Open a GitHub issue tagged enhancement and describe the user problem first, the proposed solution second.
MIT Β© 2026 Shadab
Built with β€οΈ for every job seeker who deserves a personal interview coach β not a generic chatbot.