A multi-agent deep research system built on LangGraph. Give it a question; it plans an investigation, runs several researcher agents in parallel across the web and social media, criticises its own evidence, and writes a cited report.
It is built to behave like a careful researcher rather than a search wrapper: it follows links to primary sources, notices when its sources disagree, and tells you what it could not establish.
"Is Dune: Part Three any good, and what do people actually think of it?"
-> objective, time horizon and success criteria extracted
-> 5 sub-questions planned across web, news and social channels
-> 4 researcher agents dispatched in parallel
-> 61 sources retrieved, 38 pages scraped, Reddit and YouTube sampled
-> reviewer: "needs_more - critic consensus covered, audience split not quantified"
-> 2 follow-up sub-questions dispatched
-> reviewer: "sufficient, 84/100"
-> 1,900-word report, every claim carrying a verified citation
Parallel researchers, not one agent doing everything. A single ReAct agent working through a large question serially is slow and its context degrades as unrelated results pile up. The planner splits the brief into independent sub-questions, each researched by its own agent with its own message history. They run concurrently and their findings are merged afterwards.
Citations are mechanically verified, not trusted. Every tool result registers what it
retrieved in a run-scoped evidence store and hands the model a stable identifier such as
[S14]. The model cites those identifiers; a verification pass then checks each one
against the store and strips anything invented. The reference list is built from the
store, not from the model's output, so every link in a finished report points at a page
the system actually fetched.
An adversarial review gate. After research, a reviewer grades the evidence against the brief's success criteria and against statistics the findings alone would hide, such as how many "independent" sources resolve to one domain. If it finds actionable gaps, a targeted follow-up pass runs. Three separate conditions stop the loop so it cannot spin.
Honest degradation. Where a channel is unavailable or running on a weaker fallback,
the tool result is labelled DEGRADED SOURCE, the agent is instructed not to make volume
claims from it, and the limitation reaches the report. A run never quietly pretends to
have social coverage it does not have.
START
|
scope normalise the request into an explicit brief
|
plan decompose into independent sub-questions
|
dispatch --Send--> researcher x N parallel ReAct agents
| |
| aggregate merge, compress, dedupe
| |
| review adversarial quality gate
| / \
+--- replan <-- needs_more sufficient --> synthesise
|
verify_citations
|
END
Full walkthrough in docs/CODE_FLOW.md; design rationale and the
alternatives that were rejected in docs/ARCHITECTURE.md.
git clone <your-repo> && cd Deep-Research-Agent
uv sync # or: pip install -r requirements.txt
uv run playwright install chromium
cp .env.example .env # add one model key and one search keyMinimum viable .env:
OPENAI_API_KEY=sk-...
TAVILY_API_KEY=tvly-...Then:
# One-off, streamed to the terminal
uv run deep-research "how are people receiving the new Dune film"
# Or edit the variables at the top of run_research.py and run it
uv run python run_research.py
# Or serve it
uv run uvicorn deep_research.api:app --port 8000Reports are written to reports/ as Markdown. Full setup, including every optional
channel, is in docs/SETUP.md.
| Channel | Requirement | Without it |
|---|---|---|
| Web search | Tavily, Serper, Brave or SerpAPI key | Falls back to DuckDuckGo: keyless, rate limited, weaker |
| Page scraping | playwright install chromium |
Static-only; JavaScript sites fail |
| Free script app credentials | Public JSON, no comment trees, easily rate limited | |
| X (Twitter) | X_BEARER_TOKEN, pay-per-use |
Site-restricted web search, no engagement metrics |
| YouTube | Free Data API v3 key | Channel unavailable |
| Opt-in, real account, ban risk | Web-search fallback, no post counts |
Check what is actually live at any time:
curl localhost:8000/healthA note on the social landscape, since it changed recently and the code reflects that:
X discontinued its free tier in February 2026 and moved new developers to pay-per-use
credits, with full-archive search now Enterprise-only. Instagram's Graph API has no
public hashtag-search endpoint at all. Reddit is the strongest free opinion source and
the system is tuned to lean on it. See docs/TOOLS.md for the detail,
including the ban risk on Instagram's private API.
Everything lives in .env and is grouped into five typed settings classes in
src/deep_research/config/settings.py. The knobs that change behaviour most:
MAX_RESEARCH_ITERATIONS=3 # review -> replan cycles
MAX_PARALLEL_RESEARCHERS=4 # concurrent researcher agents
MAX_TOOL_CALLS_PER_RESEARCHER=14 # budget per sub-questionModels are set per role, so a strong model plans, reviews and writes while a fast one does the high-volume tool calling:
PLANNER_MODEL=google:gemini-2.5-pro
RESEARCHER_MODEL=openai:gpt-4.1-mini
WRITER_MODEL=google:gemini-2.5-proAny provider init_chat_model supports works: openai:, anthropic:, google:, groq:.
A typical run is 3 to 8 minutes and 150k to 400k tokens. The researcher model dominates
token spend, which is why it is configured separately. To run cheaper, lower
MAX_PARALLEL_RESEARCHERS and MAX_TOOL_CALLS_PER_RESEARCHER; to run better, raise
MAX_RESEARCH_ITERATIONS.
uv run pytest # 55 tests, no network or API keys requiredThey cover the deterministic machinery: URL canonicalisation, source deduplication, citation verification, state reducers, fan-out and review routing. These are the places where a silent bug produces a report that looks right and is wrong.
src/deep_research/
config/ settings, prompts, logging
evidence/ the citation-integrity layer
graph/
builder.py graph assembly and Send fan-out
nodes/ scope, planner, researcher, aggregator, reviewer, writer
tools/
web_search.py provider chain: Tavily, Serper, Brave, SerpAPI, DuckDuckGo
scraper.py static fast path, Playwright escalation, robots, cache
social/ reddit, x_twitter, youtube, instagram
definitions.py the LangChain tools the agents see
runtime.py run and stream entry points
api.py FastAPI with SSE
cli.py
docs/ architecture, code flow, setup, tools
tests/
- Sequential-dependency questions ("find X, then use X to find Y") are handled poorly, because sub-questions are researched in parallel and cannot see each other's results. The follow-up iteration partially compensates.
- Paywalled sources are not bypassed. They are recorded as inaccessible.
- Non-English sources are retrieved but not translated.
- Instagram is effectively an honest stub unless you accept the private-API ban risk.
MIT.