【图解】DeepSeek Harness 架构解析

写在前面

昨天晚上,DeepSeek 发布了自家的 Harness 简称 dsh,黑色鲸鱼Logo,我也Clone下来部署,并直接用 dsv4-pro 来解析 dsh。

本文将结合 DeepSeek Harness 解析 DeepSeek Harness 的结果来给大家展开说一下整个 dsh 的架构。

整体架构

之前 Claude Code 源码泄漏的时候,我们解析过 CC 的架构。CC是一个非常稳健的架构:Agent Loop 是固定的,Tool 是列表里挂上去的,扩展靠 Hook 和 MCP 从边上插进来。

这种工程设计很稳,也能保证广度和深度的扩展,但如果真想改 主干,那要fork大改。

而整个 dsh 架构,我给你最简洁、最直接、最不绕弯儿的一句话总结:

DeepSeek Harness is a plugin-based agent harness on vendored Cordis: everything is a plugin

这里的 everything,除了 Tool 是插件,provider 是插件外,连 agent loop 本身都是一个插件 。 说白了就是这个 harness 没有内核。你在别的框架里要动内核才能干的事,在这里只是在 YAML 里改一行。

DSH的一次执行

下面这张图从左到右展示一次运行的组成:

  • 启动组合层:dsh CLI 选择 web profile,再把 dsh-base、dsh-web-app 和用户 patch 叠成最终配置。
  • Cordis Runtime维护共享 context。插件通过 ctx.sessions、ctx.tools、ctx.llm 等服务协作,Typed Event 负责把策略插入运行过程。
  • Capability Seams可替换能力接缝。LLM、Shell、文件系统、沙箱、审批、Skill、SubAgent 并不直接写死进 Loop, 通过 Definition、Provider、Consumer 三个角色接上 Runtime ,让某种底层能力可以整体替换,而不用修改上层 Agent 或调用者。
  • Surface / Output:同一套后端能力可以被 Web、ACP/JSON-RPC 等入口调用;Session JSONL 保存可重放事实。

看到这里,你可能对一些新概念很陌生,不要担心,下面我会一个个跟你讲明白的。

dsh web 启动时做了什么?

入口先准备一个空的 Cordis 根插件树,再把多个 Patch Layer 按顺序叠上去。 Patch 是对这棵插件配置树进行"添加、替换或禁用"的配置操作,Patch Layer 则是一组 Patch。启动关系如下:

⚠️ 我们这里先注意三个基本概念:

  • Plugin:一个具体能力
  • Bundle:一套能力组合包
  • Profile:决定本次启动使用哪些组合包

当前默认 web profile 的两个主要 bundle 是:

  • dsh-base:包含模型、Agent Loop、Session、Tools、Shell、审批、沙箱等基础配置,共 78 行配置;
  • dsh-web-app:包含 Web Server、API、浏览器模块和 UI 插件等配置,共 51 行配置。

两者合计 129 行。这就是 129 个可被 patch 定位的配置行。

同一个 id 被后层命中时,后层取得更高优先级,但配置排在前面不等于插件必须先启动。真正决定激活时机的是 Cordis 的 Service 依赖:Consumer 声明 inject,缺少依赖就等待,依赖到齐再激活。配置顺序负责覆盖,配置解决"最终装什么",依赖图解决"什么时候能运行"。

⚠️ 注意:一条 patch 是按 id 定位条目、然后替换它的整个 config,不是深度 merge

Cordis 插件与事件

在 dsh 中,一个插件通常会做三类事情:

  1. 向共享 Context 注册 Service;
  2. 监听带类型的事件,参与某段流程;
  3. 注册可撤销 Effect,让资源跟随插件生命周期释放。

Service 像是稳定插座,Event 像是流程中的插针点,Effect 则负责"拔掉插件时把现场恢复干净"。 这三者合在一起,才让 Everything is a Plugin 不至于退化成一堆全局回调。

事件分成三个域:

Turn / Step 状态机:一次对话发生了什么?

接下来我们来看 ReactLoopAgent。它的核心实现没有想象中庞大,关键方法只有 preStep()turn()step() 等几段。先用图看一次完整流转:

官方定义:

  • Step:一次模型请求,加上这次请求发起的 ToolCall;
  • Turn:零个或多个 Step,从领取输入开始,直到没有后续工作为止。

为什么 Turn 里不是固定一个 Step?因为模型第一次请求可能只返回 ToolCall。工具结果写入 Session 后,模型还要在下一次请求中看到它,才能继续推理或给出最终答案。实际流程可以拆成七步:

  1. Inbox 领取一条输入,写入 turn/start;
  2. systemPrompt.assemble() 汇总 Prompt Section、变量和 Tool Schema;
  3. agent/pre-step 允许插件改写输入或拒绝本次 Step;
  4. 写入 step/start 与 user/message;
  5. 从 Session Log 投影模型历史,llm.prepareCall() 绑定本次精确 Adapter,再流式请求;
  6. 每个流式片段写成 assistant/chunk,完成后再写 assistant/message
  7. 若存在 ToolCall,进入 Tool Pipeline,写入 tool/result 后再开下一 Step;否则关闭 Step 与 Turn。

