AI 深度技能之-解读DeepSeek Harness(一)- 初见

DeepSeek Harness 架构分析

仓库:deepseek-ai/deepseek-harness

协议:MIT 发布日期:2026-08-13(上线仅 1 天,developer preview 阶段)

技术栈:Node.js + TypeScript + pnpm workspace + Cordis 框架

代码量:1247 个 TS 源文件 + 684 个测试文件,packages/ 下按领域分目录

一、它到底是个什么东西

DeepSeek Harness(简称 dsh)是 DeepSeek 官方开源的 Agent 框架。dsh 不做产品,只做框架。它的核心口号是 Everything is a Plugin,包括模型适配器、工具注册表、会话日志、Agent 循环本身,全是可替换的插件。

一句话定位:如果你想自己造一个 Cursor / Claude Code / Codex 这样的 Agent 产品,dsh 给你造好了底盘。

二、它解决的痛点

痛点 1:Agent 框架的"核心不可改"

大多数 Agent 框架(LangChain、AutoGen、CrewAI)都有一个 privileged core。你想改 Agent 循环的行为,要么 fork 整个框架,要么继承一个 base class 然后祈祷上游别改。dsh 的做法是:没有核心 。Agent 循环本身就是一个插件(core/agent-loop),你挂一个自己的 Agent 循环插件就能替换它,其他插件不动。

这个设计来自 Cordis 框架。Cordis 源自 Koishi(一个聊天机器人框架)的生态,设计哲学是"插件贡献服务、类型化事件、可逆效应到共享上下文"。dsh 把这套搬到了 Agent 领域。

痛点 2:工具策略作为后补

很多框架的工具执行就是"调一个函数"。dsh 的工具执行管道有三层 waterfall 事件 + 一层 monotonic guard:

  1. tools/pre-execute waterfall:hooks、permission、sandbox 拦截
  2. Monotonic guards:deny 或 abstain,identity protected(不可被其他插件覆盖)
  3. tools/execute waterfall:timeout、retry、metrics 包裹 dispatch
  4. tools/post-execute waterfall:accept、block、replace、add context

这还没完。工具执行完后还有 finalizeContent(同步 content-only invariant)和 tools/result(frozen authoritative outcome)。一个工具调用从头到尾经过 6 道关。

痛点 3:会话状态不一致

Agent 跑久了,内存状态、日志、UI 显示、持久化副本会漂移。dsh 的解法是:Session Log 是唯一真相源。所有模型可见的东西都必须从日志可重建,运行时 invariant 会断言这一点。Fork、resume、transcripts、telemetry、persistence 全从这条流派生。

这条铁律的代价是:你想给模型塞一个新的可见输入,必须先发明一个 session event。不能直接拼字符串。

痛点 4:Subagent 实现五花八门

你想让 Agent 调 Agent,可能是在同进程里跑一个子 Agent,可能是 fork 当前会话,可能是调 Codex CLI,可能是调 Claude Code,可能是走 ACP 协议。dsh 把这些全抽象成 subagent provider 接口,一个接口五种实现:

  • subagent-in-process-driver:同进程子 Agent
  • subagent-spawn-in-process:进程内 spawn
  • subagent-fork-in-process:fork 当前会话
  • subagent-codex:委托给 OpenAI Codex
  • subagent-claude-code:委托给 Claude Code
  • subagent-acp:走 Agent Communication Protocol

加上 subagent-dsh-sdk(子 Agent 自己跑一个完整 dsh 实例),共 7 种 subagent 模式。每种都有完整的 continuation、inheritance、settlement、depth control 测试。

痛点 5:沙箱不可移植

Linux 有 Landlock、Mac 有 Seatbelt、Windows 有 ACL、云上有 E2B。dsh 把沙箱也抽象成 ctx.sandbox 服务,一个 provider 切换就移动了 Bash、PTY、LSP 的执行世界,不用 fork 任何工具。原生 Landlock 实现甚至写了一个 C 扩展(native/landlock-run/packages/linux-x64/),不是简单调 syscall。

三、架构总览

Cordis 框架的五个核心概念

