Build an agent that can write and run code with PydanticAI
pydantic-ai
and riza
Python libraries:
coding_agent.py
and run it:
execute_code
tool@agent.tool
is used for tools that need to access to the agent context, such as the message history.
@agent.tool_plain
is used for tools that do not need access to the agent context.
execute_code
tool only requires a single parameter, the code to execute, so we’ll use the @code_agent.tool_plain
decorator:
exec()
method with the code to execute. Optionally, we can pass in http={"allow": [{"host": "*"}]}
to allow code to make HTTP requests.
result
object with three properties: exit_code
, stdout
, and stderr
.
If exit_code
is not 0
, that means an error occurred. We raise a ModelRetry
and pass along the error. PydanticAI will tell the model to try to fix the error. (More on ModelRetry)
If exit_code
is 0, the execution did not throw and error. If stdout
is empty, this often means that the LLM generated code that did not print()
to stdout — a common cause of silent failure when LLMs generate code. We raise a ModelRetry
and pass along a message to encourage the LLM to include print statements in the code.
If exit_code
is 0 and stdout
is not empty, the code executed successfully and we return stdout
.
all_messages.json
file. This is not necessary, but is helpful for debugging and understanding the agent’s behavior which can be hidden behind PydanticAI’s abstractions.
RunResult
object. Two of RunResult
’s properties include:
result.data
: The result of the run.result.all_messages
: The full message history.result.data
and ask the user for a new message.
On subsequent runs, we call run_sync
again with message_history=result.all_messages()
to track a multi-step conversation between the agent and the user.
After each run we log the messages to all_messages.json
.
what time is it?
. Some LLM providers inject the current date into the system prompt — but not the time. To accurately report the current time, the agent will need to run a Python script.
model-structured-response
and tool-return
messages. These are interactions between the model and its tools that PydanticAI handles automatically. You can see in the model-structured-response
that the tool call is execute_code
and the argument is code that uses the datetime
module to get the current time.
The tool-return
message contains the output of the tool call which is passed back to the model, which uses the result to generate a final response. Notice the difference between the format of the tool-return
message and the final model-text-response
message.