search_the_web handed you a paragraph of sourced prose — accurate, up to date, but unstructured. When you need to feed that finding into a report, a spreadsheet, or a follow-up agent, what do you actually want back?
A specific fact and the source it came from. Not the whole paragraph — just the one claim I can put in a slide with a citation attached.
Two agents, two jobs. The first one searches and returns narrative. The second one reads that narrative and extracts a single structured fact — using result_type so the output is a typed object, not prose:
from pydantic import BaseModel
class Fact(BaseModel):
fact: str
source: strWait — the second agent gets the first agent's output as its prompt? It's not hitting the web again, it's just reading the narrative?
Exactly. The search agent owns retrieval. The extract agent owns structure. You chain them by passing narrative as the prompt to the second run:
def research_and_extract(topic: str) -> dict:
search_agent = Agent(model)
extract_agent = Agent(model, result_type=Fact)
narrative = search_agent.run_sync(topic).output
fact = extract_agent.run_sync(narrative).output
return fact.model_dump()So the LLM fills out both fields — fact and source — or the run fails. I get a guaranteed structured dict, not a paragraph I have to parse myself.
And the source field keeps the extract honest. An AI that has to name its source is far less likely to hallucinate than one writing free prose. You've added a citation requirement with one extra field.
I've been manually copying findings into a table for competitive briefings. This replaces that step — search, extract, structured row, done.
One caution: perplexity/sonar is live search — the narrative it returns describes real, current data. The extract agent reads only that narrative, so the quality of your fact depends on the quality of what search returns. Garbage in, garbage out still applies — even with two agents.
Two agents, two responsibilities.
Agent 1 — retrieval: Agent(model) with ai-search routes through perplexity/sonar, which has live web access. Pass the topic as the prompt. Get back narrative prose with embedded citations.
Agent 2 — extraction: Agent(model, result_type=Fact) reads the narrative and fills a two-field Pydantic model. The run validates the output before your code sees it.
fact.model_dump() returns {"fact": "...", "source": "..."} — a plain dict ready for downstream use.
Asking a single search agent to return structured output forces the model to search and format simultaneously. Splitting the jobs keeps each agent's prompt simple and the extract step reusable — you can point it at any narrative, not just one from this search.
search_the_web handed you a paragraph of sourced prose — accurate, up to date, but unstructured. When you need to feed that finding into a report, a spreadsheet, or a follow-up agent, what do you actually want back?
A specific fact and the source it came from. Not the whole paragraph — just the one claim I can put in a slide with a citation attached.
Two agents, two jobs. The first one searches and returns narrative. The second one reads that narrative and extracts a single structured fact — using result_type so the output is a typed object, not prose:
from pydantic import BaseModel
class Fact(BaseModel):
fact: str
source: strWait — the second agent gets the first agent's output as its prompt? It's not hitting the web again, it's just reading the narrative?
Exactly. The search agent owns retrieval. The extract agent owns structure. You chain them by passing narrative as the prompt to the second run:
def research_and_extract(topic: str) -> dict:
search_agent = Agent(model)
extract_agent = Agent(model, result_type=Fact)
narrative = search_agent.run_sync(topic).output
fact = extract_agent.run_sync(narrative).output
return fact.model_dump()So the LLM fills out both fields — fact and source — or the run fails. I get a guaranteed structured dict, not a paragraph I have to parse myself.
And the source field keeps the extract honest. An AI that has to name its source is far less likely to hallucinate than one writing free prose. You've added a citation requirement with one extra field.
I've been manually copying findings into a table for competitive briefings. This replaces that step — search, extract, structured row, done.
One caution: perplexity/sonar is live search — the narrative it returns describes real, current data. The extract agent reads only that narrative, so the quality of your fact depends on the quality of what search returns. Garbage in, garbage out still applies — even with two agents.
Two agents, two responsibilities.
Agent 1 — retrieval: Agent(model) with ai-search routes through perplexity/sonar, which has live web access. Pass the topic as the prompt. Get back narrative prose with embedded citations.
Agent 2 — extraction: Agent(model, result_type=Fact) reads the narrative and fills a two-field Pydantic model. The run validates the output before your code sees it.
fact.model_dump() returns {"fact": "...", "source": "..."} — a plain dict ready for downstream use.
Asking a single search agent to return structured output forces the model to search and format simultaneously. Splitting the jobs keeps each agent's prompt simple and the extract step reusable — you can point it at any narrative, not just one from this search.
Create a free account to get started. Paid plans unlock all tracks.