cd /news/ai-agents/a-research-agent-three-ways Β· home β€Ί topics β€Ί ai-agents β€Ί article
[ARTICLE Β· art-67337] src=pydantic.dev β†— pub= topic=ai-agents verified=true sentiment=Β· neutral

A research agent, three ways

Pydantic AI's research agent can be built in three ways: a ten-line agent using native web search for simple lookups, a production-grade agent powered by Exa's semantic search API that returns page contents in one call, and a hosted Exa Agent API that runs multi-step research passes with structured outputs. Exa's search API already powers Cursor and Devin, making it the retrieval backbone for agents that need deep research capabilities.

read6 min views1 publishedJul 21, 2026
A research agent, three ways
Image: Pydantic (auto-discovered)

Midweek, with an agent that writes code and gets it reviewed, someone asks a different kind of question: "before we commit to the migration, can it research the vendor options?" And the reflex says no, wrong agent. The reflex is thinking in agents. This week is about thinking in parts.

The basic thing you can build

Ten lines gets you a working research agent. Pydantic AI's core ships WebSearch, a capability that turns on the model's own native web search β€” Anthropic, OpenAI, Google, and Groq all have one:

import logfire
from pydantic import BaseModel
from pydantic_ai import Agent
from pydantic_ai.capabilities import WebSearch

logfire.configure()
logfire.instrument_pydantic_ai()

class Finding(BaseModel):
    claim: str
    source_url: str

class Report(BaseModel):
    summary: str
    findings: list[Finding]

agent = Agent(
    'anthropic:claude-opus-4-7',
    output_type=Report,
    capabilities=[WebSearch(allowed_domains=['docs.aws.amazon.com'])],
)

result = agent.run_sync(
    'What HA options does RDS for Postgres offer today?'
)

For a factual lookup with a small blast radius, this is enough, and output_type=Report

means the answer already comes back as validated data. But the ceiling is low: native web search returns links and snippets scored by the provider's index and doesn't give you a page-contents step, deep-research mode, or the kind of retrieval control a research agent needs when the question earns more than a lookup. Which is when the fastest path to a production-grade research agent stops passing through the shelf.

The fastest path to production

A research agent is only as good as its retrieval, and the retrieval most agents get, scrape ten blue links and hope, is the bottleneck that makes "deep research" shallow. The agents that make their living reading the web have all quietly settled on the same eyes.

Exa's tagline is the literal spec: "web search, built for AI agents." Semantic search over the live web with page contents returned in the same call, no scraping step, and a range they describe as low-latency to deep research in one API: an instant

mode for the quick lookups, auto

for balanced retrieval, and deep

/deep-reasoning

for questions that earn a multi-step pass with structured outputs. Coding agents live on that range: Exa powers Cursor's search across docs and repos, and Cognition co-founder Walden Yan is on record that "Exa powers all parts of Devin." If the agent that ships production code trusts Exa for its web-facing brain, the research agent you assemble today can too.

Hand the whole thing to Exa

The superpower version is one capability. When research is the whole question β€” plan, sub-searches, page reads, synthesis, citations β€” Exa runs it as a hosted service, the Exa Agent API, and the harness ships it as a one-line wrapper:

import logfire
from pydantic_ai import Agent
from pydantic_ai_harness.exa import ExaAgent

logfire.configure()
logfire.instrument_pydantic_ai()

agent = Agent(
    'anthropic:claude-opus-4-7',
    capabilities=[ExaAgent()],
)

result = agent.run_sync(
    'Which managed Postgres should we migrate to? Compare pricing, HA, '
    'and migration path from RDS across the main contenders. Cite every claim.'
)

ExaAgent

adds one tool, exa_agent

. The parent hands over the question, the Exa Agent API runs a multi-step research pass on their infrastructure (up to an hour if the question earns it), and the tool call defers until the run finishes with a cited answer. Follow-ups keep the run's context via previous_run_id

, so a second question about the shortlisted vendors doesn't restart from zero. Want structured output? Pass a Pydantic model as output_schema=Report

and the completed run's result is validated on the way back; a mismatch surfaces as a retry instead of silently landing.

