初见DeepSeek Harness和论文解析
DeepSeek Harness 简介
DeepSeek Harness(简称 DSH)是 DeepSeek 于 2026 年 8 月开源的一款 AI Agent 运行时框架,基于 Node.js 构建,以 MIT 协议发布。它的核心理念是 “一切皆插件”——模型适配器、工具注册表、会话日志乃至 Agent 主循环本身均为可替换插件,底层基于北大与 DeepSeek 联合研发的 Cordis 元框架实现动态组合。
DeepSeek 给出了一个直白的公式:Model + Harness = Agent。模型负责思考推理,Harness 则赋予模型“动手”的能力——读写文件、执行命令、调用工具、组织多步任务,让 AI 不再止步于对话,而是能从头到尾把事情做完。
Harness 预置了四种运行模式:标准模式提供完整的编码 Agent 能力;极简模式仅保留两个核心工具,适用于基准测试;PTC 模式让模型生成 TypeScript 脚本将多步工具调用压缩为单次交互,大幅提升效率;创造模式则允许 AI 检查和修改自身运行时配置。框架兼容 OpenAI、Anthropic、AWS Bedrock 等 40 余家模型提供商,并内置 MCP 客户端与多层沙箱安全机制。
截至 2026 年 8 月(发布仅数日),DSH 在 GitHub 已收获超 14.9 万 Star,社区贡献了超 5100 个插件,被开发者称为“Agent 领域的 Linux 时刻”。
接入 DeepSeek Harness
通过 npx 运行
安装 Node.js,然后运行:
npx @deepseek-ai/dsh web
该命令默认会在 http://127.0.0.1:3080 启动 Web UI,本机启动时还会用默认浏览器打开页面。通过 SSH 启动时只打印宿主机 URL,因为本地转发地址由 SSH 客户端或编辑器管理。传入 --no-open 可仅运行服务器而不打开浏览器。详见 Web UI 指南。
从源码运行
如需从仓库源码运行:
git clone https://github.com/deepseek-ai/deepseek-harness.git cd deepseek-harness pnpm install pnpm run build pnpm dsh web
pnpm run build 会准备仓库产物。pnpm dsh web 会直接使用这些已构建产物,不会重新构建。
安装方式摘自 DeepSeek Harness GitHub 主页,这里也将地址贴出来,大家有需要可以自己去看看:DeepSeek Harness GitHub
DeepSeek Harness 设计原理
它采用一切皆插件的架构,并由 Cordis 驱动,其设计参见论文 A Programming Paradigm for Spatiotemporal Composability。
我在阅读完论文后,也整理出了一些个人见解。
我把自己觉得比较有意思的内容摘了出来;那些理论知识我其实也没有理解得很透彻,这篇文章只是将论文中的一些原理整理出来。
1、 静态组合 vs 动态组合
传统组合(函数调用、模块导入、类继承)在编译期解析、运行期不变。而插件架构、自我进化的 agent harness 需要在运行时加载、卸载、重配置组件。尽管实践上越来越重要,动态组合的形式化基础远落后于静态组合的丰富理论。
作者提出组合性的两个正交维度(超越已被充分研究的"代数方面"):
| 维度 | 含义 | 静态情形下的退化形式 | 动态情形下的困难 |
|---|---|---|---|
| 时间可组合性 | 组件移除时其对共享环境的修改能被完全、安全地逆转 | 词法作用域(RAII、bracket 模式) | 长生命周期、有状态的效应,作用域不再被词法界定 |
| 空间可组合性 | 组件间依赖能结构化地声明、发现、解析、响应变化 | 模块导入解析 | 依赖在运行中出现、消失、更换身份 |
2、 动机示例
VSCode 插件系统(时间维度的失败):
-
所有扩展跑在共享的 extension host 进程中,无法在运行时卸载单个扩展的代码。前 100 名扩展中 87 个含可执行代码,禁用/卸载它们必须重启整个宿主。
-
deactivate钩子只是宿主进程终止前的优雅退出回调,不支持热移除;且它把"效应清理"与"效应创建"(在activate中)分离,违反局部性原则,清理的完备性无法验证。
VSCode 的空间维度失败:extensionDependencies 几乎无人使用(前 100 名仅 7 个声明);vscode.extensions.getExtension(...).exports 返回 any,无类型契约。
自我进化的 agent harness:harness 在持续服务请求的同时生成并部署对自身组件的修改。
-
没有时间可组合性 → 每次自修改都要全量重启、丢弃进程内累积状态、打断在途任务,甚至一次错误的自修改可能摧毁恢复机制本身;
-
没有空间可组合性 → 每个模块只能以 ad hoc 方式探测依赖变化,朴素替换会静默破坏依赖方或引入重载时才暴露的循环依赖。
粗粒度替代方案的代价:OS 在进程粒度提供时间可组合性、容器编排器在服务粒度提供空间可组合性。代价:重启丢失所有进程内状态(缓存、连接、部分计算),重建需秒到分钟级,还需冗余副本维持可用性;容器级编排无法表达同地址空间内的组件依赖,把本可以是本地函数调用的交互变成网络开销。
3、 贡献清单
-
形式化可逆效应(revertible effects)——局部时间可组合性;
-
形式化反应式协效应(reactive coeffects)——局部空间可组合性;
-
统一为单一上下文类型,观测等价供给效应独立性——构成一种编程范式;
-
给出动态组合演算及其元理论,把两维保证从单组件提升到整个系统;
-
实现 Cordis 元框架(核心库 + 声明式加载器 + HMR),并以 Koishi 验证。
实现:Cordis
以下至“案例:Koishi”部分为论文 §5 内容的摘译整理,非我的原创内容。
三层:核心库(效应/协效应)→ 组件加载器(配置 reconciliation + HMR)→ 应用框架(Koishi)。Table 2 给出 30 余条理论-实现对应,举要:
| 理论 | 实现 |
|---|---|
| Γ∞ | ctx,一等上下文 |
| effectΓ(e) | ctx.effect(callback) |
| Σ / Σiso / Σinter | ctx[@@store] / ctx[@@isolate] / ctx[@@intercept] |
| get(k) / set(k,v) | ctx.get(key) / ctx.set(key, value) |
| fiber ⟨d,p,e,π,σ,τ,θ⟩ | fiber(fiber.uid / fiber.inject / fiber.apply / fiber.parent / fiber.state) |
| 累加器 g | fiber.dispose |
| 承诺视图 ω | fiber.committed |
| 目标视图 target | fiber.target,由 refresh 重算 |
| 惯性 Future | fiber.inertia |
| O-Insert / O-Retire | ctx.use 与其回调的逆 |
| L-Begin/Iter/Finish | execute 的迭代循环 |
| L-Leave | refresh 标记 UNLOADING |
| L-Unload 的守卫 | unload 等待被通知的依赖方 |
1、 核心库
Algorithm 1(效应追踪):ctx.effect(callback) 是 effectiter 的实现。execute 引擎把回调当效应迭代器驱动,guard 在每步前检查(目标稳定性),每个 yield 的逆前插复合(LIFO)。dispose 翻转 armed 标志(同时停止在途迭代 + 保证恢复至多一次——二次触发会把逆施加到没有应用产生过的状态),并把自己前插到父上下文的 ctx.dispose——子效应的逆本身是父上的效应,即 𝜕²Γ 的递归结构。运行时不验证见证条件(逆是否真的恢复效应是组件作者的义务,§6.1 划定其边界)。
Algorithm 2/3(协效应操作):三个 symbol 键槽位:@@store(realm → 值)、@@isolate(key → realm)、@@intercept(key → 元数据)。set 是 ctx.effect 调用,安装和移除都调 notify。notify 遍历所有活 fiber,若变化 key 在其 inject 中且解析到同一 realm,则 refresh 之并返回受影响的 fiber(供调用方等待)。绑定只在安装它的 fiber 处于 ACTIVE 时可用——这使撤回对依赖方提前一步可见:进入 UNLOADING 的 provider 已停止提供,依赖方在其绑定全部仍在时就开始自己的拆卸。
隔离/拦截:都派生子上下文覆盖一张表,恢复是隐式的(丢弃子上下文)。
Algorithm 4/5(生命周期):
-
ctx.use(component, config):config 绑定进fiber.apply;callback(注册原语)执行时 refresh 启动子生命周期,恢复时强制目标为 ⊥ 并触发 unload——实例化是父的普通被追踪效应,卸载父级联到子; -
refresh重算 fiber.target(每个声明 key 解析到 provider 的 uid 的摘要——按 provider 而非按值识别,uid fresh 不复用,替换 provider 不会被误认);不在转移中则发起 reload 或 unload 任务; -
reload:提交解析视图(fiber.committed),execute 驱动 apply,guard 是"目标未变";完成时再检查目标——仍匹配进 ACTIVE 并 notify;不匹配链接到 unload(不论新目标是 ⊥ 还是另一组 provider); -
unload:先await所有被通知依赖方到达 INACTIVE(守卫),再施加fiber.dispose,最后按当前目标链回 reload 或落定 INACTIVE。等待置于整个恢复之前而非某个逆内部,因为 fiber 的各效应并发启动,放在其中一个里会使其余的失序。终止性对应 Thm 66:fiber 只等待已不可能满足的依赖方,provider 图按需遍历而非预先分析。
两个互补层级:转移级(reload/unload 完成时检查目标——惯性链接)+ 迭代级(execute 在每个迭代边界检查目标——部分回滚),分别对应 §4.3.3 的转移间链接与 Thm 64 依赖的转移内过期检查。
Algorithm 6(Proxy 属性访问):ctx[key] 沿 fiber 链向上走——第一个承诺视图绑定 key 的 fiber 授权访问;遇到声明了 key 但未承诺的 fiber 抛 INACTIVE_ACCESS;走到 root 无声明抛 UNDECLARED_ACCESS。与裸 ctx.get 的区别:Proxy 对照访问者自己的视图解析并在使用点强制执行规格 d;读视图而非读存储正是 Thm 63 的支点(依赖消失触发拆卸的组件在拆卸期间仍能读它)。这是运行时检查,但 d 是静态声明的,编译期检测原理上可行(§6.4)。
2、 组件加载器
声明式配置:entry = {id, url, isolate, intercept, config, disabled}。entry 能忠实指定 fiber 的原因:支撑集(Definition 67)恰好只读 τ/π/d/p,而 entry 给出全部四个——disabled 给 τ、树中父给 π、url 选定组件从而给 d 和 p。@cordisjs/group(子 entry 列表)与 @cordisjs/include(外部 YAML/JSON)是普通组件,嵌套树仍在演算内。
Reconciliation 的可靠性由元理论逐条供给:
-
Thm 73 ⇒ quiescent 状态只是最终配置的函数(增量重建与从零装配殊途同归);
-
Thm 66 ⇒ 必然 quiesce;
-
Corollary 62 ⇒ 重建一个 entry 不波及邻居;
-
Thm 63 ⇒ entry 可并发实例化,无加载顺序问题——依赖约束的是激活时机而非模块求值时机,模块可并行加载。
分派按字段取最小扰动操作:id/url 重建;isolate 重指派 realm;intercept 原地更新(读时生效,无需重载);config 交给组件 diff;disabled 卸/载。group 的 config 就是子 entry 列表 → keyed diff,与 entry 更新递归下降。
受管 realm 与 Algorithm 7:entry 可在组间移动,loader 自己管理 realm——isolate: true 要私有 realm(按 id 标记,随 entry 移动);字符串要全局共享 realm。难点:realm 符号可能被多个 fiber 共享而只有一个是 provider。解法:分隔符(每 key 一个符号 δₖ,context 上写标签、后代继承)——entry 与 provider 的标签一致 iff 两者派生自同一 isolate 作用域,即绑定是 entry 自己的、必须随它移动。重指派后,own 在依赖方与 provider 上一致 ⇒ 两者同移或同留(依赖方看到的绑定不变);分离 ⇒ 依赖方获得或失去绑定。
HMR(三阶段,§5.2.2):fiber 已界定组件的全部效应与协效应,所以模块替换 = 处置旧 fiber + 用重载的模块实例化新 fiber,无需 Webpack/Vite 那种开发者标注的 accept 边界。
-
Phase 1 模块分类:stashed=已变更,externals=不可热替换;不动点传播:有 import 被接受则接受、全部 import 被拒绝则拒绝、环中悬置者默认拒绝;
-
Phase 2 过期 entry 检测:依赖树与 accepted 相交则过期,树并入 accepted;
-
Phase 3 事务性重载:失效缓存(备份),逐 entry dispose + 重新 import + 换 fiber;任一失败则恢复缓存、全部回滚到备份组件——系统永不处于半重载状态。
3、 案例:Koishi
Koishi 是基于 Cordis 的开源聊天机器人框架,历经四年发展,已积累 4000+ 社区插件。它验证了:
-
表达力——每个功能都是上下文原语上的插件,Koishi 只贡献聊天机器人领域词汇;
-
一般性——Web 控制台是第二个独立 Cordis 应用,插件组合的是浏览器与 UI 原语。
时间维度:编排器从控制台禁用插件,效应就地撤回;HMR 保存时重应用编辑过的插件,同时保留系统别处的缓存状态与活连接——不熟练的作者也不写卸载路径就获得有序清理(局部性原则的恢复)。空间维度:真实依赖拓扑(IM 适配器、数据库驱动作为 provider,功能插件声明协效应),切换存储后端只重激活解析到它的依赖方;插件与依赖通常由不同作者编写,除连接它们的协效应外无任何协调。
效度威胁:单生态、单宿主语言、观察性证据——建立的是存在性与采纳性结果,量化对比留作未来工作。
结束语
回头看,这篇论文的贡献链条其实很清晰:可逆效应解决"能不能安全地撤",反应式协效应解决"依赖变了怎么办",动态组合演算把这两个保证从单个组件推广到整个系统,最终落到 Cordis 这个能跑的框架上。它不是一篇读完就能用的工程手册,但它回答了插件系统领域一个长期悬而未决的问题:动态组合这件事,能不能像静态组合一样被形式化地保证?论文给出的答案是能,并且给出了实现路径。
而 DeepSeek Harness 让我感兴趣的地方,恰恰是论文结尾写下的那个"未来方向"——把 Cordis 应用于自我进化的 agent harness。当我在 DSH 里切换插件、看着它热更新自己的组件时,用的正是这套理论的落地形态。论文里那些我当时读得吃力的概念——卸载时的累加器、依赖的解析视图——此刻就真实存在于这个工具的运行时里。先摸到工具,再回头读懂原理,这大概是我这次"初见"最大的收获:理论和产品之间的那层纸,被这次阅读捅破了一个角。
必须坦白的是,这篇 88 页的论文我并没有完全吃透——元理论部分的证明细节我仍是一知半解,上面的整理如果有理解偏差,欢迎指正,我会去核对修正。但即便只读懂了一部分,它带给我的启发也是真实的:原来"卸载一个插件"这样习以为常的操作背后,可以有一整套严谨的形式化保证。如果你也在用 DSH,或者对插件系统、agent 架构感兴趣,强烈推荐读一读原论文;哪怕和我一样只能读懂一部分,那部分也值回阅读的时间。后续我也会把实际使用 DSH 的体验整理出来,算是这篇"纸上得来"的下篇。
更多推荐



所有评论(0)