跳转到内容

PydanticAI 的工具调用系统实践

PydanticAI 的工具调用系统实践

学习目标

补充理解:

  • PydanticAI 的两阶段工具处理(验证与执行分离)
  • ToolDefinitionunless_nativewith_native 机制
  • Toolset 的可组合包装模式

前置知识


1. 两阶段工具处理

PydanticAI 将工具处理分为两个独立阶段

阶段 1: validate_tool_call()
├── 解析工具名称
├── 构建 RunContext
├── 运行 before_tool_validate → wrap_tool_validate → after_tool_validate
├── Pydantic schema 验证 + 自定义 args_validator
└── 返回 ValidatedToolCall (成功或失败)
阶段 2: execute_tool_call()
├── 运行 before_tool_execute → wrap_tool_execute → after_tool_execute
├── 调用 toolset.call_tool()
└── 返回工具结果

为什么分离?

  • 允许提前知道工具调用是否有效,支持更精确的事件状态报告
  • 验证失败和调用失败的错误路径不同——前者产生 ToolRetryError,后者通过 on_tool_execute_error hook 处理
  • 支持工具参数的前置校验(在调用工具之前就报错)

2. ToolDefinition 的本地/原生回退

ToolDefinition 包含两个关键字段:

  • unless_native:当指定名称的原生工具被支持时,丢弃此本地工具定义

    • 例:本地 DuckDuckGo 搜索工具的 unless_native='web_search'——当模型支持原生 WebSearchTool 时,本地工具不会出现在 wire format 中
  • with_native:当指定名称的原生工具被支持时,保留此工具定义(作为语料库成员)

    • 例:延迟加载工具的 with_native='tool_search'——当模型支持原生 ToolSearchTool 时,这些工具作为语料库成员保留

这种机制实现了透明回退:用户无需手动判断应该使用原生工具还是本地工具。

3. Toolset 可组合包装模式

AbstractToolset 是所有工具集的基类,通过 WrapperToolset 子类实现横切行为:

AbstractToolset
├── FunctionToolset — Python 函数注册为工具
├── WrapperToolset — 基类,委托给内部 toolset
│ ├── ApprovalRequiredToolset — 工具调用前检查审批
│ ├── DeferredLoadingToolset — 工具延迟加载,按需发现
│ ├── FilteredToolset — 过滤工具子集
│ ├── PrefixedToolset — 工具名称添加前缀
│ ├── RenamedToolset — 工具名称重映射
│ ├── PreparedToolset — 预定义 defer_loading 标志
│ └── ExternalToolset — 外部 MCP 工具
└── CombinedToolset — 组合多个 toolset

优势:通过组合而非继承添加行为——可以在任何工具集上添加审批、延迟加载、前缀命名等功能,无需修改基础类。

4. ToolSelector 灵活匹配

工具过滤支持四种匹配策略:

策略说明
'all'匹配所有工具
Sequence[str]精确名称匹配
dict[str, Any]深度元数据匹配(检查 metadata 字段)
Callable谓词函数 (ctx, tool_def) -> bool

这使得 capability 可以精确控制哪些工具在哪些步骤中可用。