
字节笔记本
2026年10月6日 · 约 9 分钟读完
DeepSeek Harness 开发指南讲了什么
DeepSeek 把自家的智能体运行时 Harness 开源到了 GitHub,包名 @deepseek-ai/dsh,MIT 协议,一条 npx @deepseek-ai/dsh web 就能在本地拉起 Web 界面。项目自称"一切皆插件",底层由 Cordis 框架驱动,目前处于开发者预览阶段,官方明说后续会有破坏性变更。仓库里的开发指南面向贡献者,从环境搭建一路讲到 CI 组织,信息密度不低。本文把这份指南的要点拆出来,也给想借鉴其工程实践的团队画几个重点。

环境与首次搭建
前置条件不多:Node.js 22.19 以上或 24 以上(CI 实测覆盖 22.19、24、26 三条版本线)、经 Corepack 启用的 pnpm(仓库锁定 11.7.0)、Git 2.26 以上,外加一个可选的 DeepSeek API key。首次搭建只有三步:
pnpm install
node scripts/install-lefthook.mjs
pnpm run typecheck第二条只在依赖来自缓存恢复或 postinstall 被跳过时才需要手动补跑。pnpm install 会顺手完成两件容易被忽略的事:装好 worktree 本地的 Lefthook 钩子,注册名为 dsh-translation-pairing 的 Git 合并驱动。pnpm run typecheck 成功退出,搭建即告完成。凭证只从环境变量或被 gitignore 的 .env 读取,DEEPSEEK_BASE_URL 可选,默认指向公开 API;不设置 key 时,真实 API 的 e2e 套件会自动跳过,不会挡路报错。
TypeScript 配置为什么拆成两个聚合
这个仓库最特别的设计,是 Host 与 Client 两个相互隔离的聚合程序:Host 包登记进 tsconfig.host.json,Client 包登记进 tsconfig.client.json,普通包只能进其中一个。原因藏在 cordis 的 Context 接口里:两侧在相同的键上做声明合并、注册不同的服务,放进同一个 ts.Program 就会报合并冲突。有意思的是,这个冲突只存在于编译器程序内部,模块解析永远不会触发它,所以 solution 根可以同时引用两个聚合,路径映射门面也能横跨两侧。
由此推出三条纪律:tsconfig.base.json 永不添加 include 或 files;全仓脚本构造 ts.Program 时显式以 host 或 client 聚合为种子,绝不用根 solution;新包只登记进一个聚合,同时有 Node 入口和浏览器入口不构成拆包理由。整个仓库只有 api/remotes 是例外,它拆分了 Host 与 Client 两份 tsconfig,workspace 的 constraints 门禁会遍历项目引用图,按两个 leaf 配置是否同时存在自动发现拆分包,并约束其他项目对它的引用方式。
构建按依赖顺序走五步:
tsc -b tsconfig.host.json
tsdown --env.DSH_BUILD_FACE host
tsc -b tsconfig.client.json
tsdown --env.DSH_BUILD_FACE client
pnpm run build:webTypert 只在 Host 阶段运行,分析 Host 类型并生成反射产物与 Host-for-Client Remote 投影;业务服务用 @Remote 或 @RemoteScope 声明可调用方法,Client 侧把这些贡献装配到 ctx.remote 命名空间下调用。pnpm run typecheck 因此要先完成整个 Host lib 阶段,才轮到 Client 的 TypeScript 检查;pnpm run build 则继续走完 Client tsdown 与 Web 构建。
钩子只做快检,全量交给 CI

Lefthook 在这里被定位成快速的本地检查点,三个钩子各司其职:pre-commit 校验暂存的配对文档记录、用不加载项目的 Oxlint 配置检查暂存文件(带一次有界重试的自动修复)、按需重新生成第三方声明文件、检查空白错误并执行 vendor 清单守卫;pre-merge-commit 在自动合并提交前重复配对检查;pre-push 跑 pnpm run typecheck。
更值得注意的是钩子"有意不跑"的部分:测试、快照、文档检查、构建、hygiene 全都不放进本地钩子。贡献者按改动面选最小检查集,文档改动跑 pnpm run doc-sync,动过包公开行为的同步更新所属 README 或 JSDoc,需要构建产物的检查(比如 publint 校验包入口、NodeNext 消费方校验声明文件)先跑一次 pnpm run build。想要全量可以手动 pnpm run check:all,它与钩子相互独立。穷尽式覆盖、构建产物冒烟和 Node 三版本兼容矩阵,由 keyless 的 CI 工作流按宽粒度 lane 分组兜底,真实 API 的 e2e 走独立工作流。本地体验与全量保障的边界写在文档里,不靠默契。
双语文档也能自动合并
这个仓库的文档中英成对,由 .i18n.yaml 记录配对关系。当两侧文件都能按 Git 默认文本策略干净合并时,配对合并驱动会依据祖先、当前与另一侧的文件内容,自动推导出冲突的配对记录;一旦碰上配对冲突、非文本合并配置或无效记录,驱动一律拒绝处理,宁可停在冲突状态。事后恢复跑 pnpm run resolve-translation-pairing-conflicts,它会暂存所有能安全生成的配对记录,剩下需要手工处理的才以非零状态退出;驱动运行时不可用时则退回普通文本合并,留下未解决的伴随文件和明确的恢复路径。把双语目录的合并对账做成 Git 层面的基础设施,这个思路可以直接搬走。
文档里的类型定义被逐字校验
子系统文档会把与源码等价的类型声明连同原始 JSDoc 一起粘贴,让读者同时看到确切类型定义和源码约定。为防粘贴内容随源码漂移,代码块要用 ts type-equiv 围栏标注,并在 scripts/type-equiv.manifest.json 里登记镜像的源文件与符号。pnpm run verify-type-equiv 用 TypeScript 解析器从源码抽出声明和 JSDoc,断言与文档一致:忽略空白与非 JSDoc 注释,但要求每条原始 JSDoc 都在。类可以用 public-api 投影,保留公共字段、构造函数、访问器与方法而省略实现体和私有成员。改了声明不改文档,门禁立刻变红;增删主块则要求同一变更里更新 manifest。文档漂移在这里被当成了 bug,而不是排版问题。
三个演示与一套标记约定
构建完成后有三个可直接跑的演示:pnpm dsh --profile headless "summarize this workspace" 是一次性 headless 编码智能体;pnpm run demo:cordis 能在运行时检查并修改自己的插件运行时;pnpm run demo:acp 通过 JSON-RPC stdio 暴露全新智能体会话,适合自动化集成。另外代码里的待办标记分三级:FIXME 应当阻塞发版,除非评审明确放行;TODO 尽快修,等资源到位;XXX 有空再说,优先级最低。扫代码的人一眼就能分清"发布阻塞"与"遥遥无期"。
值得抄走的三件事
一是编译器层面的隔离思路:声明合并天然冲突时,不绕开而是把边界画进项目引用图,再用门禁自动约束引用方式,规则落在配置里而不是口头约定里。二是分层的质量策略:本地钩子只做秒级快检保住提交体验,全量覆盖交给 CI,两层职责白纸黑字写进开发指南。三是文档当代码:类型定义逐字校验、双语文档配对合并,把"文档与源码不一致"当成会被门禁拦下的错误。这三件事与具体框架无关,任何维护多包仓库的团队都用得上。
仓库地址:https://github.com/deepseek-ai/deepseek-harness ,MIT 协议,开发者预览阶段,npm 包名 @deepseek-ai/dsh。



