znlgis 博客

GIS开发与技术分享 — GDAL · GeoServer · PostGIS · QGIS · OpenLayers · Cesium · FreeCAD · NPOI

第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.

翻译过来有三层含义:

  1. 模型适配器是插件工具注册表是插件session 日志是插件agent 循环本身也是插件
  2. 扩展方式不是「给内核打补丁」,而是「在别的插件旁边再挂一个插件」。
  3. 所有注册都是可逆的 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 不是「一个二进制 + 若干配置」,而是启动时从空根出发、按顺序分层叠加出来的一棵树。官方架构文档给出的叠加顺序是:

  1. profile 的 dsh.profile.bundles 列表里每个 bundle 的 patch,按列出顺序;
  2. 该 profile 自己的 cordis.patch.yml
  3. home 级的 $DSH_HOME/cordis.patch.yml
  4. 每个 --patch overlay(按命令行 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):

  1. 插件是实现 Service 的对象——可以是一个带 inject / apply(ctx) 字段的函数,也可以是 Service 子类。
  2. context 是服务的仓库——服务占据一个稳定的 ctx.<key>(如 ctx.toolsctx.llmctx.sessions),其他插件按键查找,而非 import 具体实现。
  3. 依赖用 inject 声明——插件声明自己需要哪些服务,框架等这些服务就绪后才挂载它,加载顺序由「服务需求」而非手写 boot 顺序决定。
  4. 类型化事件用于通信——服务通过 TypeScript 的 declaration merging 声明事件名,再按 emit / waterfall / parallel / serial 四种分发模式派发。
  5. 注册是可逆 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-steptools/*)与「观察/记录」类监听器的写法完全不同。详细语义放在第八章。

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 dshnode --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 monorepoworkspaces 字段涵盖 vendor/*packages/*/*native/landlock-runapps/*website。所有内部包以 @deepseek-ai/dsh-<name> 命名,跨包用包名导入。
  • vitest 不只是单测。仓库有普通单测、覆盖率门禁、keyless snapshot(无 key 回放比对)、真实 API e2e 等多套 vitest 配置——这反映了一个框架类项目对「行为契约」的重视(第十三章展开)。

1.4.1 monorepo 布局与双分面编译

package.jsonworkspaces 字段列出了 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 SDKpip 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」。
  • web profile = base + web-app,headless profile = 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/messageassistant/*tool/*持久化的 session 事件(写入日志),其余(agent/pre-stepagent/requestllm/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 关闭。
  • DiscordDeepSeek Harness Discord 社区(英文)。
  • 中文社区:企微群(扫码添加企微小助手并填问卷)与微信公众号,二维码在 README.zh.md
  • 插件发现:为自己的插件仓库添加 dsh-plugin topic,便于被搜索到。

1.7.2 developer preview 意味着什么

「developer preview」不是套话,而是对读者有实际影响的三个承诺:

  1. 命令、配置键、包名都可能变。仓库的 AGENTS.md 明确允许自由重命名与重新分包,尚未打任何稳定 tag。
  2. 旧 session 格式可能被拒绝。持久化格式随版本单调演进,旧格式在新版可能直接报错。
  3. 学习方式应以 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/awaitimport/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 UInpx @deepseek-ai/dsh web,默认 127.0.0.1:3080)、一次性 CLIdsh --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-plugin topic。
  • 与 Pi / OpenClaw / OpenCode 的差异在于:dsh 卖的不是某个好用的 agent 形态,而是一套拼装 agent 的框架

下一章:第02章:安装与环境配置 将从零搭好 dsh 的运行环境,并跑通环境就绪检查。