DeepSeek Harness:真正值得研究的,不是 Agent,而是它如何把自己拆掉

原文:https://blog.huanment.top/posts/deepseek-harness-modular

最近 DeepSeek Harness 的热度有点夸张。

但如果只把它理解成“DeepSeek 做的一个 Coding Agent”,其实很容易看错重点。

我真正感兴趣的,是它在架构上做了一个相当激进的选择:

Everything is a Plugin.

这句话不是 README 里随手写的一句宣传语。DeepSeek 官方的架构文档直接把话说得很彻底:模型适配器、Tool Registry、Session Log,甚至 Agent Loop 本身都是 Plugin;系统没有一个需要被所有功能依赖的“特权 Core”,新的能力应该通过挂载插件加入。

这件事乍看有点反常。

我们设计一个 Agent,通常会先想:

Agent
├── LLM
├── Tools
├── Memory
├── Filesystem
├── Sandbox
└── Agent Loop

然后把这些东西一点点实现出来。

DeepSeek Harness 却反过来做:

Agent
  ↓
Plugin
  ↓
Plugin
  ↓
Plugin
  ↓
Plugin

连那个“负责运行 Agent 的东西”,都没有资格成为特殊存在。

它甚至把自己最重要的部分拆掉了。

我认为,这才是 DeepSeek Harness 最值得研究的地方。

一个系统什么时候开始变得难以修改?

大型项目最麻烦的时候,通常不是代码多。

而是你发现:

改一个地方,需要知道另外十个地方。

比如一个 Agent 原本使用本地 Shell。

第一版代码可能非常简单:

await exec(command)

后来有人提出:

  • 能不能放到 Docker?
  • 能不能放到远程机器?
  • 能不能限制权限?
  • 能不能增加 PTY?
  • 能不能换成某种 Sandbox?
  • 能不能让不同 Agent 使用不同执行环境?

如果 Shell 的实现直接长在 Agent Loop 里,问题就来了。

你不是在“增加一个 Shell Provider”。

你是在修改 Agent

于是一个本来应该独立变化的东西,开始污染整个系统。

这就是大型软件最常见的一种腐化:

A 依赖 B
B 依赖 C
C 又知道 A

最后任何东西都不能真正独立。

目录可以拆得非常漂亮,类名可以设计得非常优雅,但只要变化会在模块之间到处传播,它就仍然是一团耦合的代码。

所以模块化真正解决的问题,从来不是:

“怎么把 5000 行代码拆成 50 个文件?”

而是:

“如果某个东西明天完全换掉,系统里究竟有多少地方需要跟着改?”

这才是判断模块边界的一个很好的标准。

DeepSeek Harness 的答案:别让 Agent 知道“谁在干活”

DeepSeek Harness 底层使用 Cordis。

官方架构文档里有一个非常重要的概念:Capability Seam

它把一个可替换的能力拆成三个角色:

Service Definition
        ↓
定义“我需要什么能力”
        ↓
Service Provider
        ↓
提供具体实现
        ↓
Consumer
        ↓
使用这个能力

比如 filesystem。

Consumer 只需要知道:

ctx.fs

至于背后究竟是:

Local FS
Docker FS
Remote Sandbox FS

并不重要。

这件事的价值比“抽象出一个 interface”大得多。

因为它改变的是依赖方向。

以前可能是:

Agent → LocalFilesystem

现在更接近:

Agent → Filesystem Capability ← LocalFilesystem
                              ← RemoteFilesystem
                              ← SandboxFilesystem

于是你换掉 Provider,Consumer 根本不需要知道。

官方文档甚至举了一个很有意思的例子:

Filesystem 和 subprocess provider 共同构成执行环境,因此只需要切换到远程 sandbox,Bash、PTY、LSP 等能力就可以一起迁移,而不需要给每一个能力分别做一套 provider。

这里其实藏着一个非常漂亮的架构思想:

模块化不是让每个东西都独立,而是让“变化”沿着正确的边界传播。

如果 Shell、PTY、LSP 都依赖同一个执行世界,那么切换执行世界时,它们就应该一起变化。

这才是真正合理的耦合。

更有意思的是:Agent Loop 也被拿掉了

