Skip to content

02 · 一次工具调用的完整生命周期

One tool call, end to end

模型每次想干一件事,都会经过五段钩子。下面的时序即一次中风险 bash 调用的标准旅程(各步骤数字均可在代码定位):

  1. ① 模型 → DSH
    模型发出工具调用(如 `bash`、`write`、`apply_patch`、`web_fetch`)。
  2. ② 同步硬拒闸门 · ctx.tools.guard() index.ts:L1868-1874
    不是事件,而是 tools 服务的**同步注册守卫**:`isAutoExecution` 后先 `hardDenyReason`(凭据物质 / 受保护路径 / shell 熔断),再 `symlinkEscapeReason`(realpath 逃逸工作区)。命中即返回原因字符串 → 直接拒,**不弹窗**。这一层只做硬拒,永不挂类别分类。
  3. ③ 静态评估 + 类别收紧 · tools/pre-execute index.ts:L1876-1937
    `assessTool` → `deny`(硬拒 `[auto-mode hard deny]`)/ `allow`(直接放行)/ `ask`。中间还有一层**类别收紧**(index.ts:L1896-1913):三态开关配成 `deny` 的类别在这里终端拒绝、配成 `ask` 的无条件跳过 classifier 快径直接转人工(详见 [§17](./17-category-switches))。之后若 `classifierEligible`,交给 LLM 预分类器(`classifier.classify`)再定 `allow | deny | ask`;分类器不可用 → 一律向人工(`classifier unavailable`)。
  4. ④ 终局裁决 · approval/request index.ts:L2407-2822(prepend+global)
    本插件的**核心决策管线**(详见 [§04](./04-adjudicator-pipeline)):声明规则 → 名单 → 类别开关 → 评审模式 → 熔断 → 策略硬拒 → 学习放行 → 风险分档 → LLM 复审 + 人工倒计时。产出 `allowed-once` 或 `rejected`,或委托官方面板走人工竞速。
  5. ⑤ 执行与产物登记 · tools/result index.ts:L1939-1942
    `artifacts.settle`:把本会话**成功创建**的文件登记进 `ArtifactRegistry`(后续删除该文件时可豁免)——入参 `plannedCreates` 来自评估阶段的 `artifacts.plan`。
  6. ⑥ 喂回模型 · tools/post-execute index.ts:L1963-1993(global)
    若这次工具结果 `isError` 且有超时/决策标记(`timeoutFeedback` / `decisionFeedback`),注入 `{kind:'block', feedback}` —— 让模型知道「不是因为命令错,而是被审批挡下(超时 / 规则 / 模型拒绝)」。另有独立一段**结果脱敏**(index.ts:L1974-1986):`redactResults` 开启时,成功结果同样过一遍脱敏器并记审计——喂回模型的文本不夹带秘密。
  7. ⑦ 通知冲刷 · step/end index.ts:L669-683(watchNotices 内 L676-678 分流)
    没有独立的 `on('step/end')` 注册:`watchNotices` 监听 `session/event`,在事件流里分流出 `step/end` 分支调 `flushNotices`——把「✅ Model approved」这类通知写进会话日志(`notifyUser` 开启时);已看到 tool/result 的才写(否则在 tool-calls 序列中间插入用户消息会破坏 OpenAI 消息序列 → 只打控制台)。

注意

②③ 在 approval/request 之前就把「无争议放行 / 明显拒绝」消化掉,只有真正的模糊区(ask)才进入审批面板 + LLM 复审,吞吐与安全兼得。