这样可以让 流式体验和最终语义分开保存既保证消息原始可重放,模型上下文又不必塞进一堆原始 chunk,并且Loop 也不需要承担策略 ,是否批准工具、如何改请求、Prompt 多一段什么内容、工具结果要不要裁剪,都由 Event 或 Service Plugin 完成,Loop 只维护状态机和持久化顺序

Capability Seam:换 Shell,不改 Tool,更不改 Loop

"可插拔"最容易停留在接口层。DeepSeek Harness 更进一步,把一项能力拆成三个角色:

  • Service Definition:只声明能力契约;
  • Service Provider:提供一种实现;
  • Consumer:使用能力,常见形式是暴露给模型的 Tool。

举个例子:

  • shell 定义 ShellService
  • bash-localbash-sandboxpwsh-local 提供不同实现
  • tool-bash 作为 Consumer,把能力暴露给模型。

Consumer 注入的只是 Shell 契约,所以更换 Provider 时,Tool 和 Agent Loop 都不必产生平台分支。

官方文档强调:只有 Definition、Provider、Consumer 三者齐全,才构成真正的 Capability Seam。只有一个接口,叫抽象;只有一个 Tool,叫功能;三者能独立替换,才叫 能力缝

Session Log:模型上下文不是 messages 数组

很多 Agent 原型会在内存里维护一个 messages,再顺手把它渲染到 UI。DeepSeek Harness 反过来:先把事实写进 Session Log,再为不同用途生成投影。

下面这张图把后端 Session 与前端启动放在一:

  • core/session 维护 append-only 的 SessionEvent 日志。user/messageassistant/messagetool/result 等模型可见事实全部从这里产生。
  • deriveMessages再按 Surface 规则投影出下一次 LLM 请求所需的消息。

同一份日志还可以生成:

  • 持久化 JSONL;
  • Web 实时事件;
  • Fork、Resume 与 Replay;
  • Transcript、Telemetry 与 Compact 后的新历史。

官方是这样概括它:Model-visible means logged. 任何进入模型请求的内容,都必须能从日志重建。

Web UI 也没有逃离插件架构。

  1. Host 扫描各包的 dsh.client 声明,生成带依赖边的启动清单
  2. 再把 window.__DSH_BOOT__ 注入首页;
  3. 浏览器根据清单加载 /plugins/<id>/client.js
  4. 最后组合 React Shell。

也就是说,后端和前端共享的是同一种思想:先声明组件与依赖,再由运行时组合。

最后

DeepSeek Harness 把 Agent 工程里散落的几件事连成了一个闭环:

  1. Profile 把配置组合成插件树;
  2. Cordis 用 Service、Event、Effect 管理运行时协作;
  3. Capability Seam 隔离契约、实现与调用者;
  4. Agent Loop 只维护 Turn / Step 状态机;
  5. Session Log 为模型、持久化与 UI 提供同一事实来源。

浓缩成一句话:

DeepSeek Harness 的架构中心不是 Agent Loop,是可组合、可替换、可回放的能力网络。

参考信息

相关推荐
Dr.kangder1 小时前
嵌入式面试总结(十六)——AMBA总线
面试·职场和发展·架构·嵌入式·虚拟化·总线
Dr.kangder1 小时前
嵌入式面试总结(十九)——内存泄露
单片机·算法·面试·职场和发展·架构·硬件架构
Dr.kangder2 小时前
嵌入式面试总结(十七)——DMA总线
面试·职场和发展·架构·嵌入式·总线
用户852495071842 小时前
Next.js + Redis:我的笔记,终于有了记忆
架构
Maxkim2 小时前
DeepSeek Harness 源码深度分析:像 VS Code 一样插件化的 Agent 框架
前端·架构
用户852495071842 小时前
Next.js App Router:一篇笔记的服务端奇幻漂流
架构
晴天163 小时前
Cordis 框架代码核心解析:一个可逆插件系统的实现-Day18
人工智能·ai·架构
小新讲网安4 小时前
HTTP请求走私攻击实战:CL.TE与TE.CL绕过前端服务器全解析
服务器·前端·网络·web安全·http·架构·漏洞
阿图灵17 小时前
Agentic AI 架构入门(九):Agent 通信协议全景——ACP/A2A/AG-UI/MCP
人工智能·ui·架构·ai agent·智能体·mcp·agentic ai