PydanticAI 的 Capabilities 中间件系统
PydanticAI 的 Capabilities 中间件系统
学习目标
补充理解:
AbstractCapability的 20+ 生命周期钩子体系CombinedCapability的拓扑排序与CapabilityOrdering约束- 内置 Capabilities(Thinking、WebSearch、MCP 等)的具体实现
前置知识
- Hook 系统 — 通用钩子模式
1. 生命周期钩子体系
PydanticAI 的 AbstractCapability 提供四个层级的钩子:
| 层级 | 钩子 | 用途 |
|---|---|---|
| Run | wrap_run(handler) | 包装整个 agent run |
before_run() / after_run() | run 生命周期 | |
| Node | wrap_node_run(node, handler) | 包装单个节点执行 |
before_node_run() / after_node_run() | 节点生命周期 | |
| Model | wrap_model_request(handler) | 包装模型调用 |
before/after_model_request() | 模型请求前后 | |
| Tool | wrap_tool_validate(handler) | 包装工具验证 |
wrap_tool_execute(handler) | 包装工具执行 | |
| Output | wrap_output_validate(handler) | 包装输出验证 |
wrap_output_process(handler) | 包装输出处理 |
与通用 Hook 系统的差异:
- 通用 Hook 系统通常只提供 before/after 两个点
- PydanticAI 的
wrap_*模式提供了环绕(around) 能力——可以在调用handler()前后执行任意逻辑,甚至可以跳过handler()短路
2. CombinedCapability 与拓扑排序
CombinedCapability 将多个 capability 组合为一个,并按 CapabilityOrdering 约束进行拓扑排序:
CapabilityOrdering: position: int # 显式位置 wraps: CapabilityRef # 在某 capability 之后 wrapped_by: CapabilityRef # 在某 capability 之前 requires: CapabilityRef # 依赖某 capability为什么需要拓扑排序?
- 某些 capability 必须在其他 capability 之前执行(如 Instrumentation 必须在最外层包装)
wraps约束确保正确的嵌套顺序——A.wraps = B意味着 A 的wrap_*钩子会包裹 BCombinedCapability会扁平化嵌套的CombinedCapability——避免深层嵌套导致的性能问题
3. 内置 Capabilities 分类
PydanticAI 内置的 Capabilities 按功能分为:
| 类别 | Capabilities |
|---|---|
| Provider 自适应工具 | Thinking、WebSearch、WebFetch、ImageGeneration、XSearch、MCP、ToolSearch |
| 工具处理 | PrepareTools、PrepareOutputTools、PrefixTools、IncludeToolReturnSchemas、SetToolMetadata |
| 生命周期 | Instrumentation、Hooks、ProcessHistory、ProcessEventStream、ThreadExecutor |
| Human-in-the-Loop | HandleDeferredToolCalls |
| 原生工具注册 | NativeTool |
Provider 自适应工具模式:NativeOrLocalTool 基类同时注册原生工具和本地回退工具集。当模型支持原生工具时,本地回退自动被 unless_native 机制丢弃。
4. Hooks Capability 的装饰器模式
Hooks capability 提供装饰器风格的钩子注册,作为 AbstractCapability 子类化的替代方案:
from pydantic_ai.capabilities import Hooks
hooks = Hooks()
@hooks.before_model_requestasync def log_request(ctx): print(f"Model request: {ctx.prompt}")支持超时(timeout)和按工具过滤(tool_selector),适合不需要完整 capability 类的简单钩子。