Using Agents for Deep Research: A Step-by-Step Guide
Deep research — the kind that involves multiple sources, cross-referencing, and synthesis — is time-consuming for humans. AI agents can automate much of the grunt work while leaving critical thinking to you. Here’s how to build and use a research agent effectively.
The Research Agent Workflow
A research agent follows a loop: clarify → gather → analyze → synthesize → report. Each phase can be augmented with tools and checks.
graph TD
A[Clarify Question] --> B[Gather Sources]
B --> C[Analyze & Extract]
C --> D[Synthesize Findings]
D --> E[Generate Report]
E --> F[Review & Refine]
F --> A
Phase 1: Clarify the Research Question
Vague questions yield vague answers. Start by forcing specificity.
def clarify_research_question(initial_query: str) -> ResearchSpec:
# Use LLM to refine and structure the query
prompt = f"""
Refine this research question into a clear, answerable spec:
"{initial_query}"
Output JSON with:
- main_question: The core question
- sub_questions: 3-5 specific aspects to investigate
- scope: What's IN and OUT of scope
- desired_sources: Types of sources needed (e.g., peer-reviewed, industry reports)
- depth: How deep (surface, moderate, comprehensive)
"""
response = llm.call(prompt, format="json")
return ResearchSpec(**response)
Example:
Initial: “Is magnesium good for sleep?”
Refined:
- Main: “What is the evidence for magnesium supplementation improving sleep quality in adults with insomnia?”
- Sub: 1) RCTs on magnesium and sleep latency, 2) Mechanisms of action, 3) Optimal dosage/form, 4) Safety profile, 5) Comparison to alternatives
Phase 2: Gather Sources Systematically
Humans miss sources. Agents can run parallel searches and deduplicate.
class ResearchAgent:
async def gather_sources(self, spec: ResearchSpec) -> List[Source]:
# Run searches in parallel
search_tasks = [
self.web_search(f"{spec.main_question} {sq}", max_results=10)
for sq in spec.sub_questions
]
raw_results = await asyncio.gather(*search_tasks)
# Extract and deduplicate
all_sources = []
seen_urls = set()
for results in raw_results:
for src in results:
if src.url not in seen_urls:
seen_urls.add(src.url)
all_sources.append(src)
# Filter by source quality
return [s for s in all_sources if self.is_high_quality(s, spec.desired_sources)]
Tools to use:
web_search(Google, Bing, or scholarly)arxiv_searchfor preprintspubmed_searchfor biomedicalweb_extractto get full textcite_lookupto verify references
Phase 3: Analyze and Extract Key Points
Don’t just collect — understand. Use the agent to pull claims, evidence, and limitations.
async def analyze_source(self, source: Source) -> Analysis:
# Extract full text if needed
if not source.full_text:
source.full_text = await self.web_extract(source.url)
# Prompt for structured extraction
prompt = f"""
Analyze this source for the research question: "{spec.main_question}"
Source: {source.title}
Text: {source.full_text[:4000]} # Truncate for context
Extract:
- claims: List of factual claims made
- evidence: What data supports each claim
- limitations: Study limitations noted by authors
- contradictions: How this conflicts with other sources
- relevance: How directly this answers sub-questions
Output as JSON.
"""
return await llm.call(prompt, format="json")
Run this in parallel for all sources. Then aggregate.
Phase 4: Synthesize Findings
Now combine the analyses into a coherent view.
async def synthesize(self, analyses: List[Analysis]) -> Synthesis:
# Group by sub-question
grouped = defaultdict(list)
for analysis in analyses:
for sq in analysis.relevance: # Assume we tagged relevance
grouped[sq].append(analysis)
synthesis = {}
for sq, sq_analyses in grouped.items():
prompt = f"""
Synthesize findings for sub-question: "{sq}"
Analyses:
{json.dumps([a.dict() for a in sq_analyses], indent=2)}
Provide:
- consensus: What most sources agree on
- conflicts: Where sources disagree and why
- evidence_strength: Strong/moderate/weak based on study quality
- gaps: What we still don't know
- practical_takeaway: Actionable conclusion if any
"""
synthesis[sq] = await llm.call(prompt)
return synthesis
Phase 5: Generate the Report
Finally, produce a readable report with citations.
def generate_report(self, spec: ResearchSpec, synthesis: Synthesis) -> str:
sections = []
# Introduction
sections.append(f"# {spec.main_question}\n")
sections.append(f"**Scope:** {spec.scope}\n")
# Each sub-question as a section
for sq, syn in synthesis.items():
sections.append(f"## {sq}\n")
sections.append(f"{syn}\n")
sections.append("") # Blank line
# Overall conclusions
sections.append("## Overall Conclusions\n")
sections.append(self.overall_conclusions(synthesis))
# References (would be built from sources)
sections.append("## References\n")
sections.append(self.format_references(self.sources))
return "\n".join(sections)
Critical Design Choices
1. Human-in-the-Loop at Key Points
- After clarification: You approve the refined spec
- After gathering: You review the source list for relevance/bias
- After synthesis: You challenge the agent’s interpretations
This isn’t full automation — it’s augmentation.
2. Bias Mitigation
Agents can amplify search bias. Countermeasures:
- Force diverse search terms (e.g., include “criticism of”, “limitations”, “alternative view”)
- Explicitly search for contradictory evidence
- Weight sources by methodological rigor, not just popularity
3. Handling Uncertainty
The agent should express uncertainty:
- “Sources conflict on X; 60% show effect, 40% show none.”
- “Evidence strength is moderate due to small sample sizes.”
- “No long-term studies exceed 2 years.”
4. Citation Discipline
Every factual claim must trace to a source. The agent should:
- Insert inline citations as it writes
- Flag any claim without a source
- Generate a bibliography automatically
Tools You Need
| Tool | Purpose | Example Implementation |
|---|---|---|
| Web Search | Find sources | SerpAPI, Google Custom Search, or DuckDuckGo |
| Web Extract | Get full text | Newspaper3k, readability-lxml, or fallback to scraping |
| ArXiv Search | Preprints | arXiv API |
| PubMed Search | Biomedical | NCBI E-utils API |
| DOI Lookup | Verify references | CrossRef API |
| PDF Parser | Extract text from papers | PyMuPDF or pdfplumber |
| Claim Extractor | Pull facts from text | Custom prompt + LLM |
| Contradiction Detector | Find conflicts | NLI model or LLM comparison |
| Citation Formatter | Generate references | CiteProc or manual formatting |
A Complete Example: Researching Magnesium for Sleep
Let’s walk through a real run.
Initial query: “Does magnesium help you sleep better?”
After clarification:
- Main: “What is the evidence for magnesium supplementation improving sleep quality in adults with insomnia?”
- Sub-questions:
- RCTs on sleep onset latency and efficiency
- Biological mechanisms (GABA, melatonin, cortisol)
- Optimal forms (citrate, glycinate, oxide) and doses
- Safety and side effects
- Comparison to melatonin, z-drugs, CBT-I
Gathering:
- 12 RCTs found via PubMed and Google Scholar
- 5 review papers on mechanisms
- 3 dosage comparison studies
Analysis extracted:
- Claim: “Magnesium reduces sleep onset latency by 17 minutes on average” (from 3 RCTs)
- Evidence: PSG-measured SOL in elderly patients
- Limitation: Most studies <8 weeks, small N (<50)
- Contradiction: One study showed no effect in young adults
Synthesis:
- Consensus: Magnesium helps sleep onset, especially in deficient/elderly populations
- Conflict: Effects on sleep duration are inconsistent
- Evidence strength: Moderate for sleep onset, weak for duration
- Gap: No long-term (>6 month) RCTs in general adult population
- Practical: Try 300-400mg magnesium glycinate 30min before bed; monitor for GI effects
Report: 2000-word structured review with citations, tables of studies, and clear takeaways.
The Takeaway
Agents excel at the systematic, repeatable parts of research. They don’t replace your judgment — they free it up for higher-level thinking.
Use a research agent when you need:
- Comprehensive source coverage
- Systematic extraction of claims and evidence
- Help synthesizing contradictory findings
- A first draft to challenge and refine
The best research agent is the one that makes you smarter, not the one that makes you lazy.
Start small: automate your next literature review. Then iterate on the workflow. Soon you’ll wonder how you ever researched without one.