如果让我从 DeepSeek Harness 里挑一个最能体现设计野心的地方,我反而不会选 LLM Adapter。

我会选 Agent Loop

因为 Agent Loop 几乎就是 Agent 的心脏。

通常我们会把它写成:

while (...) {
    request LLM
    if tool call:
        execute tool
    else:
        break
}

然后所有 Agent 的行为都建立在这个 Loop 上。

DeepSeek Harness 偏偏没有这么做。

官方文档明确写着:

model adapter、tool registry、session log、agent loop itself 都是 plugin。

也就是说:

Agent
  ↓
Agent Interface
  ↑
Agent Loop Plugin

Loop 只是 Agent Interface 的一个实现。

那么以后如果你想做:

普通 Tool Loop
Plan → Execute Loop
Multi-Agent Loop
Research Loop
Human-in-the-loop Loop

它们理论上都可以站在同一个 Agent 能力之上。

这时候 Agent 就不再等价于某一个固定的 while 循环。

Agent 变成了一个可以被不同运行策略解释的东西。

这其实非常重要。

因为今天我们习惯的 Agent,大多数仍然隐含着一个假设:

Agent = LLM + Tools + 一个固定循环。

DeepSeek Harness 在架构上把这个等式拆掉了。

它不是“插件很多”,而是“插件之间没有谁天生高贵”

很多项目也有插件。

但插件往往是这样的:

Core
├── Plugin A
├── Plugin B
└── Plugin C

Core 是老板。

Plugin 是员工。

Core 可以调用 Plugin,Plugin 却不能真正改变 Core。

DeepSeek Harness 的设计有意思在于,它试图把这个“老板”也取消掉。

官方架构文档甚至直接写:

There is no privileged core to patch.

新的行为不是去修改一个巨大的核心,而是“mounting a plugin beside the others”。插件卸载时,它注册的 effect 也可以反向撤销。

所以它更像:

             Cordis Runtime
                    │
       ┌────────────┼────────────┐
       ↓            ↓            ↓
    Plugin A     Plugin B     Plugin C
       ↓            ↓            ↓
    Service       Event        Service
       └────────────┼────────────┘
                    ↓
                 Context

这和“一个 Core + 一堆扩展”其实已经是两种完全不同的世界。

前者的哲学是:

Core 定义系统,Plugin 扩展系统。

后者则是:

Runtime 定义规则,Plugin 共同组成系统。

DeepSeek Harness 更接近后者。

Profiles 和 Bundles:真正把“组合”变成了一等公民

如果插件只是可以安装,事情还没有那么有意思。

真正让我觉得这个架构开始成立的,是 Profile / Bundle

DeepSeek Harness 的运行实例不是固定的一套程序,而是启动时由多个 Layer 组合出来的 Plugin Tree。

官方文档给出的结构大致是:

Profile
  ↓
Bundle
  ↓
Plugin Tree

例如 dsh-base 可以提供模型适配器、Tools、Persistence、Sandbox、Approval Policy、Settings、Credentials、Telemetry;然后 dsh-web-app 再增加 Web UI,dsh-headless 则可以提供没有服务器的运行方式。

这意味着:

dsh web

和:

dsh headless

并不是两个完全不同的程序。

它们更像是:

        同一个 Runtime
              │
       ┌──────┴──────┐
       ↓             ↓
     Web Profile   Headless Profile
       ↓             ↓
   一组 Plugins    另一组 Plugins

这时候,“产品形态”本身都变成了组合结果。

这比单纯提供一个 --headless 参数要有意思得多。

因为参数是在修改一个已经存在的产品。

而 Profile 是在:

选择你究竟要组装出什么产品。

这时候 Vite 就很好理解了

讲到这里,再看 Vite,会发现它和 DeepSeek Harness 有一种很微妙的相似性。

Vite 2 的架构改造中有一句非常关键的话:

framework agnostic core

Vite 官方当时明确表示,Vite 2 被重新设计成 framework-agnostic,框架相关的能力交给插件;新的插件系统建立在 Rollup Plugin API 之上。

这其实就是一个非常典型的“把变化赶出 Core”。

Vite 不负责成为:

React Build Tool
Vue Build Tool
Svelte Build Tool

