pydantic-ai 原生工具(Native Tools)架构指南:从 AbstractNativeTool 到 Provider 自适应能力
2026/9/14 2:22:59 网站建设 项目流程

pydantic-ai 原生工具(Native Tools)架构指南:从 AbstractNativeTool 到 Provider 自适应能力

【免费下载链接】pydantic-aiHow Python does AI. Agents, realtime voice, image generation, embeddings. Every model, every interface, typed end to end.项目地址: https://gitcode.com/GitHub_Trending/py/pydantic-ai

pydantic-ai 的原生工具(Native Tools)是一类直接由模型提供方(Anthropic、OpenAI、Google、xAI 等)在服务端执行的工具,无需本地实现函数。本文以 pydantic_ai_slim/pydantic_ai/native_tools/AGENTS.md 中的开发指南为主线,结合 native_tools 包 与 capabilities 能力层 的源码实现,系统讲解原生工具的种类、字段语义、注册机制,以及"何时把功能做成能力(Capability)而非裸工具"这一核心设计决策。读完本文,你将理解WebSearchToolXSearchToolImageGenerationTool等九个内置原生工具的内部结构与参数细节,掌握本地回退与子代理回退两种跨提供者适配模式,并了解为 pydantic-ai 新增一个原生工具时需要遵守的字段命名、校验与文档规范。

一、什么是原生工具:AbstractNativeTool基类

原生工具在 pydantic-ai 中被建模为AbstractNativeTool抽象基类,定义于 native_tools/init.py。它是一个kw_onlydataclass,所有具体工具类(如WebSearchTool)都继承它。基类提供三个核心成员:

  • kind: str = 'unknown_native_tool':原生工具标识符,作为判别器(discriminator)区分所有工具类型。每个具体工具都重写它,例如'web_search''x_search''code_execution''image_generation''mcp_server'等。
  • optional: bool = False:标记该实例是"尽力而为的升级"还是"硬性要求"。当为True时,如果模型不支持该原生工具且没有本地回退,实例会被静默丢弃而非报错;为False(默认)时,模型无法兑现该原生工具就会显式报错——用户明确要求了它,就应该大声失败而不是悄悄替换成不同行为。
  • unique_idlabel属性:前者给出唯一标识(当同一类原生工具可能被传入多个实例时,子类应重写以区分彼此,如MCPServerTool返回'mcp_server:<id>');后者提供 UI 展示用的人类可读标签。

自动注册机制

源码中,NATIVE_TOOL_TYPES是一个以kind字符串为键、工具类为值的注册表。它并非手工维护,而是通过__init_subclass__在类定义时自动填充(见 native_tools/init.py)。同时,基类实现了__get_pydantic_core_schema__,把AbstractNativeTool本身变成一个kind判别的 pydantic 联合类型,因此可以直接用于配置文件的校验。

包底部还导出了两个对用户有意义的集合:

  • SUPPORTED_NATIVE_TOOLS:所有原生工具类型的集合(frozenset),由注册表派生。
  • NATIVE_TOOLS_REQUIRING_CONFIG:需要额外配置才能使用的工具集合,包含FileSearchToolMCPServerToolMemoryToolAdvisorTool以及内部工具ToolSearchTool(见 native_tools/init.py)。这类工具不能只靠kind就可用,必须由用户显式提供 ID、URL 或模型名等参数。

二、核心设计决策:跨提供者功能优先做成 Capability

native_tools/AGENTS.md的第一条准则即全包的灵魂:凡是代表跨提供者(cross-provider)特性的原生工具,都应该有对应的能力(Capability),继承capabilities/中的NativeOrLocalTool。因为能力(Capability)才是用户向 agent 启用"提供者自适应"工具功能的首要公开 API。

能力层的设计动机记录在 capabilities/AGENTS.md:当行为涉及指令、设置、工具、原生工具、包装器、生命周期钩子或事件/历史处理时,优先用 Capability 而不是给Agent构造函数新增 kwarg。能力是"可组合的横切行为容器"。

