A client forwards a business card as plain text — name and email buried in three sentences. Right now you read it and copy the fields manually. How many of those do you process each week?
At least ten new leads a week. Reading, copy-pasting, entering into the CRM — maybe 2 minutes each. That is 20 minutes I never bill for that work.
result_type with a Pydantic model extracts structured fields directly. Define the shape, pass it to the agent, call .model_dump() on the result:
from pydantic import BaseModel
class Contact(BaseModel):
name: str
email: strSo the LLM literally has to return this exact shape? It can't give me a wall of text?
Correct. Pass the class to result_type and PydanticAI translates it into a JSON schema the model must satisfy. No free text. No parsing:
def extract_contact(text: str) -> dict:
result = Agent(model, result_type=Contact).run_sync(text)
return result.output.model_dump()result.output is a Contact instance? So I get .name and .email as real Python attributes, not strings I split on a colon?
Exactly. And .model_dump() converts it to a plain dict — ready to store in a Sheet or pass to another function. No parsing, no regex, no split(':').
I just replaced 20 minutes of CRM data entry with five lines of Python.
And every new lead goes through the same function. The model might vary in phrasing — but the shape you get back never changes.
Define the shape you want, pass it as result_type, and the model is constrained to return that exact structure:
from pydantic import BaseModel
class Contact(BaseModel):
name: str
email: str
result = Agent(model, result_type=Contact).run_sync(text)
data = result.output.model_dump()
# {'name': '...', 'email': '...'}PydanticAI translates the Contact class into a JSON schema and adds it to the model's instruction set. The model must fill every required field — if it cannot, it raises a validation error rather than returning a partial result.
result.output is a Contact instance. You access .name and .email directly as Python attributes, or call .model_dump() to get a plain dict for storage or passing downstream.
A client forwards a business card as plain text — name and email buried in three sentences. Right now you read it and copy the fields manually. How many of those do you process each week?
At least ten new leads a week. Reading, copy-pasting, entering into the CRM — maybe 2 minutes each. That is 20 minutes I never bill for that work.
result_type with a Pydantic model extracts structured fields directly. Define the shape, pass it to the agent, call .model_dump() on the result:
from pydantic import BaseModel
class Contact(BaseModel):
name: str
email: strSo the LLM literally has to return this exact shape? It can't give me a wall of text?
Correct. Pass the class to result_type and PydanticAI translates it into a JSON schema the model must satisfy. No free text. No parsing:
def extract_contact(text: str) -> dict:
result = Agent(model, result_type=Contact).run_sync(text)
return result.output.model_dump()result.output is a Contact instance? So I get .name and .email as real Python attributes, not strings I split on a colon?
Exactly. And .model_dump() converts it to a plain dict — ready to store in a Sheet or pass to another function. No parsing, no regex, no split(':').
I just replaced 20 minutes of CRM data entry with five lines of Python.
And every new lead goes through the same function. The model might vary in phrasing — but the shape you get back never changes.
Define the shape you want, pass it as result_type, and the model is constrained to return that exact structure:
from pydantic import BaseModel
class Contact(BaseModel):
name: str
email: str
result = Agent(model, result_type=Contact).run_sync(text)
data = result.output.model_dump()
# {'name': '...', 'email': '...'}PydanticAI translates the Contact class into a JSON schema and adds it to the model's instruction set. The model must fill every required field — if it cannot, it raises a validation error rather than returning a partial result.
result.output is a Contact instance. You access .name and .email directly as Python attributes, or call .model_dump() to get a plain dict for storage or passing downstream.
Create a free account to get started. Paid plans unlock all tracks.