DEEPSEEK HARNESS · 架构深读

一切皆插件

Everything is a Plugin

你刚才会话里用到的每一件东西 —— bash 工具、read/write/edit 文件操作、技能系统、 子代理、目标续跑、计划模式、甚至你正看着的这个 Web 界面 —— 在 DeepSeek Harness 里都不是内核写死的,而是一枚枚插进组合内核的插件。 内核只负责一件事:装配

SCROLL ↓ 向下滚动,把主板点亮
Fact 01 · 一个反直觉的事实

打开它的依赖清单,
你会发现几乎每一行都是一个插件包

这不是修辞。下面四个数字直接来自 @deepseek-ai/dsh 的发布包: CLI 自己的描述只有一句 —— “profile 启动器 + 插件管理 + 浏览器 UI 别名”。 它不实现任何能力,它只是把插件按顺序装起来

0
个运行时依赖
几乎全部是 @deepseek-ai/dsh-*
与 cordis-plugin-* 插件包
0
行插件清单
standard 预设的 agent.cordis.yml
拼出完整编码 Agent
0
组插件即可开工
minimal 预设:persona +
persistent-shell + 编辑器
0
个微型组合内核
Cordis:只做装配与生命周期,
不内置任何具体能力
同一个启动器,三种截然不同的 Agent —— 差别只在插件清单
内核(装配) 插件(真实能力)
条形图是示意:金色细条 = 内核占比之小;蓝紫长条 = 能力全部来自插件组合。
Interact · 亲手插一次

点击卡片,把它们插进内核

右侧终端就是模型的「工具目录」—— 现实中,Agent 每一轮能看见什么, 完全取决于这个会话挂载了哪些插件。试试下方的预设按钮, 它们对应 DSH 里真实存在的三份预设清单。

PLUGIN BOARD · 组合演示台
CORDIS 组合内核 · 只管装配 charge 0%
已插入 0 / 0 枚插件
Layering · 配置即千层饼

插件怎么组合?
一层 patch 叠一层 patch

profile 不是一份写死的配置文件,而是一个按顺序叠加的补丁层栈: 组合包先铺底,用户层逐级覆盖,命令行最后一击。悬停/点击每一层看细节。

L1

BUNDLES组合包 patch 层

dsh.profile.bundles 里按顺序列出的组合包(@deepseek-ai/dsh-basedsh-web-appdsh-headless…)各自带来一整片插件与默认配置,从 dsh 安装目录或 profile 自带的 node_modules 解析。

L2

PROFILEprofile 用户层

$DSH_HOME/profiles/<name>/cordis.patch.yml —— 这个 profile 自己的定制:增删插件、改配置。第三方插件通过 dsh plugin --profile <name> … 用 pnpm 装进来。

L3

HOMEhome 全局层

$DSH_HOME/cordis.patch.yml —— 跨 profile 生效的个人偏好,比如给所有会话统一换掉某个工具的实现。

L4

CLI--patch 临时覆盖层

启动时用 --patch 指定的最后一层,做一次性实验而不碰任何文件。整棵树可用 --dump-config 在不启动的情况下透视。

// 规则很简单:后写的赢。 配置不是被“读取”的,是被“叠”出来的。
Topology · 宿主与会话

插件插在两个平面

源码注释里写得直白:注册表、沙箱、持久化这些“所有会话共用”的东西住在宿主平面; 而 Agent 预设作为插件挂载进每个会话自己的 realm,互不碰撞 —— 第二个会话绝不会踩到第一个的配置。

HOST PLANE

宿主平面 · 进程级单例
  • 注册表 — tools / skills / commands 目录本身,供各会话分层登记
  • 沙箱与审批栈 — bash/pwsh 执行器、文件策略(如 danger-full-access)
  • 持久化 — session 投影、goal 服务、后台任务注册表
  • 模型路由 — LLM 接入与 API proxy,在任何会话存在之前就注入
MOUNT

AGENT PLANE

会话平面 · 每会话一份 realm
  • Agent 预设 — 一份 YAML 插件组合,决定这个会话是谁
  • 人设 persona — “You are a coding agent powered by {{model}}” 也是一行插件
  • 工具目录投影 — 同一进程里,每个会话看见属于自己的工具清单
  • 隔离保证 — 无 isolate 域直接拒绝挂载,杜绝两个会话互相覆盖
// 所以“一切皆插件”还有后半句:插件有归属 —— 属于进程,还是属于某一个会话。
Evidence · 源码证据

一段真实的预设清单:
连“身份”都是一行插件

摘自 config/agent-presets/code/agent.cordis.yml(有删节)。 虚线标注处,鼠标停上去看解读。

config/agent-presets/code/agent.cordis.yml
# 一个 Agent 预设 = 一份插件组合清单(AGENT-PLANE composition)
- id: persona                                
  name: '@deepseek-ai/dsh-persona'
  config:
    text: >-
      You are a coding agent powered by {{model}}.

- id: tool-bash                              
  name: '@deepseek-ai/dsh-tool-bash'
  disabled: !!js process.platform === 'win32'

- id: tool-fs                                
  name: '@deepseek-ai/dsh-tool-fs'

- id: tool-skill                             
  name: '@deepseek-ai/dsh-tool-skill'

- id: tool-goal                              
  name: '@deepseek-ai/dsh-tool-goal'
身份 = 插件 条件启停 = YAML 表达式 工具 = 注册表的一行 元能力也插件化 自主性可以拔掉

最妙的是闭环:cordis 预设自带两个技能 —— cordis-plugin-developmentediting-cordis-compositions。 没错,「教你写新插件」这件事本身,也是一个插件。 生态自己养活自己。

Finale · 内核越小,生态越大

为什么坚持一切皆插件

因为内核每少写死一个功能,社区就多一个可替换的位置。 换掉 fs-local 是另一个存储后端,换掉 compaction 是另一种记忆策略, 换掉 persona 就是另一个 Agent 人格 —— 而这一切,都不必动内核一行代码。

Kernel
微型内核
+
Composition
分层组合
=
Agents ∞
无限形态
minimal 极简
≈3 组 · 双工具编码器
standard 标准
29 行 · 计划/目标/子代理/工作流
code 代码模式
标准 + run_code:五回合变一回
内核负责装配
插件负责成为任何东西