
字节笔记本
2026年10月6日 · 约 5 分钟读完
DeepSeek Harness 权限预设机制解析
DeepSeek Harness(缩写 dsh)是 DeepSeek AI 在 GitHub 上开源的 agent harness,MIT 协议,目前处于开发者预览阶段。它的架构口号是「一切皆插件」,底层由 Cordis 框架驱动:工具、会话、审批这些运行时能力,都被拆成边界清晰的服务。权限预设(permission presets)是其中专门做「组织」的一层:它把沙箱模式(sandbox/mode)与审批策略(approval/policy)这两个相互独立的旋钮捆绑成具名预设,供客户端作为单个权限(Permissions)选择器呈现给用户。

一、只记意图,不碰强制执行
预设层是一项可选能力:它不在 agent loop 主干上,也不拥有任何强制执行。真正决定一个动作能不能落盘、要不要弹窗的,始终是两个旋钮各自的折叠结果:执行、提示词叙述与回放读取的都是 sandbox/mode 与 approval/policy 的生效值。预设切换只记录意图,再通过每个旋钮自己的规范 setter 写入。这条边界换来一个重要性质:预设层拆掉、换掉,系统的安全语义一分不变,因为它从头到尾就不是权威。
二、预设表:默认只有两行
预设是一个表键,映射到一个沙箱与审批的组合,外加可选的展示信息。默认表只有两行:workspace-write 对应「workspace-write 沙箱加 ask 审批」,danger-full-access 对应「danger-full-access 沙箱加 never 审批」。配置里的 defaultPreset 可以为新会话指定默认预设,省略时就取与两个旋钮默认值的组合相匹配的那一行。
配置错误在插件加载时即失败,是典型的失败关闭:名为 custom 的表项直接抛异常,这个名称保留给派生的「非预设」状态;在不施加隔离的 bash 执行器(没有 sandboxMode 能力事实)之上组合同样抛异常,因为预设捆绑了沙箱模式,承诺了隔离却兑现不了,就不该启动。服务因此要求一个施加隔离的 ctx.shell 执行器和 ctx.approval 同时在场。

三、current:派生而非记忆
current(events) 从旋钮折叠出实际生效的预设,而不是只看自身的切换事件:先折叠会话的生效沙箱模式(回退到执行器配置的模式),再折叠生效审批策略(先回退审批服务配置,再回退 ask)。匹配顺序是:仍然匹配的已记录选择优先,这样两个预设共享同一组旋钮值时,用户究竟选了哪个不会被吞掉;其次取声明顺序中第一个匹配的表项;都不匹配就返回 custom。
custom 只是派生值:客户端可以把它显示为当前值,但它绝不是切换目标,也绝不出现在事件 payload 中。names 按声明顺序列出可切换项;selectFor 针对一份折叠好的旋钮状态生成完整选择器,custom 只在确实派生出来时才追加进选项;optionOf 为某个表键(缺省标签回退为键名)或 custom 构建单个选项,传入其他名称抛异常。
四、set:一次切换的事件序列
set(session, name) 先解析预设,未知名称抛异常;当目标还不是生效预设时,追加一条 permission/preset 事件,随后调用 setSandboxMode 与 setApprovalPolicy 写入,且仅当该旋钮的生效值真的变化才写。同一轮次内,选择事件先于旋钮事件出现;重新选择当前生效的预设则什么都不追加。回放侧只需要 effectivePermissionPreset(events) 折叠最后一条,不需要任何追赶状态。
permission/preset 是持久、仅记日志的用户意图:它不进入模型转录,模型可见的后果由旋钮事件经各自的消费方承担。它存在的唯一理由,就是上文那个两个预设共享同一旋钮组合的场景:靠这条意图事件,current 才能分辨用户选中的是哪一个预设。
五、可复用的工程范式
这套设计有三条可以搬走。第一,意图与强制分离:组织层只做命名与记录,语义权威留在旋钮的折叠结果上,组织层因此永远可插拔。第二,派生优于记忆:当前状态从事件折叠出来,而不是另存一份会漂移的副本,回放零成本。第三,保留名钉死派生态:custom 被三条规则约束,可以显示、不可切换、不落盘,UI 与日志都骗不了人。仓库在 GitHub 的 deepseek-ai/deepseek-harness,装好 Node.js 后执行 npx @deepseek-ai/dsh web 即可在本地起 Web UI,客户端的权限选择器就是这层预设的直接入口。



