定义并注册工具#
工具是你让模型请求执行的操作。运行时负责校验参数、运行函数、追加结果,并请模型继续——你只需要编写函数。
编写带类型注解的函数#
@tool
def add(left: int, right: int) -> int:
"""Add two integers exactly.
Args:
left: First operand.
right: Second operand.
Snippet:
add(left=20, right=22)
Guidelines:
- Use this tool for exact integer addition.
"""
return left + right
@tool 会读取类型注解来构建 JSON Schema。文档字符串是面向模型的文档:Args 描述每个参数,Snippet 展示调用形式,Guidelines 说明何时使用该工具。@tool 和 @tool(name="public_name") 都可以使用。
必须提供描述和至少一条非空的 guidelines。对于小型工具,可以直接在装饰器上给出 guidelines:
from zett_agent.tools.base import tool
@tool(guidelines="Use for exact integer addition.")
def add(left: int, right: int) -> int:
"""Add two integers exactly."""
return left + right
guidelines 和 snippets 帮助模型选择正确的操作。它们不是权限检查。装饰器上的 guidelines 或 snippet 值会覆盖文档字符串中对应的章节。
模型在哪里读到指引#
默认情况下,ToolGuidelinesExtension 会把每个工具的 snippet 和 guidelines 写入系统指令。传入 placement=ToolGuidancePlacement.DESCRIPTION 则会把它们追加到每个工具自己的描述里,适合只展示工具定义的端点:
from zett_agent.extensions.tool_guidelines import ToolGuidancePlacement, ToolGuidelinesExtension
guidance = ToolGuidelinesExtension(placement=ToolGuidancePlacement.DESCRIPTION)
snippets=False 或 guidelines=False 会在两种模式下去掉相应的部分。只有发送给提供商的请求会改变;已注册的工具和已保存的历史保持原样。
设计模型可提供的参数#
每个参数都需要类型注解。支持具名的位置或关键字参数以及仅限关键字的参数;不支持仅限位置的参数、*args 和 **kwargs。默认值可以让参数变为可选,而 Pydantic 支持的类型(例如 Literal、列表和嵌套模型)可以表达各种约束。
from typing import Literal
@tool(guidelines="Choose a supported format for the answer.")
def choose_format(style: Literal["brief", "detailed"] = "brief") -> str:
"""Choose an answer format."""
return style
校验会在函数运行之前拒绝未知字段和无效输入,但它遵循 Pydantic 常规的强制转换规则,而不保证严格的 JSON 类型。当转换不安全时,请添加显式约束或检查。名称在静态工具和由扩展注册的工具之间必须唯一。请保持名称稳定,因为它们是对外暴露给模型的 API,而不只是实现层面的名称。
注册工具#
把工具传给客户端,模型现在就能请求它们了。
client = await create_agent(model, tools=[add])
模型请求调用,运行时执行调用,模型再根据结果作答。运行流式展示一次工具往返,按顺序观察推理、参数、结果和最终回答。
从 Python 中调用工具#
装饰后会返回一个AgentTool,调用它时需要传入一个参数映射:
value = await add({"left": 20, "right": 22})
同步函数在工作线程中运行;async def 工具则在事件循环上被等待执行。无效的参数会在你的代码运行之前就通不过模式校验。
选择结果类型#
字符串以纯文本形式到达模型,不带额外的 JSON 引号。
兼容 JSON 的值、Pydantic 模型和数据类都会被序列化为 JSON。
TextContent、ImageContent或它们的非空列表会变成带类型的多模态工具内容;所选的模型必须支持图像。
请返回有用且大小可控的结果,而不是打开的文件、客户端对象或任意异常。要包含模型下一步所需的事实,并避免泄露密钥或返回不必要的大块数据。返回类型注解只是为函数提供说明;装饰器不会对你的结果强制检查输出模式。
安全地处理工具失败#
处理函数抛出的普通异常,或者参数校验失败,都会变成一个失败的ToolMessage和一个TOOL_FAILED事件。下一次模型调用会看到str(exception),并可能用更好的参数重试,或者解释失败原因。原始异常可以在回调和工具执行后的钩子中获取。取消操作以及其他BaseException子类不会被转换成可恢复的工具结果。
如果模型提供商无法解码某次调用的参数(通常是因为响应被截断),这次调用就永远不会到达处理函数。ToolCall.error 中保存着原因,模型会把它作为失败的结果收到,因此可以重试。
直接使用 Python 调用(例如await add({...}))仍然会抛出异常。集成错误需要你自己清理:来自工具的异常文本可能把凭据、路径或服务响应暴露给模型。before_tool生命周期钩子中抛出的异常会让整个请求失败;如果你希望拒绝时可以恢复,请使用工具中间件。参见中间件。
控制并发执行#
工具默认使用ToolExecutionMode.PARALLEL。如果一个模型响应中包含多个可并行执行的工具,它们就可以并发执行;每个处理函数完成后,对应的事件就会到达。对于需要在本地独占执行的操作,把它标记为串行:
from zett_agent.tools.base import ToolExecutionMode
@tool(
execution_mode=ToolExecutionMode.SERIAL,
guidelines="Use to prepare a mutation; obtain approval before applying it.",
)
def prepare_change(description: str) -> str:
"""Prepare a proposed change without applying it."""
return description
运行时会先完成这一批并行调用,然后逐个运行串行工具。SERIAL不是跨会话或跨工作进程的全局锁。如果要让整个响应都保持模型的调用顺序,请在智能体或请求上设置parallel_tool_call=False。共享的可变资源仍然需要应用层面的同步和授权。
文件系统工具与 shell 工具#
FileSystemExtension(read_only=True) 会添加 read_file、view_image、glob 和 grep。可写模式还会添加 write_file、edit 和 delete_file;CodingExtension 会添加 bash,其 timeout_seconds 默认为 60。
read_file 以行范围的形式返回 UTF-8 文本。另一个独立的 read 工具(通过 ReadExtension 添加)对文本的处理相同,并且遇到 PNG、JPEG、GIF 或 WebP 文件时会返回图像和一行描述,因此一个工具就能同时处理两种情况。view_image 仍用于 HTTP(S) 图像 URL。
每个工具也有自己的扩展,应用可以只提供自己想要的那些:
from zett_agent.extensions.bash import BashExtension
from zett_agent.extensions.file_system import EditExtension, GrepExtension, ReadExtension
extensions = [ReadExtension(), GrepExtension(), EditExtension(), BashExtension()]
ToolExtension 可以用同样的方式封装任何 @tool 函数。
这些工具以你的进程权限运行,路径相对于进程的工作目录。它们不是安全沙箱:请限制进程或容器,并在为非受信任的输入启用写入或 shell 访问之前添加审批规则。过长的输出会受到限制,因此单条命令无法占用整个上下文;被截断时,完整输出会保存在一个会上报给模型的文件中。
按需加载可选工具#
对于规模较大的工具集,ToolSearchExtension 会把标记为 deferred=True 的工具排除在模型上下文之外,并让模型可以搜索它们的定义。这需要兼容的 Responses 端点或 Anthropic Messages API;普通工具调用在所有内置提供商上都能正常工作。
关于配置方法、加载技能的示例,以及加载指令和执行工具之间的区别,请参见按需加载工具和技能。