LESSON 02 · 建立全貌

一张图看懂 DeepSeek Harness 整体架构

Profile、Bundle、Preset 和插件分别处在什么位置,又是怎样连成一套系统的?

在 DeepSeek Harness 系统中,下面几个核心概念贯穿其中。先整体来理解一下每个概念的意思,以及它们之间是什么关系。心里有了这张地图,后面再看启动命令、配置文件和源码,就不容易迷路了。

这一课先不追函数,也不研究配置语法。我们只回答三个问题:系统启动时要准备什么,一个具体 Agent 怎样创建出来,以及创建以后怎样工作。

1

Harness Host

DeepSeek Harness 采用了模块化、分离式的设计。它没有把入口、模型、工具、存储和某个具体 Agent 的提示词全部绑成一个整体,而是把系统大体拆成两部分:一部分是长期运行、可以被多个 Agent 共享的 Harness Host;另一部分是用户创建或恢复 Session 时,才根据需要装配出来的 具体 Agent

因此,DSH 会经历两次不同范围的装配。程序启动时,先装配公共 Host;任务到来时,再在 Host 中为对应 Session 装配 Agent。两部分可以使用不同的插件配置,也有不同的生命周期:Host 通常跟随整套程序运行,Agent 则可以按需创建、恢复和结束。

这里的“分离”主要是职责、配置和生命周期的分离,不要把它理解成两套完全无关的程序,也不表示 Host 和 Agent 必须运行在两个独立进程中。具体 Agent 仍然由 Host 创建和管理,并使用 Host 已经准备好的公共服务。

上一课的苹果 Logo 例子里,一个 Agent 要顺利工作,除了模型,还需要素材搜索工具、视觉检查工具、Agent Loop、会话记录和权限控制。这里面有些能力属于某个具体 Agent,有些能力却可以被所有 Agent 共用。

例如,Web Server 不需要每创建一个 Agent 就重新启动一次;模型 Provider、Session 存储和工具注册中心,也没有必要为每个任务重新搭建。更合理的做法,是先启动一个长期运行的公共环境,把这些基础能力统一准备好。以后无论创建多少个 Session、运行多少个 Agent,都可以在这个环境里使用它们。

前面所说的这个长期运行环境,就是 Harness Host 实际承担的角色。这里的“宿主”不是某个具体 Agent,而是承载插件和服务的那套主程序:它负责加载插件、保存公共服务,并在需要时创建和管理 Agent。

可以先这样区分:

对象先这样理解
Harness Host长期运行的公共底座,负责装载插件并提供模型、Session、工具、入口等共享能力
具体 Agent在 Host 中按需创建的任务执行者,拥有自己的 Session、Preset、提示词和工具组合

因此,执行一次 Agent 任务并不是把整套 DeepSeek Harness 从头启动一遍。通常是先把 Host 启动好,再在其中创建或恢复具体 Agent。

2

再看整体:DSH 分三步跑起来

先看下面这张图。图里出现的词比较多,但主线其实只有三步。

DeepSeek Harness 中 Profile、Bundle、Plugin、Host、Preset 与 Agent Loop 的整体架构

图 2-1:先启动 Harness Host 这套公共底座,再为 Session 创建具体 Agent,最后由 Agent Loop 协调模型、工具和运行记录。

第一步是启动 Harness Host。模型接入、Session、工具系统、Agent Loop 和 Web 服务等公共能力,会在这个阶段准备好。此时公共底座已经就绪,但还没有决定某个 Agent 的具体工具和提示词组合。

第二步是创建具体 Agent。用户新建或恢复一个 Session 后,系统根据 Preset,决定这个 Agent 能看到哪些工具、使用哪些提示词和 Skills。

第三步才是 Agent 真正开始工作。任务从 UI、CLI 或 API 进入 Agent Loop,Loop 再去调用模型、执行工具,并把重要过程写进 SessionEvent。

这里说的“装配”,其实就是把本次需要的插件加载起来,再让它们连接到各自依赖的服务上。

3

Plugin:真正提供功能的插件

Plugin 就是插件,也是 DSH 最基本的功能单元。

文件读取可以是一个插件,终端工具可以是一个插件,模型适配器、Session 持久化、Agent Loop,甚至 Web 入口也都可以由插件提供。

所以 DSH 所说的“一切皆插件”,范围比“给 Agent 增加几个工具”更大。它的意思是,Harness 自己的大部分组成也可以按需要加载、替换或扩展。

插件之间并不是完全没有关系。一个工具插件可能需要文件系统服务,Agent Loop 需要模型、Session、工具和系统提示词等服务。插件会声明自己需要什么,Cordis 再负责等待这些依赖就绪,并把它们连接起来。

如果某个必须服务一直没有人提供,相关插件就无法正常激活。也就是说,DSH 的完整性不是靠“所有插件必须塞进固定清单”保证的,而是靠当前运行路径上的依赖是否完整来保证。

4

Profile、Bundle 和 Patch:决定宿主怎样启动

这三个概念都出现在第一步,也就是公共 Host 的启动阶段。

Profile 是一套有名字的运行方案。

例如 web Profile 表示以Web UI的方式启动DSH系统。其他 Profile 可以选择不同入口。Profile 关注的是整套程序以什么形态运行,而不是某个 Agent 具有什么样。

已经有了 Plugin,为什么还需要 Bundle

Plugin 解决的是“一个具体功能怎样接入系统”。例如,文件插件提供文件能力,模型 Provider 插件负责连接模型,Session 插件负责保存记录。可是,一套能够启动的 Host 往往需要几十个插件,而且每个插件还可能带有自己的配置。

