
字节笔记本
2026年10月6日 · 约 5 分钟读完
DeepSeek Harness 审批子系统设计解析
DeepSeek Harness(缩写 dsh)是 DeepSeek AI 在 GitHub 上开源的 agent harness,MIT 协议,目前处于开发者预览阶段。它的架构口号是「一切皆插件」,底层由 Cordis 框架驱动:工具、会话、审批这些运行时能力,都被拆成一个个边界清晰的服务缝。用户审批子系统是其中最短的一块,也是最讲究的一块,因为它只回答一个问题:某个具体动作,现在能不能执行。

一、四种结局,只有一种放行
审批结果不是布尔值,而是一个封闭的枚举:allowed-once、rejected、cancelled、unavailable。封闭意味着调用方不需要考虑第五种可能;失败关闭意味着这四种里只有 allowed-once 是放行,而且只覆盖被询问的那一次动作。返回其余任何一种,调用方都按拒绝处理。
unavailable 的语义最能体现设计取向:找不到回答器、回答者不属于自己、回答器抛异常、返回了词表之外的野值,统统归一成 unavailable,而不是打开闸门。换句话说,系统出故障时的默认行为是拒绝,不是放行。每次提问都会领到一个专属的请求 id,它只用来把 approval/asked 和 approval/decided 两个审计事件配成对,不与工具调用 id、agent id 混用。
二、会话级策略:ask 与 never
每个会话有一条生效策略。默认值 ask 会把问题交给组装好的回答器链,链上没人应答就以 unavailable 收尾;never 则完全绕开回答器,确定性地返回 rejected,一次都不询问。后者正是 CI、无人值守任务该有的姿势:不是「没人点确认」,而是「根本不问」。
策略值本身也是事件溯源的:生效值取会话日志里最后一条 approval/policy 事件,没有就回退到服务配置;setApprovalPolicy 是唯一写入口,回放日志就能重建整个策略历史。never 的短路发生在服务内部、瀑布分发之前,所以哪怕有人后来往前插一个新的回答器,也绕不过这条规则。
三、请求对象:刻意不带工具参数
ApprovalRequest 有五个字段:发起提问的 agent、工具名、可选的 callId、可选的原因说明、可选的中止信号。最值得注意的是它刻意不携带工具参数:参数已经随工具调用流式展示过一遍,回答器拿到 callId 就能把确认弹窗挂到那条调用上,不必再渲染第二份可能漂移的副本。中止信号也处理得很干净:一旦中止,请求立刻落定为 cancelled,迟到的回答一律作废。
四、一次提问,一对审计事件
ctx.approval.request 只在会话处于打开的 turn 里才能调用,空闲状态提问会在写入任何事件之前被拒绝。一次合法的提问会先追加 approval/asked,拿到唯一结局后再追加 approval/decided,两个事件成对出现;如果审计写入失败,整笔请求直接拒绝,因为返回一个没有日志的决定会破坏配对不变量。
这些审计事件只进会话日志,不进模型转录,模型能看到的是调用方派生的工具结果和运行时上下文快照。回答器则是标准的瀑布分发:拥有这个请求就返回结局,否则调用 next 交给下一个,第一个回答占满唯一的决定槽。UI 渠道可以挂人工回答器,ACP 自动化桥则给自己的 agent 提供一次性机器决策。

五、模型也能看到策略
策略不是黑盒状态:ask 与 never 的完整语义都会进入缓存安全的运行时上下文快照,setPolicy 还能切换运行中 agent 的策略并排队到下一步生效。审批状态变化时,系统在保留历史之后追加一份完整快照,而不是改写请求头里的系统提示词。dsh-tools、dsh-tool-bash 这类工具包只消费封闭结局,失败关闭。
六、可复用的工程范式
抛开 dsh 本身,这套设计给任何要给 agent 加权限闸门的团队提供了模板:结局集合要封闭,放行要一次性,故障要落向拒绝;策略要可回放、有唯一写入口;审计要成对、只进日志不进模型上下文;提问对象要最小化,不重复传输已经展示过的数据。仓库在 GitHub 的 deepseek-ai/deepseek-harness,装好 Node.js 之后执行 npx @deepseek-ai/dsh web 就能在本地起一个 Web UI,审批弹窗是体验这套设计最直观的入口。



