Case study — agentic deep-research engine
Agentic Deep-Research Engine
Ask a hard question, get an evidence-graded report, not a chatbot guess. A 13-node state machine plans the research, searches academic and web sources in parallel, weighs each source by quality, and synthesizes a cited report. It runs mostly on local models to keep a deep run at a few dollars instead of a few hundred, checkpoints every step so an 8-hour run can resume, and pauses for my approval before it spends the expensive part.
in practice
What it's actually like to use
I give it a real question, the kind I'd otherwise spend a day researching, and pick a depth: a quick pass (20–30 sources, ~30 min), a standard run, or a deep one (100+ sources, several hours). It drafts a research plan and shows it to me before doing anything expensive. I approve or adjust, it searches academic databases and the web in parallel, then pauses again to let me see the sources it found.
Once I sign off, it extracts evidence, weighs each source by quality, and synthesizes a structured report with real citations and evidence-strength ratings. What comes back isn't a confident-sounding paragraph. It's a report I can trace back to sources, with the weak evidence flagged as weak.
Two things make it practical rather than a demo: it runs the high-volume work on local models so a deep run costs a few dollars instead of a few hundred, and it checkpoints every step, so a run that takes hours can be resumed instead of restarted if something interrupts it.
The practical outcome: a day of research comes back as a cited, evidence-graded report for a few dollars.
architecture
How it's put together
13-node state machine
■ local model ■ parallel ■ gate / interrupt
Plan
- ▸Query analysis
- ▸Query enrichment
- ▸Research planning
- ▸Query expansion — fans one question into search variants
⏸ Plan approval — run pauses for human approval, resumes from checkpoint
Search
- ▸Search orchestrator — parallel: academic (Semantic Scholar · ArXiv · PubMed) · industry · web (Tavily · Exa)
⏸ Source approval — run pauses for human approval, resumes from checkpoint
Analyze
- ▸Evidence extraction — scored + weighted by source quality
- ▸Synthesis — cloud model
- ▸Synthesis coordination — cross-references + conflict resolution
- ▸Gap analysis
↺ quality gate — if synthesis scores below bar, loop back and re-synthesize (bounded retries)
Write
- ▸Section writing — parallel writers
- ▸Final assembly — report + bibliography
A state machine, not a prompt chain
Built on LangGraph as an explicit 13-node graph with typed shared state. Edges are conditional: approval gates route to a pause, a failed quality check loops synthesis back on itself. The control flow is inspectable and deterministic, which is what makes a multi-hour run debuggable.
Hybrid local + cloud routing
High-volume stages (analysis, planning, extraction, section writing) run on local Qwen models via Ollama; only synthesis reaches for Claude. Roughly 90% of the work is local, which is what turns a $100–150 all-cloud report into a $1–15 one. Every stage's model is swappable in config.
Parallel, tiered search
The search orchestrator fans out concurrently across academic sources (Semantic Scholar, ArXiv, PubMed), industry research, and general web (Tavily, Exa), then dedupes and normalizes into one source set.
Evidence weighted by source quality
Sources are scored on journal tier, citation count, author h-index, methodology, sample size, and recency. Synthesis weights high-quality evidence more heavily, and the report surfaces an evidence-strength rating rather than treating a blog and a meta-analysis as equal.
Human-in-the-loop before the spend
At configurable depths the graph interrupts twice, after planning and after search, routing to a pause the UI turns into an approval step. Expensive synthesis never runs on a plan or a source set I haven't seen.
Checkpointed and resumable
State is persisted to a SQLite checkpointer after every node. A deep run spanning hours survives a crash, a restart, or an approval pause. It resumes from the last checkpoint instead of paying for the whole thing again.
engineering highlights
The parts I'm proud of
The graph decides when to stop and when to try again
Two conditional edges carry the logic that makes it trustworthy. One routes to a human-approval pause based on the run's depth. The other is a self-correction loop: after synthesis, a quality gate scores the result, and if it's below bar and retry is worthwhile, the graph loops back and re-synthesizes, bounded so it can't spin forever.
workflow.add_conditional_edges("plan_approval", should_approve_plan, {
"approved": "search_orchestrator",
"needs_approval": END, # interrupt → resume from checkpoint on approve
})
workflow.add_conditional_edges("synthesis_coordination", should_retry_synthesis, {
"retry": "synthesis", # gate failed + retry worthwhile → re-synthesize
"continue": "gap_analysis", # passed, or retries exhausted → move on
})90% local, 10% cloud: the cost lever
The trick is treating "which model runs this step" as configuration, not code. Each node reads its model from config, so the cheap high-volume stages stay on a local Qwen model and only synthesis spends cloud tokens. Same graph, a tenth of the bill, and any stage can be swapped without touching the pipeline.
models:
query_analysis: { provider: ollama, model: qwen3:8b }
evidence_extraction: { provider: ollama, model: qwen3:32b }
synthesis: { provider: anthropic, model: claude-3-5-sonnet }
section_writing: { provider: ollama, model: qwen3:32b }
# swap any stage's model without changing a line of graph codeDepth is a dial, not a rewrite
Citations that actually resolve
stack
Built with
Python · LangGraph (state machine + SQLite checkpointer) · Ollama-hosted Qwen models for local inference · Claude for synthesis · FastAPI backend with streaming progress · Next.js frontend · Tavily / Exa / Semantic Scholar / ArXiv / PubMed for retrieval.
A personal research tool; model choices per stage are configurable and shown as designed.