如果让每个 Profile 都直接罗列全部插件,很快会遇到几个问题:

  • webheadless 等运行方案都需要 Session、模型和 Agent Loop,同一份公共配置会被复制很多遍;
  • 增加或调整一个核心插件时,需要同时修改多个 Profile,很容易出现遗漏;
  • 插件虽然都写进了名单,但配置不一致或者缺少配套插件,整套系统仍然可能无法正常工作;
  • 第三方想复用一套成熟底座时,只能复制一大段配置,后续也很难跟随原方案升级。

Bundle 就是为了解决这类“成套复用”问题。它把一组经常一起出现的插件及其默认配置整理成一个可以反复使用的配置层。Profile 不必了解其中每个插件的细节,只需要选择自己需要的 Bundle,再按顺序把这些配置层叠加起来。

web Profile 为例,它主要组合两层 Bundle:

web Profile
    ├── @deepseek-ai/dsh-base
    │     提供 Session、模型注册、工具系统、Agent Loop 等公共底座
    └── @deepseek-ai/dsh-web-app
          在公共底座上增加 Web Server、API 和浏览器界面

headless 运行方案也可以复用同一个 dsh-base,只把第二层换成适合一次性命令行任务的 Bundle。这样公共能力只需要维护一份,不同运行形态只补充自己的差异。

Bundle 是一组成套的插件配置。

Bundle 的重点不是再发明一种功能单元,而是把多个 Plugin 组织成可复用的装配方案。它在启动时会展开为配置层,最终仍然由 Cordis 加载其中列出的具体插件。

因此,引入 Bundle 主要带来四个好处:

  1. 减少重复:多个 Profile 可以共享同一套公共插件配置;
  2. 分层组合:先加载基础 Bundle,再叠加 Web、Headless 或业务 Bundle;
  3. 保持一致:相关插件和默认配置作为一组维护,不容易只改其中一部分;
  4. 方便扩展和升级:业务项目可以复用官方底座,只维护自己的增量配置。

Patch 是对默认配置的局部修改。

你可能只想调整端口、更换模型配置、关闭某项功能,或者增加自己的插件。这时不用复制整套 Bundle,只需要用 Patch 修改其中一小部分。

把三者连起来看,就是:

Profile 选择运行方案
    ↓
Bundle 提供成套配置
    ↓
Patch 调整需要修改的部分
    ↓
得到最终插件树

最终交给 Cordis 的不是几个抽象名词,而是一棵已经合并好的插件配置树。Cordis 会按照这棵树加载真正的 Plugin。

5

Preset:决定这个 Agent 会什么

公共 Host 启动完成后,系统已经具备模型、Session、Agent Loop 和入口等基础能力。但这时还没有决定某个 Agent 应该使用哪些工具、提示词和 Skills。

Preset 就是针对某一类 Agent 的组成方案。

例如,一个精简编码 Agent 可以只保留少量核心工具;一个标准编码 Agent 可以加入文件编辑、终端、搜索、Plan 和子 Agent;另一个业务 Agent 则可以换成订单查询、知识库检索和内部审批工具。

因此,Profile 和 Preset 虽然都在“选择一套配置”,但选择的对象完全不同:

概念什么时候使用决定什么
Profile程序启动时整套 Harness 以什么形态运行
Preset创建或恢复 Agent 时这个 Agent 有哪些工具、提示词和 Skills

同一个 Web Host 可以同时服务多个 Session,这些 Session 也可以选择不同 Preset。没有必要为了换一种 Agent,就重新启动整套 Harness。

6

Cordis Context:让插件找到彼此

插件被选中以后,还需要一个共同的运行环境。这个环境就是 Cordis Context。

插件可以把自己提供的服务登记到 Context,也可以声明自己需要哪些服务。你在源码中经常看到的 ctx.llmctx.tools,就是 Context 中的服务入口。

服务先这样理解
ctx.agents创建、查找和驱动 Agent
ctx.sessions管理 Session 与运行记录
ctx.llm提供统一的模型访问入口
ctx.tools汇总工具并处理工具执行
ctx.systemPrompt汇总各插件提供的系统提示词

Service 表示“系统需要有人负责这件事”,Provider 表示“具体由谁来做”。例如 ctx.llm 是统一的模型服务,DeepSeek 模型适配器或其他模型适配器可以提供不同实现。

工具的情况又不太一样。一个 Agent 往往需要文件、终端、网页等多个工具,因此多个工具插件可以同时注册到 ctx.tools,它们不是只能二选一。

7

Agent Loop:让系统持续工作

前面的步骤只是把能力准备好。接到任务后,真正负责推进过程的是 Agent Loop。

它大致会重复下面几件事:

  1. 读取当前 Session 中已经发生的事情;
  2. 整理系统提示词、对话和可用工具;
  3. 调用模型,让模型判断下一步;
  4. 如果模型要求调用工具,就把请求交给工具系统;
  5. 把模型回复和工具结果记录成 SessionEvent;
  6. 带着新的记录继续下一轮,直到可以回复用户。

SessionEvent 可以先理解成 Agent 的运行记录。它不只保存聊天文字,也会保存工具调用、工具结果以及其他需要恢复的信息。正因为这些过程被记录下来,Agent 才能在下一轮继续工作,界面也能重新显示之前发生了什么。

8

带着这张地图进入后面的课程

现在可以把整套架构压缩成三层:

  • Profile、Bundle、Patch 和 Cordis 负责把公共 Host 启动起来;
  • Session 与 Preset 负责准备某个具体 Agent;
  • Agent Loop、模型、工具和 SessionEvent 负责让任务持续运行。

后面的课程会把这些部分逐一放大。下一课先从 pnpm dsh web 开始,看第一步的公共 Host 到底是怎样启动的。

下一课 · 03

从 pnpm dsh web 看完整启动过程

一条命令怎样一步步变成一套可以接收任务的 Web Agent?

0 人点赞,0 人看过