NativeOrLocalTool:原生工具 + 本地回退的配对

基类NativeOrLocalTool定义于 capabilities/native_or_local.py,其工作方式一目了然:

  • 当模型支持该原生工具:移除本地回退,使用原生工具;
  • 当模型不支持该原生工具:移除原生工具,保留本地工具。

它暴露两个关键配置字段:

  • nativeTrue(默认,使用子类默认的原生工具实例)、False(禁用原生工具,始终走本地工具)、一个AbstractNativeTool实例(使用该具体配置)、或一个可调用对象(每次运行通过RunContext动态创建原生工具,返回None表示省略)。
  • localNone(自动检测本地回退)、True(选择默认本地回退)、False(禁用本地回退,只用原生工具)、命名策略字符串(如'duckduckgo')、一个Tool/AbstractToolset实例,或一个裸可调用对象(自动包装成Tool)。

__post_init__负责把声明解析为具体对象,并做三类快速失败检查(native_or_local.py):

  1. native=Falselocal=False同时成立 → 报UserError
  2. native=False但约束字段要求原生工具(如allowed_domains)→ 报UserError
  3. native=False却没有显式本地回退 → 报UserError(否则会静默变成一个"什么都不做"的空能力)。

内置的WebSearchWebFetchImageGeneration能力都是该基类的子类,分别通过重写_default_native()_default_local()_resolve_local_strategy()_requires_native()等钩子定义各自行为。

本地回退(Local Fallback):WebSearchWebFetch

文档明确举出的第一类回退是本地函数工具回退:在提供者没有原生支持时,能力自动回退到本地 function tool。

以 capabilities/web_search.py 的WebSearch为例:默认情况下它使用模型的原生 web search,在原生不支持的模型上抛UserError。传入local='duckduckgo'(或local=True)即可启用本地 DuckDuckGo 回退,但需要安装可选依赖组:

pip install "pydantic-ai-slim[duckduckgo]"

_resolve_local_strategy的实现(web_search.py)展示了命名策略的解析方式:local=True归一化为'duckduckgo',然后延迟导入pydantic_ai.common_tools.duckduckgo.duckduckgo_search_tool;如果依赖缺失,会抛出带安装提示的UserErrorlocal=同样接受任何可调用对象、ToolAbstractToolset作为自定义回退。

值得注意的细节是_requires_native()(web_search.py):当设置了blocked_domainsallowed_domainsmax_usesexternal_web_access=False时,能力强制要求原生工具——因为这些约束只有原生工具能兑现,此时本地回退被抑制,模型不支持就会报错,从而防止约束被静默违反。类似地,capabilities/web_fetch.py 的WebFetch通过local=True启用本地抓取回退(需要pip install "pydantic-ai-slim[web-fetch]"),其allowed_domains/blocked_domains在原生不可用时由本地强制。

本地回退的实现细节体现在get_toolset()(native_or_local.py):当原生工具存在时,本地工具集被包装进PreparedToolset,其 prepare 函数给本地工具定义打上unless_native=<native 工具的 unique_id>标记——这正是"模型支持原生工具时移除本地工具"的运行时机制。

子代理回退(Subagent Fallback):XSearchImageGeneration

文档指出的第二类回退是子代理回退:通过fallback_subagent_model把任务委托给一个运行其他提供者模型的子代理。这类能力包括ImageGenerationXSearch

以 capabilities/x_search.py 的XSearch为例:在 xAI 模型上直接使用原生 X 搜索,无需额外配置;在非 xAI 模型上,必须显式设置fallback_subagent_model为一个支持XSearchTool原生工具的 xAI 模型(例如'xai:grok-4.3'),否则使用XSearch会直接报错——没有默认的子代理模型

