工具执行生命周期
ZeroClaw 工具是模型在一次对话轮次中可以调用的能力。工具目录说明可以调用什么;执行生命周期说明一次调用如何变得安全、可观测、可取消,并对提供方可见。
当更改涉及内置工具、MCP 工具激活、agent 循环、审批策略、工具调用流式事件、收据、观察者事件、工具结果历史、取消,或 channel 入口与 agent 侧动作之间的边界时,请使用此页面。
执行路径
| 步骤 | 所有者 | 审查合同 |
|---|---|---|
| 工具定义 | zeroclaw-api::tool::Tool | 工具具有稳定的名称、描述、JSON schema、异步 execute 和归属。 |
| 工具组装 | 运行时工具工厂和作用域注册表 | 该代理仅接收由 bundles、MCP config、risk profile 以及每次运行的 narrowing 所允许的工具。 |
| 转化上下文解析 | ResolvedAgentExecution | 本轮开始时已有一个已解析的 bundle:model access、registry、approval manager、observer、runtime knobs、MCP activation handle 和 receipt generator。 |
| 提供程序请求 | agent::turn::tool_specs 和 provider 调用 | Native-tool 提供方接收结构化规范;text-protocol 提供方接收提示词指令,除非 strict parsing 将其隐藏。 |
| 工具调用解析 | agent::turn::parse_response 和解析器辅助函数 | 原生调用和文本工具调用会在可用时规范化为带有提供方 ID 的已解析调用。 |
| 准备 | agent::turn::call_prep | Hooks、delivery defaults、approval、prompt-required duplicate guards 和 ordinary duplicate-call guards 在 dispatch 之前运行。 |
| 执行 | agent::tool_execution | 调用会根据策略、取消和激活约束按顺序或并行运行。 |
| 结果记录 | post_exec、results_collect 和 history_append | 结果会被排序、记录、观察、可选地开具回执、设定边界,并附加回提供者历史。 |
| 循环控制 | run_tool_call_loop | 模型会看到工具结果,并且可以继续,直到返回最终文本、被取消,或达到迭代上限。 |
运行时将这些步骤分开,以便审查可以询问是哪个边界发生了变化。添加工具不同于扩大审批策略、修改提供方工具规范、更改观察者负载或持久化结果。
工具定义和注册
每个工具都实现了 Tool trait:
#![allow(unused)]
fn main() {
#[async_trait]
pub trait Tool: Send + Sync + Attributable {
fn name(&self) -> &str;
fn description(&self) -> &str;
fn parameters_schema(&self) -> serde_json::Value;
async fn execute(&self, args: serde_json::Value) -> anyhow::Result<ToolResult>;
}
}
ToolResult 很小:success、output 和 error。工具实现不应各自发明自己的日志记录或审批流程。分发器负责统一的开始/结果事件、回执、观察者记录、进度消息以及历史转换。
工具规范会为提供方请求重新构建。当前的 ToolSpec 通过 Arc 共享大型 schema,因此线上的格式保持不变,同时避免在每次迭代时进行深度克隆。
已解析的执行上下文
入口点不应通过内联重新推导 policy 来组装一个 turn。turn 引擎接收一个 ResolvedAgentExecution bundle,用于稳定的按 agent 依赖:模型绑定、有效的工具注册表、observer 和 approval 句柄、已解析的运行时参数、延迟的-MCP 激活集、model-switch 回调,以及可选的 receipt 生成器。
按消息的状态保留在该 bundle 之外:history、streaming sinks、event channels、steering messages、cancellation token、memory injection state,以及 ingress envelope。
当 PR 添加新的执行输入时,优先将其通过此已解析上下文或显式的每轮 ToolLoop 状态进行传递。避免使用隐藏的全局变量,或在某条工具路径内重新查找配置。
可用性和 MCP 激活
模型只能调用对当前轮次有效的工具:
- static tools 来自作用域注册表;
excluded_tools在提示/规范暴露之前以及执行之前移除名称;- native-tool providers 接收结构化规范,以实现有效的工具;
- text-protocol providers 仅在允许文本工具调用时才接收工具指令;
- 严格解析可能会完全隐藏文本工具协议;
tool_filter_groups决定当前轮次中哪些 MCP 工具 schema 可见。mode = "always"组可以预先激活符合条件的延迟 MCP 包装器,而dynamic组仅在当前用户消息匹配其关键字时才公开工具;- deferred MCP 可以暴露一个
tool_search存根,而不是每个 MCP 包装器。
延迟的 MCP 激活在当前轮次内是有状态的。tool_search 会将匹配的 MCP 存根解析到共享的 ActivatedToolSet 中;后续调用可以执行这些已激活的包装器。过滤组本身并不授予能力:作用域注册表、MCP 策略和拒绝列表仍然决定哪些包装器可以存在。
不要将 tool_search 与它激活的工具并行运行。调度器会强制任何包含 tool_search 的批次按顺序执行,因此查找不会与激活发生竞态。委派/子代理路径必须传递它们被授予的已激活集合;否则,委派的一轮可能会声明或尝试使用其执行器无法解析的工具。
审批与准备
准备工作在 executor 运行工具之前发生:
before_tool_call钩子可以取消或重写名称/参数。- 对于感知频道的工具,可能会注入频道投递默认值。
- 运行时会清除参数中的任何“approved”标记。
- 审批门会根据
ApprovalManager评估该工具。 - 已批准的调用会恢复运行时批准标记。
- 重复调用保护会移除重复的相同调用,除非该工具被豁免。
Approval 有不同的入口:
- CLI 管理器会提示操作员,并支持
yes、no和always。 - 非交互式通道管理器会自动拒绝需要提示的工具,除非该通道提供内联审批回传通道。
- ACP/web 后台通道可以将审批请求传达给真实操作员,即使该回合本身是非交互式的。
DenyWithEdit/ 替换响应会被清理并变为合成工具结果;原始工具不会执行。
Approval 是执行前控制。它不是收据,也不能证明某个工具已运行。审计条目会记录该决定以及作出决定的 channel 或 backchannel。
必需提示的 shell 调用增加了额外的循环保护:如果代理在获得批准之前重复相同的必需提示 shell 调用,循环将中止,而不是一遍又一遍地提示。
派发、取消和顺序
执行器会在运行工具之前立即发出一个待处理的 TurnEvent::ToolCall,这样流式客户端就可以显示实时运行卡片。工具完成后,它会使用相同的关联 id 发出对应的 TurnEvent::ToolResult。
仅在以下情况下允许并行执行:
- 运行时旋钮可启用并行工具;
- 该批处理有多个可执行调用;
- 批次中的任何调用都不需要批准;
- 该批次不包含
tool_search。
否则,调用将按顺序执行。顺序分发会在每次调用之前检查取消状态,并在取消时停止分发后续调用。并行分发可能会在某些兄弟调用完成的同时中断其他调用;已完成的调用保留其真实的终态结果,只有未完成的调用会得到中断结果。
有序结果向量为每个原始模型调用保留一个槽位。准备阶段会为已取消、已拒绝、已替换或已去重的调用填充槽位;执行阶段会填充其余槽位。这会在某些调用从未执行或并行调用乱序完成时,仍然保持提供方历史记录的顺序。
结果、收据和历史记录
成功的工具执行会将空输出规范化为 (no output)。当 [agent.tool_receipts] enabled = true 时,在结果追加到历史记录之前,成功执行可以从活动的收据作用域接收一张收据。Channel-runtime 路径和 direct-turn 路径具有不同的作用域生命周期;Tool receipts 页面说明了确切的 HMAC 格式和密钥生命周期细节。
回执是结果证据。它们不是审批决定,不是持久审计记录,不是链,也不会为被拒绝、被替换、被阻止、失败或中断的调用生成。
执行后:
- observer
ToolCallStart事件携带工具名称、可用时的提供方 tool-call id、参数、channel、agent alias 和 turn id; - 终端观察器
ToolCall事件新增持续时间、成功标志和已清理结果,同时重复 span 导向后端所需的关联字段; - 进度流会显示开始/完成行,并带有已清理的失败文本;
after_tool_call钩子会在已执行的调用后运行;- 结果在追加到模型可见历史之前,会被
max_tool_result_chars限制; - loop-detection 会使用结果内容,除已配置的忽略工具外;
- 下一个 provider 请求会看到 assistant 的工具调用轮次以及按顺序排列的工具结果。
工具结果并不是长期记忆,除非发生了记忆写入。它们可能是当前轮上下文、持久化的会话历史、流式 UI 事件、观察者/日志记录,或带有回执的结果。在 PR 和评审中请准确命名该表层。
此页面不拥有的内容
Channel 适配器和网关负责入站传输、认证、配对、webhook 解码和回复投递。工具执行在一次轮次到达 agent 循环且模型已发出工具调用后开始。
Config 生命周期负责工具相关设置的加载、保存、覆盖和重新加载。此页面仅涵盖在它们进入 turn 之后解析出的值。
安全和自主性文档拥有该策略词汇。本页展示该策略如何应用于一个具体的工具调用。
内存和载荷生命周期负责历史记录、文件、媒体和记忆的持久性与隐私边界。本页介绍向这些表面提供数据的工具结果路径。
后台工作生命周期在工具启动委派工作或子代理工作时负责管理生命周期更长的约定。工具返回任务 ID 并不会使其执行可在重启后恢复。
审阅者清单
对于工具执行更改,在审阅者签字确认前,请先回答以下问题:
- 哪个边界发生了变化:工具定义、注册表组装、审批、执行、回执、观察者事件、历史记录,还是 UI 流式传输?
- 该工具是否仍可通过正常的工厂路径进行归属和注册?
- 模型是否只会看到为这个 agent/run/iteration 允许的工具?
excluded_tools、按运行收窄、tool_filter_groups和延迟的 MCP 激活现在还一致吗?- 需要提示的调用是否会按顺序运行,并询问正确的批准界面?
- 非交互式执行会拒绝,还是会使用真实的回传通道,而不是静默批准?
- 重复调用和重复提示保护是否保留?
- 取消只会关闭未完成的工具卡片/结果吗?
- 在用户或密钥负载可能出现的地方,observer/log/progress surfaces 是否已清理并受限?
- 收据被描述为成功执行的证据,而不是批准、持久性或零知识证明吗?
- 该 PR 是否为其更改的用户可见表面提供边界级验证:CLI、channel、ACP/WS、gateway、cron,或 delegate/subagent?
源指针
规范文档:
关键代码入口点:
- Tool trait 和 result shape:
crates/zeroclaw-api/src/tool.rs - 观察者工具事件:
crates/zeroclaw-api/src/observability_traits.rs - 切换执行上下文:
crates/zeroclaw-runtime/src/agent/turn/execution.rs - 转发动机运行表和循环:
crates/zeroclaw-runtime/src/agent/turn/mod.rs - 工具调用准备与审批:
crates/zeroclaw-runtime/src/agent/turn/call_prep.rs和crates/zeroclaw-runtime/src/agent/turn/approval_gate.rs - 工具分发:
crates/zeroclaw-runtime/src/agent/tool_execution.rs - 工具收据:
crates/zeroclaw-runtime/src/agent/tool_receipts.rs - 结果收集/历史追加:
crates/zeroclaw-runtime/src/agent/turn/results_collect.rs和crates/zeroclaw-runtime/src/agent/turn/history_append.rs - 审批管理器:
crates/zeroclaw-runtime/src/approval/mod.rs - 作用域工具组装和延迟 MCP 激活:
crates/zeroclaw-runtime/src/tools/scoped.rs