Agent Zero 工具开发指南:从 Tool 基类契约到插件化分发与验证
本指南以 skills/a0-development/references/tools.md 为核心骨架,系统讲解 Agent Zero 框架中"工具(Tool)"的完整开发契约:包括 Tool 基类与 Response 返回协议、工具的发现与分发路径、四种存放位置的选择策略、提示词(Prompt)契约、进度上报与干预流程,以及配套 DOX 文档与测试验证规范。读完本文后,你将掌握在 Agent Zero 中新增一个自定义工具、将其正确注册为插件工具并为模型编写可用提示词的全部实操方法,并理解工具执行生命周期背后的源码原理。
工具在 Agent Zero 中的地位
在 Agent Zero 中,工具是 Agent 与外部世界交互的基本单元:模型的每次函数调用都会落到某个具体工具上,工具执行结果再写回消息历史,构成"模型发起调用 → 框架分发 → 工具执行 → 结果回填"的闭环。与许多框架将工具写成普通函数不同,Agent Zero 为工具定义了严格的类契约:所有工具都派生自 helpers.tool.Tool 基类,并实现统一的异步执行接口。
这套契约的核心文件(Source Anchors)如下:
| 职责 | 仓库路径 |
|---|---|
| 工具基类与响应契约 | helpers/tool.py |
| 核心工具目录的 DOX 契约 | tools/AGENTS.md |
| 工具分发路径(执行入口) | agent.py |
| 方法式工具的参考实现 | tools/skills_tool.py |
| 示例 profile 工具 | agents/_example/tools/example_tool.py |
| 提示词组织规范 | prompts/AGENTS.md |
工具基类契约:Tool 与 Response
最小可运行工具
所有工具都从 helpers.tool.Tool 派生,并实现 async def execute(self, **kwargs) -> Response。最小实现如下:
from helpers.tool import Tool, Response
class MyTool(Tool):
async def execute(self, **kwargs) -> Response:
return Response(message="done", break_loop=False)
从源码看,Tool 是抽象基类,execute 被 @abstractmethod 装饰(见 helpers/tool.py),因此任何没有实现 execute 的子类都无法被实例化,这从语言层面保证了工具契约不会被绕过。
Response 返回协议
Response 是一个 dataclass(见 helpers/tool.py),字段含义如下:
| 字段 | 含义 |
|---|---|
message |
作为工具结果追加到消息历史并回显给 Agent 的文本 |
break_loop |
为 True 时终止当前消息循环(如"任务完成,停止思考") |
additional |
可选的附加元数据,随工具结果一起写入(默认 None) |
break_loop 的执行语义可以在分发代码中得到验证:在 agent.py 的 _execute_tool_request 中,只有当 response.break_loop 为真时,框架才会清空 pending 状态并直接返回工具消息,终止本轮循环。
工具实例的注入字段
Tool.__init__ 的签名(见 helpers/tool.py)表明,每个工具实例在构造时都会收到以下上下文:
agent:当前 Agent 实例,可访问历史、上下文、配置等;name:工具名称(即模型调用时的tool_name);method:可选的方法名,用于"方法式工具"(method-style tool),例如skills_tool:load中的load;args:模型传入的参数字典;message:触发本次工具调用的原始消息;loop_data:当前消息循环的状态数据。
其中 method 参数是方法式工具的关键:skills_tool 将多个动作(list、search、load、read_file)收敛在同一个工具类中,通过 action/method 参数分派(见 tools/skills_tool.py),这避免为每个动作单独创建一个工具文件。
工具发现与分发路径
当模型请求调用某个工具时,框架按以下链路完成分发:
- MCP 工具优先:
_execute_tool_request首先尝试从 MCP 配置中查找同名工具(见 agent.py),若命中则直接使用 MCP 工具; - 本地工具回退:未命中 MCP 时,调用
self.get_tool(...); - 按 profile 层级搜索文件:
get_tool通过subagents.get_paths(self, "tools", name + ".py")沿 Agent 的文件夹层级搜索名为<tool_name>.py的模块(见 agent.py),找到后用extract_tools.load_classes_from_file加载其中的Tool子类; - 兜底 Unknown:若任何路径都找不到,则实例化
tools.unknown.Unknown作为兜底,向 Agent 说明工具不存在。
工具执行前后还接入了扩展点与干预点,完整执行顺序(见 agent.py)为:
handle_intervention() # 执行前检查暂停/干预
tool.before_execution() # 打印调用参数、创建日志对象
extension "tool_execute_before" # 执行前扩展钩子
response = await tool.execute() # 执行工具本体
extension "tool_execute_after" # 执行后扩展钩子
tool.after_execution(response) # 清理结果写入历史并回显
handle_intervention() # 执行后再次检查
if response.break_loop: return # 终止循环
值得注意,before_execution 会把每个参数通过 tool_output_update 扩展实时输出到界面,并打印彩色日志(见 helpers/tool.py);after_execution 则负责把结果文本 sanitize_string 后写入历史(hist_add_tool_result)并更新日志对象(见 helpers/tool.py)。因此工具实现者无需自己处理日志与历史回填。
工具的存放位置与选择策略
新工具放在哪里,直接决定它的作用域与分发优先级。框架按以下位置查找工具(优先级从高到低取决于 profile 层级):
| 位置 | 用途 |
|---|---|
tools/ |
核心框架工具,只承载框架自带行为,不建议直接新增 |
plugins/<plugin>/tools/ |
随插件分发的内置插件工具 |
usr/plugins/<plugin>/tools/ |
用户级插件工具,自定义插件开发的首选位置 |
agents/<profile>/tools/ |
profile 级局部工具(需先验证当前版本的发现机制再依赖示例) |
仓库现状印证了这一划分:核心工具如 a2a_chat.py、search_engine.py、skills_tool.py 等直接位于 tools/ 下,而大量能力(编辑器、浏览器、邮件、语音等)都以插件形式打包在 plugins/ 各目录的 tools/ 子目录中。参考 tools/AGENTS.md 的 Ownership 声明,绝大多数新工具应当打包进插件,而不是直接塞进根级 tools/,否则会破坏"核心目录只承载框架行为"的边界。
Prompt 契约:让模型学会使用工具
仅有 Python 类还不够——模型必须知道工具的存在、JSON 参数形状与使用时机。因此每个工具都需要一个面向 Agent 的提示词片段(Prompt Fragment),框架按文件名约定自动装载:agent.system.tool.<tool_name>.md。
常见放置位置:
- 核心工具提示词:
prompts/agent.system.tool.<tool_name>.md(如 prompts/agent.system.tool.a2a_chat.md); - 插件工具提示词:
plugins/<plugin>/prompts/agent.system.tool.<tool_name>.md; - 用户插件提示词:
usr/plugins/<plugin>/prompts/agent.system.tool.<tool_name>.md; - profile 覆盖提示词:
agents/<profile>/prompts/agent.system.tool.<tool_name>.md。
参考仓库中的示例工具,agents/_example/tools/example_tool.py 对应提示词 agents/_example/prompts/agent.system.tool.example_tool.md,其中明确写道"该文件因命名为 agent.system.tool.*.md 而自动被包含进系统提示词",并给出了模型的调用 JSON 示例:
{
"thoughts": [
"Let's test the example tool...",
],
"headline": "Testing example tool",
"tool_name": "example_tool",
"tool_args": {
"test_input": "XYZ",
}
}
prompts/AGENTS.md 进一步给出提示词开发红线:不得在模板中放入密钥、真实 API Key 或用户私有数据;修改占位符与文件名前必须先阅读渲染路径;prompt 变更会改变 Agent 行为,编辑应保持窄而有意。
变更纪律:凡是改动工具名、参数形状、输出行为、安全规则或 break_loop 语义,都必须同步更新对应的提示词片段与测试。这是因为提示词与代码是同一契约的两面,只改一边必然造成模型调用与实际实现脱节。
进度上报与人工干预
对长耗时或需要外部结果的工作流,工具实现者应遵循以下规范:
await self.set_progress(...):通过tool_output_update扩展把进度实时推送到界面,同时写入本地self.progress(见 helpers/tool.py);self.add_progress(...):仅在本地累积进度文本,不触发扩展事件(见 helpers/tool.py),适用于不希望频繁刷界面的场景;- 暂停/干预:对于长运行或依赖外部结果的工作流,按 tools/AGENTS.md 的 Local Contracts,应在关键节点使用
await self.agent.handle_intervention(...)(定义见 agent.py),让暂停/干预流程生效。分发代码在before_execution前后、execute前后共插入四次干预检查点,工具执行期间 Agent 随时可以被安全暂停; - 安全规范:工具输出在写入日志或返回前必须脱敏/掩码(sanitize 与 mask),严禁把密钥、凭据泄露进日志或消息历史。
after_execution中对message调用sanitize_string正是这一规范的底层防线。
核心工具的 DOX 文档契约
Agent Zero 对根级核心工具实施"文件级 DOX"(file-documented DOX)制度:
- 规则:
tools/下的每个直接*.py工具模块,必须有同目录下同名追加.dox.md的配套文档(即tools/*.py↔tools/*.py.dox.md),例如 tools/search_engine.py 对应 tools/search_engine.py.dox.md、tools/a2a_chat.py 对应 tools/a2a_chat.py.dox.md; - DOX 文件拥有的内容:工具用途、参数/概念、输出与
break_loop行为、副作用、重要辅助依赖、提示词契约说明与验证指引(见 tools/AGENTS.md Local Contracts); - 同步义务:工具模块的新增、删除、改名或行为变更,必须与配套 DOX 在同一变更中完成,不允许删除/改名后遗留过期 DOX;
- 边界:插件工具的工具文档属于该插件自己的 docs 或 DOX 契约,不应写入根级
tools/,从而保持核心目录文档的纯粹性。
验证与测试
工具改动后的验证路径分为四层:
- 定向工具测试:修改工具或其提示词契约后,运行该工具的针对性测试;
- 提示词/快照测试:当工具指令或输出形状变化时,运行 prompt/snapshot 相关测试,防止系统提示词预算或渲染结果漂移(可参考 tests/test_prompt_protocol.py 等测试);
- DOX 覆盖检查:对根级核心工具,用脚本或 shell 循环逐一核对每个
tools/*.py都有同名.py.dox.md(这正是tools/AGENTS.mdVerification 一节要求的标准做法); - 插件级检查:若工具属于插件作用域,还需按该插件自身 AGENTS.md 执行插件专属检查项。
实战:从零新增一个插件工具的 Checklist
综合上述契约,在 Agent Zero 中新增一个自定义插件工具的最小工作流如下:
- 在插件目录(推荐
usr/plugins/<plugin>/tools/)创建my_tool.py,定义class MyTool(Tool)并实现async def execute(self, **kwargs) -> Response; - 依据执行语义正确设置
Response(message=..., break_loop=...),需要元数据时补充additional; - 在
usr/plugins/<plugin>/prompts/创建agent.system.tool.my_tool.md,写明工具名、JSON 参数形状、使用时机与示例调用; - 长流程中使用
await self.set_progress(...)上报进度、await self.agent.handle_intervention(...)尊重暂停/干预,输出前对敏感信息脱敏; - 为该工具补充插件内的文档(DOX 或 docs),并运行定向测试、prompt/snapshot 测试与插件专属检查。
遵循以上五步,即可让一个新的工具能力被框架自动发现、被模型正确调用、被日志系统完整记录,并保持与 Agent Zero 核心契约长期兼容。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust4.24 K638- DDeepSeek-V4.1-FlashDeepSeek-V4.1-Flash 是一个多模态混合专家(MoE)模型,拥有 5520 亿骨干参数,并支持最多一百万 token 的上下文长度。该模型原生支持图像和文本输入,并以自回归方式生成文本Python670
SlideSCIPPT插件,支持素材库、AI助手、一键添加图片标题,复制粘贴位置、一键图片对齐、一键插入Markdown(加粗、超链接等行内样式、代码块、LaTeX等块级样式)、便捷导出图片!C#230
hello-agents📚 《从零开始构建智能体》——从零开始的智能体原理与实践教程Python52874
new-apiAI模型聚合管理中转分发系统,一个应用管理您的所有AI模型,支持将多种大模型转为统一格式调用,支持OpenAI、Claude、Gemini等格式,可供个人或者企业内部管理与分发渠道使用。🍥 A Unified AI Model Management & Distribution System. Aggregate all your LLMs into one app and access them via an OpenAI-compatible API, with native support for Claude (Messages) and Gemini formats.Go22545
JeecgBoot🔥企业级低代码平台集成了AI应用平台,帮助企业快速实现低代码开发和构建AI应用!前后端分离架构 SpringBoot,SpringCloud、Mybatis,Ant Design4、 Vue3.0、TS+vite!强大的代码生成器让前后端代码一键生成,无需写任何代码! 引领AI低代码开发模式: AI生成->OnlineCoding-> 代码生成-> 手工MERGE,显著的提高效率,又不失灵活~Java36351