该能力还展示了互斥校验的实践:__post_init__检查fallback_subagent_modellocal不能同时指定(x_search.py),因为二者都是非 xAI 模型下的回退路径,同时设置会让其中一个被静默忽略。_default_local()在设置fallback_subagent_model后,从pydantic_ai.common_tools.x_search构建x_search_tool(model=..., native_tool=...),子代理内部依然运行原生XSearchTool,因此allowed_x_handles等句柄约束在两条路径上都得到遵守(_requires_native()在设置了fallback_subagent_model时返回False,理由正是子代理也运行原生工具)。

ImageGeneration能力(capabilities/image_generation.py)更进一步,提供三种互斥的回退实现,同时指定多个会抛UserError

  1. local:接受ImageGenerator(走直接图像生成 API)、自定义Tool、工具集或可调用对象;
  2. fallback_image_model:接受ImageGenerationModel'provider:model'字符串(如'openai:gpt-image-2'),直接调用图像生成 API 而不是跑第二个 agent;
  3. fallback_subagent_model:运行一个具备图像生成能力的会话模型子代理(如'openai-responses:gpt-5.4''google:gemini-3-pro-image'),图像来自该模型的原生ImageGenerationTool

源码中还区分了两类几何/输出设置的去向:dimensions与超出原生词汇表的aspect_ratio只能由直接生成器兑现(原生工具无法表达,native=False才能保证生效,否则对应路径会发出警告);而backgroundinput_fidelitymoderationoutput_compressionoutput_formatqualitysize等是原生工具专属设置,直接生成器会忽略并警告。这些细节体现了"能力层必须精确说明每个设置在哪条路径上生效"的设计纪律。

三、没有可靠跨提供者抽象的工具:保持纯NativeTool

指南第二条给出了反向的边界:对于没有可靠跨提供者抽象、纯提供者专属的工具,就作为包裹在NativeTool中的原生工具存在,不要强行添加一层薄薄的 provider-agnostic 能力——除非该特性在多个提供者上有支持或有有意义的回退语义。

NativeTool定义于 capabilities/native_tool.py:它是一个简单能力,把单个AgentNativeTool(静态AbstractNativeTool实例或动态可调用对象)注册到 agent 上,等价于Agent(capabilities=[NativeTool(my_tool)])。它还提供from_spec类方法,支持两种 YAML 配置形式:

# 扁平形式 NativeTool: kind: web_search search_context_size: high # 显式形式 NativeTool: tool: kind: web_search

from_spec内部通过pydantic.TypeAdapter(AbstractNativeTool)校验配置,得益于基类的判别联合 schema,任何内置工具类型都能被正确解析。

四、请求级参数:暴露在工具类字段而非只放在 Model Settings

指南的第三条针对"提供者 API 中有控制原始工具输出是否包含的请求级参数"(例如 xAI 的include、OpenAI 的include):这类参数应作为工具类的字段暴露,而不是只放在 model settings 里。用户配置XSearchTool(...)时应该能发现所有相关选项;model settings 保留为向后兼容的替代方案。

一个教科书式的例子是XSearchTool.include_output(native_tools/init.py):默认False时模型只在内部使用搜索结果、返回文字摘要;设为True后,原始搜索结果会以NativeToolReturnPart形式出现在响应中,程序可以访问搜到的帖子、来源与元数据。它同时也可以在XaiModelSettings.xai_include_x_search_output中设置——工具字段为主 API,model settings 是向后兼容的备选,二者并存。

五、提供者支持的文档三处维护

指南要求提供者支持情况必须记录在三个地方,任何一处遗漏都视为违规:

  1. 工具类 docstring 中的 'Supported by' 列表:每个工具类与每个提供者专属字段都带一个 "Supported by:" 小节。例如WebSearchTool类级列表写明 Anthropic、OpenAI Responses、Groq、Google、xAI、OpenRouter 六家;而user_location字段级列表则进一步细分到 Anthropic、OpenAI Responses、xAI、OpenRouter(还附上各提供者官方文档链接)。
  2. docs/native-tools.md的提供者支持表格:仓库根目录下的 docs/native-tools.md 集中维护一张"工具 × 提供者"支持矩阵,供用户快速横向对比。
  3. 字段级 docstring 中的提供者专属语义:例如WebSearchTool.external_web_access明确说明"OpenAI 的 legacyweb_search_preview工具会忽略此参数";ImageGenerationTool.output_compression区分 OpenAI(仅 jpeg/webp,默认 100)与 Google Vertex AI(仅 jpeg,默认 75)。

