第一章:总体架构
本章目标:帮助你建立对 DeepSeek Harness 的全局认知——它是什么、为什么存在、技术栈如何选型、核心抽象有哪些、包布局如何组织、运行时启动链如何工作。阅读本章后,你应该能回答"如果我要修改某个功能,应该去哪个目录找代码"。
1.1 什么是 DeepSeek Harness
DeepSeek Harness(以下简称 dsh)是一个全插件化的 Agent 运行时框架。它的设计目标是:
所有能力都是插件:模型适配器、工具注册、会话日志、Agent 循环本身——每一个都是可替换的 Cordis 插件。
没有特权核心:你通过在配置中挂载一个插件来扩展 dsh,而不是通过修改核心代码。
配置驱动组合:一个运行中的 dsh 实例是从有序的配置层(称为 Profile 和 Bundle)组合而成的插件树。
简单来说:dsh 是一个 Agent 运行时基础设施,让开发者能通过配置文件而不是写代码来组装一个完整的 AI Agent。
1.2 技术栈
| 层次 | 技术选择 | 说明 |
|---|---|---|
| 语言 | TypeScript (ESM) | 所有包使用 "type": "module",纯 ESM |
| 运行时 | Node.js ^22.19 或 >=24 | 利用 Node 原生 TypeScript 支持和最新的模块 API |
| 包管理 | pnpm Workspaces | Monorepo 管理,workspace 协议链接 |
| 插件框架 | Cordis (vendored) | 源码级内置于 vendor/,完整控制框架层 |
| 类型验证 | Schemastery + Zod | 配置 schema(Schemastery)和运行时数据 schema(Zod) |
| 构建 | tsdown (bundling) + tsc (类型) | tsdown 打包运行时,tsc 生成类型声明 |
| 测试 | Vitest | 单元测试、快照回放、端到端测试 |
为什么 Vendored Cordis
Cordis 是一个通用的插件框架(cordiverse/cordis),dsh 选择源码级内嵌而非 npm 依赖,原因:
完全审计:框架代码和产品代码在同一个 monorepo,可 diff、可审查
本地修补:当发现框架 bug 时,可以直接修复而无需等待上游发版
Scope 重命名:所有 vendored 包从上游名字重命名为
@deepseek-ai/*,避免在 npm 上抢占上游包名
Vendored 包的完整清单(来自 vendor/README.md):
| 目录 | npm 名称 | 上游版本 |
|---|---|---|
cosmokit/ | @deepseek-ai/cosmokit | 1.8.1 |
schemastery/ | @deepseek-ai/schemastery | 3.18.0 |
cordis/ | @deepseek-ai/cordis | 4.0.0-rc.7 |
loader/ | @deepseek-ai/cordis-plugin-loader | 1.0.0-rc.5 |
include/ | @deepseek-ai/cordis-plugin-include | 1.0.4 |
group/ | @deepseek-ai/cordis-plugin-group | 1.0.0 |
timer/ | @deepseek-ai/cordis-plugin-timer | 1.1.2 |
hmr/ | @deepseek-ai/cordis-plugin-hmr | 1.0.15 |
logger-console/ | @deepseek-ai/cordis-plugin-logger-console | 1.0.0 |
1.3 包布局总览
仓库根目录结构:
核心包组
packages/ 下按能力族分组(完整的组映射见 packages/README.md):
以下是各包组的职责概要:
| 包组 | 职责 | 代表包 |
|---|---|---|
| core/ | 产品 API 脊柱 | core/session, core/agent, core/agent-loop, core/tools, core/system-prompt |
| llm/ | LLM 能力 | llm/llm(抽象服务), llm/llm-deepseek(DeepSeek 实现) |
| session/ | 持久化数据面 | session/session-persistence-jsonl(JSONL 后端), session/session-projection |
| tools/ | 模型面向的工具 | 每个能力插件注册一个或多个工具 schema |
| bundle/ | 可安装的 Patch 层 | bundle/headless, bundle/web-app, bundle/sdk-app, bundle/acp-app |
| boot/ | 启动胶水 | boot/app-boot(Profile 加载 + Cordis 初始化) |
| extensions/ | 运行时扩展 | extensions/cordis-host-runner, extensions/cordis-client-runner |
| interaction/ | 人类协作 | interaction/user-approval, interaction/user-questions |
| credentials/ | 凭证与授权 | credentials/authorization |
| sdk/ | JSON-RPC SDK | sdk/server, sdk/client |
1.4 核心抽象
在深入各子系统之前,先理解 dsh 的几个核心抽象:
1.4.1 Session(会话)
一个 Session 是一次 Agent 与用户的完整交互。它包含:
SessionHeader:元数据(ID、创建时间、配置)
SessionEventLog:一个仅追加的事件流,记录所有用户消息、助手回复、工具调用等
内存状态:运行时维护的投影(projection),从日志增量派生
核心设计原则:Model-visible ⟺ logged——任何到达模型请求的内容都必须可从日志重建。
1.4.2 Agent(智能体)
Agent 是一个可取消的工作单元,拥有:
Inbox:接收用户消息和注入上下文
Step:一次模型请求 + 工具调用序列
Turn:零或多个 Step,从 claim input 到 close
1.4.3 Capability Seam(能力接缝)
能力接缝是 dsh 中可替换能力的设计模式,由三个角色组成:
Service Definition:声明接口
Service Provider:实现接口
Consumer:使用接口,通常是面向模型的工具
关键洞察:一个 provider 的切换就能改变整个产品。文件系统和子进程 provider 共享同一个执行世界,所以把它们指向远程沙箱就能同时移动 Bash、PTY 和 LSP。
1.4.4 Profile(配置档)
Profile 是一个命名的插件组合,存储在 Harness home 中。它:
列出它堆叠的 Bundle
保存用户自己的
cordis.patch.yml保持 out-of-tree 插件的安装状态
1.4.5 Bundle(捆绑包)
Bundle 是 Cordis 配置行和它们挂载的代码的分发格式。一个 Bundle 声明自己在 package.json 的 dsh.bundle 字段中。
层次叠加顺序:
1.5 运行时启动链
当用户运行 dsh --profile headless "task" 时,以下是发生的完整链条:
关键源码文件
| 文件 | 职责 |
|---|---|
apps/cli/src/profile-boot.ts | CLI 启动入口,解析 Profile、堆叠 Patch 层、挂载 Cordis 树 |
packages/boot/app-boot/src/profile.ts | loadProfile() 实现,Bundle 加载、Patch 组合 |
vendor/cordis/src/context.ts | Cordis 上下文(Context 类),服务注册中心 |
vendor/loader/src/index.ts | Cordis Loader,解析 cordis.yml 配置 |
vendor/include/src/index.ts | Cordis Include,配置 Patch 层的应用逻辑 |
启动参数流
启动参数不是 launcher 的业务。profile-boot.ts 将参数通过 ctx.cmdlineArgs 提供给插件树,任何注入的 app 插件都可以读取这个不可变快照。
1.6 事件体系
dsh 使用三种事件域来解耦系统:
1.6.1 Session Events(会话事件)
持久化事实,追加到日志并通过 session/event 广播。用于需要存活过重载的事实。
1.6.2 Agent Events(Agent 事件)
agent/* 域,携带一个活的 Agent:inbox、step、status、request、validation、continuation。用于观察或拦截进行中的工作。
1.6.3 Capability Events(能力事件)
附加策略和适配器到接缝(fs/*、tools/*、telemetry/*),不导入循环本身。
事件派发模式
| 模式 | 等待? | 派发顺序 | 返回值? | 用途 |
|---|---|---|---|---|
emit | 否 | 注册顺序 | 无 | 通知观察者 |
waterfall | 否 | 注册顺序 | 有 | 中间件,next() 委托 |
parallel | 是 | 并行 | 无 | 扇出 |
serial | 是 | 注册顺序 | 有 | 有序执行 |
bail | 否 | 直到 bail | 有 | 短路 |
1.7 Turn 流程概览
一个 Turn 是一次完整的 Agent 交互:
关键事实:
turn/*、step/*、user/message、assistant/*、tool/*是持久化会话事件其余(
agent/pre-step、agent/request、llm/stream、tools/*)是活的扩展点Waterfall 事件(
agent/pre-step、agent/request、llm/stream、三个tools/*)的监听者必须调用next()来委托
1.8 小结
| 概念 | 一句话解释 |
|---|---|
| Cordis | 插件框架,所有功能通过它组装 |
| Profile | 命名的插件组合配置 |
| Bundle | 可分发的配置行 + 代码 |
| Session | 一次交互的完整记录(仅追加日志) |
| Agent | 可取消的工作单元,拥有 Inbox |
| Turn | Agent 一次完整交互(多个 Step) |
| Step | 一次模型请求 + 工具调用 |
| Capability Seam | 可替换能力的三角色模式 |
下一步:第二章:环境与运行——了解如何搭建开发环境、理解 CLI 启动过程和 Profile 机制的细节。