Agent Zero 的 response 工具深度解析:从ResponseTool实现到消息循环终止机制
【免费下载链接】agent-zeroAgent Zero AI framework项目地址: https://gitcode.com/GitHub_Trending/ag/agent-zero
导读
response是 Agent Zero 框架中承担"最终答复"职责的核心工具(tool):当智能体完成一项任务、或当前没有活跃任务时,它把最终结果输出给用户,并凭借break_loop语义终止消息循环。本文以仓库中的 tools/response.py.dox.md 为骨架,结合 tools/response.py 的完整实现、helpers/tool.py 的工具基类契约、helpers/errors.py 的可修复异常体系以及 agent.py 的循环调度逻辑,讲清楚该工具的参数契约、执行生命周期、错误自修复机制和测试验证方式。读完你不仅能透彻理解response工具的底层原理,还能举一反三地掌握 Agent Zero 全部内置工具的统一实现规范。
一、DOX 文档的定位:工具职责的"持久化契约"
Agent Zero 的tools/目录采用"实现 + DOX"双文件扁平结构:每个*.py文件旁边都伴随一个*.py.dox.md,例如 tools/response.py 对应 tools/response.py.dox.md。DOX(Documentation of eXecution)文件并非简单注释,它记录的是该模块的职责、运行时契约、副作用与验证方式,并要求在工具参数、输出结构、break_loop行为、干预(intervention)处理或 Prompt 指令发生变化时同步更新。
针对response工具,这份 DOX 文档明确锁定了以下契约:
- 职责:向用户输出最终或中间阶段的智能体回复;
- 实现归属:
response.py持有运行时实现,DOX 文件持有关于该实现的责任、契约、副作用与验证的持久化笔记; - 依赖边界:仅依赖
helpers.errors与helpers.tool两个导入域; - 变更要求:工具参数、输出形状、
break_loop行为、干预处理、Prompt 指令或副作用任何一项变化,都必须同步更新 DOX。
这种"实现与契约分离、强制同步"的模式,让 Agent Zero 的工具在快速迭代中始终保有可审计的接口边界。
二、ResponseTool类结构与生命周期钩子
2.1 类声明与父类契约
helpers/tool.py 定义了所有工具的共同基类Tool,其构造签名接收agent、name、method、args、message与loop_data,并声明了三个生命周期方法:
execute(**kwargs):抽象方法,执行工具主体逻辑,必须返回Response;before_execution(**kwargs):执行前钩子,默认打印工具名与参数并创建日志对象;after_execution(response, **kwargs):执行后钩子,默认将结果写入历史(hist_add_tool_result)并更新日志。
ResponseTool(tools/response.py#L5)正是这三个钩子的具体实现,且刻意覆盖了父类的默认行为——尤其是after_execution,它选择不写入历史、不输出内容(见下文第四节)。
2.2 核心数据结构Response
execute的返回值是 helpers/tool.py 中的 dataclassResponse:
@dataclass class Response: message: str break_loop: bool additional: dict[str, Any] | None = Nonemessage:要交给 Agent 循环处理的文本;break_loop:是否终止消息循环(response工具固定为True);additional:可选的附加数据,例如在responsesAPI 架构下可携带_responses_output_item(见 agent.py#L1213-L1217)。
三、参数契约:text优先、message兜底、否则自修复
3.1 execute 的完整实现
ResponseTool.execute的实现极其精简,全部逻辑如下(tools/response.py#L7-L14):
async def execute(self, **kwargs): for key in ("text", "message"): message = self.args.get(key) if isinstance(message, str) and message.strip(): return Response(message=message, break_loop=True) raise RepairableException( "response tool requires a non-empty top-level text or message string argument" )它依次检查两个顶层字符串参数:
text:首选参数。只要它是非空(strip()后不为空)的字符串,就直接作为回复内容;message:兼容参数。当text缺失或为空白时回退使用;- 失败路径:两者都缺失或为空,则抛出
RepairableException。
3.2 RepairableException:让 Agent 自己"修复"参数
RepairableException定义在 helpers/errors.py#L83-L86,其类注释点明了设计意图:
"An exception type indicating errors that can be surfaced to the LLM for potential self-repair."
这是 Agent Zero 错误分层体系(RepairableException/InterventionException/HandledException)中的关键一环。当response工具因参数不合法而抛出该异常时,Agent 主循环(agent.py#L1180-L1187)会将其转换为历史中的 warning 消息(hist_add_warning),把错误详情回写给 LLM,让模型感知到"刚才的工具调用格式有误",从而自行修正参数并重试,而不是直接崩溃终止任务。
可以推断的设计权衡:text/message双参数的存在是为了兼容历史 Prompt 输出,而"必须非空"的强校验则防止智能体输出空白回复导致死循环或空内容持久化。
四、before / after 钩子:日志职责的迁移与收尾
4.1 before_execution:静默化
tools/response.py#L16-L19 中的before_execution只剩一条注释和一个pass:
async def before_execution(self, **kwargs): # self.log = self.agent.context.log.log(type="response", heading=..., content=self.args.get("text", "")) # don't log here anymore, we have the live_response extension now pass这段注释本身即是重要信息:日志职责已经从工具内部迁出。早期版本在这里写type="response"的日志,现在改由live_response扩展负责实时响应的展示。这符合 Agent Zero 的扩展(extension)架构——UI 相关的实时输出统一收敛到扩展层,工具本体保持纯净。
4.2 after_execution:标记日志完成
tools/response.py#L21-L26 的after_execution同样不做历史写入与终端输出:
async def after_execution(self, response, **kwargs): # do not add anything to the history or output if self.loop_data and "log_item_response" in self.loop_data.params_temporary: log = self.loop_data.params_temporary["log_item_response"] log.update(finished=True) # mark the message as finished其唯一副作用是:如果循环数据loop_data.params_temporary中存在log_item_response日志条目,则将其标记为finished=True,用于在 UI 上结束"正在生成"的状态。注释明确说明不向历史或输出追加任何内容——这正是response作为"终端答复"工具的语义:内容只经break_loop路径返回给上层,不再污染消息历史。
对比父类 helpers/tool.py#L54-L59 的默认after_execution(会hist_add_tool_result并打印结果),可以清晰看到ResponseTool有意识地覆盖了这套默认行为。
五、break_loop=True:消息循环的终止开关
break_loop是Response契约中最具控制力的字段。ResponseTool无论走text还是message分支,都固定返回break_loop=True。Agent 主循环在 agent.py#L1222-L1224 与 agent.py#L1497-L1498 两处(分别对应常规工具执行与responses架构路径)统一处理:
if response.break_loop: self._clear_responses_pending_state() return response.message即:一旦工具返回break_loop=True,循环立即返回response.message作为本轮的最终结果,终止"工具调用 → 模型再推理"的迭代链条。这也解释了为什么 Prompt 规范要求"仅在任务完成或没有活跃任务时才使用该工具"。
对照参考:框架内绝大多数工具(如plugins/_browser/tools/browser.py、plugins/_code_execution/tools/code_execution_tool.py、plugins/_document_query/tools/document_query.py)都返回break_loop=False,把控制权交还给模型继续推理;只有response与示例工具(见 agents/_example/tools/response.py)才设置break_loop=True。这一对比恰好勾勒出 Agent Zero 的循环语义:工具默认不打断推理,仅"答复"拥有终止权。
六、Prompt 指令:模型视角的使用规范
工具的实现契约之外,Agent Zero 还通过 Prompt 模板约束模型的调用行为。prompts/agent.system.tool.response.md 给出了模型视角的完整规范:
### response: final answer to user ends task processing use only when done or no task active args: `text` default to balanced, concise answers: informative but tight, not terse and not verbose.核心约束包括:
- 语义:向用户输出最终答复,结束任务处理;
- 使用时机:仅当任务完成或没有活跃任务时;
- 参数:
text; - 风格基调:平衡而简洁——信息量充足但紧凑,既不生硬简短也不冗长。
并附带标准调用示例(JSON):
{ "thoughts": [ "..." ], "headline": "Providing final answer to user", "tool_name": "response", "tool_args": { "text": "Answer to the user" } }此外,prompts/agent.system.response_tool_tips.md 补充了一条关键提示:对于长篇幅的既有文本,不要重新编写,而应使用§§include(path)语法直接引用文件内容。这保证了最终答复可读性与上下文窗口的平衡。
七、验证与测试:如何回归检查该工具
DOX 文档明确要求:行为变更后需运行针对性测试验证工具与 Prompt 契约,无聚焦测试时则做冒烟测试(smoke-test agent 执行)。文档记录的关联测试包括:
- tests/chunk_parser_test.py
- tests/rate_limiter_test.py
- tests/test_browser_agent_regressions.py
- tests/test_chat_compaction.py
- tests/test_dirty_json.py
- tests/test_download_toast_regressions.py
- tests/test_fasta2a_client.py
- tests/test_fastmcp_openapi_security.py
这些测试大多并非直接单测ResponseTool,而是覆盖与response输出链路相邻的契约面——例如break_loop返回的消息如何进入聊天持久化(test_chat_compaction)、错误参数触发的自修复路径、以及 WebUI 下载/推送等下游消费行为。验证策略的核心思路是:通过端到端与相邻模块的回归,间接保障response的契约不被破坏。
八、源码级全景回顾
将以上剖析汇总为一张对照表,即可看清response工具在 Agent Zero 中的完整坐标:
| 关注点 | 依据 | 结论 |
|---|---|---|
| 职责 | tools/response.py.dox.md | 输出最终/中间答复,拥有终止循环的能力 |
| 类结构 | tools/response.py#L5 | ResponseTool(Tool),实现三个生命周期钩子 |
| 参数契约 | tools/response.py#L7-L14 | text优先、message兜底、均非空,否则抛RepairableException |
| 返回结构 | helpers/tool.py#L11-L15 | Response(message, break_loop=True, additional=None) |
| 循环终止 | agent.py#L1222-L1224 / agent.py#L1497-L1498 | break_loop=True时返回消息并结束本轮 |
| 错误自修复 | helpers/errors.py#L83-L86 | RepairableException转 warning 回写 LLM |
| 日志收尾 | tools/response.py#L21-L26 | 标记log_item_response为 finished,不写历史 |
| Prompt 规范 | prompts/agent.system.tool.response.md | 仅任务完成时使用,参数为text,风格平衡简洁 |
| 长文本技巧 | prompts/agent.system.response_tool_tips.md | 用§§include(path)引用既有文件 |
从这份实现中可以看出 Agent Zero 工具设计的三个鲜明特征:契约文档化(DOX 与实现强制同步)、错误可修复化(以RepairableException驱动 LLM 自纠错)、副作用最小化(日志交给扩展层、历史交给上层循环)。理解了response,也就掌握了阅读 Agent Zero 其余全部内置工具与插件工具(如plugins/_a0_connector、plugins/_browser等)的统一方法论——它们共享同一套Tool基类、同一套Response契约与同一套 DOX 同步纪律。
【免费下载链接】agent-zeroAgent Zero AI framework项目地址: https://gitcode.com/GitHub_Trending/ag/agent-zero
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考