1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
29 small wins to finish your pathNext lesson →
Requests and responses in a run
A run is a conversation between the agent and the model, kept as a list of messages you can print to see exactly what was sent and returned.
Last updated: 28 Sep, 2026 · Pydantic AI 2.51
The run in Agent basics and a first run returned an answer. That answer came from a message exchange, and result.all_messages() hands you the whole list.
Listing the messages of a run
for message in result.all_messages():
print(message.kind)
for part in message.parts:
print(" ", part.part_kind)Output
request
user-prompt
response
textWhat the two messages hold
- A request goes from the agent to the model; a response comes back.
- Each message holds parts. The request has one
user-promptpart, your ticket; the response has onetextpart, the answer. - kind and part_kind are the two labels you read to follow a run.
Watching a run grow with a tool
A tool is a Python function the model can ask the agent to call. Function tools in Pydantic AI covers tools in full; one here is enough to see the message list grow:
@agent.tool_plain
def lookup_order(order_id: str) -> str:
"""Look up an order's status."""
return f"Order {order_id} has shipped."
result = agent.run_sync("Where is my order A-1001?")
for message in result.all_messages():
for part in message.parts:
content = getattr(part, "content", None) or getattr(part, "args", None)
print(f"{message.kind:8} {part.part_kind:12} {content}")
print(result.usage)Output
request user-prompt Where is my order A-1001?
response tool-call {'order_id': 'a'}
request tool-return Order a has shipped.
response text {"lookup_order":"Order a has shipped."}
RunUsage(input_tokens=115, output_tokens=17, requests=2, tool_calls=1)Reading the four messages
- The first response is a
tool-callpart with arguments, not the answer. - The agent runs the tool and sends the result back as a
tool-returnpart in a new request. - The model then writes its
textanswer, so the run made two requests andusageshowstool_calls=1. - The test model called the tool with
order_id='a': it calls every tool once with made-up arguments that fit the types. A real model would readA-1001from the ticket.
Hover or tap a piece to see what it is and which lesson built it.
Trace a run
Pick one to watch it run, step by step.
A request vs a response
| request | response | |
|---|---|---|
| Direction | Agent to model | Model to agent |
| Common parts | user-prompt, tool-return, retry-prompt | text, tool-call |
| Who fills it | Your prompt and tool results | The model |
When you read the message list
- Working out why an agent called a tool, or which arguments it sent.
- Storing a conversation to continue it later, covered in the message-history part.
- Debugging a run that made more requests than you expected.
Watch out. The test model's tool arguments are invented to fit the types, not read from the ticket, so never treat them as a real answer. Its job here is to show the run, not to sort the ticket.
Related
- Previous: Agent basics and a first run
- Next: FunctionModel: write a stand-in model
- Reference: Message history
Try it yourself
- Add a second tool,
refund_order(order_id: str) -> str, and print the parts again. - Print
result.new_messages(). How does it differ fromall_messages()here? - Print
result.response, the model's last response.
Slow is fine. Stopping is the only problem.