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_search for preprints
  • pubmed_search for biomedical
  • web_extract to get full text
  • cite_lookup to 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:
    1. RCTs on sleep onset latency and efficiency
    2. Biological mechanisms (GABA, melatonin, cortisol)
    3. Optimal forms (citrate, glycinate, oxide) and doses
    4. Safety and side effects
    5. 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.