这套三处文档纪律保证了同一事实在不同入口(类内省、API 参考、横向对比表)保持同步。

六、字段命名:优先直通提供者 API 字段名

指南进一步要求:当工具字段直接映射提供者 API 的字段名时,优先使用那个名字——用户可能正开着提供者的官方文档对照使用 pydantic-ai 文档。

从 native_tools/init.py 的实现可以清晰看到这一原则:WebSearchTool.search_context_sizeuser_locationblocked_domainsallowed_domainsmax_usesexternal_web_access均与 OpenAI Responses / Anthropic 等提供者的参数同名;XSearchTool.allowed_x_handlesexcluded_x_handlesfrom_dateto_date与 xAI X-search 的参数一致;AdvisorTool.modelmax_tokenscaching与 Anthropic advisor 工具定义的字段一一对应。FileSearchTool.file_store_ids则针对三家映射到不同概念(OpenAI 的 vector store ID、Google 的 file search store 名、xAI 的 collection ID),也在字段 docstring 中逐一说明。

七、快速失败:__post_init__校验互斥与上限

指南要求"在__post_init__中校验互斥性与数量上限,用清晰的错误消息快速失败"。源码中有两个典型实现:

XSearchTool.__post_init__(native_tools/init.py):

def __post_init__(self) -> None: if self.allowed_x_handles is not None and self.excluded_x_handles is not None: raise ValueError('Cannot specify both allowed_x_handles and excluded_x_handles') if self.allowed_x_handles and len(self.allowed_x_handles) > 20: raise ValueError('allowed_x_handles cannot contain more than 20 handles') if self.excluded_x_handles and len(self.excluded_x_handles) > 20: raise ValueError('excluded_x_handles cannot contain more than 20 handles')

AdvisorTool.__post_init__(native_tools/init.py):

def __post_init__(self) -> None: if self.max_tokens is not None and self.max_tokens < 1024: raise ValueError('AdvisorTool.max_tokens must be at least 1024')

这种"构造期即失败"(fail fast at construction)的哲学还延伸到能力层:ImageGeneration__post_init__中同时拒绝dimensionsaspect_ratio并存、拒绝把裸ImageGenerationModel传给local、拒绝无 provider 前缀的fallback_image_model字符串,错误消息都具体指明应该改用哪个字段。

八、名称往返(Round-Trip):原生工具名的历史一致性

最后一条准则最隐蔽也最关键:pydantic-ai 中的原生工具名必须能在提供者 API 中往返(round-trip)。如果提供者 API 使用不同的函数名——例如 xAI 在线上发送的是x_keyword_search而不是 pydantic-ai 里的x_search——那么在回放历史(replaying history)时必须保留原始名称,否则旧对话中的工具调用无法与新的工具定义对上。

这一要求对应仓库中的历史回放与线缆契约(wire contract)测试体系,例如 tests/test_ref_sibling_wire_contract.py 与 tests/test_thinking_wire_contract.py 所覆盖的场景:agent 运行、暂停、恢复或导入历史消息时,工具引用的命名必须与首次调用时一致。为新增工具设计时,务必确认提供者是否会对工具名做改写,并保证适配器在回放路径上保留提供者侧的真实名称。

九、九个内置原生工具速查表

当前仓库 native_tools/init.py 导出的原生工具及其要点如下:

