summarize_and_classify gave you two separate values — a summary string and a label string — stored in two separate variables. What happens when you need three or four pieces of information from the same ticket at once?
I'd have to run three agents back-to-back and stitch the outputs together manually. That's a lot of variables to keep track of — and a lot of extra model calls.
One model call, all three fields, one typed object back. You define a Pydantic model that looks exactly like the form your triage team fills out:
from pydantic import BaseModel
class Ticket(BaseModel):
category: str
urgency: str
one_line_summary: strSo the LLM literally has to return this exact shape? It can't give me a paragraph or skip a field?
That's the contract. PydanticAI validates the output before your code ever sees it — if the model tries to return prose instead of a structured object, the run raises an error rather than silently passing you garbage. Three fields in, three fields out, typed:
def triage_ticket(text: str) -> dict:
agent = Agent(model, result_type=Ticket)
result = agent.run_sync(text)
return result.output.model_dump()model_dump() converts the Pydantic object to a plain dict — so I can pass it straight to a spreadsheet, a webhook, or the next agent in the chain.
Or directly into your ticketing system's API. The object is just a dict at that point — no parsing, no regex, no "extract the urgency from line 3 of the reply." Your triage queue becomes a function that takes a ticket and returns a typed row.
This is the model I should have had for the last year of reading support escalations. Category, urgency, summary — that's the form we fill out manually in Jira every morning.
And now you've automated the fill-in step. Next, you'll take a batch of tickets and run this against all of them in a list comprehension — the same pattern scales to 200 tickets without changing a line of the triage logic.
A single-field result_type=Literal[...] constrains one value. A Pydantic model constrains an entire record — every field, every type, in one call.
Define the model above the entry-point function. Fields are plain type annotations. The agent fills them all or the run fails — no silent partial results.
.model_dump() converts the Pydantic instance to a plain dict: ready for an API payload, a spreadsheet, or the next agent's prompt.
Three separate agents mean three round-trips and three chances for inconsistency. One result_type=Ticket call extracts all fields in a single pass — cheaper, faster, and coherent because the model sees the full ticket for all fields at once.
Do not add Optional fields unless data is genuinely absent sometimes. Required fields force the model to commit — optional fields invite skipping.
summarize_and_classify gave you two separate values — a summary string and a label string — stored in two separate variables. What happens when you need three or four pieces of information from the same ticket at once?
I'd have to run three agents back-to-back and stitch the outputs together manually. That's a lot of variables to keep track of — and a lot of extra model calls.
One model call, all three fields, one typed object back. You define a Pydantic model that looks exactly like the form your triage team fills out:
from pydantic import BaseModel
class Ticket(BaseModel):
category: str
urgency: str
one_line_summary: strSo the LLM literally has to return this exact shape? It can't give me a paragraph or skip a field?
That's the contract. PydanticAI validates the output before your code ever sees it — if the model tries to return prose instead of a structured object, the run raises an error rather than silently passing you garbage. Three fields in, three fields out, typed:
def triage_ticket(text: str) -> dict:
agent = Agent(model, result_type=Ticket)
result = agent.run_sync(text)
return result.output.model_dump()model_dump() converts the Pydantic object to a plain dict — so I can pass it straight to a spreadsheet, a webhook, or the next agent in the chain.
Or directly into your ticketing system's API. The object is just a dict at that point — no parsing, no regex, no "extract the urgency from line 3 of the reply." Your triage queue becomes a function that takes a ticket and returns a typed row.
This is the model I should have had for the last year of reading support escalations. Category, urgency, summary — that's the form we fill out manually in Jira every morning.
And now you've automated the fill-in step. Next, you'll take a batch of tickets and run this against all of them in a list comprehension — the same pattern scales to 200 tickets without changing a line of the triage logic.
A single-field result_type=Literal[...] constrains one value. A Pydantic model constrains an entire record — every field, every type, in one call.
Define the model above the entry-point function. Fields are plain type annotations. The agent fills them all or the run fails — no silent partial results.
.model_dump() converts the Pydantic instance to a plain dict: ready for an API payload, a spreadsheet, or the next agent's prompt.
Three separate agents mean three round-trips and three chances for inconsistency. One result_type=Ticket call extracts all fields in a single pass — cheaper, faster, and coherent because the model sees the full ticket for all fields at once.
Do not add Optional fields unless data is genuinely absent sometimes. Required fields force the model to commit — optional fields invite skipping.
Create a free account to get started. Paid plans unlock all tracks.