Fletcher Keeley

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.

13
graph nodes
~10x
cheaper (local/cloud split)
100+
sources on a deep run
2
human-approval gates

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 expansionfans one question into search variants

Plan approval — run pauses for human approval, resumes from checkpoint

Search

  • Search orchestratorparallel: academic (Semantic Scholar · ArXiv · PubMed) · industry · web (Tavily · Exa)

Source approval — run pauses for human approval, resumes from checkpoint

Analyze

  • Evidence extractionscored + weighted by source quality
  • Synthesiscloud model
  • Synthesis coordinationcross-references + conflict resolution
  • Gap analysis

↺ quality gate — if synthesis scores below bar, loop back and re-synthesize (bounded retries)

Write

  • Section writingparallel writers
  • Final assemblyreport + bibliography
high-volume stages → local Qwen; synthesis → Claude every node checkpointed — resume a multi-hour run
01

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.

02

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.

03

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.

04

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.

05

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.

06

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.

Fig. — conditional edges (LangGraph)
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.

Fig. — per-stage model config
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 code

Depth is a dial, not a rewrite

One config drives everything that scales with effort: source targets, which approval gates fire, retry budgets. "Quick" skips the approvals and caps sources for a 30-minute answer; "deep" turns on both human gates and reaches 100+ sources over several hours. Same 13 nodes, different dial.

Citations that actually resolve

Evidence extraction keeps each finding tied to its source through synthesis, so the final report's citations trace back to real, quality-scored documents, with a bibliography assembled from the same source records, not re-invented at the end.

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.