{"slug": "a-research-agent-three-ways", "title": "A research agent, three ways", "summary": "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.", "body_md": "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.\n\nThe basic thing you can build\n\nTen lines gets you a working research agent. Pydantic AI's core ships [ WebSearch](https://ai.pydantic.dev/api/capabilities/), a capability that turns on the model's own native web search — Anthropic, OpenAI, Google, and Groq all have one:\n\n``` python\nimport logfire\nfrom pydantic import BaseModel\nfrom pydantic_ai import Agent\nfrom pydantic_ai.capabilities import WebSearch\n\nlogfire.configure()\nlogfire.instrument_pydantic_ai()\n\nclass Finding(BaseModel):\n    claim: str\n    source_url: str\n\nclass Report(BaseModel):\n    summary: str\n    findings: list[Finding]\n\nagent = Agent(\n    'anthropic:claude-opus-4-7',\n    output_type=Report,\n    capabilities=[WebSearch(allowed_domains=['docs.aws.amazon.com'])],\n)\n\nresult = agent.run_sync(\n    'What HA options does RDS for Postgres offer today?'\n)\n```\n\nFor a factual lookup with a small blast radius, this is enough, and `output_type=Report`\n\nmeans 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.\n\nThe fastest path to production\n\nA 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.\n\n[Exa](https://exa.ai)'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](https://exa.ai/docs/reference/search-api-guide): an `instant`\n\nmode for the quick lookups, `auto`\n\nfor balanced retrieval, and `deep`\n\n/`deep-reasoning`\n\nfor 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.\"](https://exa.ai/) If the agent that ships production code trusts Exa for its web-facing brain, the research agent you assemble today can too.\n\nHand the whole thing to Exa\n\nThe 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](https://exa.ai/docs/reference/agent-api-guide), and the harness ships it as a one-line wrapper:\n\n``` python\nimport logfire\nfrom pydantic_ai import Agent\nfrom pydantic_ai_harness.exa import ExaAgent\n\nlogfire.configure()\nlogfire.instrument_pydantic_ai()\n\nagent = Agent(\n    'anthropic:claude-opus-4-7',\n    capabilities=[ExaAgent()],\n)\n\nresult = agent.run_sync(\n    'Which managed Postgres should we migrate to? Compare pricing, HA, '\n    'and migration path from RDS across the main contenders. Cite every claim.'\n)\n```\n\n`ExaAgent`\n\nadds one tool, `exa_agent`\n\n. 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`\n\n, so a second question about the shortlisted vendors doesn't restart from zero. Want structured output? Pass a Pydantic model as `output_schema=Report`\n\nand the completed run's result is validated on the way back; a mismatch surfaces as a retry instead of silently landing.\n\nThis 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.\n\nCompose when the pillars pay off\n\nSometimes 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](https://github.com/pydantic/pydantic-ai-harness/tree/main/pydantic_ai_harness/exa) —\n\n`web_search`\n\nreturns each hit with its most relevant excerpts (so surveys stay cheap) and `get_page`\n\nreads 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:\n\n``` python\nimport logfire\nfrom pydantic import BaseModel\nfrom pydantic_ai import Agent\nfrom pydantic_ai_harness import CodeMode\nfrom pydantic_ai_harness.exa import ExaSearch\nfrom pydantic_ai_backends import ConsoleCapability             # filesystem\nfrom pydantic_ai_summarization import ContextManagerCapability # compaction\nfrom pydantic_ai_todo import TodoCapability                    # planning\n\nlogfire.configure()\nlogfire.instrument_pydantic_ai()\n\nclass Finding(BaseModel):\n    claim: str\n    source_url: str\n\nclass Report(BaseModel):\n    summary: str\n    findings: list[Finding]\n\nagent = Agent(\n    'anthropic:claude-opus-4-7',\n    output_type=Report,\n    capabilities=[\n        CodeMode(),\n        TodoCapability(),\n        ConsoleCapability(),\n        ContextManagerCapability(max_tokens=180_000),\n        ExaSearch(\n            include_deep_search=True,\n            include_domains=['docs.aws.amazon.com', 'planetscale.com', 'neon.tech'],\n        ),\n    ],\n)\n```\n\n`include_deep_search=True`\n\nexposes a third tool, `deep_search`\n\n, 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`\n\nnarrows 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](https://pydantic.dev/logfire) and one trace shows the plan, each sub-search, and the synthesis — which is how you debug a researcher.\n\nWhy this matters\n\nBecause 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.\"\n\nIt also composes forward. [When agents build agents](/articles/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.\n\nAgents 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.\n\nGetting started\n\n`pydantic-ai-slim`\n\nalready carries `WebSearch`\n\n, so the native version costs nothing more than the model provider you're already paying. `uv add \"pydantic-ai-harness[exa]\"`\n\nputs both `ExaAgent`\n\nand `ExaSearch`\n\non the shelf, and an [Exa API key](https://dashboard.exa.ai) in `EXA_API_KEY`\n\nconnects them — the free credits cover all three agents in this post. Layer in `[codemode]`\n\nand the community packages (`pydantic-ai-backend`\n\n, `summarization-pydantic-ai`\n\n, `pydantic-ai-todo`\n\n) when you're ready to compose the deeper build. The [capability matrix](https://github.com/pydantic/pydantic-ai-harness#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.\n\nA 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.", "url": "https://wpnews.pro/news/a-research-agent-three-ways", "canonical_source": "https://pydantic.dev/articles/harness-exa", "published_at": "2026-07-21 09:00:00+00:00", "updated_at": "2026-07-21 17:20:11.637357+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "ai-products", "ai-research", "developer-tools"], "entities": ["Pydantic AI", "Exa", "Anthropic", "OpenAI", "Google", "Groq", "Cursor", "Devin"], "alternates": {"html": "https://wpnews.pro/news/a-research-agent-three-ways", "markdown": "https://wpnews.pro/news/a-research-agent-three-ways.md", "text": "https://wpnews.pro/news/a-research-agent-three-ways.txt", "jsonld": "https://wpnews.pro/news/a-research-agent-three-ways.jsonld"}}