This is the parts-list punchline for research: the parent agent gets a researcher on staff, context isolation included, and the hundred thousand tokens spent reading sources land on Exa's side of the API call. That's the sub-agent pillar of "deep agents" delivered as a capability import β€” no assembly required.

Compose when the pillars pay off

Sometimes you do need to build: sources you allowlist, a citation bar the model has to clear, notes that accumulate across runs, a research process that fits how your team actually works. Then the harness's shelf earns its keep. Same Exa retrieval, exposed as individual tools this time via ExaSearch β€”

web_search

returns each hit with its most relevant excerpts (so surveys stay cheap) and get_page

reads a chosen URL in full, both with the research strategy that turns two tools into one shipped inside the capability β€” plus the harness parts that give a long run its spine:

import logfire
from pydantic import BaseModel
from pydantic_ai import Agent
from pydantic_ai_harness import CodeMode
from pydantic_ai_harness.exa import ExaSearch
from pydantic_ai_backends import ConsoleCapability             # filesystem
from pydantic_ai_summarization import ContextManagerCapability # compaction
from pydantic_ai_todo import TodoCapability                    # planning

logfire.configure()
logfire.instrument_pydantic_ai()

class Finding(BaseModel):
    claim: str
    source_url: str

class Report(BaseModel):
    summary: str
    findings: list[Finding]

agent = Agent(
    'anthropic:claude-opus-4-7',
    output_type=Report,
    capabilities=[
        CodeMode(),
        TodoCapability(),
        ConsoleCapability(),
        ContextManagerCapability(max_tokens=180_000),
        ExaSearch(
            include_deep_search=True,
            include_domains=['docs.aws.amazon.com', 'planetscale.com', 'neon.tech'],
        ),
    ],
)

include_deep_search=True

exposes a third tool, deep_search

, Exa's multi-step deep mode as a single call for the questions that deserve it, and the capability's guidance grows one sentence to teach the model when to escalate. include_domains

narrows retrieval to the vendors' own docs β€” a rule the model can't reword its way around. The todo list keeps a three-hour run pointed at the question, files hold the notes and the draft between compactions, and every claim in the report traces to a URL Exa actually returned. Instrument the whole thing with Logfire and one trace shows the plan, each sub-search, and the synthesis β€” which is how you debug a researcher.

Why this matters

Because it's the week's argument in miniature, told in three lines of imports. Start with what ships. Level up when the ceiling gets in the way. Compose when the shape of the problem needs to be yours. Same agent from Monday all the way down, picking up one capability at each step, always answering to the same public API. That's what a standard library is: the point where "build a research agent" stops meaning "start a project" and starts meaning "compose an afternoon."

It also composes forward. When agents build agents ends on loops that run, learn, and re-arm; a research loop that keeps its notes in files and its plan in todos re-arms with everything it learned yesterday, and Exa is the part that keeps its eyes fresh β€” live search each cycle, not a crawl that ages. The report you commission next is the worst one it will ever write.

Agents Week opened with an argument about herds: you'll run more agents than you can hand-raise. The reason that's survivable is the recomposition you just watched: agents assembled from shared, swappable, inspectable parts are cattle by construction. The bespoke agent, the one built from scratch around a private framework, was always the pet.

Getting started

pydantic-ai-slim

already carries WebSearch

, so the native version costs nothing more than the model provider you're already paying. uv add "pydantic-ai-harness[exa]"

puts both ExaAgent

and ExaSearch

on the shelf, and an Exa API key in EXA_API_KEY

connects them β€” the free credits cover all three agents in this post. Layer in [codemode]

and the community packages (pydantic-ai-backend

, summarization-pydantic-ai

, pydantic-ai-todo

) when you're ready to compose the deeper build. The capability matrix tracks the rest of the parts list, first-party and community, and every row is an issue or PR where your vote steers what gets built next.

A research agent for the cost of an import β€” a lookup, a hosted researcher, or an afternoon spent composing the loop you actually want. The rest of the week is the same move on harder ground: real compute Thursday, then a cloud the agent is allowed to break Friday.

── more in #ai-agents 4 stories Β· sorted by recency
── more on @pydantic ai 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/a-research-agent-thr…] indexed:0 read:6min 2026-07-21 Β· β€”