Agent Zero 工具系统开发契约与 DOX 文档规范:以 tools/AGENTS.md 为纲解读工具实现、响应契约与验证流程
导读
本文以 Agent Zero 仓库中 tools/AGENTS.md 为骨架,系统讲解框架内置 Agent 工具(Tool)模块的归属边界、开发契约、输出/中断语义与文件级 DOX 文档规范。读者将掌握如何编写符合框架要求的 Tool 子类、正确返回 Response、处理干预(intervention)与敏感信息,并通过 *.py.dox.md 文件维护工具文档的长期同步——这些能力是扩展 Agent Zero 或为插件编写专属工具时的必备基础。
一、工具目录的所有权与职责边界
tools/ 目录是 Agent Zero 框架内置 Agent 工具(core agent tool)的唯一归属地。根据 tools/AGENTS.md 的定义:
- 目录职责:拥有可供 Agent Zero agents 使用的核心工具实现,并保持工具执行契约(execution contracts)、进度日志(progress logging)、干预处理(intervention handling)与工具结果格式化(tool-result formatting)的稳定。
- 发现机制:该目录下的工具模块由框架自动发现,模块内部必须定义
Tool子类。 - 基类归属:共享的工具基类与响应契约位于 helpers/tool.py,不属于
tools/目录本身。 - 插件工具归属:插件专属的工具应放在对应插件的
tools/目录中,而不是塞进框架根tools/。
从当前仓库实际布局看,tools/ 下共有约 20 个工具模块,每个模块都遵循"同名 .py + 同名 .py.dox.md"的成对组织方式,例如 tools/notify_user.py 与 tools/notify_user.py.dox.md、tools/scheduler.py 与 tools/scheduler.py.dox.md。这种"代码与文档同目录扁平共存"的结构,正是该目录被定义为"file-documented DOX profile"的直接体现。
二、工具开发契约:Tool 基类、execute 与 Response
2.1 基类契约
tools/AGENTS.md 明确了两条硬性契约:
- 工具必须继承自
helpers.tool.Tool; - 必须实现
async def execute(...)。
查看 helpers/tool.py 的源码可以看到契约的完整形态:
@dataclass
class Response:
message: str
break_loop: bool
additional: dict[str, Any] | None = None
class Tool:
def __init__(self, agent, name, method, args, message, loop_data, **kwargs):
self.agent = agent
self.name = name
self.method = method
self.args = args
self.loop_data = loop_data
self.message = message
self.progress = ""
@abstractmethod
async def execute(self, **kwargs) -> Response:
pass
Tool 构造函数接收 Agent 实例、工具名、方法名(action)、参数、消息与循环数据;execute 是每个工具必须实现的抽象异步方法,其返回值类型必须是 Response。
2.2 返回值语义:Response.message 与 break_loop
所有工具执行完毕后必须返回 helpers/tool.py 中定义的 Response:
message:返回给 Agent 的文本结果,会写入消息历史(after_execution中通过hist_add_tool_result记录)。break_loop:布尔值,决定 Agent 主循环是否中断。True表示终止当前循环(如 tools/response.py 在收到合法回复文本时返回break_loop=True),False表示继续循环(绝大多数工具如此)。
以 tools/response.py 为例,它从参数中查找 text 或 message,找到非空字符串即返回 Response(message=..., break_loop=True);否则抛出 RepairableException,明确要求"response tool requires a non-empty top-level text or message string argument"。
2.3 生命周期钩子
基类还提供了三个可覆写的生命周期钩子(helpers/tool.py):
before_execution:打印"Using tool"日志、创建日志对象,并逐参数输出(nice_key将snake_case参数名转为易读标题)。after_execution:将工具结果清洗后写入历史、打印响应并更新日志。set_progress/add_progress:通过tool_output_update扩展点向 WebUI 推送实时进度。
tools/parallel.py 的 ParallelTool 通过覆写 before_execution(置空 self.log)与 after_execution(只写历史、不产生额外日志)来适配并行执行场景;tools/response.py 的 ResponseTool 则覆写 after_execution 以避免把回复内容重复写入历史,并利用 loop_data.params_temporary["log_item_response"] 标记消息完成状态。这些覆写示例展示了契约的灵活边界。
三、本地契约细则:干预、脱敏与非破坏性
tools/AGENTS.md 的 "Local Contracts" 部分对行为边界提出了四项要求,每一项都能在源码中找到对应实现。
3.1 干预处理(intervention)
当工具是长时间运行或结果需要尊重暂停/干预流程时,使用
await self.agent.handle_intervention(...)。
典型实现见 tools/search_engine.py:搜索完成后调用 await self.agent.handle_intervention(searxng_result),将搜索结果交给干预流程,若 Agent 处于暂停状态则等待恢复。同样,tools/wait.py 在计算等待时长之前先调用 handle_intervention(),确保等待工具尊重暂停语义。
3.2 敏感信息脱敏
在记录、返回或存储工具输出前,必须对 secrets 进行清理或掩码。
基类 after_execution 使用 sanitize_string(来自 helpers/strings.py)清洗响应文本后再写入历史与日志(helpers/tool.py)。这与 prompts/agent.system.secrets.md 中关于密钥处理的提示约束共同构成"日志不落敏感信息"的完整防线。
3.3 非破坏性约束
除非工具契约明确说明该行为,否则不得执行破坏性的文件系统或网络操作。
即:破坏性能力(删除、覆盖、对外网络副作用)必须显式写进工具的参数契约与 DOX 文档,由调用方(Agent / prompt 指令)明确授权后才能执行,而不是在工具内部隐式"顺手"完成。
3.4 契约与实现的对应关系
为便于核对,下表汇总了当前仓库中典型工具与其契约要点的对应关系(实现文件均在 tools/ 下):
| 工具模块 | 核心职责 | Response/break_loop 行为 | 关键依赖 |
|---|---|---|---|
notify_user.py |
向用户发送通知 | break_loop=False,校验 type/priority/message |
helpers/notification.py |
response.py |
产出最终回复并结束循环 | 命中 text/message 时 break_loop=True |
helpers/errors.py |
parallel.py |
并行启动/等待/取消子工具调用 | break_loop=False,支持 wait、timeout、job_ids |
helpers/parallel_tools.py |
wait.py |
按时长或绝对时间等待 | break_loop=False,校验时间戳与时长 |
helpers/wait.py、helpers/localization.py |
scheduler.py |
创建/查询/更新/运行定时任务 | 同上下文运行任务时 break_loop=True,其余为 False |
helpers/task_scheduler.py |
search_engine.py |
通过 SearXNG 搜索并返回前 10 条结果 | break_loop=False,先过干预流程 |
helpers/searxng.py |
unknown.py |
未识别工具时生成纠错提示 | break_loop=False,附带可用工具列表 |
extensions/python/system_prompt/_11_tools_prompt.py |
四、DOX 文件规范:每个工具的"配套文档"义务
tools/AGENTS.md 用较大篇幅定义了本目录特有的 DOX 规范:
- 成对约束:目录中每个直接
*.py工具模块,必须存在同名*.py.dox.md文件(命名方式为在完整 Python 文件名后追加.dox.md)。 - 文档职责:
*.py.dox.md负责记录工具目的(purpose)、参数/概念(arguments/concepts)、输出与break_loop行为、副作用(side effects)、重要辅助依赖、prompt 契约注意事项与验证指引。 - 同步义务:工具模块新增、删除、重命名或行为变更时,必须在同一次变更中同步更新其
*.py.dox.md;工具删除或重命名后不得遗留过期 DOX。
以 tools/notify_user.py.dox.md 为例,其结构完整覆盖了上述义务:Purpose(发送面向用户的通知)、Ownership(NotifyUserTool 及其 execute 方法)、Runtime Contracts(Tool 子类 + Response 返回)、Key Concepts(AgentContext.get_notification_manager.add_notification、NotificationType、NotificationPriority、self.agent.read_prompt 等被调用依赖)、Work Guidance 与 Verification(指向 tests/test_tool_action_contracts.py)。
4.1 为什么需要"行为与文档同改"?
从源码可以推断其设计动机:tools/ 是扁平目录,工具参数契约既被 execute 读取(如 notify_user 读取 message/title/detail/type/priority/timeout,见 tools/notify_user.py),又被 prompt 工具指令与 WebUI 引用。若仅改代码不改 DOX,工具的真实参数与文档描述将脱节,进而误导 Agent 的调用决策和下游开发者的理解。DOX 文件因此承担了"权威契约快照"的角色。
五、工作指引:简洁输出、复用 helpers、同步 Prompt
tools/AGENTS.md 的 "Work Guidance" 给出三条实操准则:
- 输出克制:工具输出要足够简洁以节省消息历史,同时保留可执行的细节。例如
search_engine只截取前 10 条结果(SEARCH_ENGINE_RESULTS = 10,见 tools/search_engine.py),scheduler的查询类 action 用json.dumps(..., indent=4)输出结构化任务列表。 - 逻辑下沉 helpers:可复用的解析、provider、文件系统或网络逻辑放在
helpers/。scheduler.py把 cron 校验、时区归一化、任务计划解析等重逻辑全部委托给 helpers/task_scheduler.py,自身只保留 action 分发与参数组装,是"薄工具、厚 helpers"的范本。 - Prompt 同步:工具名、参数或行为变更时,必须同步更新 prompt 中的工具指令。框架通过 prompts/agent.system.tools.md 向 Agent 注入可用工具清单,其中明确要求"use ONLY the tools listed below. match names exactly. do NOT invent tool names",因此工具改名/增删参数后若不同步 prompt,Agent 将调用到不存在的工具(落入
unknown.py的纠错流程)。
六、验证要求:测试驱动与 DOX 覆盖率检查
tools/AGENTS.md 的 "Verification" 定义了变更后的验证路径:
- 定向工具测试:改动工具或 prompt 契约后运行针对性测试。仓库中 tests/test_tool_action_contracts.py 与 tests/test_tool_request_normalization.py 等即覆盖工具参数归一化与行为契约。
- Prompt/快照测试:工具指令或输出结构变化时运行 prompt 相关测试(如 tests/test_prompt_protocol.py),防止提示词与工具实际行为漂移。
- DOX 覆盖检查:用脚本或 shell 循环验证每个
tools/*.py都有匹配的tools/*.py.dox.md。这一步可落成一条简单的检查命令:
for f in tools/*.py; do
[ -f "${f}.dox.md" ] || echo "MISSING DOX: ${f}.dox.md"
done
该检查确保"文件级文档覆盖"(file-level documentation coverage)作为目录级质量门槛持续生效。
七、扩展实践:如何新增一个符合契约的 Agent 工具
综合上述契约,新增工具的完整步骤可以归纳为四步(与 tools/AGENTS.md 的规则一一对应):
- 实现模块:在
tools/下创建your_tool.py,定义class YourTool(Tool)并实现async def execute(self, **kwargs) -> Response,读取self.args中的参数,返回合法的Response。 - 同步 DOX:在同一次变更中创建
your_tool.py.dox.md,写明 Purpose、Ownership(类与方法签名)、Runtime Contracts(参数、输出、break_loop行为、副作用)、Key Concepts(被调用的 helpers)与 Verification。 - 同步 Prompt:若工具暴露给 Agent 使用,在对应 prompt 工具指令中登记名称与参数,避免 Agent 调用未知工具。
- 验证:运行针对性测试与 DOX 覆盖检查;若涉及子任务/并行/等待等语义,参照 tools/parallel.py 与 tools/wait.py 的
handle_intervention用法补齐干预处理。
插件场景下,将上述文件放入插件自己的 tools/ 目录即可,无需触碰框架根 tools/,这正体现了 tools/AGENTS.md 中"Plugin-specific tools belong in plugin tools/ directories"的边界划分。
结语
tools/AGENTS.md 表面上是一份面向仓库开发者的维护文档,实质上浓缩了 Agent Zero 工具系统的全部关键约定:从 Tool/Response 基类契约、干预与脱敏边界,到 DOX 配套文档与测试验证闭环。理解这份契约,就等于掌握了为 Agent Zero 安全、规范地扩展任何工具能力的完整方法论。进一步研究可沿两条路径深入:一是通读 helpers/tool.py 与 helpers/parallel_tools.py 等基座实现,二是对照 tools/notify_user.py.dox.md 等 DOX 样板,模仿其结构与粒度。
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