要看懂 dsh,先得看懂 Cordis。五个概念:

  1. Plugin = Service 实现 :一个插件可以是带 injectapply(ctx) 的函数,也可以是 Service 子类。Cordis 把它的生命周期挂载到当前 context。
  2. Context = Service 仓库 :服务从 context 里用稳定 key 认领(ctx.toolsctx.llmctx.sessions),其他插件通过 key 找服务而不是 import 具体实现。
  3. inject 声明依赖:插件说自己需要哪些服务,Cordis 等这些服务都到位了才挂载它。启动顺序靠依赖声明而不是手动排序。
  4. 类型化事件 :服务通过 TypeScript declaration merging 声明事件名,然后按 emit / waterfall / parallel / serial 四种模式分发。分发模式是事件公共契约的一部分。
  5. 注册是可逆效应 :prompt section、tool schema、adapter、provider、listener 全通过 ctx.effect()ctx.on() 安装,reload 和 teardown 时按注册逆序自动撤销。

Profile 和 Bundle 的分层配置

一个运行中的 dsh 是启动时从有序层级组合出来的插件树。

  • Profile :存在 Harness home 里的命名组合,列出它堆叠的 bundle,存放 out-of-tree 插件,保存用户的 cordis.patch.ymlwebheadless 是官方模板。
  • Bundle :Cordis config row 和它们挂载的代码的分发格式。每个 bundle 在自己的 package.json 里用 dsh 字段声明:dsh.profile 列出 profile 的 bundle,dsh.bundle 指向 bundle 的 patch 文件。

层级应用顺序:profile 列出的 bundle 顺序 → profile 的 cordis.patch.yml → home 级 patch → --patch overlay。Patch 通过 id 定位 row,替换整条 config 或插入新 row。

dsh --profile web --dump-config 能打印你机器实际启动的插件树。任何 row 都可以用你自己的 patch 替换。

Turn Flow(回合流)

这是 dsh 的心脏。一个 step 是一次模型请求加它调用的工具。一个 turn 是零或多个 step:它在第一个 input 被 claim 之前打开,在什么都不欠的时候关闭。

bash 复制代码
turn/start
  claim next-step input + 一条排队消息
  组装 prompt section + tool schema
  -> agent/pre-step                   reject | enter(messages)
     reject,或第一次 enter 被重写为空 -> 关闭 turn 不产生 step
     step/start
     把 entered messages 追加为 user/message
     从日志派生模型历史
     agent/request -> llm/stream -> assistant/chunk* -> assistant/message
     tool/call* -> tools/pre-execute -> tools/execute -> tools/post-execute -> tool/result*
     step/end
     工具还欠一次请求,或 next-step input 到了 -> claim -> 下一个 step
  -> agent/turn-stopping
turn/end

