DeepSeek-Harness 架构:一切皆插件到底是什么意思
DeepSeek-Harness 如何把工具、命令、技能、MCP、hooks 和权限全部实现成同一套插件机制,以及能力接缝模式与完整的 packages 全景图。
DeepSeek-Harness 的口号是"一切皆插件",这是一个可以拿去核实的结论,而不是营销话术:工具、斜杠命令、技能、MCP 桥接、hooks、模型适配器、权限、后台任务、子代理委派——每一个产品特性都是通过一个针对 Cordis Context 注册的插件实现的,没有任何一个特性另外配了一套独立的 manifest 格式。本文按照真实的"特性→机制"映射表,以及支撑它的 packages/ 目录树,把这件事讲清楚。
一个可以核实的结论
dsh 自己的开发者 cookbook 记录了一张"特性 → 机制"表,把每个产品能力精确映射到某个插件具体是怎么实现它的:
| 能力 | 实现方式 |
|---|---|
| 工具 | ctx.tools.register();schema 自动组装进模型 prompt |
命令(人类命令,/xxx) | ctx.commands 注册;永远不会变成一条模型消息 |
| 技能 | 注册一段 prompt + 一个工具;被调用时把内容注入 context |
| MCP | 每个 MCP 服务器对应一个插件:发现其工具后调用 ctx.tools.register()(详见我们的MCP 指南) |
| Hook | 监听生命周期扩展点(agent/session-start、agent/pre-step、tools/pre-execute、session/event);详见我们的hooks 与命令指南 |
| 模型适配器(LLM) | 通过 ctx.llm.registerAdapter() 注册的 LlmAdapter 子类 |
| UI / Web 客户端节点 | 监听 session/event,或注册一个 ConversationNodeDefinition 往内置 Web UI 里加节点 |
| 权限/沙箱 | 从 tools/pre-execute 返回 allow/deny/ask;由 ctx.sandbox 支撑 |
| 后台/定时任务 | 注册在 ctx.jobs 上;定时器触发 followup(..., {source: {kind: 'cron'}}) |
| 子代理委派 | 注册在 ctx.subagents(一个具名 provider 注册表)上(详见我们的子代理指南) |
这张表的意义不在于罗列细节——而在于说明:技能、命令、hook、MCP 桥接这些在别的一些 harness 里各自有独立 manifest 格式和目录约定的东西,在 dsh 里全部是同一种机制的不同用法。每种能力类型都没有独立的分类系统或注册文件 schema。
能力接缝:这张表背后的最小单元
上表的每一行都是一个能力接缝(capability seam)——dsh 术语表对"由一个 Service Definition、一个或多个 Service Provider、一个或多个 Consumer 组成的完整可替换单元"的命名。packages/shell 是标准范例:
- Service Definition:
dsh-shell把ShellExecutor这个抽象定义成一个 CordisService。 - Service Provider:
dsh-bash-local和dsh-bash-sandbox是两个可互换的具体实现——一个直接跑命令,另一个在沙箱内跑。 - Consumer:
dsh-tool-bashinject这个服务,把它作为工具暴露给模型。
替换 provider(本地执行 vs 沙箱执行),所有构建在这个接缝上的 consumer 都不需要改动就能继续工作,因为它们只依赖抽象的 ShellExecutor 接口,而不是某个具体实现。这个模式——定义一个抽象服务,注册可互换的 provider,让 consumer inject 时不关心当前生效的是哪个 provider——在上表几乎每一种能力上都重复出现。想了解让这个模式在机制层面成立的 Cordis 原语(Context、Service、inject、Fiber 生命周期),参见Cordis 详解。
packages 目录全景巡览
dsh 的 packages/ 目录下大约有 150 个子包。按能力域分组,主要的有:
| 能力域 | 代表包 | 覆盖内容 |
|---|---|---|
| LLM 适配器 | llm-deepseek、llm-pi-ai、llm-retry | DeepSeek 官方 chat-completions 适配器,一个用于设计验证的"孪生"适配器,以及跨 provider 的重试逻辑 |
| MCP | mcp/mcp-client | 以 client 角色桥接外部 MCP 服务器的 Tools |
| Skill | skill、skill-filesystem、skill-badge | 技能 provider 注册表,加上一个本地文件系统 provider 和一个内置的"badge"技能 |
| Subagent | subagent、subagent-claude-code、subagent-codex、subagent-acp、subagent-dsh-sdk、subagent-fork-in-process、subagent-spawn-in-process | 委派给进程内会话、通过 ACP 委派给外部代理,或官方直接委派给 Claude Code / Codex |
| Hooks 桥接 | hooks-claude-code、hooks-codex | 复用现有的 Claude Code / Codex hooks.json shell-hook 配置 |
| Sandbox | sandbox-local、sandbox-policy、sandbox-windows-acl | Linux bwrap/Landlock、macOS Seatbelt、Windows ACL 受限令牌三种后端 |
| Web 访问 | web-search-exa、web-search-perplexity、web-search-deepseek、web-fetch-http | 可插拔的搜索 provider,加上一个匿名公共 HTTP(S) fetch provider |
| ACP | acp/acp | 面向外部 GUI/编排客户端的"仅供自动化"ACP 服务器 |
| SDK | sdk/client、sdk/server、sdk/protocol | TypeScript SDK,以及Python SDK同样用到的共享 stdio JSON-RPC 协议 |
| E2B 云沙箱 | e2b/e2b、e2b/fs-e2b、e2b/subprocess-e2b | 把本地执行环境换成 E2B 的云沙箱环境 |
| LSP | lsp/lsp、lsp/lsp-stdio、lsp/tool-lsp | 语言服务器能力接缝(ctx.lsp),支持 definition/references/hover |
| Terminal | terminal/terminal、terminal/tool-terminal | 带 owner-scoped ID 的持久化 PTY 会话接缝 |
| Workflow | workflow/workflow、workflow/tool-ralph | 一个通用工作流引擎,加上 Ralph loop 工具 |
| Goal | goal/goal、goal/command-goal | 挂在会话上的持久完成目标 |
| Session 持久化 | session-persistence-jsonl、session-persistence-sqlite | 两种可选的落盘格式 |
| 遥测 | session-telemetry、session-telemetry-otel | OTLP 导出,默认关闭 |
这不是一份穷举清单——而是理解"微内核"在实践中到底意味着什么最关键的那些能力域:核心启动流程很小,几乎每一个你会认为是"dsh 的一部分"的特性,都是这约 150 个包中的某一个,以插件形式加载进来,而不是硬编码在一个单体核心里。
这套架构带来了什么好处
- 一套生命周期覆盖一切。 无论你在构建的是一个工具、一个 hook,还是一整个 UI 面板,HMR、依赖解析、卸载时的自动清理(通过
ctx.effect())都是同一套机制——学一次 Cordis,就覆盖了所有特性类别。 - 没有 manifest 动物园。 技能、命令、hook、MCP 各自不需要学一套独立 schema——一种插件注册模式覆盖全部,一旦内化,心智模型就很精简。
- 可替换的实现是设计使然。 能力接缝模式(服务 + 可互换的 provider + consumer)意味着沙箱后端、记忆存储、搜索 provider 这类东西都可以被替换,而不需要动 consumer 的代码。
这套架构付出的代价
- 从外部看,一切都长得一样。 一个工具注册和一个 hook 注册,外层都以
apply(ctx) { ... }开头——你必须实际读进去才知道一个具体插件提供的是哪种能力,因为没有目录命名或 manifest 约定能让你一眼看出来(不像别的一些 harness 里,比如一个带SKILL.md文件的skills/目录那样一目了然)。 - 理解任何一个单独特性都需要 Cordis 素养,不只是高级功能才需要。 因为没有可以走捷径浏览的 manifest,哪怕理解一个简单的 hook,也需要在基础层面理解
Context/inject/生命周期——原语部分见Cordis 详解。 - 从有各能力独立 manifest 的 harness 过来,一开始可能会觉得不适应。 如果你习惯了 Claude Code 那种技能/命令/hook/MCP 各自独立 manifest 的约定,dsh 这套单一机制需要一点重新学习——直接对照参见从 Claude Code 迁移。
FAQ
"一切皆插件"只是营销话术吗?
不是——它可以对照上面这张"特性→机制"表逐条核实。表里列出的每一个能力,确实都是以针对 ctx 的插件注册来实现的,底下没有藏着任何独立的 manifest 格式。
dsh 架构里"一个特性"的最小单元是什么?
是能力接缝——一个 Service Definition,加上它的 Provider(们),加上它的 Consumer(们)。单个插件通常只扮演这三种角色之一,而不是独自构成整个接缝。
"一切皆插件"意味着我可以移除核心特性吗?
原则上,对任何以可替换插件形式实现的东西来说是可以的——这正是这套设计的意义所在——但基础 bundle 默认接好了大多数 profile 需要的插件,所以移除一个通常意味着换一个替代实现,而不是完全不用它运行。
dsh 一共带了多少个包?
packages/ 下大约 150 个子包,覆盖上表列出的各个领域——LLM 适配器、MCP、技能、子代理、沙箱、SDK 等等。
如果想在这套架构上做开发,应该从哪里入手?
从一个单独的工具或 hook 入手,因为两者用的都是同一套基础的 Context/apply() 模式——参见插件开发入门和Cordis 详解了解底层原语。
Next steps
想了解让这套架构成立的框架原语(Context、Service、inject、Fiber 生命周期),阅读Cordis 详解。想看 MCP 和 hooks 具体是如何嵌入这同一套机制的,参见MCP 指南和hooks 与命令指南。想查阅相关术语,看术语表;想了解这与 Claude Code 各能力独立 manifest 的设计相比如何,阅读DeepSeek-Harness vs Claude Code。