Function tools: letting the model look things up
A tool is a Python function the model can call. Pydantic AI builds its description from your type hints and docstring and runs it when the model asks.
ORDERS = {
"A-1001": "shipped on 12 March",
"A-1002": "waiting for stock",
}@agent.tool_plain
def lookup_order(order_id: str) -> str:
"""Look up the status of an order.
Args:
order_id: The order id, like A-1001.
"""
return ORDERS.get(order_id, "not found")result = agent.run_sync("Where is my order A-1001?")
print(result.output)
print(result.usage)@agent.tool_plain adds the function to the agent. The stand-in saw the order id and a tool, called lookup_order, and wrote its answer from the result: two requests and one tool call, the loop from lesson 2 with a real value.
What the model reads about a tool
def peek(messages, info):
tool = info.function_tools[0]
print(tool.name)
print(tool.description)
print(json.dumps(tool.parameters_json_schema, indent=2))
return ModelResponse(parts=[TextPart("ok")])
agent = Agent(FunctionModel(peek))
agent.tool_plain(lookup_order)
agent.run_sync("hi")The name is the function's name. The description is the docstring's first part, and the Args: section became the description of order_id in the schema. Pydantic AI reads Google, NumPy and Sphinx docstring styles. The model chooses tools from these words alone, so they are worth writing well.
agent.tool_plain(lookup_order) without the @ does the same as the decorator, which is handy for functions defined elsewhere. Agent(..., tools=[lookup_order]) is a third way.
Arguments are validated
When the model calls a tool, Pydantic checks the arguments against the type hints before your function runs. A wrong type goes back to the model as a retry prompt, as in lesson 7, so a function with order_id: str can rely on getting a string.
- Add
refund_order(order_id: str, amount: float) -> strand print both tools' schemas. - Ask about
A-1002, then aboutA-5555. - Remove the docstring and print the description again.
Every expert started right here.