第01章:项目概览与核心定位
DeepSeek Harness(命令行名 dsh)是 DeepSeek AI 开源的一个 agent harness(智能体框架)。它不是模型评测工具,也不是某个大模型的聊天客户端,而是一个你可以完全掌控、可替换、可编程的智能体运行时底盘。本章建立对 dsh 的整体认知:它是什么、不是什么、核心架构口号、底层驱动框架、技术栈画像、三种产品入口,以及本教程 14 章的阅读路径。
写作时点声明:本文写作于 2026 年 8 月,对应仓库版本
0.1.0-rc.7。项目目前处于 developer preview(开发者预览) 阶段,官方在 README 中明确警告 THERE WILL BE COMPATIBILITY-BREAKING CHANGES(将出现破坏兼容性的变更)。文中所有命令、配置键、包名均以master分支原文核对,但仍可能随上游迭代而失效,请以仓库当前代码为准。
1.1 定位澄清:dsh 是什么、不是什么
1.1.1 一句话定位
官方 README 给出的定义只有一句话:
DeepSeek Harness (
dsh) is an open-source agent harness developed by DeepSeek AI.
中文版 README.zh.md 把 agent harness 译为「智能体框架」。这是理解 dsh 的锚点:它是一套承载智能体运行的框架——负责把「模型 + 工具 + 循环」这三样东西装配成一只可工作的 agent,并提供观测、配置、扩展的机制。推理本身由模型 API 完成,dsh 不做训练、不做推理加速。
1.1.2 「harness」一词的歧义
「harness」在英文工具生态里是一个被严重过载的词,dsh 用它容易让读者产生误判。最常见的两个「harness」家族是:
| 含义 | 代表项目 | 做什么 |
|---|---|---|
| 评测 harness(evaluation harness) | EleutherAI/lm-evaluation-harness、早期 HELM | 用基准题集给语言模型打分,产出评测分数 |
| 智能体 harness(agent harness) | DeepSeek Harness(dsh) |
承载 agent 的运行框架,装配模型、工具与循环 |
dsh 与 lm-evaluation-harness 没有任何关系。 两者只是共享了「harness」这个词。评测框架的「harness」是「测试夹具 / 跑道」,用来套住模型做测量;dsh 的「harness」是「马具 / 底盘」,用来把模型、工具、循环装配成一只能干活、能观测、能替换的智能体。方向完全相反:一个负责「考」,一个负责「跑」。
这个歧义有实际后果:如果你带着「评测框架」的预期去搜文档、读命令、看 issue,会处处对不上号——dsh 里没有 benchmark、没有数据集、没有评分,有的是 profile、bundle、session log、工具管线。先摆正这个预期,后续章节的术语才有落点。
1.1.3 它不是什么
顺着上面的澄清,可以列出一组明确的「不是什么」:
- 不是评测框架。它不跑 MMLU、HumanEval 之类的基准,也不产出分数。
- 不是 DeepSeek 官方聊天客户端。虽然默认接 DeepSeek 模型,但模型适配器本身是可替换插件,可以接 Anthropic、OpenAI 或其他 OpenAI 兼容端点(详见第四章)。
- 不是一个「写死的 CLI 程序」。
dsh本身只是 profile 的启动器,真正的产品形态由插件树组合出来(见 1.2 节)。 - 不是对 ChatCompletion API 的薄封装。它下面是一套完整的插件框架(Cordis)、session 日志系统、工具执行管线与权限沙箱,工程厚度远超「包一层 HTTP」。
那么它是什么?一句话:一个「一切皆插件」的智能体运行时框架,让你从配置层把整套产品拼出来,也让你从配置层替换掉其中任何一块。
1.1.4 常见误读速答
几个高频问题,直接给出准确答案:
| 问题 | 答案 |
|---|---|
dsh 和 DeepSeek 官网的 Chat 是一回事吗? |
不是。Chat 是面向人的聊天产品;dsh 是面向开发者的 agent 运行时,让你自己拼装模型、工具与循环。 |
dsh 能评测模型吗? |
不能。它是运行框架,不跑基准、不产分数,与评测 harness 无关。 |
dsh 只能用 DeepSeek 模型吗? |
不是。默认挂 DeepSeek 适配器,但适配器是插件,可接 Anthropic、OpenAI 或任意 OpenAI 兼容端点(见第四章)。 |
dsh 和 Claude Code / Cursor 是一类吗? |
形态上更接近「可编程的 agent 运行时」:它卖的不是某个成品交互界面,而是拼装与替换任意一块的能力。 |
用 dsh 需要写代码吗? |
日常用 Web UI 不需要;但它的价值上限在于「写插件从配置层改产品」,那需要 TypeScript。 |
1.2 一切皆插件:核心架构口号
dsh 的架构口号直接写在 README 里:
It uses an architecture where everything is a plugin.
这七个字是理解整个项目的第一性原理。它不是说「支持插件扩展」,而是说产品本身没有特权内核——连最关键的部分都是插件,因而都可被替换。
1.2.1 没有特权内核
传统框架的结构是「内核 + 扩展」:内核写死一套行为,扩展只能在预留的钩子上做加法。dsh 反其道而行。官方架构文档的原文是:
Every part of the product is a plugin, including the model adapter, the tool registry, the session log, and the agent loop itself, so every part is replaceable from configuration.
There is no privileged core to patch: you extend dsh by mounting a plugin beside the others, and registrations are effects that unwind when their plugin unloads.
翻译过来有三层含义:
- 模型适配器是插件、工具注册表是插件、session 日志是插件、agent 循环本身也是插件。
- 扩展方式不是「给内核打补丁」,而是「在别的插件旁边再挂一个插件」。
- 所有注册都是可逆的 effect,插件卸载时自动回卷——所以「热插拔」是天然的,不是后加的。
1.2.2 核心包只是挂在 context 上的服务
架构文档给出一张核心包对照表,说明「产品」如何由插件拼成。这些包各自贡献一个挂在共享 context 上的服务,彼此通过服务键(ctx.<key>)查找,而不是互相 import 具体实现:
| 包 | 负责什么 | ctx 键 |
|---|---|---|
core/session |
append-only 的 SessionEvent 日志 + 内存存储 |
ctx.sessions |
core/system-prompt |
提示分节与工具 schema 组装 | ctx.systemPrompt |
core/tools |
作用域化的工具注册表 + 带守卫的执行管线 | ctx.tools |
core/agent |
Agent 接口、存活注册表、agent/* 事件 |
ctx.agents |
core/agent-loop |
实现该接口的默认驱动 | ctx.agentLoop |
core/scope |
每个 agent 的作用域化注册原语 | 库,无键 |
llm/llm |
消息/流词汇表 + 模型适配器 seam | ctx.llm |
看到这张表,你就能理解「一切皆插件」的后果:如果 core/agent-loop 这个默认驱动不满足你的需求,你可以写一个插件注册自己的 ctx.agentLoop,把它整个换掉——不需要 fork 仓库,不需要改内核,因为内核本来就不存在。
1.2.3 注册即 effect:可逆是默认语义
插件对 context 的每一次贡献(注册一个提示分节、一个工具 schema、一个模型适配器、一个事件监听器)都是通过 ctx.effect() 或 ctx.on() 完成的。这两个 API 的关键性质是:注册会返回一个 disposer,插件卸载时框架按注册顺序反向调用这些 disposer,把贡献「卷」回去。
这个设计的意义在于:插件之间的依赖通过「服务需求」而不是「手动启动顺序」表达,热重载、配置热更新、沙箱隔离、子作用域都建立在同一套可逆语义之上。Cordis 教程把这个思想浓缩成一句话:
Registrations are reversible effects.
1.2.4 组装顺序:从空根到完整插件树
「一切皆插件」还意味着:一个运行中的 dsh 不是「一个二进制 + 若干配置」,而是启动时从空根出发、按顺序分层叠加出来的一棵树。官方架构文档给出的叠加顺序是:
- profile 的
dsh.profile.bundles列表里每个 bundle 的 patch,按列出顺序; - 该 profile 自己的
cordis.patch.yml; - home 级的
$DSH_HOME/cordis.patch.yml; - 每个
--patchoverlay(按命令行 argv 顺序)。
规则是后层胜出:一个 patch 按行 id 定位目标行,替换它的整份 config(不是深合并字段),也可以插入新行。于是「换掉一块」在实践上就是「写一个 patch 覆盖某个 id 的 config」。你可以用一条命令查看本机实际会 boot 出什么树(第五章展开):
dsh --profile web --dump-config
这条命令打印的每一行,都可以被你自己写的 patch 替换掉——这就是「从配置层替换任意一块」的字面实现。
1.2.5 一个最小插件的形状
为了让「插件」这个词落到实处,这里给一个示意结构(精确 API 签名见第八章与 vendor/ 内 Cordis 源码,此处只展示形状,不承诺签名):
// 一个插件:函数形态,可选 inject 声明依赖,apply(ctx) 挂载贡献
export function apply(ctx) {
// 注册一个工具到共享的工具注册表(可逆:卸载时自动撤销)
ctx.effect(() => {
ctx.tools.register({
name: 'lookup',
description: 'look up a symbol in the project',
// schema、execute 等省略
})
return () => {
// disposer:插件卸载时回卷这次注册
}
})
// 监听一个类型化事件
ctx.on('session/event', (event) => {
// 观察或改写,取决于事件的分发模式
})
}
三个要点对应 1.2 的三层含义:注册走 ctx.effect()(可逆)、依赖走 inject(按服务需求排序)、事件走 ctx.on(类型化通信)。看懂这个骨架,第八章的 Cordis 范式入门就是水到渠成。
1.3 底层框架:Cordis 与「时空可组合」
dsh 没有自己发明插件系统,它骑在 Cordis 之上,并把 Cordis 源码 vendored 进仓库。
1.3.1 Cordis 是什么
Cordis 是一个插件框架,其设计写在一篇论文里:A Programming Paradigm for Spatiotemporal Composability(《一种面向时空可组合性的编程范式》)。dsh 对它的使用方式概括起来是五条(参见 cordis-primer):
- 插件是实现
Service的对象——可以是一个带inject/apply(ctx)字段的函数,也可以是Service子类。 - context 是服务的仓库——服务占据一个稳定的
ctx.<key>(如ctx.tools、ctx.llm、ctx.sessions),其他插件按键查找,而非import具体实现。 - 依赖用
inject声明——插件声明自己需要哪些服务,框架等这些服务就绪后才挂载它,加载顺序由「服务需求」而非手写 boot 顺序决定。 - 类型化事件用于通信——服务通过 TypeScript 的 declaration merging 声明事件名,再按
emit/waterfall/parallel/serial四种分发模式派发。 - 注册是可逆 effect——见 1.2.3。
1.3.2 一句话解释「时空可组合」
「Spatiotemporal Composability」听起来玄,落到 Cordis 上其实很具体:
- 空间(spatial)维度:多个插件在同一棵 context 树上并存,通过稳定的服务键在「同一时刻共存的名字空间」里彼此找到对方、互相组合。谁实现、谁消费,靠键解耦。
- 时间(temporal)维度:每个注册都带生命周期,随插件加载建立、卸载回卷,且支持作用域(scope)隔离——不同 agent 实例可以有各自的注册子集。
也就是说:「空间」解决「同时存在的东西怎么拼」,「时间」解决「先后建立/拆除的东西怎么不互相污染」。 这正是 dsh 能「换掉任何一块」的理论根基。
举一个贯穿全书的具体例子:模型供应商。dsh 的默认 DeepSeek 适配器和可选的第三方适配器,都往同一个服务键 ctx.llm 上注册。空间上,agent 循环不关心「具体是哪个适配器」,它只按 ctx.llm 找服务——换一个注册者,就换了整个产品的模型后端。时间上,每个适配器的注册带 disposer,配置热更新时旧的注册被原子替换、旧的监听被回卷,不会残留半条请求路径。第四章配模型、第十二章写 LLM 适配器,本质都是在这两个维度上做文章。
1.3.3 vendored 进 vendor/
Cordis 的源码以 vendored 形式存放在 vendor/ 目录,同步流程见 vendor/README.md。这意味着:dsh 依赖的 Cordis 是一个钉在仓库内、版本可控的副本,而不是随 npm 漂移的外部依赖。对教程读者而言,记住一点即可:本书第八章讲插件范式时用的 Cordis API,其权威定义就在 vendor/ 里。
1.3.4 事件的四种分发模式
Cordis 的类型化事件有四种分发模式,事件名决定了它属于哪一种(这是事件公共契约的一部分,cordis-primer 有完整定义):
| 模式 | 是否等待 | 派发顺序 | 是否有返回值 |
|---|---|---|---|
emit |
否 | 监听者按注册顺序观察 | 无 |
waterfall |
否 | 监听者按注册顺序观察 | 有 |
parallel |
是 | 所有监听者并行观察 | 无 |
serial |
是 | 监听者按注册顺序观察 | 有 |
其中 waterfall 最值得记住:监听者收到 (...args, next),调用 next() 才继续委托给下一个服务,不调就短路整条链。这决定了「策略/拦截」类监听器(如 agent/pre-step、tools/*)与「观察/记录」类监听器的写法完全不同。详细语义放在第八章。
1.4 技术栈画像
dsh 的工程栈是一条纯正的 Node.js / TypeScript 现代化工具链。从根 package.json 可以读出以下事实:
| 维度 | 取值 | 说明 |
|---|---|---|
| 运行时 | Node.js ^22.19.0 \|\| >=24.0.0 |
engines 字段硬性下限;CI 覆盖 22.19 / 24 / 26 |
| 语言 | TypeScript(^6.0.3) |
全仓强类型,事件与配置类型用 declaration merging 生成 |
| 包管理 | pnpm 11.7.0(workspace monorepo) |
packageManager 字段钉死版本,经 Corepack 启用 |
| 模块体系 | ESM-only | 根 package.json 声明 "type": "module",无 CommonJS 包袱 |
| 测试 | vitest(^4.1.8) |
单测、覆盖率、snapshot、e2e 多套配置 |
| 构建 | tsc + tsdown + vite | 双 aggregate(Host / Client)分面构建 |
| 源码直跑 | tsx | pnpm dsh 用 node --import tsx/esm 直接跑 .ts 入口 |
几个值得展开的点:
- 版本下限的写法。
^22.19.0 || >=24.0.0的意思是:要么用 22.19 以上的 22.x,要么用 24 以上的新版本。中间没有 23.x 的位置——23 是奇数号非 LTS 版本,被这个区间明确排除。CI 实际覆盖的是 22.19、24、26 三个点。 - ESM-only。整个仓库只产出 ESM,没有给 CommonJS 留后门。如果你之前写过 Node 工具,这一点会影响你写插件时的导入写法(第八章展开)。
- pnpm workspace monorepo。
workspaces字段涵盖vendor/*、packages/*/*、native/landlock-run、apps/*、website。所有内部包以@deepseek-ai/dsh-<name>命名,跨包用包名导入。 - vitest 不只是单测。仓库有普通单测、覆盖率门禁、keyless snapshot(无 key 回放比对)、真实 API e2e 等多套 vitest 配置——这反映了一个框架类项目对「行为契约」的重视(第十三章展开)。
1.4.1 monorepo 布局与双分面编译
根 package.json 的 workspaces 字段列出了 monorepo 的顶层结构:
"workspaces": [
"vendor/*",
"packages/*/*",
"native/landlock-run",
"native/landlock-run/packages/*",
"apps/*",
"website"
]
两个对开发者最要紧的事实:
- 内部包统一以
@deepseek-ai/dsh-<name>命名,跨包用包名导入,不写相对路径。packages/下按职责分组(core/、llm/、fs/、shell/、skill/、subagent/、bundle/等,第九章给全览)。 - TypeScript 分两面编译:Host aggregate(
tsconfig.host.json)与 Client aggregate(tsconfig.client.json)。普通包只登记进其中一面。原因是 Cordis 的Context接口在 host/client 两侧用相同键做了 declaration merging,把两面塞进同一个ts.Program会报冲突。这个「双分面」设计影响构建顺序与包归类,细节见第九章。
1.5 三种产品入口
dsh 对外有三种使用形态,都从同一个「插件树」长出来,只是挂载的 bundle 不同。
1.5.1 入口一:Web UI(dsh web)
零安装直跑:
npx @deepseek-ai/dsh web
命令启动 Web UI,默认监听 http://127.0.0.1:3080(见 Web UI 指南)。Web UI 是 web profile 的呈现,底层挂了 web server、API 网关、workspace、前端静态资源等一堆插件。它适合日常使用和快速上手,也是本教程第三章的主角。
1.5.2 入口二:一次性 CLI(dsh --profile headless)
不启动任何服务器,跑一个一次性任务、打印最终答案、退出:
dsh --profile headless "summarize this workspace"
headless profile 挂载的是一个 one-shot runner:读入任务文本 → 创建一个持久化 agent session → 提交任务 → 等它安静下来 → 把最后一段非空的 assistant 文本写到 stdout → 退出(completed 退出码 0,否则 1)。全程不开监听端口、不写 stderr,适合脚本化和管道化。它需要 DEEPSEEK_API_KEY。详见第五章。
1.5.3 入口三:Python SDK 与 ACP(程序化接入)
如果你不想用图形界面也不想用命令行,dsh 提供两条程序化通道:
- Python SDK:
pip install deepseek-harness-sdk,用DeepSeekHarness(...)上下文管理器在 Python 里启动捆绑的运行时并调用harness.run(...)。它捆绑了自己的 runtime,不需要系统装 Node.js(见 Python SDK 教程)。 - ACP(Agent Client Protocol):通过 JSON-RPC stdio 暴露全新 agent session 的自动化 server,供编辑器或其他进程接入(见第七章)。
三种入口汇总:
| 入口 | 命令 / 形态 | 适合场景 | 是否需要 API key |
|---|---|---|---|
| Web UI | npx @deepseek-ai/dsh web |
日常使用、快速上手 | 首次进 Settings 填 |
| 一次性 CLI | dsh --profile headless "任务" |
脚本化、CI、管道 | 是(DEEPSEEK_API_KEY) |
| Python SDK / ACP | deepseek-harness-sdk / JSON-RPC stdio |
程序嵌入、编辑器集成 | 是 |
1.5.4 三种入口从同一棵插件树长出来
这三个入口不是三套独立实现,而是同一棵插件树挂不同的 bundle:
- 三个内置 bundle 都骑在同一个底层 bundle
dsh-base之上——它提供模型适配器、工具、持久化、沙箱与审批策略、settings/credentials、telemetry 等公共能力(见 dsh-base README)。 dsh-web-app在 base 上加浏览器应用;dsh-headless在 base 上加一个「无服务器的一次性 runner」。webprofile = base + web-app,headlessprofile = base + headless。
因此你理解一次「插件树怎么拼」,就同时理解了三个入口——这也是第五章 CLI / Profile / Bundle 体系会展开的主题。
1.5.5 源码 checkout 下的三条演示命令
如果你从源码克隆(第二章路线 B),开发指南提供了三条演示命令,正好对应三种入口的源码形态。先构建一次,然后:
pnpm run build
# 1. headless 一次性任务(需 DEEPSEEK_API_KEY)
pnpm dsh --profile headless "summarize this workspace"
# 2. 自指的 cordis 演示:agent 能 inspect 并修改自己活着的插件运行时(需 key,默认 web,可换 acp)
pnpm run demo:cordis
# 3. ACP 自动化 server:经 JSON-RPC stdio 暴露全新 agent session(需 DEEPSEEK_API_KEY)
pnpm run demo:acp
第二条 demo:cordis 尤其值得记住:它让一个 agent 去检查和修改自己的插件运行时——这直观展示了「插件树是可被程序操纵的运行时对象」,而不是编译期定死的东西。
1.6 关键概念预告
下面这些术语会在后续章节反复出现,这里先建一个「术语索引」。理解它们不需要现在深入,但需要知道每个词大致指什么:
| 概念 | 一句话解释 | 详见 |
|---|---|---|
| profile | 一个具名的插件组合,存于 $DSH_HOME/profiles/<name>,列出它堆叠的 bundles、装哪些 out-of-tree 插件、以及用户自己的 cordis.patch.yml |
第五章 |
| bundle | Cordis 配置行 + 其所挂载代码的分发格式;dsh-base / dsh-web-app / dsh-headless 是三个内置 bundle |
第五章 |
| plugin 树 | dsh 启动时按「bundle 顺序 → profile patch → home patch → --patch overlay」分层组合出的运行时结构 |
第五章 |
| session log | append-only 的 SessionEvent 日志,是模型所看到上下文的唯一来源 |
第十章 |
| turn / step | 一个 step = 一次模型请求 + 它调用的工具;一个 turn = 0 或多个 step | 第十章 |
| capability seam | 可替换能力,由「接口声明 / 实现 / 消费方」三角色构成,换一个 provider 就换掉整个产品 | 第十一章 |
ctx.llm |
模型适配器的服务键;加一个模型供应商就是往这个键上注册一个 adapter | 第四、十一章 |
其中 turn/step 和 session log 是理解「dsh 如何驱动 agent」的两块基石,这里各给一个精确定义(源自架构文档):
- step:一次模型请求,加上该请求所调用的所有工具。
- turn:零个或多个 step。turn 在第一个输入被认领前开启,在「不再欠任何东西」时关闭。所以一次用户输入可能展开成多次模型请求与多轮工具调用,但这些都属于同一个 turn。
把这两个定义展开成时序(事件名保留英文,注释为中文,完整版见第十章):
turn/start # 一个 turn 开启
claim next-step input + one queued message # 认领下一步输入 + 一条排队消息
assemble prompt sections + tool schemas # 组装提示分节 + 工具 schema
-> agent/pre-step reject | enter(messages) # 可拒绝/改写本轮输入
step/start # 一个 step 开始
append entered messages as user/message # 入日志
derive model history from the log # 从日志投影模型历史
agent/request -> llm/stream -> assistant/chunk* -> assistant/message # 模型流式响应
tool/call* -> tools/pre-execute -> tools/execute -> tools/post-execute -> tool/result* # 工具管线
step/end # step 结束
tools owe another request -> claim -> next step # 工具欠一次请求则继续下一步
-> agent/turn-stopping # 决定 turn 是否停下
turn/end # turn 结束
这张图里藏着一个关键点:turn/*、step/*、user/message、assistant/*、tool/* 是持久化的 session 事件(写入日志),其余(agent/pre-step、agent/request、llm/stream、三个 tools/*)是实时扩展点——前一类是「事实」,后一类是「你可以拦截的钩子」。
理解「模型可见 ⟺ 已入日志」这条铁律也在这里埋个伏笔:任何进入模型请求的内容,都必须能从 session log 重建出来——运行时有一条 invariant 硬断言这一点。这是第十章的核心主题。
1.6.1 扩展点速览:新行为往哪挂
「一切皆插件」的另一个推论是:每种新行为都对应一个明确的扩展点。架构文档给了一张「目标 → 机制」的决策表,这里摘录最常用的几行(翻译自 architecture.md,完整版见第十一章):
| 目标 | 机制 |
|---|---|
| 加一个模型供应商 | 往 ctx.llm 注册它的适配器 |
| 加一个面向模型的能力 | 往 ctx.tools 注册;其 schema 自动并入提示组装 |
| 加 shell 执行 | 注册一个 ctx.shell 后端 |
| 加文件系统访问或策略 | 注册 ctx.fs provider 或监听 fs/* 事件 |
| 拦截一次请求 / 工具 / turn | 用对应的 agent/* 或 tools/* 事件 |
| 加面向模型的上下文 | 调用 agent.inject(),落入下一次请求 |
| 给单个 session 换能力集 | 组合一个 agent preset(服务行需 isolate realm) |
| fork 一个活跃 session | ctx.sessions.fork(source, boundary?, childSessionId?) |
| 把注册限定到单个 agent | 用该 agent 的 agent.ctx |
这张表先放在这里,作为「第一章到第十二章之间的一座桥」:等你读到第十二章要「加一个东西」时,可以回头用它定位该挂在哪。
1.7 生态与项目事实
以下是写作时点(2026 年 8 月)的仓库与生态事实,均以 GitHub 页面与根 package.json 为准:
| 项目 | 详情 |
|---|---|
| 项目名 | DeepSeek Harness(CLI:dsh) |
| 开发者 | DeepSeek AI |
| 仓库 | github.com/deepseek-ai/deepseek-harness |
| 官网主页 | deepseek.com/harness |
| Stars | 约 152k(写作时点) |
| 许可证 | MIT |
| 当前版本 | 0.1.0-rc.7(根 package.json) |
| 阶段 | developer preview,官方警告将出现破坏性变更 |
| fork 状态 | 官方未启用 fork(issues 关闭,改走 Discussions) |
| 底层框架 | Cordis(vendored 进 vendor/) |
1.7.1 社区与支持渠道
官方给出的社区渠道(见 README):
- GitHub Discussions:反馈与 bug 报告统一走 Discussions,仓库的 issues 关闭。
- Discord:DeepSeek Harness Discord 社区(英文)。
- 中文社区:企微群(扫码添加企微小助手并填问卷)与微信公众号,二维码在 README.zh.md。
- 插件发现:为自己的插件仓库添加
dsh-plugintopic,便于被搜索到。
1.7.2 developer preview 意味着什么
「developer preview」不是套话,而是对读者有实际影响的三个承诺:
- 命令、配置键、包名都可能变。仓库的 AGENTS.md 明确允许自由重命名与重新分包,尚未打任何稳定 tag。
- 旧 session 格式可能被拒绝。持久化格式随版本单调演进,旧格式在新版可能直接报错。
- 学习方式应以
master为准。写本文时核对的所有命令都来自master原文,但数周后可能失效——遇到偏差时,以仓库当前代码为准。
1.7.3 仓库里还有什么
除 README 与代码外,仓库还包含几个对读者有用的部分(均在 master 分支根目录):
| 路径 | 内容 |
|---|---|
CONTRIBUTING.md |
贡献指南(含中文版),第十三章展开 |
AGENTS.md |
面向 agent 的仓库操作说明与命令清单 |
THIRD_PARTY_NOTICES.md |
第三方依赖及其许可证披露 |
LICENSE |
MIT 许可证 |
docs/ |
官方文档(架构、开发指南、用户指南、cookbook、子系统参考等) |
.agents/notes/ |
Agent Note:按日期组织的设计决策记录,是本教程第 9–13 章的一手素材 |
其中 .agents/notes/ 值得一提:dsh 团队把大量「为什么这样设计」的决策写成 Agent Note 存档。本教程后半部的「设计动机」讲解,相当一部分可以溯源到这些 note。
1.8 与博客已有教程的定位对照
本博客已经写过几篇编码 agent 类教程(Pi、OpenClaw、OpenCode 等)。dsh 与它们不是替代关系,而是处在坐标系的另一个位置。对比如下:
| 维度 | Pi | OpenClaw | OpenCode | dsh(本教程) |
|---|---|---|---|---|
| 一句话定位 | 极简终端编码 agent 工具集 | 多智能体网关 | 开源 AI 编码代理 | 一切皆插件的智能体框架 |
| 核心哲学 | 原语而非功能,核心极小 | 通道/节点/路由编排 | 省 token 的编码代理 | 无特权内核,全插件可替换 |
| 扩展机制 | TypeScript Extension + Skill | 插件 + 通道 | 配置 + MCP/命令 | Cordis 插件 + bundle/patch 层 |
| 形态 | 终端 TUI | 多聊天平台网关 | 终端 TUI | Web UI + 一次性 CLI + SDK |
| 可编程性 | SDK + RPC | 高 | 中 | 高(Python SDK + ACP/JSON-RPC) |
| 底层框架 | 自研(pi-ai / pi-agent-core / pi-tui) | 自研 | Vercel AI SDK 生态 | Cordis(vendored) |
关键差异一句话:Pi / OpenCode 是「给你一个好用的编码 agent」,OpenClaw 是「编排多个 agent 与通道」,而 dsh 是「给你一套拼装 agent 的框架」——它的价值不在默认形态,而在「你可以从配置层换掉任何一块、并把这些拼装沉淀成可复用的 profile/bundle/插件」。如果你对 Pi 的 Extension 系统感到意犹未尽、想掌控 agent 循环本身,dsh 是那个更深的答案。
几条具体的衔接关系,供读过前面教程的读者定位:
- 读过 Pi 教程:Pi 的「原语而非功能」与
dsh的「一切皆插件」内核相通,但实现范式不同——Pi 用 TypeScript Extension + Skill,dsh用 Cordis 插件 + bundle/patch 层。如果你在 Pi 里写过 Extension 拦截tool_call,在dsh里对应的是监听tools/*或agent/*事件(第八章)。 - 读过 OpenClaw 教程:OpenClaw 的「多智能体路由」在
dsh里对应 subagent provider 谱系——从全新子 agent 到「委托给另一产品的 turn」,都挂在同一个 provider 接口后面(第十一章)。 - 读过 OpenCode 教程:OpenCode 偏「省 token 的配置优化」,
dsh偏「运行时架构的掌控」。两者都在 DeepSeek 生态里,但dsh是 DeepSeek AI 官方出品、并由 Cordis 驱动的框架级项目。
1.9 本教程阅读路线图(14 章)
本教程共 14 章,前半部讲「用」,后半部讲「开发」,最后一章收束排障:
| 章 | 标题 | 侧重 |
|---|---|---|
| 第 01 章 | 项目概览与核心定位 | 使用(本章) |
| 第 02 章 | 安装与环境配置 | 使用 |
| 第 03 章 | 快速入门:Web UI 首次交互 | 使用 |
| 第 04 章 | 模型配置与供应商体系 | 使用 |
| 第 05 章 | CLI 与 Profile / Bundle 体系 | 使用 |
| 第 06 章 | Python SDK 编程接入 | 使用 |
| 第 07 章 | ACP 与 JSON-RPC 自动化接入 | 使用 / 开发 |
| 第 08 章 | Cordis 插件范式入门 | 开发 |
| 第 09 章 | 源码架构与仓库布局 | 开发 |
| 第 10 章 | 核心运行时:Session 日志与 Agent 循环 | 开发 |
| 第 11 章 | Capability Seam 与核心子系统 | 开发 |
| 第 12 章 | 扩展开发实战:插件 / 工具 / LLM 适配器 | 开发 |
| 第 13 章 | 测试体系与贡献指南 | 开发 |
| 第 14 章 | 排障、安全与最佳实践 | 排障 |
建议的阅读方式:
- 只想用起来:读第 1–4 章即可,装好环境、跑通 Web UI、配好模型。
- 想写脚本/自动化:补第 5–7 章,掌握 CLI、Python SDK 与 ACP。
- 想写插件或二次开发:第 8 章(Cordis 范式)是下半场的门,务必先过;再按第 9–13 章深入。
- 遇到问题:第 14 章是排障索引。
三个阶段的定位再明确一下:
- 使用阶段(第 1–7 章) 的目标是「把
dsh当成一件好用的工具」:装好、跑通、配好模型,再用 CLI/SDK 把它嵌进你的工作流。这一阶段你不碰插件代码,但会反复碰到 profile、bundle、patch、session 这些词——它们正是第二阶段要拆开的东西。 - 开发阶段(第 8–13 章) 的目标是「把
dsh当成一个框架来改」:先吃透 Cordis 插件范式(第 8 章),再看懂源码布局(第 9 章)、核心运行时(第 10 章)、能力 seam(第 11 章),最后落到写插件/工具/适配器(第 12 章)与测试贡献(第 13 章)。 - 排障阶段(第 14 章) 把两阶段的坑位收拢成一张可查的索引,并给出安全与最佳实践。
1.9.1 学习前的准备
虽然每一章都从基础讲起,但最好具备以下前置条件:
- 会用终端:会执行命令、理解当前工作目录(
cwd)、会设置环境变量(bash 或 PowerShell 均可)。 - 了解 Markdown:文档、部分配置与示例都以 Markdown 呈现。
- 能读 JSON / YAML:配置与 profile 清单是 YAML,
package.json是 JSON。不需要手写复杂文件,但至少要能读。 - 了解 API Key 概念:如果你在任意 LLM 平台拿过 API Key,就足够了。
- TypeScript 基础(仅开发阶段需要):日常用 Web UI / CLI 完全不需要写代码;第 8–13 章写插件需要了解函数、
async/await、import/export等基本语法。 - 不要求会 Python:只有第 6 章 Python SDK 需要,且会单独讲。
- 不要求有 agent 开发经验:第一章就是起点。
1.10 本章小结
本章建立了对 dsh 的整体认知:
dsh是 DeepSeek AI 开源的 agent harness(智能体框架),与 lm-evaluation-harness 等评测框架无关——「harness」在这里指承载智能体的底盘,而非测试夹具。- 核心架构口号是「Everything is a plugin」:模型适配器、工具注册表、session 日志、agent 循环本身都是插件,没有特权内核,任何一块都能从配置层替换。
- 底层驱动框架是 Cordis(vendored 进
vendor/),其「时空可组合」思想可概括为:空间上多插件通过服务键并存组合,时间上注册作为可逆 effect 随加载/卸载建立与回卷。 - 技术栈为 Node.js
^22.19.0 || >=24.0.0+ TypeScript + pnpm 11.7.0 workspace monorepo + ESM-only + vitest。 - 三种入口:Web UI(
npx @deepseek-ai/dsh web,默认127.0.0.1:3080)、一次性 CLI(dsh --profile headless "任务")、Python SDK / ACP。 - 关键概念已预告:profile、bundle、plugin 树、session log、turn/step、capability seam、
ctx.llm。 - 生态事实:MIT、约 152k stars、
0.1.0-rc.7、developer preview(破坏性变更警告)、社区走 GitHub Discussions / Discord / 企微群、插件加dsh-plugintopic。 - 与 Pi / OpenClaw / OpenCode 的差异在于:
dsh卖的不是某个好用的 agent 形态,而是一套拼装 agent 的框架。
下一章:第02章:安装与环境配置 将从零搭好 dsh 的运行环境,并跑通环境就绪检查。