DeepSeek 开源的 DeepSeek Harness(命令行 dsh)把 Agent 运行时做成可组合、可替换、可审计的系统,而不是「大模型 + 终端 + 文件读写」的简单叠加。模型适配器、工具注册表、会话持久化、沙箱策略,乃至驱动多轮 tool 调用的 agent loop,在官方架构文档里都算插件;没有一块必须 fork 主仓库才能改的「特权内核」。
本文沿着两条线进行解读。一条是把 dsh 读成三层:最外层是 Profile 与 Bundle 的组合层(Profile 是有名字的一套启动配置;Bundle 是 npm 插件包,包里带一份 cordis.patch.yml,写明要加载哪些插件,Profile 按顺序叠多个 Bundle 拼出完整能力);中间是 Cordis 上的 运行时主干 (官方文档叫 spine,指 session、tools、agent、llm 等 core 包,一次 Turn 的数据流分别调用一些模块);最内层是以 Session 日志为事实源的数据与 capability 后端。另一条是官方说的「一切皆插件」------不是功能多、能装扩展就够了,而是任何能力都通过插件挂载、通过 ctx.effect() 做可撤销注册,agent loop 本身也不例外;日常扩展是旁挂插件、挂事件,而不是改 loop 源码。
本文依据仓库 架构文档 与 Cordis 入门 梳理,不展开插件开发 cookbook,也不写安装手册。DeepSeek Harness 目前还是 developer preview,官方说明接口会有 breaking change;下文只帮助理解架构,正式用起来以当时官方文档和 changelog 为准。
三种角色,读不同接口
Harness 至少有三条读法,取决于你是接业务、写插件,还是接模型端点。Turn 流程、插件扩展、模型接入,三块内容不同,下面分三块写。
若你的日常是调 API、拼 prompt、接业务,最该先建立的是 Turn 流程与 Session 日志:模型究竟在什么时刻看到哪些 message、tool 结果如何回到下一轮。UI 只是 session/event 的一种渲染;replay(按日志把历史对话重新播放或重建上下文)与 fork 都依赖同一条日志。
若你要写 dsh 插件或改 capability(新模型后端、新 tool、新沙箱),重点在 Cordis 的 ctx.<key> 服务与事件扩展点:该挂 ctx.tools 还是监听 agent/pre-step,patch 该改哪一行 bundle 配置。
若你要接自托管或云端模型端点,重点在 ctx.llm 适配器接口:dsh 通过适配器发起 chat/completions 式请求,自身负责多轮 inbox、审批、持久 session、子 agent;模型 forward 与 KV 调度不在 Harness 仓库内,官方文档也把 serving 引擎划在边界之外。
「一切皆插件」在架构里指什么
github 项目的 README 与架构文档都用 Everything is a plugin 概括 dsh。这句话若只理解成「功能很多、社区能写扩展」,会低估它在架构上的含义。
第一,产品里没有「改内核打补丁」这条路径。Cordis 上的每个组成部分------DeepSeek 模型适配器、bash 工具、JSONL 会话持久化、默认 agent loop 驱动器------都以插件行的形式出现在组合配置里。官方表述是:扩展 dsh 的方式是把插件挂载到其他插件旁边;注册走 ctx.effect(),插件卸载时会撤销,Cordis 文档里把这种可撤销注册叫作 effect。
第二,连 agent loop 也是可替换插件。core/agent-loop 实现公开的 Agent 接口,向 ctx.agentLoop 注册;core/agent 定义接口与 agent/* 事件名。扩展插件依赖 agent,不依赖 agent-loop,这样默认 loop 可以换掉,扩展也不必绑死在某一版 loop 实现上。若 loop 不能换,「一切皆插件」就退化成「工具能插、loop 不能换」,与文档不符。
第三,「插件」在 Harness 里是有实现约束的,不是任意脚本。插件是 Cordis Service,通过 inject 声明「我需要哪些服务先就绪」,框架按依赖顺序加载;在共享 Context 上占用稳定的 ctx.sessions、ctx.tools 等键。插件之间通信用类型化事件(emit / waterfall / parallel / serial),注册走 ctx.effect() 或 ctx.on()。waterfall 是一串监听器依次处理:每个监听器可以改内容,再调用 next() 传给下一个;若直接 return 而不调用 next(),后面的监听器不会再被调用------写审批、改 prompt、拦 tool 时必须记住这一点。
第四,组合层本身也是「插件的插件」。发行版里的 web、headless 不是写死在二进制里,而是 Profile 模板:每个 Profile 列出要叠放的 Bundle 顺序,外加用户目录下的 cordis.patch.yml。Bundle 是一份 Cordis 配置 patch,插入一批插件行;上一层 patch 可以按 id 整行替换下一层的同名行。换 Bundle 顺序或 home 级 patch,就能换整套默认能力组合,而不必 fork 主仓库。
Harness 的可替换性体现在运行时图上:任意节点都可在配置里换成另一个 Service 实现;默认扩展路径是加插件、挂事件,而不是改 agent-loop 源码。官方也写明:若要改 loop 本身,必须同步更新架构文档------那是框架级变更,一般写插件的人用不上。
三层架构
把 dsh 想成组合层 → 运行时主干 → 数据与 capability 后端,三层职责边界清楚,对照源码目录(packages/core、packages/llm、packages/shell 等)时不容易搞混。
第一层:组合层(Profile · Bundle · patch)
运行中的 dsh 是一棵在启动时拼出来的插件树。拼树规则写死在 boot 流程里,顺序如下(后者覆盖前者同名条目):
- 空入口列表;
- Profile 的
dsh.profile.bundles列表,按声明顺序依次应用每个 Bundle 的 patch; - 该 Profile 目录下的
cordis.patch.yml; - Harness home(默认
~/.dsh)下的cordis.patch.yml; - 命令行
--patch指定的 overlay。
Profile 是 $DSH_HOME/profiles/<name> 下有名字的一套配置:一份 package.json(含 dsh.profile.bundles 与可选树外插件依赖)+ 用户 patch。Bundle 是 npm 包,在 package.json 的 dsh.bundle.patch 指向自己的 cordis.patch.yml。每个发行版 Profile 的第一层几乎都是 dsh-base:模型适配、工具、持久化、沙箱与审批、设置与凭据、遥测等通用 Agent 底座;dsh-web-app 叠浏览器 UI;dsh-headless 叠一次性任务 runner(无 HTTP 服务)。
Patch 的语义值得单独记:按 id 命中某插件行时,替换的是整份 config,不是 deep merge。想改模式相关参数,应在对应 mode bundle 里写全字段,不能指望只写改动字段会自动和默认配置合并。想看清本机实际启动树,可运行:
css
dsh --profile web --dump-config
打印出的每一行都可以被你自己的 patch 按 id 替换------这是组合层「可观测、可覆盖」的具体含义。
组合层的代价是理解成本变高:同一个 dsh web 命令,在不同 home、不同 profile patch 下可能是不同的工具集、不同的沙箱策略、不同的模型路由。好处是同一套 CLI 可服务桌面 Web、无头 CI、定制企业 Profile,而无需维护多条 fork。
第二层:运行时主干(Cordis · core · 事件)
组合层解决「加载哪些插件」;第二层解决「加载后一次 Agent 任务如何推进」。
Cordis 是 vendored 进 Harness 仓库的插件框架(源码在仓库里,不单独从 npm 拉一份)。对读架构的人,记住五件事即可:插件是 Service;Context 是服务仓库;inject 声明依赖顺序;类型化事件是扩展主通道;注册是可撤销的 effect。
core 六包构成官方文档里的运行时主干,一次 Turn 的数据流大致经过它们:
| 包 | Context 键 | 职责 |
|---|---|---|
| session | ctx.sessions | 仅追加的 SessionEvent 日志与内存 store |
| system-prompt | ctx.systemPrompt | 提示词片段与 tool schema 组装 |
| tools | ctx.tools | 作用域化工具注册表与 guarded 执行流水线 |
| agent | ctx.agents | Agent 接口、活跃 registry、agent/* 事件 |
| agent-loop | ctx.agentLoop | 实现 Agent 接口的默认驱动器 |
| llm | ctx.llm | 消息/流式词汇表与模型适配器接口 |
scope 库无独立 ctx 键,提供按 agent 隔离注册的原语------同一 session 里若有多 agent preset,工具可见性可不同。
事件分三个域,选错域是插件 bug 的常见来源:
| 域 | 典型名字 | 何时用 | 生命周期 |
|---|---|---|---|
| Session | turn/ , user/message, assistant/ , tool/* | 必须重放、持久、fork 的事实 | 写入日志,经 session/event 广播 |
| Agent | agent/pre-step, agent/request, agent/inbox/* | 只在当前这一轮运行期间拦截、改 prompt、看队列 | 内存态,不替代日志 |
| Capability | fs/ , tools/ , telemetry/* | 给接口挂策略/适配器,无需 import loop | 依具体事件 |
Session 与 Agent 的分工可以记一句:要做 transcript、审计、恢复,订 Session;要做 UI 状态、当轮拦截,订 Agent。SDK 文档明确:需要可 replay 的数据应消费 session/event;agent/* 是当轮协调用的 live API。
Waterfall 与 serial 的区别也会落在插件里:agent/pre-step、agent/request、llm/stream、tools/pre-execute 等是 waterfall,监听者可以改写请求或拒绝本 step;agent/turn-stopping 是 serial 终止检查点,没有 next()。Pre-step 拒绝时,官方流程仍可能关闭一个不含任何 step 的 turn,日志里留下「尝试过但未调用模型」的记录------查日志时这段也要看,否则搞不清用户消息为什么没进模型。
第三层:数据与 capability 后端(Session 日志 · 接口层)
第三层不是「更底层的 C++」,而是运行时主干以下、可整包替换的实现部分。
Session 日志是模型所见上下文的唯一投影源。deriveMessages() 从 append-only 事件流生成模型 history;流式 UI 则保留原始 assistant/chunk,这样 UI 回放时才和当时一致。官方不变量:模型可见 ⟺ 已记录------任何进入模型请求的内容都必须能从日志重建;新增模型可见输入,必须扩展 SessionEventMap 并从日志渲染,不能只在内存里追加未写入日志的 context。
由此带来一个工程后果:Compaction、fork、resume、telemetry、导出 transcript 全都读同一条流。Compaction 插件(如 dsh-compaction-basic)在 agent/pre-step 做压力判断,在 agent/request-error 处理上下文溢出------仍是在不破坏日志语义的前提下替换表面。Fork 用 ctx.sessions.fork(source, boundary?, childSessionId?) 在边界事件处分裂会话 lineage。
Capability 接口层是第三层的组织原则。一项能力要完整,需三角色:Service Definition(接口)、Provider(实现)、Consumer(常用面向模型的 tool)。只注册 Provider 不算完成。官方举例:filesystem 与 subprocess 共享同一执行世界------把 Provider 指到远程沙箱,bash、PTY、LSP 会一起迁过去,而不必为每个工具 fork 一份沙箱代码。Subagent 同理:同一接口后可接「进程内新 agent」或「委派给外部产品」等不同 Provider。
一次 Turn 如何走完(把三层连起来)
术语:Step = 一次模型请求 + 其触发的 tool;Turn = 零个或多个 Step,从 claim 输入到不再欠工作为止。
官方 agent-lifecycle 时序图把 User、Driver、Session、LLM、Tools 四条线画全;看图时注意实线写 Session 与虚线写 agent/* 的分工------前者是 replay 依据,后者是当轮协调。

Agent 轮次与步骤生命周期(官方文档 agent-lifecycle.zh.md)
图中从 followup 进 inbox,到 turn/start、claim、agent/pre-step waterfall,再到 deriveMessages → LLM 流式 → tool/call/tool/result,最后 agent/turn-stopping 与 turn/end,与下文 ASCII 同构,只是补上了 SDK/UI 监听器侧的事件名。Pre-step 若拒绝首条 claim,Driver 仍可能关闭零 step 的 turn------日志里会留下「尝试过但未进模型」的记录。
下面 ASCII 是 architecture 文档 Turn 流程的压缩版,把组合层、主干、日志串起来读:
bash
[用户/命令] → Agent.inbox
→ turn/start(Session)
→ claim 下一 step 输入 + 一条 queued message
→ 组装 prompt sections + tool schemas(system-prompt)
→ agent/pre-step(waterfall:可 reject / 改写 messages)
└─ 若首条 claim 被拒或改空:关闭 turn,可能 0 个 step,仍写 Session
→ step/start → user/message(Session)
→ deriveMessages() 从 Session 投影 history
→ agent/request → llm/stream → assistant/chunk* → assistant/message(Session)
→ tool/call* → tools/pre-execute → execute → post-execute → tool/result*(Session)
→ step/end
→ 若 tool 仍需继续执行或 inbox 又有 next-step 输入:再 claim → 下一 step
→ agent/turn-stopping(serial)
→ turn/end(Session)
输入经单一 inbox 进入驱动器:有的消息立刻唤醒 driver;agent.inject() 注入的上下文会暂存在 inbox 中,直到后续消息一起被 claim。Pre-step 决定模型本轮真正看到什么;这与用户界面上显示的 composer 文本可能不完全同一回事------插件可以在 pre-step 层做策略。
对 headless Profile:上述流程结束后打印最终答案并退出,无 Web 服务器;对 web Profile:同一主干上再叠 host/client 插件,UI 订阅 session/event 与 agent/status。两种模式共享 Turn 语义,差别在组合层多叠了哪些 bundle。
对照表:Session 事件 vs Agent 事件
| 维度 | Session 事件 | Agent 事件 |
|---|---|---|
| 典型例子 | turn/start, user/message, tool/result | agent/pre-step, agent/inbox/claimed, agent/status |
| 是否持久 | 是,append-only 日志 | 否,当轮内存态 |
| UI replay | 应用此流 | 不宜单独 replay |
| 插件拦截 prompt | 间接(改写入日志前的 pre-step 结果) | pre-step / request waterfall 直接拦截 |
| 读错域的后果 | 把临时状态当历史,fork 丢数据 | 只订 Agent 做审计,重启后对不上 transcript |
Capability 一例:沙箱、文件系统、Shell 如何联动
官方 tool-execution-pipeline 流程图说明:tool 从模型输出到写回 Session,不必改 agent loop------策略全挂在 tools/* waterfall 与守卫上。

工具执行流水线(官方文档 tool-execution-pipeline.zh.md)
读图顺序:tool/call 先记入 Session(执行前已持久),再经 tools/pre-execute(权限、沙箱、hook)→ 单调守卫 → 可选 ctx.approval → tools/execute 包裹真正的 execute() → tools/post-execute 改写结果 → finalizeContent → tools/result 通知 → 最终 tool/result 写 Session`。左侧 denied 分支说明:审批拒绝时跳过 tool body,仍要产出可 replay 的结果事件------这与「模型可见 ⟺ 已记录」一致。下文沙箱、fs、shell 插件,都落在这条流水线的 pre-execute 与 execute 段,而不是 loop 状态机里。
dsh-base bundle 在 POSIX 上默认启用 bash 栈(bash-sandbox + tool-bash),Windows 上换成 pwsh 栈;两套互斥, incomplete patch(只禁 pwsh 不禁 bash)会在加载时失败,因为同一 bash 服务不可双注册。Permission preset、user-approval、fs-sandbox 与 sandbox-local 共同构成「模型触发的进程与写文件」的约束面------它们在 interaction 与 sandbox/fs/shell 包中,不改变 loop 状态机,只挂在 capability 事件与 approval 接口上。
这对读架构的含义是:安全策略是组合层 + capability 插件的可替换实现,不是界面上勾几个选项那么简单。企业若要把执行迁到 E2B 等远程沙箱,替换的是 Provider 指向,主干上的 tool 调用形状可以不变。
人类协作平面:与 loop 正交
approval、permission preset、ask-user tool、human commands(ctx.commands)位于 interaction 组。文档强调:它们通过现有 agent/session 契约集成,不改变 loop。命令可在无模型 turn 下 dispatch;ask-user 则把人类回答以 tool 结果形式回到 Session。Automation 场景可走 ACP(Agent Client Protocol)server,与 Web UI 的交互适配器并行------同一主干,不同入口界面。
想加新能力,该改哪
架构文档有一张「新行为该挂在哪」的对照表,插件作者常查。正文摘四行:
| 目标 | 机制 |
|---|---|
| 新模型 Provider | 在 ctx.llm 注册适配器 |
| 新模型可见能力 | 在 ctx.tools 注册;schema 进入 prompt 组装 |
| 拦截请求 / tool / turn | agent/* 或 tools/* 事件;turn-stopping 结束 turn |
| 持久 session 状态 | 扩展 SessionEventMap;渲染与 replay 从日志来 |
更完整的映射见官方 extension cookbook;本篇不逐条展开。
二次开发:做 OpenClaw 式产品时 Harness 落在哪里
OpenClaw、Hermes Agent 一类产品,我们最熟悉的几个功能:IM Channel(微信、飞书)、可扩展工具、Skill 包、子 agent 编排,以及「用得越久越聪明」那套自我进化能力。DeepSeek Harness 官方交付的默认形态是 Web 工作台、headless 单次任务、ACP 与 Python SDK------没有内置 Channel 网关,也没有一键接个人微信。若你要做同类产品,Harness 更适合当 Agent 运行时底座,产品差异用树外 npm 插件 + Bundle + Profile 叠出来,而不是 fork 主仓库改 core。
2026 年 8 月前后,GitHub 上已有一批可安装的社区插件(awesome-dsh-plugin、AdamPlatin123/awesome-dsh-plugins、0xsline/awesome-deepseek-harness 等列表在持续索引)。它们不是同一种写法:有的挂在 Cordis 插件树里,有的是包在 Harness 外面的独立进程。读社区仓库,大致能归纳出四条集成路径------做产品前先选对路径,比先看 star 数更重要。
路径一:Bundle 插件(dsh.bundle.patch)。包内自带 cordis.patch.yml,dsh plugin --profile web add <npm 包> 会把 bundle 推进 Profile 的 dsh.profile.bundles 层栈;host 侧改动通常要重启 dsh。多 Agent 编排(NanmiCoder/dsh-agent-teams)、可治理 Workflow(icetomoyo/dsh_workflow)、视觉工具(liustack/modlens 的 read_image)、记忆面板(csyangwen/dsh-memory-evolve)等,这类插件常见走 bundle 安装。社区文档(如 vlln/plugin-registry 的插件类型说明)把这类叫 bundle 插件:组合层随包分发,适合一整包能力。
路径二:纯 Cordis 插件(无 dsh.bundle)。同样是 npm 包,但安装后在 Profile 的 cordis.patch.yml 里以 insert 行挂单个 Service;改配置可热更新,多数 host 改动不用重启。适合单点能力:通知(omdsh-dev/dsh-notification)、@file 引用(omdsh-dev/dsh-at-file)、对话内生成式 UI(omdsh-dev/dsh-genui)等。plugin-registry 薄控制台里的「insert 插件区」管的就是这类插件。
路径三:Client / 新 Profile 表面。Web 侧增强常见 host + client 两部分:host 注册 HTTP/WebSocket 路由与 Cordis 服务,client 用 dsh.client 或 portal 挂进官方 Web UI。omdsh-dev/DSH-better-sidebar 暴露 ctx.betterSidebar,第三方可注册 Tab 与文件预览器;zhu1090093659/dsh-web-ui、Small-tailqwq/dsh-deep-whale 做皮肤和任务面板。终端党则用定制 Profile 叠 patch:ccch1mneyyy/dsh-TUI 在 dsh-base 上再加 TUI bundle,订阅 session/event 做终端投影,文档写明不改 core 源码------模型调用、tool、fork、compaction 仍归官方主干。bruc3van/dsh-desktop、Ruler4396/dsh-launcher 用桌面壳负责启动 dsh,Harness 仍在子进程里跑。
路径四:树外 Channel 桥(常不是 Cordis 插件)。飞书、Telegram、QQ 机器人里,成熟实现往往是独立 npm 包 + adapter,而不是 cordis.patch.yml 里一行 insert。以 PlutoKeating/dsh-lark-bot 为例:飞书 WebSocket 进 bridge/,会话路由在 session/,再经 adapters/ 接官方 @deepseek-ai/dsh-sdk-client(JSON-RPC runtime,默认)、ACP(审批卡)或 legacy headless;本地状态在 ~/.dsh-lark/,与 ~/.dsh 里的 Profile 解耦。这和 OpenClaw Gateway 更像:IM 层与 Agent 后端分开,Harness 当后端,而不是插件树里多一个 tab。产品难度在 session 路由、流式卡片、工作区隔离与 /model 等运维命令,不在 agent-loop 源码。
官方 打包与安装插件 与社区 dsh-handbook、make-dsh-plugin skill 面向路径一、二;路径三、四需要额外读各仓库的 AGENTS.md / 架构说明。vlln/plugin-registry 补了安装态管理(bundle 层栈 reconcile、insert 启停),不改变运行时语义。
下面这张表仍是能力对照(OpenClaw / Hermes 语境 → Harness 接口);右列「社区样例」帮助对照上文的四条路径。
| 产品能力 | Harness 落点 | 社区里常见接法 |
|---|---|---|
| Channel:飞书、Telegram、QQ | 无内置;需树外桥接 ctx.agents / SDK / ACP |
路径四:dsh-lark-bot 等独立包 + adapter;Telegram/QQ 同类 |
| Web / TUI / 桌面入口 | 官方 Web;或新 Profile + client 插件 | 路径三:DSH-better-sidebar、dsh-web-ui、dsh-TUI、desktop launcher |
| 工具(bash、文件、API、视觉) | ctx.tools + tools/* |
路径一/二:modlens(read_image)、dsh-vision-toolkit;MCP 仍走 dsh-mcp-client |
| Skill | ctx.skills + skill 工具 |
官方接口;dsh-memory-evolve 等另做技能/待办面板 |
| Sub-agent / 多 Agent | ctx.subagents + 相关 tool |
路径一:dsh-agent-teams(Web 活动面板 + 团队协议 tool) |
| 工作流 / 委派 | 内置 workflow tool(一次性);更高层需自建 |
路径一:dsh_workflow(capsule、.dsh/workflow-runs/ 持久化、ctx.jobs) |
| 长期记忆 / 跨 session | Compaction 读 Session;无单一「记忆模块」 | 路径一:dsh-memory-evolve(文件轨 + 可选 git 同步);dsh-turn-rewind(回退);MCP 记忆 overlay |
| 通知与摸鱼 | 无官方 IM 推送 | 路径二:dsh-notification;UI 皮肤/小游戏走 client 插件 |
| 插件生态本身 | dsh plugin add、Profile patch |
列表:awesome-dsh-plugin 等;基建:plugin-registry;索引:dshfind.com |
单独说一下 IM 接入。OpenClaw 把聊天软件当成主入口来做;Harness 文档里的官方 Web 路径是订 session/event、驱动 ctx.agents。社区飞书 bot 说明不必把 IM 写进 Cordis 树:adapter 接到 SDK runtime 即可复用同一套 Turn/Session。审批若要走 ACP,桥接层需切 DSH_LARK_ADAPTER=acp;纯 SDK 模式则更像远程会话客户端。微信/个人号协议、多租户、合规运维仍要产品自建------装一个插件解决不了。
Tool、Skill 与多模态是社区 bundle 最集中的区域。modlens 代表典型做法:给纯文本模型注册 read_image,必要时在模型选择器加 (modlens vision) 包装路由,vision 引擎在插件内 failover,不改 Harness 配置主干。新工具仍走 ctx.tools 与 tools/pre-execute 沙箱链;Skill 走 ctx.skills 按需加载,与 OpenClaw Skills 概念相近但实现独立。
Sub-agent 与工作流社区已叠两层。dsh-agent-teams 在 ctx.subagents 语义上移植 Claude Code AgentTeams:建队、成员邮箱、任务依赖,Web 右上角活动面板订 live 事件。dsh_workflow 明确不替换官方一次性 workflow tool,而是在其上加可命名、可持久、可 pause/resume 的 capsule 层,run graph 落盘到 .dsh/workflow-runs/,斜杠命令走 ctx.commands,子任务仍经 ctx.subagents spawn。OpenClaw 式「父 bot 派子 bot」在 Harness 里首先是 tool + 独立 Session;Workflow 插件把临时编排变成可保存、可复查的流程文件。
官方 Compaction 只解决上下文太长时的压缩;dsh-memory-evolve 用多轨文件记忆 + Web 设置页 + 一批 memory/todo 工具做跨 session 状态,可选 git 分支同步------这是路径一的 bundle 产品,不是改 SessionEventMap。Harness 自带的 tool-cordis 动态包仍只活在进程内存。社区若说「自我进化」,持久记忆靠的是插件写盘 + 用户确认队列;若指运行时改插件,仍应走正规 dsh.bundle 发布。接近 OpenClaw「Skill 热加载」的是 insert 插件热更新,或重启后 bundle 更新,不是 agent 自动改 loop。
若你规划产品路线图:IM 与品牌界面优先考虑路径四独立仓库 + SDK/ACP adapter;工作台、视觉、编排、记忆打成路径一 Bundle 经 Profile 复用;小功能实验用路径二 insert 迭代;终端或全屏 UI 用路径三新 Profile。只有要改 Turn 状态机或 Session 事件语义时,才值得 fork deepseek-harness 主仓。developer preview 下接口仍可能变;树外 Channel 桥与 npm 插件至少能跟版本 pin,而不必每次 rebase 一整棵 monorepo。动手前可 dsh --profile web --dump-config 对照本机插件树,并读目标仓库的 compatibility 矩阵(如 dsh-lark-bot、dsh_workflow 均锁定 dsh 快照)。
范围与代价
架构文档把 Harness 的职责写得很具体:Profile 怎么叠、Turn 怎么推进、Session 怎么记、tool 怎么执行、人类怎么插进来审批------这些都在仓库里。模型在 GPU 上怎么 forward、KV 怎么排,不在 dsh 里;远程沙箱怎么运维,也不是 Harness 替你包办,你能做的是把 filesystem、subprocess 的 Provider 指过去。
全插件之后,换沙箱、换模型、换 web/headless 表面,多数时候是改 patch 或加 bundle 行;replay、fork、压缩都读同一条 Session 流。反过来,理解成本会变高:同一个 dsh web,换 home 或 profile patch,工具集和沙箱策略可能就变了,不执行 --dump-config,很难对自己机器说清楚究竟加载了什么。还有一个插件作者常踩的坑:模型能看见的内容必须写进日志------只在内存里藏 state,fork 和 compaction 会对不上。
Typert RPC、Python SDK 打包、Web client 插槽、workflow/ralph、Windows 与 POSIX 双栈 patch,这篇没写;入口在仓库 packages/ 分组 README 和用户指南里。
收尾
DeepSeek Harness 值得单独读架构,因为它同时回答两个问题:系统怎样叠出来(Profile → Bundle → patch → 插件树),以及「一切皆插件」约束下扩展默认走哪(挂 Service、订事件、写 Session,而不是改 loop)。三层架构是读代码与读配置的地图;全插件论是改系统的规则------agent loop 无特权,模型可见性绑定日志,capability 用接口三件套替换后端。
若你下一步要动手:应用开发者先跟一条 Turn 的 Session 事件序列;插件作者先读 agent/pre-step 与 extension 表;接模型端点时从 ctx.llm 适配器与 --dump-config 看清本机加载了哪些 bundle。
源码与文档入口:deepseek-ai/deepseek-harness。