它只负责提供:

Dev Server
Module Graph
Transform Pipeline
Build
Plugin Runtime

然后:

@vitejs/plugin-react
@vitejs/plugin-vue
@vitejs/plugin-rsc
...

把具体框架接进来。

现在 Vite 官方文档依然把 React、Vue 等框架支持明确列为 Plugin;官方 Plugin Registry 到 2026 年已经列出了超过 5500 个插件

这个数字其实很能说明问题。

Vite 并没有自己实现 5500 种能力。

它只是提供了一个:

别人愿意在上面继续建设的接口。

Vite 最聪明的地方,是它没有试图“支持 React”

这句话听起来有点奇怪。

Vite 当然支持 React。

但从架构角度看,Vite 并没有把 React 变成 Vite 的内部概念。

React 是:

Vite
  ↓
Plugin API
  ↓
@vitejs/plugin-react

Vue 也是:

Vite
  ↓
Plugin API
  ↓
@vitejs/plugin-vue

这意味着 Vite 不需要知道 React 内部究竟发生了什么。

而 React 团队也不需要修改 Vite 的核心。

双方只需要遵守一个共同的协议。

这就是插件系统真正强大的地方:

它不是在消灭依赖,而是在控制依赖应该停在哪里。

Vite 官方现在的 Project Philosophy 甚至直接把这一点概括为 Lean Extendable Core:核心保持精简、长期可维护,同时提供足够强的 primitive 和 API,让插件覆盖多样化需求。

这句话其实非常值得大型项目反复琢磨。

DeepSeek Harness 和 Vite,真正相似的是“把未知留在外面”

回到 DeepSeek Harness。

它和 Vite 并不是因为“都有 Plugin API”才值得放在一起比较。

真正相似的地方是:

它们都拒绝提前回答一个问题:未来到底需要什么。

Vite 没有决定:

未来一定只有 React 和 Vue

所以它留下了 Plugin API。

DeepSeek Harness 也没有决定:

未来的 Agent 一定只有这一种 Loop
未来的执行环境一定是本地
未来的模型一定通过这一种方式接入

所以它把:

LLM
Tools
Filesystem
Sandbox
Agent Loop
Session
UI

全部拆成可以组合的能力。

这两个项目的核心思路可以压缩成一句话:

核心只负责那些必须稳定的东西,变化交给外部。

区别只是 Vite 的“外部”主要是前端工具生态,而 DeepSeek Harness 面对的是 Agent 运行时生态。

模块化真正可怕的地方:复杂度不会消失,但不会相乘

假设有:

3 个 LLM
3 个 Sandbox
3 个 Filesystem
4 个 Agent Loop

如果它们彼此强耦合,你真正面对的不是 13 个模块。

而是大量组合关系:

LLM × Sandbox × Filesystem × Loop

一旦这些组合需要特殊处理,复杂度会迅速膨胀。

模块化的价值并不是让复杂度凭空消失。

而是把:

“每一种组合都需要特殊代码”

变成:

“每个模块只需要遵守自己的契约”

于是增加一个 LLM:

旧系统
  ↓
修改 Agent
修改 Tool
修改 Loop
修改配置
……

模块化系统
  ↓
增加一个 LLM Plugin

这就是为什么大型系统越往后,越应该在意模块边界。

小项目里,耦合甚至是一种效率。

因为你今天写:

agent.llm = deepseek

明天改一下就好了。

但项目一旦活了三年、五年,参与者从一个人变成一百个人,需求从十个变成一千个,这种“顺手写进去”的东西就会开始收利息。

而且是复利。

插件生态的真正价值,不是“官方少写代码”

这是很多人理解插件系统时容易忽略的地方。

如果插件只是为了让官方团队少写几行代码,那其实没有什么了不起。

真正有价值的是:

Core
 ↓
API
 ↓
第三方开发者
 ↓
新的能力
 ↓
新的组合
 ↓
新的用户
 ↓
新的需求
 ↓
更多插件

它会形成一种正反馈。

Vite 现在就是非常典型的例子。

Vite 官方文档提到,随着生态成熟,Nuxt、SvelteKit、Astro、React Router、Analog、SolidStart 等项目都把 Vite 作为自己的基础设施。

