
字节笔记本
2026年10月6日 · 约 6 分钟读完
DeepSeek Harness 进程沙箱设计解析
DeepSeek Harness(dsh)是 DeepSeek AI 开源的 Agent harness,主打"一切皆插件"的架构,底层构建在 Cordis 框架上。Agent 要替用户执行 shell 命令、读写文件,最先要回答的问题是:怎么给这些与宿主共享文件系统和内核的子进程划一条硬边界?dsh 的答案是一个名为 dsh-sandbox 的进程沙箱接缝:它把子进程的 argv 包装进文件效果策略里执行,消费方完全不需要耦合某个特定平台的运行器。本文把它的工作机制拆开来看。

一个接缝,多套后端
在 dsh 的包结构里,dsh-sandbox 只定义接口,dsh-sandbox-local 提供本地实现,覆盖三类平台:Linux 的 bwrap 与 Landlock、macOS 的 Seatbelt、Windows 的 ACL 受限令牌。真正负责 spawn 进程的两个消费方是 dsh-bash-sandbox 和 dsh-pwsh-sandbox。值得留意的是,容器、microVM 和远程执行并不是这个接缝的实现者,它们属于完整能力接缝的同级方案,而不是 ctx.sandbox 的提供方。
模式与强制执行是两回事
SandboxMode 词表只有三个值,并且只管控文件系统效果。read-only 要求后端拒绝一切写入,只保留 /dev/null 这类必需接收器,POSIX 运行器要为 shell 留这个出口,Windows ACL 运行器则不给任何显式可写根目录;workspace-write 开放工作区根目录以及后端承诺的临时区域;danger-full-access 直接绕过隔离,选择这条路的消费方会原始 spawn 命令,根本不经过 ctx.sandbox。网络与进程可见性不在这个词表的定义范围内。
强制执行完整性由后端如实上报,分 full 与 partial 两级。full 表示后端管控了该模式承诺的全部文件效果;partial 表示活跃后端或较旧的内核 ABI 只能覆盖其中一个子集,文档点名的情形包括较旧的 Landlock ABI,以及 Windows ACL 运行器的 Everyone 权限与硬链接边界。需要绝对保证的消费方必须把 partial 拒之门外,或者把这个差异显式暴露给上层,不能当作 full 来用。
策略按每次调用携带
这套设计里最关键的取舍是:沙箱策略不属于提供方,而属于每一次能力调用。完整的执行策略包含 mode、workspaceRoot 和会话标识三项,由 ctx.sandboxPolicy.resolve() 统一解析:显式批准的模式优先于会话日志里最后一次 sandbox/mode 事件,再优先于部署默认值;工作区根默认取会话不可变的 cwd,没有 Agent 参与的调用回退到部署配置。cwd 会先按文件系统语义规范化,再做词法规范化,这样带 symlink 加 .. 的路径也能指向进程真正运行的那个目录。
按调用携带策略带来一个直接好处:两个消费方可以在同一时刻用不同边界请求同一个提供方,比如 bash 跑在 read-only 之下,而一个受限子 Agent 需要自己的状态目录可写;一次已批准的提权重试也只是携带更宽策略的新调用,提供方的状态不需要任何改动。
包装出来的 argv 与两套 stderr 方言
confine(argv, policy) 返回的 ConfinedArgv 里,除了替换后的 argv(运行器、配置、分隔符加上原始命令),还带着三样东西:后端实际达成的 enforcement、拒绝签名 denialSignatures、运行器失败规则 runnerFailureRules。
拒绝签名回答的是"沙箱是否正常工作"。受限命令被拦下时,各后端在 stderr 留下的字符串并不相同:bwrap 的只读绑定报 EROFS,Landlock 报 EACCES,Seatbelt 报 EPERM。消费方判断拒绝时只匹配当前后端自己的方言,而不是跨后端的并集,因为并集会声称一些某个后端根本不会产生的拒绝。

运行器失败规则回答的是"沙箱基础设施是否自己坏了"。runner 在执行命令之前就失败,意味着命令从未运行。判定条件是进程以非零状态退出,可选地满足退出码门控,并且剩余 stderr 行中存在大小写不敏感的致命签名;判定之前会先按整行精确匹配剔除信息性排除项,避免无害的运行器通知被当成失败证据。消费方的检查顺序因此是固定的:先查运行器失败并作为基础设施故障上报,再查拒绝签名,最后才归因为普通任务失败;匹配到的行仍可当作错误详情,分类过程不改写 stderr。
fail-closed 是底线
整个子系统有一条不能让步的红线:对受限策略而言,静默的无隔离透传永远不合法。confine 要么返回一个会真正强制执行的 argv,要么在没有可用后端时抛出 SandboxUnavailableError(错误码 SANDBOX_UNAVAILABLE)。功能性探测用来在多运行器链里仲裁选择,链上只剩一个候选时可以跳过探测,因为它自己的拒绝就是 fail-closed 的终点。
对 Agent 工程的三点启示
一是策略与机制分离:模式词表、工作区边界与平台强制执行各管各的,换平台不必动协议。二是把"沙箱拦住了命令"和"沙箱自己没起来"拆成两套信号,排障时才能分清是任务失败还是基础设施故障。三是 fail-closed 要写进类型定义与错误路径,而不是停留在文档口号里。
该项目已在 GitHub 开源(deepseek-ai/deepseek-harness),MIT 许可证,目前处于 developer preview 阶段,兼容性仍会快速变动,感兴趣可以用 npx @deepseek-ai/dsh web 直接启动体验。



