拆解 DSH 的一切皆插件架构:从启动组装、Cordis 三件套到 turn/step 循环与 Session Log,核心是 agent loop 可整体热替换,为 agent 自进化铺好基础设施。
2026 年 8 月 13 日,DeepSeek 发布了自己的第一个 agent harness,名字就叫 DSH(DeepSeek Harness)。DSH 与 Codex、Claude Code 这些主流 harness 的差异是结构性的:其他 harness 把 agent loop 写死在内核里,DSH 把它做成了一个可以在运行中整体替换的插件,这个选择的后果延伸到了架构的每个角落。
这篇博客循序渐进地回答四个问题:DSH 是什么?一次运行如何组装出来?运行起来之后,循环与日志如何协作?以及这套架构究竟适合谁?
先看熟悉的结构。Codex、Claude Code 这类主流 harness 都有一个稳定的核心:一段硬编码的控制流负责组装上下文、请求模型、执行工具,扩展从边上挂进来。skill 是 Markdown 文件,MCP 是一段配置,hook 是事件触发的脚本。这种设计很稳,扩展也够用,但主干改不了,想换掉核心逻辑只有 fork 源码重新编译一条路。
DSH 可以压缩成一句话:一个基于 Cordis 运行时的插件化 harness,一切皆插件。工具是插件,模型 provider 是插件,session 是插件,连 agent loop 本身也是一个插件。这个 harness 没有特权内核,别的框架里要动内核才能做的事,在这里改一行配置就行。
两条路线的差异,本质上是两种插件模型:
这个难题正是 Cordis 存在的意义。它替每个插件记住装载时做过的每一件事,卸载时逐件撤销;它盯着依赖关系,依赖到位通知启动,依赖离开通知停下;热更新失败时事务性回滚,永远不会卡在半新半旧的中间态。Cordis 在生产环境里支撑过 4000 多个插件的生态,这套机制是 DSH 后面一切设计的地基。
DSH 启动时没有固定的功能清单,最终跑起来是什么样,完全由配置组合决定。
组合涉及三个概念,粒度从小到大:
组装的全过程:Profile 挑选 Bundle,Bundle 圈定 Plugin,最终在进程里运行的只有 Plugin。
以默认的 web profile 为例,这份 profile 声明了 dsh-base 和 dsh-web-app 两个 bundle。dsh CLI 从一棵空的插件配置树开始,依次叠上三层:
dsh-base:基础 bundle,带来模型、agent loop、session、工具、shell、审批、沙箱等基础能力的插件配置;dsh-web-app:界面 bundle,带来 Web Server、API 和 UI 等 web-app 插件配置;id 定位前两层的任意条目,整条替换它的配置。叠加规则只有一条:后叠的层优先。树上每个条目对应一个插件的配置,所以两个 bundle 装进来的任何插件,都可以被 patch 定位、替换或禁用。
配置树定下来之后,插件还不会立刻运行:每个插件用 inject 声明自己需要的服务,缺依赖就等待,依赖到齐才激活。分工因此很清楚:装什么,归配置顺序管;何时运行,归依赖图管。
装载之后,一个插件与框架的全部交互可以归成三类:
ctx.tools、ctx.llm、ctx.sessions;Service 对外提供能力,Event 参与流程,Effect 保证卸载干净。三件套合在一起,一切皆插件才没有退化成一堆全局回调。
默认的 loop 实现叫 ReactLoopAgent。它实现 core/agent 定义的 Agent 接口,其他插件只依赖这个接口,从不依赖具体实现,这是循环可以整体换掉的前提。
循环把工作组织成两个嵌套层次:
step 数量不固定的原因很直接:模型第一次请求可能只返回工具调用,工具结果落进日志后,模型要在下一次请求里看到它,才能继续推理或给出最终答案。每多一轮工具调用,就多一个 step。
驱动器由一台小状态机门控,只有 idle、maintenance、running 三个相位。状态切换会发出 agent/status 事件,任何插件都能旁观循环的生命周期。
一轮流转切成三个阶段看,逻辑就很清楚。
阶段一:接收输入,组装上下文
send、followup、steer、inject 进入 Inbox 队列,唤醒驱动器,写入 turn/start;systemPrompt.assemble() 组装 prompt 段落、变量和工具 schema;agent/pre-step 给插件第一次介入机会:拒绝本步,或改写输入批次。阶段二:发起一次流式的模型请求
step/start,认领的输入此刻才落成持久化的 user/message;agent/request 允许插件替换 provider、模型与调用参数,历史消息从会话日志投影而来;assistant/chunk,完成后再写一条完整的 assistant/message,实时展示与最终语义分开保存;请求失败时由 agent/request-error 决定是否重试。阶段三:决定继续还是结束
tool/result 落进日志,开下一个 step;agent/turn-stopping 给插件最后一次反对机会,没人反对就写入 turn/end,本轮结束。
工具调度按执行模式分组:
maxParallelToolCalls 控制并发上限。每个调用都经过 tools/pre-execute、tools/execute、tools/post-execute 三段流水线,超时、审批、限流、观测这类策略全部挂在这三段上。
还有一个容易忽略的细节:结果按模型发起调用的顺序提交,先完成的调用要等前面的槽位就绪,保证模型看到的世界始终一致。
纵观整个循环,其实只有两类构件:
turn/start、user/message、assistant/message、tool/call 这些边界全部写进会话日志,负责记录事实;agent/pre-step、agent/request、agent/request-error、agent/turn-stopping 四个接缝,负责让插件介入。Loop 只维护状态机和持久化顺序,策略全部住在接缝上。是否批准工具、如何改请求、prompt 里多加一段什么内容,都由事件或 Service 插件完成。大多数定制根本不需要碰循环本身。
很多 agent 原型在内存里维护一个 messages 数组,再顺手渲染到 UI。DSH 把方向倒了过来:先把事实写进追记式的 Session Log,再为不同用途生成投影。
模型可见的历史从不单独存储,每次请求前由 deriveMessages() 从日志现算出来。同一份日志派生出所有下游视图:
这条不变量可以概括成:任何进入模型请求的内容,都必须能从日志重建。直接的好处是,无论换成哪个 loop 实现,审计、回放、恢复用的都是同一套日志工具。
可插拔最容易停留在接口层,DSH 把一项能力拆成三个角色:
shell 定义 ShellService;bash-local、bash-sandbox、pwsh-local;tool-bash。关键在于 Consumer 注入的只是契约。把 bash-local 换成 bash-sandbox,工具和 agent loop 一行代码都不用改,也不会出现平台分支。
先把 agent loop 放到一边,声明式和命令式两条路线的能力上限其实一样,最终取决于业务代码本身的质量。想给 Codex 的常驻 MCP server 加运行时热替换,加一个 reload 端点就能做到,只是撤销、清理、依赖协调这些坑要自己踩。DSH 把这些基础设施全部铺好,单是 fiber.ts 一个文件就有 750 行,代价是更高的开发门槛:通过框架接口注册副作用、声明依赖、返回撤销函数,还要理解插件生命周期状态机。
日常场景里,声明式几乎总是够用。搜索服务本来就是短交互,改配置重启一次两三秒;skill 是纯文本,下次读取自动生效。真正配得上运行时热替换的组件要同时满足两个条件:在进程内持有跨 turn 的状态,且这种状态没有持久化到 Session Log。满足条件的组件少之又少,上下文管理器算一个典型,它持有哪些内容保留、哪些压缩的策略状态。
不过有一个能力,声明式模型补多少代码都追不上。硬编码的 run_turn() 只在预设时间点开放 hook,你没办法在运行时把单 agent 循环换成多 agent 协作循环,也没办法把发起请求、等待响应、执行工具的串行逻辑改成流式解析加并行执行。DSH 里换一套 loop,只需要写一个实现相同 Agent 接口的插件,在配置里替换上去,框架自动卸载旧插件,把注册过的事件和服务干净撤销,等依赖就位后平滑启动新循环。
回头看,Cordis 那整套机制,副作用跟踪、依赖通知、事务性热更新,本质上都在支撑 loop 可替换这一个目标,其余特性更像副产品。这套架构真正铺平的是 agent 自进化的基础设施:agent 在运行中给自己生成一个新工具插件,框架不重启进程、不中断任务就把它热加载进来;生成的代码出了错,事务性回滚退回上一个稳定状态。模型这一侧的条件也已成熟,生成几十行的工具插件早就是日常操作,生成几百行、能处理状态与调度的完整 loop 也踩在顶尖模型的能力边界之内。DSH 的 loop 本身就是可读可改的 TypeScript 插件,接口规范、代码结构、生命周期约束对模型天然可见;反观把 loop 编译进二进制的方案,就算模型写得出新循环,系统里也没有一个插槽能把它装上去。
如果日常用 Codex 或 Claude Code 写码,DSH 不会让体验变好:声明式模型足够简单,重启两三秒完全可以接受,Cordis 的机制对你更多是额外的复杂度。如果你想让 harness 自我演化,动态切换 loop 策略,一边运行一边长出新能力,DSH 则是目前唯一把底层基础设施搭建完整的方案。
[1] DeepSeek. (2026). deepseek-harness [Source code]. GitHub. https://github.com/deepseek-ai/deepseek-harness
[2] Cordiverse. (2026). A programming paradigm for spatiotemporal composability [Source code]. GitHub. https://github.com/cordiverse/paper
[3] Yage. (2026). 深度剖析 DeepSeek 最新的 Harness DSH:为了自进化这盘醋包了一整盘饺子. yage.ai. https://yage.ai/share/dsh-deep-analysis-20260813.html
[4] 【图解】DeepSeek Harness 架构解析. (2026). CSDN. https://deepseek.csdn.net/6a7f254310ee7a33f29b0548.html
[5] sing1ee. (2026). DeepSeek Harness Agent Loop 2026:驱动每一轮对话的可替换插件内部解析. 博客园. https://www.cnblogs.com/sing1ee/p/22479295