甚至到了 Vite 8,Vite 已经进一步把构建底层切换到了 Rolldown,同时继续保持对 Rollup Plugin API 的兼容;官方还专门推出了 Plugin Registry 来应对不断增长的插件生态。

这就出现了一个非常有意思的现象:

Vite 的影响力已经远远超过了 Vite 自己的代码。

因为它创造的不是某几个功能。

而是一块“生态可以长出来的土地”。

DeepSeek Harness 现在最值得观察的,也正是这件事

DeepSeek Harness 目前仍然是 Developer Preview,官方 README 明确提醒会存在兼容性破坏。

所以现在就断言它一定会成为 Agent 世界的“Vite”,显然太早。

但它的架构方向已经非常清楚。

尤其是官方甚至专门提供了:

dsh-plugin

这个 GitHub Topic,帮助第三方插件被发现。

这意味着它并不是单纯把 Plugin 当成内部工程技巧。

它已经开始把:

插件 → 第三方开发 → 生态

当成产品设计的一部分。

而这才是事情开始变得有意思的地方。

但真正困难的,从来不是“插件化”

插件系统很好听。

实际上也非常难做。

因为一旦把能力从 Core 拿出去,就意味着:

Core
 ↓
必须稳定

Plugin:

Plugin
 ↓
必须能够依赖这个稳定边界

所以真正困难的问题变成:

这个边界究竟应该画在哪里?

画错了,插件系统会变成:

Plugin
  ↓
偷偷依赖 Core 内部实现
  ↓
Core 一改
  ↓
全生态爆炸

画得太死,又会变成:

Plugin API
  ↓
什么都不能做

Vite 的 Plugin API 能够活到今天,一个很重要的原因就是它不是凭空发明一套巨大接口,而是建立在 Rollup 已经验证过的 Plugin Interface 之上,再加入 Vite 自己的能力。官方文档目前仍明确强调这一点。

DeepSeek Harness 现在面临的其实也是类似的问题。

它需要证明:

Capability
Plugin
Event
Context
Profile
Bundle

这些边界不仅今天好用,而且能够承受未来生态不断往里面塞东西。

插件系统真正的考验,不是第一百个插件能不能写出来。

而是第一百个插件写出来以后,第一个插件还能不能正常工作。

最后,重新看“Everything is a Plugin”

所以现在再回头看这句话:

Everything is a Plugin.

它就不再像一句很酷的架构口号了。

它真正表达的是一种非常克制的设计:

不要把未来写进 Core。

DeepSeek Harness 没有把 Agent Loop 写成不可动摇的核心。

没有把某一种 LLM 写死。

没有把某一种 Filesystem 当成唯一答案。

甚至没有把 Web UI 当成 Agent 本身。

它试图留下的是:

Runtime
Capability
Contract
Composition

然后让真正的系统在启动时长出来。

这和 Vite 的思路其实非常像。

Vite 没有试图成为 React。

它让 React 通过 Plugin 成为 Vite 的一部分。

DeepSeek Harness 也没有必要告诉你:

“Agent 应该是什么。”

它更像是在提供:

“Agent 可以由什么组成,以及这些东西应该如何组合。”

这可能是大型系统发展到一定阶段后,一个非常重要的转折。

早期的软件喜欢把东西做进去。

成熟的软件开始学会把东西拿出来。

把 React 拿出 Vite Core。

把 LLM 拿出 Agent Core。

把 Sandbox 拿出 Agent Loop。

把具体能力拿出 Runtime。

然后留下一个越来越小、却越来越重要的东西:

边界。

因为一个系统最后真正难以复制的,往往不是里面有多少代码。

而是它有没有能力,让完全陌生的人写出来的东西,在不修改核心的情况下,成为它的一部分。

如果 DeepSeek Harness 最终真的能把这套机制做成一个成熟的 Agent 生态,那么它最值得记住的可能并不是“DeepSeek 做了一个很强的 Coding Agent”。

而是:

它第一次认真尝试把 Agent 从一个“程序”,变成了一种可以被整个生态共同组装的基础设施。

这件事,可能比再多一个 Agent Loop 有意思得多。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