源码拆解 · Agent Runtime · 插件树
这不是一篇“怎么用命令行”的说明,而是沿着入口、Profile、Bundle、Patch 和 Cordis,把它的运行时装配链路拆开看。
如果只看使用方式,DeepSeek Harness 像是一个很普通的命令行项目:
dsh web或者更直接一点:
dsh --profile web但真正有意思的地方不在“命令很短”,而在于这条命令背后并不是启动一个固定内核,而是按配置把一棵 Agent 插件树装配出来。
也就是说,dsh 不是在“打开一个应用”,而是在“拼装一个运行时”。
这篇按四层来拆
1DeepSeek Harness与OpenClaw、Codex、Claude Code 对比
2DeepSeek Harness 的项目组织
3DeepSeek Harness 的技术架构
4DeepSeek Harness 的入口与源码阅读
一、DeepSeek Harness 与 OpenClaw、Codex、Claude Code 的对比
要理解 DeepSeek Harness,最好不要只把它放在“命令行 Agent 工具”这个框里看。它和 OpenClaw、Codex、Claude Code 都在做 Agent,但它们解决问题的切入点并不一样。
1. DeepSeek Harness:把 Agent 做成可装配运行时
DeepSeek Harness 最鲜明的特点,是它把“系统如何被组装”本身做成了核心。
它关心的不是单个 Agent 怎么回答,而是:
一个 Agent 系统应该由哪些插件组成? Web、Headless、CLI 是否能共享同一套底座? Session、Tool、LLM、Sandbox、Host 是否能被拆开组合? 一条命令能否通过 Profile / Bundle / Patch 拼出不同运行时?
所以它更像一个 Agent Runtime Harness:不是只给你一个 Agent,而是给你一套把 Agent 系统装起来的机制。
DeepSeek Harness 的重点,是“把 Agent 的运行时变成一棵可配置的插件树”。
2. OpenClaw:以Gateway为中心的长期运行平台
OpenClaw 更像一个长驻型 Agent 平台。
它的关注点不是只跑一次命令,而是围绕 Gateway 建立一整套长期运行能力:
多会话 多渠道 工具路由 插件治理 节点连接 定时任务 文件、浏览器、消息、设备等能力接入
如果说 DeepSeek Harness更强调“运行时如何装配”,OpenClaw更强调“Agent 如何稳定地生活在一个平台里”。
3. Codex / Claude Code:面向 Coding Task的产品化 Agent
Codex 和 Claude Code 的产品形态更明确:它们主要面向代码任务。
它们的核心体验通常围绕:读取项目上下文、修改代码、运行测试、管理权限、使用 sandbox,以及通过 hooks / skills / 配置扩展工作流。
它们也有工具、上下文、权限和运行环境,但公开呈现出来的重点更多是:
如何把 coding task 做得更可靠、更可控、更像一个开发者助手。
四者差异放在一张心智表里:
DeepSeek Harness:运行时装配系统
OpenClaw:长驻Agent平台
Codex:代码任务Agent产品
Claude Code:本地/项目内编码Agent助手
它们都有“Agent + 工具 + 上下文”,但组织方式不同。
DeepSeek Harness 最值得拆的地方在于:它不是先写死一个 Agent,再往上挂插件;而是先设计插件树,再由插件树长出 Agent 系统。
二、DeepSeek Harness 的项目组织
从项目组织上看,DeepSeek Harness 不是一个“单入口、单内核、单应用”的结构。它更像一个 monorepo:上层是应用入口,中间是启动和装配逻辑,底层是一组可复用包。
apps/ cli/ # dsh 命令入口 web/ # Web 形态入口或界面packages/ boot/ # profile / bundle / patch 装配逻辑 core/ # Agent 运行时核心抽象 session/ # 会话与事实日志 tool/ # 工具注册与调用 llm/ # 模型适配 sandbox/ # 执行隔离 host/ # Host / Web / API 连接层*.patch.yml / cordis.patch.yml # 插件树装配说明具体命名可能会随着版本变化,但阅读时可以抓住这条主线:
应用入口 → 启动层 → 配置装配层 → Cordis插件层 → Agent能力层1. apps:不同产品形态的入口
apps 通常负责面向用户的入口。CLI 入口接收 dsh 命令,Web 入口负责浏览器界面、API 通道和 Host 连接,Headless 入口负责无界面任务执行。但这些入口本身不应该被理解成“全部业务逻辑”。它们更像不同外壳:Web 是一个外壳,CLI 是一个外壳,Headless 也是一个外壳。
2. packages:能力被拆成可组合模块
packages 是理解 DeepSeek Harness的重点。这里面通常沉淀 boot、runtime、session、tools、llm、sandbox、host 等模块。这套组织方式的核心,是把 Agent 系统拆成能力模块,而不是把所有逻辑塞进一个主循环。
3. patch / profile:项目组织里的“装配语言”
如果只看源码目录,还不能完全理解 DeepSeek Harness。因为真正决定“跑起来是什么形态”的,经常不是某个 .ts 文件,而是一组配置。
profile选择场景,bundle提供能力包,patch决定最终启用哪些插件和配置。
代码提供零件,配置负责装配,运行时承载结果。
三、DeepSeek Harness 的技术架构
DeepSeek Harness 的技术架构,可以用一棵树来描述:
Profile └─ Bundles └─ Patches └─ Cordis Plugins ├─ Session ├─ Agent Loop ├─ Tools ├─ LLM Adapter ├─ Sandbox ├─ Host / Web └─ Skills / Subagents / MCP ...这棵树里,每一层都有自己的职责。
1. Profile:场景选择器
Profile 决定这次要启动哪种形态。比如 web 是带浏览器 UI 和 Host 的完整形态,headless 是无界面的一次性执行形态。
所以同一个 dsh,可以长出不同运行时。这不是“命令参数不同”那么简单,而是运行时结构不同。
2. Bundle:能力包
Bundle 负责把一组相关能力打包在一起。它解决的问题是:不要每次都从零列插件,而是把常见能力组合沉淀成包。
3. Patch:最后的装配说明书
Patch告诉系统哪些插件要启用、哪些配置要覆盖、哪些能力要替换、哪些模块要按顺序加载。
这不是简单的“开关配置”,而是结构化装配。所以在 DeepSeek Harness 里,配置不只是参数,而是运行时形状的一部分。
4. Cordis:插件树的骨架
如果说 DeepSeek Harness 的“树”有骨架,那这个骨架就是 Cordis。
Cordis 在这里承担的角色,像一个共享的运行时容器:插件往里面注册服务,服务之间通过 Context 互相注入,事件和副作用可追踪、可撤销,生命周期由Loader和 Fiber管理。
Context:共享空间
Service:可注入能力
Event:运行中发生了什么
Fiber:插件生命周期的载体
Loader:负责把插件真正拉起来
这套机制的好处是,系统可以按需加载、按层叠加、随时替换、卸载可回收。能力不是写死在核心里,而是被挂接到树上。
5. Session 和 Agent Loop 也被拆成插件能力
很多 Agent 项目会把“对话循环”和“会话存储”当成核心内核。DeepSeek Harness 更像是把这些都拆开:
Session:事实日志 Agent Loop:控制循环 Tools:工具执行面 LLM:模型适配面 Sandbox:执行隔离面
这意味着你不是在一个黑盒里“调用 Agent”,而是在一个可拆的系统里“组合 Agent”。
两种思路的差别:
传统思路:Agent = prompt + model + tools + memory
DeepSeek Harness:Agent = 一组插件能力的组合结果
所以,想换模型,换 Provider 插件;想换执行方式,换 Sandbox Provider;想换工具,换 Tool 插件;想换界面,换 Host / Web 层。核心循环不必重写,更多时候只是换装配。
6. Web 和 Headless 为什么能共享底座?
DeepSeek Harness 不是先做一个 Web,再做一个 CLI。它更像是:先有一套基础插件树,再往上挂不同表层,Web 和 Headless 只是不同 Profile。
Web 需要 Host、浏览器 UI、API 通道、状态同步和前后端协作;Headless 需要解析命令、启动 Agent、执行任务和输出结果。
但它们共享底下那棵树:Session、Agent Loop、Tools、LLM、Sandbox。
这就像同一辆车底盘,能装轿车壳,也能装货车壳。底层不变,表层变。
7. 它和普通插件系统有什么区别?
普通插件系统常见的样子是:主程序是固定的,插件只是外挂,插件负责补功能。
DeepSeek Harness 更像:主程序本身就是插件树的结果,插件不是边角料,而是结构本身,配置不是附属物,而是装配语言。
谁进入运行时,谁就成为系统的一部分。
这意味着系统边界是可塑的,运行时形状是可配置的,产品能力是可拼接的。
四、DeepSeek Harness 的入口与源码阅读
最后回到那条命令:
dsh web如果只停留在“它启动了 Web”,其实就错过了源码里最关键的一层:dsh 并不是直接启动 Web 应用,而是先解析场景,再装配运行时,最后让插件树自己长起来。
读源码时要盯住这条主链路:
① CLI 参数层:把用户命令变成启动意图
② Profile 层:决定本次运行时形态
③ Bundle 层:叠加基础能力包
④ Patch 层:生成最终插件装配图
⑤ Cordis 层:创建 Context,加载插件,管理生命周期
⑥ Runtime 层:Session、Agent Loop、Tools、LLM、Sandbox 开始协作
1. 第一站:CLI入口不是“大 main”,而是“分发器”
很多项目的入口文件会把初始化、配置、业务逻辑揉在一起。但 DeepSeek Harness 的 CLI 入口更克制,它更像一个分发器。
它大致负责三类工作:
解析命令行参数,例如用户输入的是 dsh web、dsh --profile web,还是 headless 任务。把参数规整成启动配置,例如 profile 名称、工作目录、环境变量、调试选项。 把控制权交给 profile boot,而不是在 CLI 层直接拼业务。
所以源码阅读不要只盯着入口文件里有没有复杂逻辑。它故意不复杂,因为真正的复杂度被后移到了 Profile / Bundle / Patch 装配层。
apps/cli/src/bin.ts ↓ 解析 argv / commandapps/cli/src/profile-boot.ts ↓ 根据 profile 进入启动流程packages/boot/... ↓ 读取并合成运行时装配配置入口越薄,越说明这个项目把“启动”当成了装配问题,而不是把所有东西塞进 main 函数。
2. 第二站:Profile 不是皮肤,而是运行时形态
Profile 很容易被误解成“配置文件”。但在 DeepSeek Harness 里,它更像运行时形态的选择器。
比如 web profile 不只是多开一个网页,它意味着这次启动需要引入 Host、Web UI、API 通道、状态同步等一整组插件。而 headless profile 则会更偏向命令执行、任务运行和结果输出。
可以把 Profile 想象成启动蓝图:
web:基础 Agent 能力 + Host + Web UI + 通信层
headless:基础 Agent 能力 + 命令执行 + 结果输出
custom:按需求叠加特定插件和配置
这就是为什么同一条 dsh 命令可以长出不同系统:入口没有变,装配蓝图变了。
3. 第三站:Bundle 负责复用,Patch 负责定型
如果每个 profile 都从零开始列插件,系统会很快失控。所以 DeepSeek Harness 会把常见能力沉淀成 Bundle。
Bundle 解决的是“复用”:比如基础运行时、工具系统、模型适配、Session 管理,这些能力可以被多个场景共享。
Patch 解决的是“定型”:它决定最后到底启用哪些插件、覆盖哪些配置、调整哪些依赖顺序。
profile: web imports: - base bundle - agent bundle - tool bundle - host/web bundle patches: - cordis.patch.yml - web.patch.yml - local override读这里时,重点不是记住每个 patch 文件名,而是看它们如何合并:谁先提供默认能力,谁再覆盖配置,谁最后决定插件树的最终形状。
Bundle 像零件包,Patch 像装配图。两者合起来,才决定这次 dsh 召唤出来的到底是哪棵树。
4. 第四站:Cordis Context 是插件树真正落地的地方
当 Profile、Bundle、Patch 合成完,系统还没有真正“活起来”。它只是得到了一张装配图。
接下来 Cordis 会做关键一步:创建 Context,然后按装配图加载插件。
这一步很关键。因为从这里开始,插件不再是配置里的名字,而变成运行时里的服务、事件和生命周期。
create Context ↓load plugin A → register serviceload plugin B → consume service Aload plugin C → listen events / expose tools ↓start fibers / manage dispose ↓runtime ready这里可以重点看三件事:
- 服务注入:
插件不是互相硬编码调用,而是通过 Context 找能力。 - 生命周期:
插件加载、启动、清理不是随手写副作用,而是被 Fiber / Loader 管起来。 - 依赖顺序:
谁先注册服务、谁后消费服务,会直接影响整棵树能不能成立。
5. 第五站:Agent Loop 不是孤立循环,而是服务协作结果
到了 Agent Loop 这一层,系统才开始进入我们熟悉的“Agent 工作流”:读会话、调用模型、选择工具、执行动作、写回结果。
但在 DeepSeek Harness 里,这个循环不是孤立存在的。它依赖前面已经挂好的服务:
Session:提供事实日志和上下文来源
LLM Adapter:负责把统一请求转给具体模型提供方
Tools:负责工具声明、权限、调用与结果回填
Sandbox:负责隔离命令执行和副作用
Host/Web:负责把运行状态呈现给用户界面
所以读源码时,不要把 Agent Loop 当成唯一核心。更准确的说法是:Agent Loop 是插件树装配完成后的一个协作节点。
6. 一条 dsh 命令的完整运行路径
把上面几层连起来,一条 dsh web 大致可以读成这样:
dsh web ↓CLI 解析命令与参数 ↓选择 web profile ↓加载 profile 声明的 bundles ↓合并 cordis.patch.yml 与场景 patch ↓生成插件树配置 ↓创建 Cordis Context ↓按依赖顺序加载插件 ↓Session / Tools / LLM / Sandbox / Host 注册为服务 ↓Agent Loop 开始消费这些服务 ↓Web UI 通过 Host/API 观察和驱动运行时这就是为什么我说:dsh 不是启动器,而是装配器。
7. 建议的源码阅读顺序
如果你要自己扒开源码,建议按这个顺序读,而不是从 UI 或某个工具实现开始乱翻:
第一步:看 apps/cli/src/bin.ts,确认命令如何被解析。
第二步:看 apps/cli/src/profile-boot.ts,确认 profile 如何接管启动。
第三步:看 packages/boot,确认 bundle / patch 如何被读取和合并。
第四步:看 cordis.patch.yml,确认插件树里到底挂了哪些插件。
第五步:再回头看 Session、Agent Loop、Tools、LLM、Sandbox、Host 的具体实现。
这样读的好处是,你不会被局部实现带跑。你先知道“树怎么长出来”,再去看每根枝条具体怎么工作。
读 DeepSeek Harness,最重要的不是找到某个神秘函数,而是抓住装配顺序:CLI → Profile → Bundle → Patch → Cordis Context → Runtime Services。
结语:一行命令,召唤一棵树
很多人看 Agent 框架,第一眼会关心:能不能调模型,能不能接工具,能不能做网页,能不能跑本地任务。
这些都重要,但还不够。
更关键的是:它到底是把能力堆在一起,还是把能力组织成系统?
DeepSeek Harness 的答案很明确:它选择后者。
所以那条看似普通的 dsh 命令,本质上不是“启动程序”,而是“召唤一棵树”。
而这棵树,才是它真正的产品形态。
夜雨聆风