turn/*step/*user/messageassistant/*tool/* 是持久化 session event;其余是跨三个域的 live extension point。agent/pre-stepagent/requestllm/stream、三个 tools/* 事件是 waterfall(listener 必须 call next() 委托);agent/turn-stopping 是 serial 且没有 next()

六个核心包

职责 ctx key
core/session append-only 的 SessionEvent 日志和内存存储 ctx.sessions
core/system-prompt prompt section 和 tool schema 组装 ctx.systemPrompt
core/tools 有 scope 的工具注册表和受保护的执行管道 ctx.tools
core/agent Agent 接口、live registry、agent/* 事件 ctx.agents
core/agent-loop 实现该接口的默认 driver ctx.agentLoop
core/scope 每 agent 的 scoped-registration 原语 library,无 key
llm/llm message 和 stream 词汇 + adapter seam ctx.llm

Capability Seam(能力接缝)

一个 seam 是一个可替换的能力,三个角色:Service Definition 声明接口、Service Provider 实现、Consumer 使用(通常是 model-facing tool)。一个包可能身兼多职,但只一个角色不算 seam。

Seam 是为什么"一个 provider 切换就改变整个产品"。filesystem 和 subprocess provider 共享一个执行世界,所以指向远程沙箱就把 Bash、PTY、LSP 全搬过去,没有 provider fork。Subagent provider 也是同样道理,背后从 fresh child agent 到委托另一个产品,差异全藏在接口后面。

四、几个有意思的设计

1. 事件分发模式写进公共契约

Cordis 的四种分发模式(emit / waterfall / parallel / serial)不是实现细节,是事件的公共契约。新事件必须用 @mode tag 声明分发模式,生成的 catalog 会检查声明和调用点是否一致。

这比"看着文档写 listener"靠谱。声明 waterfall 的事件,listener 就知道必须 call next();声明 serial 的就知道有返回值且按顺序。类型系统替你检查。

2. Monotonic Guard 不受插件顺序影响

工具执行的 monotonic guard 是一个特别的设计。普通 waterfall listener 的执行顺序受注册顺序影响,但 monotonic guard 的"deny"决策不可被后续 listener 覆盖。Identity protected。

这意味着安全策略可以独立于业务逻辑插入,不用担心某个后注册的插件把你的 deny 给 next() 掉了。这比 Odysseus 的 tool_security.py 靠"先检查后执行"的注释约束强得多。

3. Session Log 的"model-visible means logged"铁律

dsh 在 core/session 里有一个 runtime invariant:任何到达模型请求的东西必须能从日志重建。如果你给模型塞了一个新的可见输入但没发明对应的 session event,invariant 会断言失败。

这逼着开发者把所有模型可见的状态变更都做成显式的、可重放的、可序列化的事件。代价是写新功能时要先扩展 SessionEventMap。收益是 Fork、resume、transcripts、telemetry、persistence 全自动从同一条流派生,不可能漂移。

4. 7 种 Subagent Provider 背后的一致接口

subagent 的差异有多大?同进程跑一个子 Agent 和调 Codex CLI 是完全不同的事。但 dsh 把它们统一到 ctx.agents 接口后面:

  • 父 Agent 通过 tool-subagent 工具发起委托
  • subagent provider 决定委托怎么落地
  • subagent-in-process-driver 在同进程挂载子 Agent
  • subagent-codex 通过 Codex CLI 协议委托
  • subagent-claude-code 通过 Claude Code 协议委托
  • subagent-acp 走 ACP 标准协议
  • subagent-dsh-sdk 让子 Agent 自己跑一个完整 dsh

每种 provider 都有 continuation(继续跑)、inheritance(继承父 Agent 的能力集)、settlement(结算结果回传)、depth control(限制嵌套深度)的测试。subagent-multisubagent-parallelsubagent-mixed 三个测试 snapshot 证明这些 provider 可以混用。

5. 防御性编程模式文档化

docs/defensive-patterns.md 是一份"硬仗来的 bug 类规则"文档。每条规则都是一个真实发生过或差点发生的 defect class,写成防止复现的规则。举几条:

"报告正交结果要独立" :一个进程可能既 timeout 又 exit 0(因为它 trap 了信号)。每个独立事实(timedOutsignalexitCode)要各自上报,不能把一个 flag 嵌进另一个的分支。否则调用方读到截断的 run 当成干净成功。

"异步状态不是同步状态"agent.followup() 没有每消息完成或结果;background job 的完成和 turn boundary 竞态;reader.close() 既可能是 EOF 也可能是 disposal。不要把 agent/statuswhenIdle() 当成一次 follow-up 的结果。如果自动化调用方真的拥有一个 run,必须显式定义自己的 interval(从它的消息 durable inbox receipt 到下一个 whole-agent idle)。

"Dispose 必须到达静默,不是仅请求" :teardown 发了 kill/abort 但在工作停之前返回会留孤儿。cleanup 必须 async 并 await 子进程的 exit(kill → await done),并且在 kill 之前关闭 listener/notification registry,这样迟到的 completion 会保持沉默。

"不要给 untrusted output 递环境变量或可预测路径" :spawned command 拿到的是被擦过的 env(drop *KEY*/*SECRET*/*TOKEN*/*PASSWORD*),harness 凭证不会泄漏到 output、env 或 spill 文件里。Temp/spill 文件用 0700 私有目录、随机名、exclusive owner-only open('wx'0o600),因为可预测的 world-readable 路径会招来 symlink race 和披露。

"Unlink 链接型路径" :可能是 symlink 或 Windows junction 的路径,用 lstatSync().isSymbolicLink() 然后用 unlinkSync 删除。unlink 只删 link 拒绝真实目录,不会跟随 link 进入目标。Windows rmSync(link) 在 junction 上抛 ERR_FS_EISDIR;递归删除可能穿过 junction 进入目标。

这些不是理论规则,是从真实 bug 提炼的。docs/postmortem/ 有 4 份 postmortem 记录具体的失败案例。

6. Landlock 原生 C 扩展

Linux Landlock 是内核级文件访问控制。大多数项目要么不用,要么用纯 JS 的 binding。dsh 写了一个 C 扩展 native/landlock-run/packages/linux-x64/,还提供 arm64 prebuild。这意味着沙箱不是"最佳 effort",是内核强制。

对应 macOS 有 Seatbelt(bash-sandbox/tests/seatbelt.e2e.ts),Windows 有 ACL(pwsh-sandbox/tests/acl.e2e.ts),云上有 E2B(packages/e2b/)。四个平台四种沙箱后端,全走 ctx.sandbox 接口。

7. Typert:类型安全的插件协议生成器

