Build a deep research agent as a stateful workflow, not a long chat loop

Researchstandard research10 searches10 pages scrapedMay 22, 2026 at 04:51 PM ET

Research Summary

Build a deep research agent as a stateful workflow, not a long chat loop

Executive takeaways

Recommended architecture

Minimal architecture: typed single-researcher loop

Use this when you want the simplest reliable first version.

Components:

Flow:

1. turn user request into a research brief

2. broad search and extraction

3. note extraction into typed evidence objects

4. gap and contradiction analysis

5. targeted follow-up searches

6. synthesis into claim table and outline

7. draft report

8. verify/repair citations

9. finalize

Advanced architecture: supervisor + parallel evidence workers + verifier

Use this when you need better breadth, lower wall-clock time, or production reliability.

Components:

This is the architecture most likely to feel like “real deep research” instead of fancy browsing.

Custom-harness design

1) Single-agent loop vs planner/executor split

Single-agent loop

Pros:

Cons:

Planner/executor split

Pros:

Cons:

Recommendation: start with one structured controller that plans explicitly, then graduate to supervisor + workers + verifier.

2) One long-running context vs state externalization

Long context is fast to prototype but expensive, fragile, and hard to replay.

Externalized state gives you durability, inspectability, compaction, and reproducibility.

Recommendation: keep only a compact state summary in-context:

Everything else belongs in storage.

3) Serial tools vs parallel workers

Serial is easier to debug but slow and more vulnerable to early-path bias.

Parallel workers improve breadth and latency, but require dedupe, ranking, and merge discipline.

Recommendation: parallelize evidence gathering, not final judgment. Workers should return structured packets, never final prose.

4) Browser-first vs search/extract-first

Search/extract-first is cheaper, faster, more structured, and better for dedupe and citations.

Browser-first is slower and flakier, but essential for dynamic pages, forms, JS-heavy sites, screenshots, and interactive verification.

Recommendation: use a cascade:

1. search API

2. extraction

3. crawl/map if needed

4. browser fallback

5. screenshot/vision only when visual evidence matters

5) Prompt-only control vs typed state / finite-state machine

Prompt-only control is flexible but brittle.

Typed state with a flexible FSM gives you deterministic gates, retries, checkpointing, and testability.

Recommended phases:

Do not let the model jump to final output until evidence gates pass.

Control-loop pseudocode

Typed action schema

Observation schema

Minimal loop sketch

`python

def deep_research(task: str, budget: Budget) -> Report:

state = ResearchState.new(task=task, budget=budget)

checkpoint(state)

state.brief = llm_structured(

prompt="Turn the task into a research brief, subquestions, and source strategy.",

input={"task": task},

schema=ResearchBrief,

)

state.phase = "BROAD_SEARCH"

checkpoint(state)

while not should_stop_research(state):

decision = llm_structured(

prompt=RESEARCH_CONTROLLER_PROMPT,

input={

"brief": state.brief,

"phase": state.phase,

"open_questions": state.open_questions(),

"claims": state.claim_summaries(),

"sources": state.source_summaries(),

"budget": state.budget_status(),

"allowed_actions": allowed_actions(state.phase),

},

schema=NextAction,

)

obs = execute_action_with_retries(decision, state)

append_trace(decision, obs)

if obs.status == "retryable_error":

state.errors.append(obs)

continue

if obs.status == "fatal_error":

state.failures.append(obs)

state = transition_after_failure(state, obs)

checkpoint(state)

continue

state = reduce_observation(state, decision, obs)

state = update_phase(state)

checkpoint(state)

outline = llm_structured(

prompt="Create an outline from supported claims and preserve uncertainty.",

input=state.evidence_brief(),

schema=ReportOutline,

)

draft = llm_structured(

prompt="Write the report using only supported claims and notes.",

input={"outline": outline, "claims": state.supported_claims()},

schema=DraftReport,

)

verified = verify_citations(draft, state)

draft = repair_or_qualify_unsupported_claims(draft, verified)

return finalize_report(draft, verified)

`

Advanced supervisor / worker sketch

`python

def advanced_deep_research(task: str, budget: Budget) -> Report:

state = ResearchState.new(task, budget)

brief = planner.create_brief(task)

state.set_brief(brief)

subquestions = planner.decompose(task)

state.add_questions(subquestions)

checkpoint(state)

breadth_jobs = [

WorkerJob(

question=q,

instructions="Map the source landscape. Prefer primary sources. Return evidence packets only.",

max_searches=5,

max_extracts=8,

)

for q in select_parallelizable_questions(state)

]

packets = run_workers_parallel(breadth_jobs, max_workers=6, timeout_s=180)

for packet in packets:

validate_packet(packet)

state.merge(packet)

dedupe_sources(state)

rank_sources(state)

gaps = analyst.find_gaps(state)

contradictions = analyst.find_contradictions(state)

deep_jobs = [

WorkerJob(

question=t.question,

instructions="Resolve this gap or contradiction with higher-quality sources and exact quotes/spans.",

max_searches=4,

max_extracts=6,

)

for t in prioritize(gaps + contradictions)

]

deep_packets = run_workers_parallel(deep_jobs, max_workers=4, timeout_s=240)

for packet in deep_packets:

validate_packet(packet)

state.merge(packet)

dedupe_sources(state)

rank_sources(state)

update_claim_statuses(state)

if not evidence_gates_pass(state) and state.budget.can_continue():

return continue_research(state)

outline = synthesis_agent.make_outline(state)

draft = synthesis_agent.write_report(outline, state.claims, state.notes)

citation_result = citation_agent.verify_and_attach(draft, state.sources, state.notes)

draft = repair_draft(draft, citation_result)

save_replay_trace(state)

return final_editor.polish(draft)

`

Eval and testing

Measure at least these:

Task success

Factual grounding

Citation correctness

Coverage

Contradiction handling

Robustness / efficiency

Regression suite

Build frozen fixtures for:

For each fixture, store:

Pitfalls

Short implementation roadmap

Phase 1 — MVP

Phase 2 — reliable deep research

Phase 3 — advanced multi-agent system

A strong default build

If I were building this in an OpenClaw-like custom harness, I would start with:

Sources

1
2
3
4
5
6
7
8
9
10