The last three lessons returned strings. What happens when you need two fields out of one snippet — a fact and its source?
Ask the model to return them separated by a pipe or a newline, then split? That feels fragile — it breaks the first time the model uses a different delimiter.
Exactly fragile. The cleaner move is result_type=FactModel — a Pydantic class tells the agent the shape, and result.output is a typed object with named fields already parsed:
from pydantic import BaseModel
class Fact(BaseModel):
fact: str
source: str
result = Agent(model, result_type=Fact).run_sync(snippet)
print(result.output.fact, result.output.source)You never parse. Pydantic validates the JSON the model returns and hands you an object.
So result.output isn't a string anymore — it's a Fact instance with attributes I can reach directly?
Exactly. The type of result.output always matches whatever class you pass as result_type. No regex, no split, no defensive code. Wire it into the search pattern:
def extract_fact_from_search(query: str) -> dict:
results = search(query, count=5)
snippet = results[0]["snippet"]
agent = Agent(model, result_type=Fact, system_prompt="Extract the key fact and its source.")
extracted = agent.run_sync(snippet).output
return extracted.model_dump().model_dump() turns the Pydantic instance into a plain dict — easy to JSON-serialize or hand to another function.
Why .model_dump() instead of returning the Fact object directly?
The function's return type is dict, and most callers downstream want plain data, not a class instance. .model_dump() gives you {"fact": "...", "source": "..."} — trivially serializable, trivially testable, trivially passable to another agent as a prompt. The Pydantic object is an intermediate; the dict is the output.
And the system_prompt tells the agent what to extract, while the result_type tells it how to shape the answer — two levers, one call.
Exactly. Prompt for intent, type for structure. Both together give you a reliable two-field extraction from any snippet — the beginning of a real research pipeline.
TL;DR: result_type=FactModel returns a validated Pydantic instance; .model_dump() hands you a plain dict.
class Fact(BaseModel) — declare the fields and typesresult_type=Fact — Pydantic validates the model's JSON outputsystem_prompt — tells the agent what to extract| Step | Code |
|---|---|
| Retrieve | search(query, count=5) |
| Extract | Agent(model, result_type=Fact).run_sync(snippet) |
| Serialize | .output.model_dump() |
Every extraction function for the rest of the track has this shape.
The last three lessons returned strings. What happens when you need two fields out of one snippet — a fact and its source?
Ask the model to return them separated by a pipe or a newline, then split? That feels fragile — it breaks the first time the model uses a different delimiter.
Exactly fragile. The cleaner move is result_type=FactModel — a Pydantic class tells the agent the shape, and result.output is a typed object with named fields already parsed:
from pydantic import BaseModel
class Fact(BaseModel):
fact: str
source: str
result = Agent(model, result_type=Fact).run_sync(snippet)
print(result.output.fact, result.output.source)You never parse. Pydantic validates the JSON the model returns and hands you an object.
So result.output isn't a string anymore — it's a Fact instance with attributes I can reach directly?
Exactly. The type of result.output always matches whatever class you pass as result_type. No regex, no split, no defensive code. Wire it into the search pattern:
def extract_fact_from_search(query: str) -> dict:
results = search(query, count=5)
snippet = results[0]["snippet"]
agent = Agent(model, result_type=Fact, system_prompt="Extract the key fact and its source.")
extracted = agent.run_sync(snippet).output
return extracted.model_dump().model_dump() turns the Pydantic instance into a plain dict — easy to JSON-serialize or hand to another function.
Why .model_dump() instead of returning the Fact object directly?
The function's return type is dict, and most callers downstream want plain data, not a class instance. .model_dump() gives you {"fact": "...", "source": "..."} — trivially serializable, trivially testable, trivially passable to another agent as a prompt. The Pydantic object is an intermediate; the dict is the output.
And the system_prompt tells the agent what to extract, while the result_type tells it how to shape the answer — two levers, one call.
Exactly. Prompt for intent, type for structure. Both together give you a reliable two-field extraction from any snippet — the beginning of a real research pipeline.
TL;DR: result_type=FactModel returns a validated Pydantic instance; .model_dump() hands you a plain dict.
class Fact(BaseModel) — declare the fields and typesresult_type=Fact — Pydantic validates the model's JSON outputsystem_prompt — tells the agent what to extract| Step | Code |
|---|---|
| Retrieve | search(query, count=5) |
| Extract | Agent(model, result_type=Fact).run_sync(snippet) |
| Serialize | .output.model_dump() |
Every extraction function for the rest of the track has this shape.
Create a free account to get started. Paid plans unlock all tracks.