packages/typert/generator/ 是一个 codegen 工具。它分析 Cordis 服务的 TypeScript 类型,生成跨进程的类型定义。这意味着你写一个 Cordis 插件,它的服务接口可以自动暴露给客户端(browser、子进程、远程)使用,类型安全。

packages/typert/protocol/ 是协议定义,packages/typert/registry/ 是服务注册,packages/typert/loader/ 是加载器。这一套让 dsh 的 Web UI 可以类型安全地调用 host 侧的服务。

五、短板和风险

1. Cordis 学习曲线陡峭

Cordis 不是主流框架。它的源头是 Koishi(一个中国 QQ 机器人生态),文档主要在中文社区。dsh 虽然有英文文档和 primer,但开发者要先理解 Service、Context、inject、四种 dispatch mode、reversible effects、waterfall semantics 这些概念,才能写第一个插件。

这比 LangChain 的"import一个chain"门槛高得多。dsh 自己也意识到这个问题,提供了 7 篇 cordis-tutorial 从零教起。

2. developer preview 阶段,不稳定

README 加粗写着:"THERE WILL BE COMPATIBILITY-BREAKING CHANGES." 上线仅 1 天,API 还在剧烈迭代。现在基于 dsh 做产品,要做好跟着改的准备。

3. 代码量巨大但分散

1247 个 TS 源文件分散在 packages/ 下的几十个目录里。一个完整的 Agent 能力(比如"subagent")横跨 7 个 package,要理解全貌得读多个目录。docs/subsystems/ 有 50+ 个文档页,但初学者定位"我要的功能在哪个包"仍需时间。

4. 文档密集但门槛高

docs/ 下有 architecture、cordis-primer、cordis-tutorial(7 篇)、cordis-api(6 篇)、subsystems(50+ 篇)、cookbook(6 篇)、postmortem(4 篇)、defensive-patterns、testing、glossary。文档质量很高,但量太大,对新开发者不友好。

5. 没有内置模型

dsh 不自带 DeepSeek 模型。你要自己接 model adapter(通过 ctx.llm)。虽然 subagent-codexsubagent-claude-code 可以委托给 Codex/Claude Code,但如果你要用 DeepSeek 自己的模型,得自己写 adapter 或等社区贡献。

6. 前端生态复杂

Web UI 在 packages/client/ 下,用 React + Vite。有 ui-primitives(30+ 组件)、ui-cordis(Cordis 运行时可视化)、ui-settings-pluginsui-sidebarui-skillui-subagentui-attachment 等十几个 UI 包。定制 UI 需要理解 Cordis 的 client-runner 架构,不是简单的 React 组件复用。

六、一句话评价

DeepSeek Harness 是近两年 Agent 框架领域架构设计最讲究的一个。它不堆功能,堆抽象。Cordis 的 Service + Event + Seam 三件套,配合 Session Log 铁律、Monotonic Guard、Subagent Provider 接口,把"造一个 Agent 产品"这件事的工程门槛拉到了新高度。

代价是学习曲线陡峭,developer preview 不稳定,文档量大但门槛高。但对于想认真造 Agent 产品的团队,这是目前最值得研究的框架。

MIT 协议是它最大的优势。

相关推荐
oort1231 小时前
OortCodex 是奥尔特云 OortCloudSmart 推出的国产化工程化 AI 编码 Agent(奥尔特云编码智能体)
大数据·人工智能·算法
TheBestRucy1 小时前
基于Dify的旅游攻略&王者荣耀攻略智能助手项目
服务器·开发语言·人工智能·python·算法·旅游
chen_zn951 小时前
《WAM 系列》Fast-WAM|视频共训练|跳过未来想象|实时WAM
人工智能·具身智能·vla·世界模型·世界动作模型
能年玲奈喝榴莲牛奶1 小时前
资产和漏洞管理系统(AI)
人工智能·安全·web安全
DS随心转小程序1 小时前
借助 AI 导出鸭简化各类办公场景下文心输出 word 文档全流程操作
人工智能·aigc·word·豆包·deepseek·ai导出鸭
lifallen1 小时前
Orca 与 Emdash:同样管理多个 Agent,差别在谁来调度
人工智能·学习·ai·软件构建·开源软件·ai编程
m0_547486661 小时前
《人工智能通识基础与AIGC应用》全套PPT课件2026
人工智能·aigc
AI刀刀1 小时前
腾讯元宝粘贴到 word 格式混乱,AI 导出鸭一键规整排版
人工智能·c#·word·ai导出鸭
测开小菜鸟2 小时前
AI Agent 智能体:当大模型长出“手”和“记忆”
大数据·人工智能·数据挖掘