工具类kind主要提供者关键字段
WebSearchToolweb_searchAnthropic、OpenAI Responses、Groq、Google、xAI、OpenRoutersearch_context_size(low/medium/high,默认 medium)、user_locationblocked_domainsallowed_domainsmax_usesexternal_web_access
XSearchToolx_searchxAIallowed_x_handles/excluded_x_handles(互斥,各限 20)、from_date/to_date(naive 时间按 UTC 解释)、enable_image_understandingenable_video_understandinginclude_output
CodeExecutionToolcode_executionAnthropic、OpenAI Responses、Google、Bedrock (Nova2.0)、xAIfiles(上传文件,仅匹配提供者的文件被使用)
WebFetchToolweb_fetchAnthropic、Googlemax_usesallowed_domains/blocked_domains(互斥)、enable_citationsmax_content_tokens
ImageGenerationToolimage_generationOpenAI Responses、Googleactionbackgroundinput_fidelitymoderationmodel(如gpt-image-2)、output_compressionoutput_formatpartial_images(0–3)、qualitysizeaspect_ratio
MemoryToolmemoryAnthropic无参
MCPServerToolmcp_serverOpenAI Responses、Anthropic、xAIidurl(OpenAI 支持x-openai-connector:<connector_id>)、authorization_tokendescriptionallowed_toolsheaders
FileSearchToolfile_searchOpenAI Responses、Google (Gemini)、xAIfile_store_idsmax_num_resultsinstructionsretrieval_mode(hybrid/semantic/keyword)
AdvisorTooladvisorAnthropic、OpenRoutermodel(executor 咨询的更强模型)、max_uses(每请求上限)、max_tokens(≥1024)、caching'5m'/'1h'临时缓存 TTL)

其中FileSearchTool是"完全托管的 RAG":由提供者处理文件存储、分块、嵌入生成与上下文注入,pydantic-ai 侧只需传入 store ID。AdvisorTool的字段与 Anthropic advisor 工具定义 1:1 映射,OpenRouter 作为网关 server tool 只接受modelmax_tokens子集并忽略其余字段——这些差异都在字段级 docstring 中标注。

此外,_tool_search.py中还有框架内部的ToolSearchToolkind='tool_search'),它不直接导出给用户,而是由ToolSearch能力按提供者选择三种模式之一驱动:原生服务端搜索(Anthropic 的bm25/regex、OpenAI 的服务端执行tool_search)、原生客户端执行(Anthropic 的 tool-reference 块、OpenAI 的execution='client'调用本地可调用对象)、以及本地search_tools函数工具回退(见 native_tools/_tool_search.py)。

十、新增原生工具:八条检查清单

综合 native_tools/AGENTS.md 与源码实现,为 pydantic-ai 新增一个原生工具时应依次确认:

  1. 跨提供者判定:该特性在多个提供者上有支持或合理回退语义吗?是 → 创建继承NativeOrLocalTool的能力;否 → 保持NativeTool包裹的纯原生工具。
  2. 回退路径:本地回退(function tool)还是子代理回退(fallback_subagent_model)?无回退时,不支持的模型必须显式报错(optional=False语义)。
  3. 请求级参数:提供者 API 的请求级参数(如include)要在工具类上暴露字段,model settings 只作向后兼容备选。
  4. 三处文档:类 docstring 的 'Supported by'、docs/native-tools.md 提供者表格、字段级 docstring,缺一不可。
  5. 字段命名:直通提供者 API 字段名,方便用户对照提供者文档。
  6. 快速失败校验:在__post_init__中用清晰消息校验互斥与上限。
  7. 名称往返:确认提供者是否改写工具函数名,保证历史回放时保留提供者侧原始名称。
  8. 配置需求:需要用户提供额外参数的工具(FileSearchMCPMemoryAdvisorToolSearch)要进入NATIVE_TOOLS_REQUIRING_CONFIG集合,避免用户以为只传kind即可用。

遵循这八条,既能保证用户通过能力层获得提供者自适应的统一体验,又能让提供者专属细节在工具类与文档中透明可见——这正是 pydantic-ai 原生工具体系"typed end to end"设计哲学在工具层的落地。

【免费下载链接】pydantic-aiHow Python does AI. Agents, realtime voice, image generation, embeddings. Every model, every interface, typed end to end.项目地址: https://gitcode.com/GitHub_Trending/py/pydantic-ai

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询