DeepSeek Harness 工具执行流水线:一次工具调用的守卫、执行与定格本文是 dsh 官方参考「工具执行流水线」的导读。上一篇文章《Agent 生命周期》里,工具
<div class="detail-content-box has-mask large">
<h1 style="font-size: 20px;font-weight: 500;color: #2B77BF;line-height: 1.8;margin-bottom: 12px;text-align: center;"><span>DeepSeek Harness 工具执行流水线:一次工具调用的守卫、执行与定格</span></h1><blockquote style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.55);line-height: 1.8;margin-bottom: 24px;"><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>本文是 </span><code><span>dsh</span></code><span> 官方参考「工具执行流水线」的导读。上一篇文章《Agent 生命周期》里,工具调用只是时序伪代码里的一行 </span><code><span>tool/call* -> tools/pre-execute -> tools/execute -> tools/post-execute -> tool/result*</span></code><span>;这一篇把它完整展开——<span style="font-weight: 500;">一次工具调用如何被三组 waterfall(瀑布式事件)层层改写,最终定格为一条 </span></span><code><span><span style="font-weight: 500;">tool/result</span></span></code><span><span style="font-weight: 500;"> 事件</span>。</span></p></blockquote><hr/><h2 style="font-size: 17px;font-weight: 500;color: #2B77BF;line-height: 1.8;margin-bottom: 12px;"><span>〇、先记住一句话</span></h2><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">一次工具调用 = 三次 waterfall(pre-execute/execute/post-execute)+ 一次结果定格(快照 →finalizeContent→tools/result)。</span></span></p><ul style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">前置瀑布</span>(</span><code><span>tools/pre-execute</span></code><span>)管"能不能跑":钩子、权限、沙箱、审批;</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">执行瀑布</span>(</span><code><span>tools/execute</span></code><span>)管"怎么跑":超时、重试、指标包裹工具体;</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">后置瀑布</span>(</span><code><span>tools/post-execute</span></code><span>)管"结果怎么用":接受、阻止、替换、添加上下文;</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">结果定格</span>:注册表对结果做<span style="font-weight: 500;">无损快照</span>,</span><code><span>finalizeContent</span></code><span> 执行最后的<span style="font-weight: 500;">仅内容不变式</span>,</span><code><span>tools/result</span></code><span> 同步通知,最终冻结为<span style="font-weight: 500;">唯一一份</span>模型可见的权威事实。</span></section></li></ul><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>理解这条主线,后面所有细节都是它的展开。</span></p><hr/><h2 style="font-size: 17px;font-weight: 500;color: #2B77BF;line-height: 1.8;margin-bottom: 12px;"><span>一、全景:一次工具调用的完整旅程</span></h2><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>先看整体流程图,再逐段拆解。</span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js">graph TD A[Assistant message 含 tool-call 块] --> B[Session event: tool/call执行前记录] B --> C[UI pending cardpresentCall args] C --> D[”tools/pre-execute waterfallhooks · permission · sandbox”] D --> E{单调守卫 deny 或 abstain+ ctx.approval 一次性审批} E -- ”denied 或 审批被拒” --> F[工具体被跳过denied · rejected · cancelled] E -- allow --> G[”tools/execute waterfalltimeout · retry · metrics 环绕 dispatch”] G --> H[注册的工具 execute body] H --> I[Tool-owned 事件todo/write · fs/observed · hook/* · tool/code-dispatch] I --> J[”tools/post-execute waterfallaccept · block · replace · add context”] J --> K[Registry 外层规范化无损快照 pipeline/result] K --> L[ToolDefinition.finalizeContent最后仅内容不变式] L --> M[tools/result 同步通知冻结的权威结果] M --> N[Active-batch additionalContexts FIFO结果之后注入 user/message] N --> O[Session event: tool/result单一 model-facing 结果] O --> P[Tool batch settled 批次结算] P --> Q[UI completed cardpresentResult args result]</pre></section><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>注意图里最关键的一句话:<span style="font-weight: 500;">tools/pre-execute→ 单调守卫 →tools/execute→tools/post-execute这三个 waterfall 可以改写一次调用</span>;而 finalizeContent 与 tools/result 在它们之后运行,由工具定义自身控制,不再参与改写。</span></p><hr/><h2 style="font-size: 17px;font-weight: 500;color: #2B77BF;line-height: 1.8;margin-bottom: 12px;"><span>二、起点:模型发出 tool-call</span></h2><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>流水线从模型输出一个工具调用块开始:</span></p><ol style="list-style-type: decimal;font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">Assistant message 包含 tool-call 块</span>——模型决定调用某个工具;</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">Session event:</span></span><code><span><span style="font-weight: 500;">tool/call</span></span></code><span> 在<span style="font-weight: 500;">执行之前</span>就被记录下来(持久化事实,可回放);</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">UI pending card</span>:界面立刻展示一张"进行中"卡片,调用 </span><code><span>presentCall(args)</span></code><span> 把参数呈现给用户。</span></section></li></ol><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>从这一刻起,调用进入流水线。注意顺序:<span style="font-weight: 500;">先落tool/call事件、再进守卫</span>——即使后面被拒绝,这次"试图调用"的事实也已经留档。</span></p><hr/><h2 style="font-size: 17px;font-weight: 500;color: #2B77BF;line-height: 1.8;margin-bottom: 12px;"><span>三、第一道闸门:tools/pre-execute waterfall</span></h2><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>这是调用能否放行的第一道(也是最主要的一道)闸门。它承载三类关注点:<span style="font-weight: 500;">钩子(hooks)、权限(permission)、沙箱(sandbox)</span>。各种插件挂在这里,对调用做出裁决。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>3.1 可能的裁决</span></h3><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>瀑布式事件允许每个监听者对调用做出自己的决定:</span></p><table><thead><tr class="firstRow"><th style="text-align: left;"><section><span>裁决</span></section></th><th style="text-align: left;"><section><span>含义</span></section></th></tr></thead><tbody><tr><td style="text-align: left;"><code><span>allow</span></code></td><td style="text-align: left;"><section><span>放行</span></section></td></tr><tr><td style="text-align: left;"><code><span>deny</span></code></td><td style="text-align: left;"><section><span>拒绝(本轮调用不执行)</span></section></td></tr><tr><td style="text-align: left;"><code><span>throw</span></code></td><td style="text-align: left;"><section><span>抛错(wrapper 抛错会向上冒泡)</span></section></td></tr><tr><td style="text-align: left;"><code><span>ask</span></code></td><td style="text-align: left;"><section><span>需要询问用户</span></section></td></tr><tr><td style="text-align: left;"><code><span>allowed-once</span></code></td><td style="text-align: left;"><section><span>一次性放行</span></section></td></tr></tbody></table><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>3.2 单调守卫(monotonic guards)</span></h3><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>在瀑布之外,注册表还维护着一组<span style="font-weight: 500;">单调守卫</span>:</span></p><ul style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><section><span>每个守卫只能选择 </span><code><span><span style="font-weight: 500;">deny</span></span></code><span><span style="font-weight: 500;"> 或 </span></span><code><span><span style="font-weight: 500;">abstain</span></span></code><span>(弃权)——守卫<span style="font-weight: 500;">不能放行</span>,只能拦或不管;</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">身份受保护</span>:守卫的裁决不会被其他环节绕过或重排;</span></section></li><li style="margin-bottom: 0px;"><section><span>所有"<span style="font-weight: 500;">不得重新排序的所有者策略</span>"也以已注册守卫的形式存在——即使有 </span><code><span>ctx.approval</span></code><span> 之类的交互流程,它们仍会被执行。</span></section></li></ul><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>3.3 ctx.approval:一次性审批</span></h3><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>ctx.approval 提供<span style="font-weight: 500;">一次性(one-shot)审批提示</span>:需要用户拍板时,它发起一次询问。</span></p><ul style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><section><span>它在<span style="font-weight: 500;">单调守卫之前</span>处理"询问"环节;</span></section></li><li style="margin-bottom: 0px;"><section><span>如果审批<span style="font-weight: 500;">缺席或无法回答</span>,结果一律按 </span><code><span><span style="font-weight: 500;">deny</span></span></code><span> 处理——拿不到明确许可,就不放行。</span></section></li></ul><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>3.4 被拒的后果</span></h3><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>一旦出现<span style="font-weight: 500;">denied 或 approval refused</span>,工具体(tool body)<span style="font-weight: 500;">被完全跳过</span>:不执行、也不产生副作用,调用直接以拒绝/取消收场(rejected、cancelled、unavailable 等状态)。</span></p><blockquote style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.55);line-height: 1.8;margin-bottom: 24px;"><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>小结:</span><code><span>tools/pre-execute</span></code><span> 决定了"这次调用有没有资格跑"。</span></p></blockquote><hr/><h2 style="font-size: 17px;font-weight: 500;color: #2B77BF;line-height: 1.8;margin-bottom: 12px;"><span>四、执行:tools/execute waterfall</span></h2><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>通过守卫之后,进入执行阶段。这个 waterfall 把<span style="font-weight: 500;">环绕分发(dispatch)的关注点</span>包在真正工具体的外面:</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>4.1 环绕关注点</span></h3><table><thead><tr class="firstRow"><th style="text-align: left;"><section><span>关注点</span></section></th><th style="text-align: left;"><section><span>作用</span></section></th></tr></thead><tbody><tr><td style="text-align: left;"><section><span><span style="font-weight: 500;">timeout(超时)</span></span></section></td><td style="text-align: left;"><section><span>限制一次调用最长执行时间,到期强杀</span></section></td></tr><tr><td style="text-align: left;"><section><span><span style="font-weight: 500;">retry(重试)</span></span></section></td><td style="text-align: left;"><section><span>允许对失败调用进行有限重试</span></section></td></tr><tr><td style="text-align: left;"><section><span><span style="font-weight: 500;">metrics(指标)</span></span></section></td><td style="text-align: left;"><section><span>采集调用耗时、成功率等观测数据</span></section></td></tr></tbody></table><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>这些都由其他插件在 tools/execute 上包装实现,工具体本身不需要关心。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>4.2 注册的工具 execute body</span></h3><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>最内层是<span style="font-weight: 500;">已注册工具的execute()主体</span>——真正干活的代码。它执行过程中会产生两类事件:</span></p><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">① 文件系统意图事件(仅 tool-fs 的变更)</span></span></p><ul style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><code><span>fs/write-intent</span></code><section><span>(写入意图)</span></section></li><li style="margin-bottom: 0px;"><code><span>fs/edit-intent</span></code><section><span>(编辑意图)</span></section></li></ul><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>这些是"先读后写"策略的门禁点:<span style="font-weight: 500;">文件系统的先读后编辑检查位于tool-fs之下,通过fs/*事件实现</span>。它由专门的策略插件(如 dsh-fs-observation-policy)挂接,<span style="font-weight: 500;">不改变工具 schema</span>。</span></p><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">② Tool-owned 会话事件(工具自己发出的)</span></span></p><ul style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><code><span>todo/write</span></code><section><span>(任务清单更新)</span></section></li><li style="margin-bottom: 0px;"><code><span>fs/observed</span></code><section><span>(文件已被观察)</span></section></li><li style="margin-bottom: 0px;"><code><span>hook/invoked</span></code><section><span>、</span><code><span>hook/result</span></code><span>(钩子被调用及其结果)</span></section></li><li style="margin-bottom: 0px;"><code><span>tool/code-dispatch</span></code><section><span>(code 模式的代码分派)</span></section></li></ul><blockquote style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.55);line-height: 1.8;margin-bottom: 24px;"><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>若 wrapper 在执行中抛错(</span><code><span>wrapper throws</span></code><span>),异常沿 </span><code><span>tools/execute</span></code><span> 向上冒泡,按 </span><code><span>throw</span></code><span> 处理。</span></p></blockquote><hr/><h2 style="font-size: 17px;font-weight: 500;color: #2B77BF;line-height: 1.8;margin-bottom: 12px;"><span>五、收尾:tools/post-execute waterfall</span></h2><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>执行完成后,结果先经过后置瀑布——这是"结果级"的最后改写机会:</span></p><table><thead><tr class="firstRow"><th style="text-align: left;"><section><span>行为</span></section></th><th style="text-align: left;"><section><span>含义</span></section></th></tr></thead><tbody><tr><td style="text-align: left;"><section><span><span style="font-weight: 500;">accept</span></span></section></td><td style="text-align: left;"><section><span>接受当前结果</span></section></td></tr><tr><td style="text-align: left;"><section><span><span style="font-weight: 500;">block</span></span></section></td><td style="text-align: left;"><section><span>阻止该结果(视作失败/丢弃)</span></section></td></tr><tr><td style="text-align: left;"><section><span><span style="font-weight: 500;">replace</span></span></section></td><td style="text-align: left;"><section><span>用新内容替换结果</span></section></td></tr><tr><td style="text-align: left;"><section><span><span style="font-weight: 500;">add context</span></span></section></td><td style="text-align: left;"><section><span>向会话附加额外上下文</span></section></td></tr></tbody></table><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>到这里,<span style="font-weight: 500;">一次调用可以被改写的环节全部结束</span>。接下来进入"定格"阶段——结果不再被 waterfall 改写。</span></p><hr/><h2 style="font-size: 17px;font-weight: 500;color: #2B77BF;line-height: 1.8;margin-bottom: 12px;"><span>六、结果定格:从快照到权威结果</span></h2><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>6.1 Registry 外层规范化:无损快照</span></h3><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>注册表对候选结果做<span style="font-weight: 500;">外层规范化(outer normalization)</span>:</span></p><ul style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><section><span>对 </span><code><span>pipeline/result</span></code><span> 做<span style="font-weight: 500;">无损快照(snapshot)</span>;</span></section></li><li style="margin-bottom: 0px;"><section><span>如果快照本身失败,会<span style="font-weight: 500;">先把失败规范化</span>(</span><code><span>throws</span></code><span> 变成 </span><code><span>isError</span></code><span> 之类的结构),再继续走后面的不变式。</span></section></li></ul><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>快照的意义:让后续回调看到的是<span style="font-weight: 500;">同一份固定的结果</span>,而不是可能被并发改动的活对象。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>6.2 ToolDefinition.finalizeContent:最后的仅内容不变式</span></h3><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span><span style="font-weight: 500;">finalizeContent</span>由工具定义自身声明,是<span style="font-weight: 500;">最后一个仅内容(content-only)的不变式</span>:</span></p><ul style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><section><span>它<span style="font-weight: 500;">同步执行</span>,只允许调整内容;</span></section></li><li style="margin-bottom: 0px;"><section><span>它使用的是<span style="font-weight: 500;">已经随快照固定</span>的结果;</span></section></li><li style="margin-bottom: 0px;"><section><span>它不参与 waterfall 改写——这是定义自己收尾的最后一道关。</span></section></li></ul><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>6.3 tools/result:同步通知冻结结果</span></h3><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>tools/result 是一个<span style="font-weight: 500;">同步通知</span>,把<span style="font-weight: 500;">冻结的、权威的</span>结果分发给监听者。此刻结果已经定型,监听者只能观察,不能再改。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>6.4 Active-batch additionalContexts FIFO</span></h3><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>如果本次调用属于一个"活动批次",批次的<span style="font-weight: 500;">additionalContexts</span>会以<span style="font-weight: 500;">FIFO</span>顺序,在<span style="font-weight: 500;">已记录的工具结果之后</span>注入 user/message。这样保证:注入的上下文总是排在本批结果后面,不会打乱时序。</span></p><h3 style="font-size: 17px;font-weight: 400;color: #2B77BF;line-height: 1.8;margin-bottom: 24px;"><span>6.5 Session event:tool/result</span></h3><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>最后,流水线产出一条<span style="font-weight: 500;">tool/result</span>会话事件——这是<span style="font-weight: 500;">唯一一份面向模型(model-facing)的结果</span>。无论中间经过多少次改写,模型最终看到的只有这一份定格结果。</span></p><hr/><h2 style="font-size: 17px;font-weight: 500;color: #2B77BF;line-height: 1.8;margin-bottom: 12px;"><span>七、批次结算与 UI 呈现</span></h2><ul style="font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">Tool batch settled</span>:当一批调用(例如模型一次输出中的多个工具调用)的所有 </span><code><span>tool/result</span></code><span> 事件都记录完成后,批次结算;</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">UI completed card</span>:界面把"进行中"卡片翻转为"已完成"卡片,调用 </span><code><span>presentResult(args, result)</span></code><span> 同时呈现<span style="font-weight: 500;">原始参数</span>与<span style="font-weight: 500;">最终结果</span>。</span></section></li></ul><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>至此,一次工具调用的完整旅程结束:从 tool/call 到 tool/result,全程有据可查、可回放。</span></p><hr/><h2 style="font-size: 17px;font-weight: 500;color: #2B77BF;line-height: 1.8;margin-bottom: 12px;"><span>八、三个 waterfall 能力速查表</span></h2><table><thead><tr class="firstRow"><th style="text-align: left;"><section><span>Waterfall</span></section></th><th style="text-align: left;"><section><span>时机</span></section></th><th style="text-align: left;"><section><span>承载能力</span></section></th><th style="text-align: left;"><section><span>能改写调用吗</span></section></th></tr></thead><tbody><tr><td style="text-align: left;"><code><span>tools/pre-execute</span></code></td><td style="text-align: left;"><section><span>执行前</span></section></td><td style="text-align: left;"><section><span>钩子、权限、沙箱、审批(含单调守卫 + </span><code><span>ctx.approval</span></code><span>)</span></section></td><td style="text-align: left;"><section><span>✅ 可拒绝/放行</span></section></td></tr><tr><td style="text-align: left;"><code><span>tools/execute</span></code></td><td style="text-align: left;"><section><span>执行中</span></section></td><td style="text-align: left;"><section><span>超时、重试、指标;工具体本体;</span><code><span>fs/*</span></code><span> 意图与 tool-owned 事件</span></section></td><td style="text-align: left;"><section><span>✅ 可抛错</span></section></td></tr><tr><td style="text-align: left;"><code><span>tools/post-execute</span></code></td><td style="text-align: left;"><section><span>执行后</span></section></td><td style="text-align: left;"><section><span>accept / block / replace / add context</span></section></td><td style="text-align: left;"><section><span>✅ 可改结果</span></section></td></tr><tr><td style="text-align: left;"><code><span>finalizeContent</span></code><section><span> + </span><code><span>tools/result</span></code></section></td><td style="text-align: left;"><section><span>定格后</span></section></td><td style="text-align: left;"><section><span>仅内容不变式、同步通知</span></section></td><td style="text-align: left;"><section><span>❌ 只读、不改写</span></section></td></tr></tbody></table><hr/><h2 style="font-size: 17px;font-weight: 500;color: #2B77BF;line-height: 1.8;margin-bottom: 12px;"><span>九、几个值得记住的设计点</span></h2><ol style="list-style-type: decimal;font-size: 15px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;" class="list-paddingleft-1"><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">三处 waterfall 是"一次调用可被改写的全部"</span>:所有钩子、策略、审批都挂在这三个点上,过了 </span><code><span>tools/post-execute</span></code><span> 就再没人能动它;</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">守卫只能拦、不能放</span>:单调守卫 </span><code><span>deny or abstain</span></code><span>,且身份受保护——这保证了"安全策略不可被绕过";</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">拿不到许可 = 拒绝</span>:</span><code><span>ctx.approval</span></code><span> 一次性询问,缺席或无法回答一律 </span><code><span>deny</span></code><span>,工具体被跳过;</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">文件系统先读后写不碰 schema</span>:通过 </span><code><span>fs/*</span></code><span> 意图事件实现,是 </span><code><span>tool-fs</span></code><span> 之下的门禁,而不是工具定义的改动;</span></section></li><li style="margin-bottom: 0px;"><section><span><span style="font-weight: 500;">结果只有一份</span>:中间再多的 replace / add context,最终都以单一 </span><code><span>tool/result</span></code><span> 事件呈现给模型——回放时只有一个"权威答案"。</span></section></li></ol><p style="font-size: 17px;font-weight: 400;color: rgba(0,0,0,0.9);line-height: 1.8;margin-bottom: 24px;"><span>一句话收尾:<span style="font-weight: 500;">dsh 用"三道瀑布 + 一次定格"把一次工具调用变成了可审计、可拦截、可改写、且只留一份权威结果的过程</span>。理解了这条流水线,就理解了为什么在 dsh 里加一个审批策略、换一个沙箱、或改造某个工具的行为,都是在"往流水线上挂插件",而不是改工具本身。</span></p><section><span><br/></span></section> <div class="content-mask">
<a href="javascript:void (0);" class="mask-text login-trigger">
<i class="iconfont icon-suo"></i>
登录查看剩余 70% 内容
<i class="iconfont icon-arrow-right-o"></i>
</a>
</div>
</div>
</div>
免费获取企业 AI 成熟度诊断报告,发现转型机会
关注公众号

扫码关注,获取最新 AI 资讯
3 步完成企业诊断,获取专属转型建议
已有 200+ 企业完成诊断