- Notifications
You must be signed in to change notification settings - Fork1k
Description
Description
While working on human-in-the-loop (HITL) tool calling (see#1817), I've been implementing my own agent on top of the lower-level pydantic-ai modules (and thus not using theagent
module). This means dealing with tool calling more directly.
In a HITL scenario you often want to send the human a representation of what actions the agentwould take. A "diff" of a sort. So given a tool function of type:
ToolParams=ParamSpec('ToolParams',default=...)"""Retrieval function param spec."""ToolFuncContext=Callable[Concatenate[RunContext[AgentDepsT],ToolParams],Any]"""Atoolfunctionthattakes`RunContext`asthefirstargument.
We want another function with the same arguments but a different return type:
classDiff:"""A human-readable diff showing what the tool would do."""ToolFuncDiffContext=Callable[Concatenate[RunContext[AgentDepsT],ToolParams],Diff]
So when the agent wants to call a tool that needs a HITL, we take the arguments we would have passed to the actual tool and instead pass them to this diff function, producing a diff. We then send the diff to the human and, if they approve it, call the actual tool.
To do this well (i.e. without duplicating much of what pydantic-ai does) we need access to a function that, given a tool call (e.g.ToolCallPart
) returned by the model, transform it into Python function parameters, so we can use them to call functions other than the tool itself.
In general, I see pydantic-ai as providing (at least) 3 core piece of functionality for tools:
- Given a Python function, create a schema that can be given to the model to describe the available tool call.
- Given a tool call from the model, convert the model response into function arguments that can be passed to the function.
- Given the function's return value, convert it into a format that can be given back to the model.
All these pieces already exist in pydantic-ai, but they aren't (completely) exposed. I think exposing them would make pydantic-ai more usable as a library, which we can build our own agents upon (ifagent
doesn't work for our use case).
I think this is in fact a strength of the whole pydantic ecosystem: to be able to use pieces to build your own things without having to use a all-or-nothing framework.
References
No response