封面

DeepSeek Harness 实战手册

一切皆插件的 Agent 框架,从安装到上手,再到自己写插件

Web UICLI / HeadlessPython SDK插件开发2026 首发

DeepSeek Harness 实战手册

一切皆插件的 Agent 框架,从安装到上手,再到自己写插件

副标题:DeepSeek 官方开源 Agent 框架(dsh)保姆级中文教程

版本:2026 年 8 月 · 对应 dsh 开发者预览版(0.1.x)

写在前面:这波热度,为什么轮到你

2026 年 8 月 13 日晚,DeepSeek 官方在 GitHub 上开源了 DeepSeek Harness(dsh)——一个"一切皆插件"的智能体(Agent)框架。

接下来的 48 小时里发生了什么?

媒体把它称作"Agent 界的 Android"。社区在狂欢,评测在刷屏,新的插件像雨后春笋一样冒出来。但热闹归热闹,真正能让你接住这波流量的,是你比大多数人更早、更系统地学会它。

市面上现有的中文资料要么是新闻稿,要么是零散的安装笔记,还没有一本"从安装到写插件、从 Web UI 到 Python SDK"的完整教程。这本书就是来补这个缺口的。

这本书能给你什么

读完这本书,你会:

  1. 明白 DeepSeek Harness 到底是什么、解决什么问题、和 Claude Code、Codex 这类工具有什么区别;
  2. 用一行命令在本地跑起来 Web UI,完成第一个真实任务;
  3. 会用无头(headless)模式和 Python SDK,把 Harness 嵌进自己的脚本和流水线;
  4. 从零写一个自己的插件、一个自己的工具,并打包成可分发、可安装的插件包;
  5. 了解它的架构哲学——为什么"一切皆插件"这句话不是营销,而是设计;
  6. 避开开发者预览版的常见坑,拿到一张清晰的继续学习路线图。

怎么读这本书

书中所有命令都标注了运行环境。代码块可以直接复制。凡是标注"示例/示意"的内容,请在理解后改写为你自己的实现——毕竟,这个框架最擅长的就是让你把"你自己的实现"接进去。

最后提醒一句:dsh 目前是开发者预览版,官方明确说"未来会出现破坏兼容性的变更"。这意味着两件事:一是现在学,你就是第一批吃螃蟹的人;二是版本升级时,多留意官方 Release 和迁移说明。

好,我们开始。

第 1 章 DeepSeek Harness 是什么:一场"一切皆插件"的实验

1.1 一句话定义

DeepSeek Harness(简称 dsh)是 DeepSeek AI 官方开发并开源的智能体框架(agent harness)。

"harness" 在英语里原意是"马具、挽具",工程语境下指"把动力接出来、把缰绳握在手里"的那层装置。放在 AI 里,harness 就是模型和世界之间的那层"接线":它决定模型能看到什么、能调用什么工具、能改哪些文件、能跑什么命令、以及每一步怎么被记录和审计。

换句话说,模型本身是一台"发动机",而 harness 是把发动机装进车架、接上方向盘、油门和仪表盘的那套系统。DeepSeek Harness,就是 DeepSeek 官方为自家模型(尤其是 DeepSeek V4 系列)打造的一套开源"车架"。

1.2 它解决什么问题

2025 年以来,Claude Code、OpenAI Codex、Cursor 等产品已经证明了"AI 编程智能体"的价值:给模型一个工作区,它就能读代码、跑命令、改文件、自动修复测试。但这类产品大多是封闭的成品——你能配置它,但很难真正改造它。

DeepSeek Harness 把这件事反过来做了:

这对普通开发者的意义是:你不再等官方给你加功能,你可以自己加。 想要一个"给 agent 加知识库检索"的工具?写个插件。想要把文件系统换成远程沙箱?换个 provider。想要在模型请求前注入上下文?监听一个事件。

1.3 它不是什么

为了避免误解,把边界说清楚:

1.4 和主流工具怎么比

把 dsh 放进坐标系里看,更容易理解它的位置:

维度Claude Code / CodexDeepSeek Harness
定位面向任务的成品工具面向改造的框架(harness)
可扩展性支持 MCP、skill 等扩展一切皆插件,连循环本身都可换
模型绑定自家/指定模型自带 DeepSeek 适配器,支持 OpenAI 兼容端点
数据与控制平台管理本地运行,日志、凭据、配置全在本地
适合谁想马上干活的人想理解、定制、自动化的人

注意:这不是"谁更好"的对比,而是"分工不同"。很多人的真实路径是——先用成品工具体验,再用 dsh 搭自己的自动化流水线,甚至把自己的插件反过来分享给别人。

1.5 为什么现在值得学

三个理由:

  1. 窗口期红利。发布两天 10 万+ Star,说明热度极高;但中文系统性教程还几乎没有。先学会、先输出,你就能在社区里占据"第一批讲解者"的位置。
  2. 官方在快速迭代。预览版意味着功能每天都在变,社区资料大多会过时。以官方仓库和本文为准,比看二手转述靠谱得多。
  3. 插件生态刚起步。288 个插件仓库听起来多,但在"一切皆插件"的框架里,这还只是开始。早期写插件,就像 App Store 刚上线时做 App——竞争少、被看到的机会大。

1.6 本章小结

下一章,我们把"一切皆插件"这句话拆开,看看它的五个核心概念。

第 2 章 五个核心概念:插件、Bundle、Profile、Seam 与事件

"一切皆插件"听上去像口号,但它在 dsh 里是有具体含义的。这一章介绍五个绕不开的概念,懂了它们,后面所有章节都会顺理成章。

2.1 插件(Plugin):一切能力的单元

在 dsh 里,插件是一个导出 apply 函数的 TypeScript 模块。框架加载插件时调用 apply(ctx),把上下文对象 ctx 交给你,你通过 ctx 注册能力:

import type { Context } from '@deepseek-ai/cordis'

export const name = 'my-plugin'

export function apply(ctx: Context) {
  // 在这里注册能力:工具、事件监听、定时器……
}

这就是一个完整插件的骨架。模型适配器是插件,工具是插件,会话日志是插件,Web UI 是插件,连"智能体循环"本身都是插件。没有需要打补丁的特权内核——扩展 dsh 的方式,就是把插件挂载到其他插件旁边。

插件有三个关键特性:

2.2 组合包(Bundle):插件的分发格式

单看一个插件不够,你得知道"怎么把一堆插件装到一起、分发给别人"。

组合包(bundle)是一个附带一层配置的 npm 包。它的 package.json 里用 dsh.bundle 字段声明自己贡献的 patch 文件:

{
  "name": "dsh-hello-plugin",
  "version": "0.1.0",
  "type": "module",
  "main": "index.js",
  "files": ["index.js", "cordis.patch.yml"],
  "dsh": { "bundle": { "patch": "./cordis.patch.yml" } }
}

cordis.patch.yml 就是它要插入的配置层。别人把这个 bundle 装进自己的 profile,你贡献的插件行就生效了。

一句话记住:插件是能力,bundle 是"能力包"的快递盒。

2.3 Profile:一份可启动的组合

Profile 是存放在 Harness home($DSH_HOME/profiles/<name>)下的具名装配,回答的问题是:"这套配置由哪些 bundle 按什么顺序组成?"

它包含:

发行版内置两个模板 profile:

其余 profile 通过 dsh plugin 命令创建和维护,一般不需要手写 manifest。

2.4 层叠与 patch:配置是怎么组合的

运行中的 dsh 是一棵由多层 patch 叠加而成的插件树。生效配置按以下顺序逐层应用(后层覆盖前层,按行覆盖):

  1. profile 的 dsh.profile.bundles 所列各 bundle 的 patch(按列表顺序);
  2. profile 自己的 cordis.patch.yml
  3. home 级的 $DSH_HOME/cordis.patch.yml(机器级共享偏好);
  4. 命令行 --patch <path> 覆盖层(按参数顺序)。

每条 patch 按 id 定位某一行,替换其整个 config 值(不是深合并)。想看自己机器上实际组合出来的配置树:

dsh --profile web --dump-config

打印出来的任何一行,理论上都可以被你的 patch 替换。这就是"一切皆插件"落地的关键机制:改配置就是改代码,改代码就是改配置。

2.5 Seam:可替换能力

一个 seam(能力接缝) 是一项可替换能力,由三种角色组成:

举个例子:文件系统。本地提供方让你在本地改文件;把 provider 换成远程沙箱(官方示例里有 E2B 的 POC overlay),Bash、PTY、LSP 会一并搬过去,因为它们共享同一个执行世界。Consumer 的代码一行不用改。

这就是"换一个提供方就能改变整个产品"的原因,也是 seam 和普通"接口"的区别:单一角色不是 seam,把定义、实现、使用三者一起设计才是。

2.6 事件:真正的扩展点

在 dsh 里,事件不只是"通知",它们是架构意义上的扩展点。分三类:

事件域作用何时用
会话事件追加到日志的持久事实需要重载后仍然存在的数据
Agent 事件(agent/*携带活跃 Agent:inbox、步骤、状态、请求观察或拦截进行中的工作
能力事件fs/*tools/*telemetry/* 附加策略给已有能力加策略和适配器

Cordis(dsh 底层的插件框架)为事件提供四种分发模式:

你会经常打交道的是 agent/*tools/* 系列事件。比如 agent/pre-step 决定模型这一轮看到什么,agent/request 可以在每次模型请求前替换推理档位,tools/pre-execute 可以给工具执行加审批策略。

2.7 三个容易混的概念

很多新手在这里绕晕,用一张表钉死:

概念回答的问题载体
插件我能贡献什么能力?TypeScript 模块(apply(ctx)
Bundle我分发什么?npm 包 + dsh.bundle patch
Profile这套东西由什么组成、怎么启动?$DSH_HOME/profiles/<name> 目录 + dsh.profile

2.8 本章小结

概念讲完了,下一章动手——用一行命令把 dsh 跑起来。

第 3 章 五分钟上手:Web UI 从零跑通

3.1 环境要求

先确认两件事:

  1. Node.js:官方要求 ^22.19.0>=24.0.0。在终端跑 node -v 查看版本,不满足就去 nodejs.org 下载 LTS 以上版本。
  2. 一个 DeepSeek API Key:到 DeepSeek 开放平台申请。没有 Key 也能启动界面,但跑任务前必须配置。
小提示:如果你在国内网络环境,npm 源可能慢。可以设置镜像源(如 npmmirror)后重试,教程本身不需要科学上网。

3.2 一行命令启动

安装 Node.js 后,打开终端执行:

npx @deepseek-ai/dsh web

首次运行会下载 dsh 包(npx 会提示是否安装,选 yes)。启动成功后,终端会打印访问地址,默认是:

http://127.0.0.1:3080

用浏览器打开它,你会看到 dsh 的 Web UI。

想固定版本、日常使用?

可以全局安装,以后直接敲 dsh

npm install -g @deepseek-ai/dsh
dsh web

想读源码、追最新提交?

开发者预览期迭代很快,很多人选择源码运行:

git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install
pnpm run build
pnpm dsh web

源码运行适合两类人:想改插件的,以及想第一时间跟进官方提交的。

3.3 首次配置:三步走

第一步:配置 API Key

进入 设置 → 模型,找到 DeepSeek 卡片,粘贴你的 API Key 并保存。

关于安全,三个要点:

第二步:选择工作区

点击 选择工作区,添加你启动 dsh 时所在的目录(或任意你想让 agent 干活的项目目录),然后选中它。

注意:选中工作区之前,会话输入框是不可用的。这是设计,防止 agent 在没明确工作区时乱跑。

第三步:跑第一个任务

新建一个会话,输入类似这样的任务:

Summarize this repository and identify its main packages.

也可以换成中文:

总结这个仓库,列出它的主要包和各自的职责。

你会看到 agent 开始:读取文件 → 制定计划 → 调用工具 → 汇报结果。当操作在当前权限策略下需要审批时,Web UI 会弹窗问你。点允许,它就继续。

3.4 它能做什么:一次真实的体验

把你手头任何一个项目目录作为工作区,试试这些任务:

你会发现 dsh 的行为模式:先规划,再动手,边做边记录,遇到权限边界停下来问你。这就是"harness"和"聊天机器人"的区别——它有工作区、有工具、有审批、有审计日志。

3.5 常见启动问题速查

现象原因与处理
node -v 版本过低升级到 Node 22.19+ 或 24+
端口 3080 被占用换个端口:dsh web --port 8080(注意 --port 属于 web 应用参数,要放在 dsh 自己的参数之后)
提示 MISSING_CREDENTIAL还没配置 API Key,去 设置 → 模型 保存
提示 UNKNOWN_MODEL选一个已配置的模型,或检查自定义提供方的模型列表
输入框不可用还没选中工作区
国内网络下载慢配置 npm 镜像源后重试

3.6 本章小结

下一章,我们离开图形界面,看看命令行和无头模式怎么玩。

第 4 章 命令行与无头模式:把 Harness 变成可脚本化的工具

Web UI 适合人机交互。但真正让 dsh 值钱的,是它可以被脚本和 CI 调用。这一章讲 dsh 命令本身。

4.1 命令语法:先搞清"谁的参数"

dsh 是启动器,它只解析自己的 flag,把剩下的参数交给 profile 里的应用插件解析。一句话:launcher 的参数在前,应用的参数在后,遇到第一个启动器不认识的部分,就开始算应用的。

dsh --profile web --port 8080       # --port 属于 web 应用
dsh --profile headless "run the tests"
dsh --profile web --help            # 打印 web 应用帮助
dsh --help                          # 打印启动器自己的帮助

4.2 入口模式一览

命令作用
dsh web启动 Web UI(--profile web 的别名)
dsh --profile headless "任务"无头模式:跑一个一次性会话,打印最终回复后退出
dsh --profile <name>启动任意命名 profile
dsh plugin --profile <name> <pnpm 参数...>管理 profile 的插件(转发给 pnpm)
dsh --profile web --dump-config打印组合后的配置树(不启动)
dsh --dump-default-config打印默认配置

4.3 无头模式:一行命令跑一个任务

无头模式是脚本化的核心。它接受一个任务文本,创建并持久化一个全新会话,跑完后打印 agent 的最终文本回复并退出:

dsh --profile headless "fix the failing test in this workspace"

注意:

4.4 用 --patch 加载你自己的配置

这是开发插件的日常命令。你写了一个插件和对应的 patch 文件,不想装进 profile,只想临时试一下:

dsh web --patch ./scratch-plugin/cordis.yml

多个 patch 可以叠加,按参数顺序应用。用 --dump-config 可以先看组合结果再启动。

4.5 dsh plugin:管理 Profile 的插件

前面说过,profile 的 manifest 不需要手写,dsh plugin 帮你维护:

dsh plugin --profile demo add ./hello-plugin          # 安装本地插件
dsh plugin --profile demo add github:you/hello-plugin # 从 GitHub 安装
dsh plugin --profile demo remove dsh-hello-plugin     # 移除

dsh plugin 本质上是在 profile 目录里转发 pnpm 命令,所以 addremoveinstall 等 pnpm 子命令都可用。注意它只管理 profile 的依赖和 bundle 层,不会动你的全局环境

4.6 实际场景:把它接进 CI

无头模式 + 退出码 + 会话日志,这三样组合起来就是一个完整的 agent 流水线:

# 示例:让 dsh 在 CI 里修测试并留档
DEEPSEEK_API_KEY=${{ secrets.DEEPSEEK_API_KEY }} \
  dsh --profile headless "inspect this repo, run the tests, fix failures, report what you changed"

进阶玩法:

4.7 本章小结

下一章:Python SDK——不通过命令行,直接在程序里驱动 Harness。

第 5 章 Python SDK:把 Harness 嵌进你自己的程序

命令行适合"跑一次",Python SDK 适合"在程序里编排"。它让你在自己的 Python 代码里创建 harness、跑任务、拿结果,就像调用一个普通库。

5.1 环境要求(先看这条!)

特别注意:官方明确说明 Python SDK 暂不支持 Windows agent——因为底层持久化 PTY 后端需要 POSIX 终端环境。Windows 用户请用 WSL、Linux 服务器或容器来跑 SDK 示例。

5.2 安装

克隆仓库(拿内置示例),建虚拟环境,安装 SDK:

git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
python -m venv .venv
. .venv/bin/activate
python -m pip install deepseek-harness-sdk

好消息:安装后的运行时不需要系统再装 Node.js,SDK 自带同版本内置运行时。

5.3 设置环境变量

export DEEPSEEK_API_KEY=sk-your-key-here
# 如果走 OpenAI 兼容代理,而不是默认官方端点:
# export DEEPSEEK_BASE_URL=http://127.0.0.1:8000/v1
# export DSH_MODEL=deepseek-v4-flash
# export DSH_SYSTEM_PROMPT='You are a helpful software engineer assistant.'

如果模型不是默认 DeepSeek 官方端点提供的,务必设置 DEEPSEEK_BASE_URL

5.4 跑官方内置示例

仓库自带的 examples/jsonrpc-agent/minimal.py 是对 SDK 的轻量包装,针对隔离的工作区和会话目录跑一个任务:

python examples/jsonrpc-agent/minimal.py \
  --workspace /absolute/path/to/workspace \
  --session-root /absolute/path/to/sessions \
  --session-id example-001 \
  "Inspect the repository and fix the failing tests."

脚本会打印 agent 的最终回复。同时,会话目录会收到 JSONL 格式的日志——里面完整记录了组装后的模型请求和工具调用。这对调试和审计非常有用。

5.5 在自己的代码里用 SDK

from pathlib import Path

from deepseek_harness import DeepSeekHarness

config = Path("examples/jsonrpc-agent/minimal.cordis.yml").resolve()
workspace = Path("/absolute/path/to/workspace").resolve()
sessions = Path("/absolute/path/to/sessions").resolve()

with DeepSeekHarness(
    provider="deepseek-official",
    model="deepseek-v4-flash",
    max_tokens=49_152,
    cwd=str(workspace),
    session_root=str(sessions),
    cordis=str(config),
) as harness:
    result = harness.run(
        "Inspect the repository and fix the failing tests.",
        session_id="example-001",
    )

print(result.final_response)

几个关键点:

5.6 这个示例组合的"配方"

看明白 minimal 组合里有什么,你就知道"最小 harness"长什么样:

属性
系统提示词DSH_SYSTEM_PROMPT,缺省为 "You are a helpful software engineer assistant."
模型--modelDSH_MODEL → 默认 deepseek-v4-flash
面向模型的工具仅持久 bashstr_replace_editor
Bash 超时300 秒
编辑器输出上限16,000 字符
上下文压缩关闭
文件系统裸本地后端(编辑器用绝对路径)
会话持久化session_root 下未压缩的 JSONL
沙箱danger-full-access——只能在可丢弃的 checkout 或容器里跑

注意最后一行的警告:这个最小组合没有任何沙箱限制,Bash 和编辑器能改运行时进程可见的任何路径。生产环境请换上更严格的策略。

5.7 一个批量任务小例子

把上面的模式套进循环,就是一个最简单的批量自动化:

from pathlib import Path
from deepseek_harness import DeepSeekHarness

tasks = {
    "repo-a": "Run the test suite and fix failures.",
    "repo-b": "Find TODO comments and turn them into issues.",
    "repo-c": "Update the dependency versions in package.json.",
}

with DeepSeekHarness(
    provider="deepseek-official",
    model="deepseek-v4-flash",
    cwd="/work",
    session_root="/sessions",
    cordis=Path("minimal.cordis.yml").resolve(),
) as harness:
    for repo, task in tasks.items():
        result = harness.run(task, session_id=repo)
        print(f"--- {repo}: {result.final_response[:200]}")

每个 repo 用独立 session id,彼此状态互不污染;/sessions 下留下每次任务的完整 JSONL 日志,方便事后排查。

5.8 本章小结

下一章进入重头戏:插件开发。

第 6 章 插件开发实战:从 Hello 到第一个工具

这一章全程动手。目标:创建一个插件 → 加载进 Web UI → 再给它加一个能被模型调用的工具。整个流程在 15 分钟内可以走完。

6.1 准备:一个源码 checkout

插件开发建议从官方仓库的源码 checkout 开始(这样能用 pnpm dsh 和内置的 build 环境):

git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install
pnpm run build

然后在仓库根目录建一个实验项目:

mkdir -p scratch-plugin/src

6.2 第一个插件:Hello

创建 scratch-plugin/src/my-plugin.ts

import type { Context } from '@deepseek-ai/cordis'

export const name = 'hello-plugin'

export function apply(ctx: Context) {
  console.log('[hello-plugin] plugin loaded!')
}

这就是一个完整插件:导出 nameapply(ctx)。框架加载时调用 apply,把上下文 ctx 交给你。

6.3 加载它:--patch 覆盖层

创建 scratch-plugin/cordis.yml。注意插件路径必须是绝对路径,把 /absolute/path/to/deepseek-harness 换成你自己的:

- insert:
    - id: hello
      name: '/absolute/path/to/deepseek-harness/scratch-plugin/src/my-plugin.ts'

用覆盖层启动 Web UI:

pnpm dsh web --patch ./scratch-plugin/cordis.yml

打开 http://127.0.0.1:3080,你应该在启动终端的日志里看到:

[hello-plugin] plugin loaded!

恭喜,你的第一个插件已经跑起来了。

6.4 插件的自动清理

通过 ctx 注册的任何东西——事件监听、工具、定时器——在插件卸载时都会被自动清理,你不用手动 removeListener 或 clearInterval。

如果需要手动清理的资源(比如一个网络连接),用 ctx.effect() 声明:

import type { Context } from '@deepseek-ai/cordis'

export function apply(ctx: Context) {
  ctx.effect(() => {
    const timer = setInterval(() => {
      console.log('heartbeat')
    }, 5000)

    // 返回的函数在插件卸载时执行
    return () => clearInterval(timer)
  })
}

6.5 声明依赖:inject

如果插件要用其他服务(如工具注册表 tools、模型层 llm),声明 inject,框架会等服务就绪再加载:

import type { Context } from '@deepseek-ai/cordis'

export const name = 'my-tool-plugin'
export const inject = ['tools']

export function apply(ctx: Context) {
  ctx.tools.register(/* ... */)
}

6.6 开发一个工具:greet

scratch-plugin/src/my-plugin.ts 替换成下面这个"问候工具":

import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'

export const name = 'greet-tool'
export const inject = ['tools']

export function apply(ctx: Context) {
  ctx.tools.register(defineTool({
    name: 'greet',
    description: 'Greet someone by name.',
    parameters: {
      name: { type: 'string', required: true, description: 'The name to greet' },
    },
    output: {
      schema: { type: 'string' },
      render: (_args, value) => [{ type: 'text', text: value }],
    },
    async execute(args) {
      return `Hello, ${args.name}!`
    },
  }))
}

拆解一下这个 DSL:

重启开发命令(如果还在运行):

pnpm dsh web --patch ./scratch-plugin/cordis.yml

在 Web UI 里输入:

Use the greet tool to greet Ada.

模型会调用 greet,并收到 Hello, Ada! 这个工具结果——一个能被模型自主调用的工具就诞生了。

6.7 插件的三种形态

函数形式之外,还有对象形式和类形式:

// 对象形式
export default {
  name: 'my-plugin',
  inject: ['tools'],
  apply(ctx: Context) {
    // ...
  },
}
// 类形式:需要向其他插件提供服务时使用
import { Service, type Context } from '@deepseek-ai/cordis'

export default class MyService extends Service {
  static inject = ['tools']

  constructor(ctx: Context) {
    super(ctx, 'myService')
  }
}

大多数场景函数形式就够了;类形式意味着你的插件本身是一个服务,其他插件可以依赖它。

6.8 本章小结

下一章,让插件学会"接受配置"。

第 7 章 插件配置与热重载:让插件可被使用者定制

写死的插件没人愿意用。这一章让你的插件接受 cordis.yml 里的配置,并理解 dsh 的热重载机制。

7.1 定义 Config:类型 + Schema

导出 Config 类型和同名的 Schemastery schema,默认值直接写在 schema 里:

import type { Context } from '@deepseek-ai/cordis'
import Schema from '@deepseek-ai/schemastery'

export const name = 'my-plugin'

export interface Config {
  greeting: string
  maxRetries: number
  verbose?: boolean
}

export const Config: Schema<Config> = Schema.object({
  greeting: Schema.string().default('Hello'),
  maxRetries: Schema.number().default(3),
  verbose: Schema.boolean().default(false),
})

export function apply(ctx: Context, config: Config) {
  console.log(config.greeting)  // 用户传的值,或 schema 默认值
}

要点:不要导出普通对象作为 Config,它不满足 Cordis 要求的 Standard Schema 接口,框架会校验失败。

7.2 在 patch 里传配置

scratch-plugin/cordis.yml 的插件行里加 config

- insert:
    - id: hello
      name: './src/my-plugin.ts'
      config:
        greeting: 'Hi there'
        maxRetries: 5

插件加载时,Cordis 通过导出的 schema 校验配置,缺的字段自动填默认值。

7.3 严格校验

需要强约束的场景,用 Schema 表达约束本身:

export const Config = Schema.object({
  apiKey: Schema.string().required(),
  timeout: Schema.number().default(30000),
  mode: Schema.union(['fast', 'accurate']).default('fast'),
})

配置不合法时插件会加载失败并给出明确错误,而不是静默用错误值跑——这正是框架喜欢的"响亮失败"。

7.4 设计原则:没有硬编码的可调参数

dsh 的约定:凡是不同部署可能需要不同值的参数,都必须定义为配置字段。

// 错误:硬编码超时
const TIMEOUT = 30000

// 正确:可配置,默认 30000
export interface Config {
  timeoutMs: number
}

检验标准一句话:能不能在 cordis.yml 里改这个值,而不需要改代码? 能,就对了。

7.5 热重载(HMR)

cordis.yml 里某个插件的 config 后,框架会卸载旧实例、加载新实例,不需要重启 dsh。

由于所有注册都是 effect(可逆副作用),替换后不会残留旧实例的注册。开发插件时,这就是你最快的调试循环:改配置 → 自动重载 → 看效果。

7.6 本章小结

下一章:把插件打包成可安装的 bundle,发给别人用。

第 8 章 打包与发布:把插件变成别人能装的东西

本地 --patch 只能自己玩。想分享,就得把插件打包成 bundle,装进别人的 profile

8.1 两个概念,两种 manifest

再强调一次(这是最容易绕晕的地方):

bundle 是你编写并分发的东西;profile 是用户启动的东西。没有东西同时是两者。

8.2 做一个 bundle

目录结构:

hello-plugin/
├── package.json       # 声明 dsh.bundle
├── cordis.patch.yml   # profile 列出本 bundle 时应用的层
└── index.js           # 插件模块

package.json

{
  "name": "dsh-hello-plugin",
  "version": "0.1.0",
  "type": "module",
  "main": "index.js",
  "files": ["index.js", "cordis.patch.yml"],
  "dsh": { "bundle": { "patch": "./cordis.patch.yml" } }
}

index.js(插件入口,和第 6 章一样的逻辑):

export const name = 'hello-plugin'

export function apply() {
  console.log('[hello-plugin] plugin loaded!')
}

cordis.patch.yml(和 --patch 一样的 patch 数组,区别是插件行按包名引用,Node 才能解析到已安装的代码):

- insert:
    - id: hello
      name: dsh-hello-plugin
小知识:没有 dsh.bundle 声明的包也能装,但只是普通依赖,不会激活任何层。库类包(供其他插件 import)就用这种格式。

8.3 安装进 profile

在包含 hello-plugin 的目录里:

dsh plugin --profile demo add ./hello-plugin

首次使用会初始化 profile(自动把 @deepseek-ai/dsh-base 作为第一个 bundle),pnpm 链接你的 checkout,dsh 检测到 dsh.bundle 后把它追加进 dsh.profile.bundles

先验证再启动:

dsh --profile demo --dump-config   # 可以看到 "# == dsh-hello-plugin" 层
dsh --profile demo

移除同样简单:

dsh plugin --profile demo remove dsh-hello-plugin

8.4 加载顺序(重要)

生效配置在空根之上按顺序叠加:

  1. dsh.profile.bundles 所列各 bundle 的 patch(按列表顺序);
  2. profile 自己的 cordis.patch.yml
  3. home 级 $DSH_HOME/cordis.patch.yml
  4. 每个 --patch <path> overlay。

推论(给 bundle 作者的两条铁律):

8.5 三种分发方式

方式一:发布到 npm(推荐)

pnpm publish

用户 dsh plugin add dsh-hello-plugin 安装的就是预构建代码,不需要任何构建权限。在 pnpm publish 前把 lib/ 构建好即可。

方式二:交付 tarball

pnpm pack

用户执行:

dsh plugin add ./hello-plugin-0.1.0.tgz

方式三:从 GitHub 安装(有坑,务必看)

dsh plugin --profile demo add github:you/hello-plugin

坑在于:git 安装拉的是源码,不是构建产物,没有任何环节运行你的 build 脚本,TypeScript 包到手时没有 lib/ 输出,加载会失败。两边各要做一件事:

allowBuilds:
  dsh-hello-plugin: true

然后重新 add

8.6 安全警告:不要随便授权

请如实看待 allowBuilds 这项授权:它允许该包的代码在安装时于你的机器上执行,且不在 agent 运行的任何沙箱之内。

8.7 本章小结

下一章,往深处走:看看"一切皆插件"的架构到底长什么样。

第 9 章 架构进阶:为什么"一切皆插件"不是口号

这一章给想深入源码的读者。不用背,当"地图"读即可——遇到具体问题时知道去哪查。

9.1 Cordis:底层插件框架

dsh 底层是 Cordis(以 vendor 方式引入的插件框架),它的设计来自论文《A Programming Paradigm for Spatiotemporal Composability》。五个核心概念:

  1. 插件是实现 Service 的对象:函数形式、对象形式或 Service 子类;
  2. 上下文是服务的容器:服务占据稳定的 ctx.<key>(如 ctx.toolsctx.llmctx.sessions),其他插件通过 key 查找,而不是 import 具体实现;
  3. inject 声明依赖:加载顺序通过服务依赖表达,而非手动编排启动序列;
  4. 类型化事件用于通信emit(观察)、waterfall(中间件/短路)、parallel(并行)、serial(按序传值);
  5. 注册是可逆副作用:一切注册在 reload 和 teardown 时自动撤销。

这五条你已经在前面的章节里用过前三条,现在知道它们是框架级约定,不是 dsh 独有的临时设计。

9.2 核心包:一张地图

职责ctx 键
core/session仅追加的会话事件日志ctx.sessions
core/system-prompt提示词片段与工具 schema 组装ctx.systemPrompt
core/tools作用域化的工具注册表 + 带把关的执行流水线ctx.tools
core/agentAgent 接口与活跃 agent 注册表ctx.agents
core/agent-loop默认的 agent 循环驱动器ctx.agentLoop
core/scope按 agent 划分作用域的注册原语库,无 ctx 键
llm/llm消息与流式词汇表、适配器 seamctx.llm

注意最后一行:agent loop 本身也是一个可替换的包。这就是"没有特权内核"最有力的证据——连循环都能换。

9.3 事件域与轮次流程

一次对话的"轮次"是这样流转的(简化版):

turn/start
  → 领取输入
  → 组装提示词片段 + 工具 schema
  → agent/pre-step        ← 可以改写或拒绝输入
    → step/start
    → 写入 user/message
    → 从日志推导模型历史
    → agent/request       ← 可以替换模型/推理档位
    → llm/stream          ← 流式响应
    → assistant 消息
    → 工具调用: tools/pre-execute → tools/execute → tools/post-execute
    → step/end
  → 若还有未完成工作或新输入 → 进入下一个 step
  → agent/turn-stopping
turn/end

关键理解:

9.4 会话日志:模型可见即已记录

dsh 有一条运行时不变式:

模型可见即已记录。 抵达模型请求的一切,都必须能从会话日志重建。

因此:

对插件作者的意义:别绕过日志偷偷给模型塞上下文——那既违反设计,也会在回放和审计时穿帮。

9.5 能力 seam 三件套

第 2 章讲过 seam = Service Definition + Service Provider + Consumer。架构上的推论:

9.6 新行为挂哪里:扩展点速查表

目标机制
添加模型提供方ctx.llm 注册适配器
添加面向模型的能力ctx.tools 注册;schema 加入提示词组装
添加 shell 执行注册 ctx.shell 后端
添加用户命令ctx.commands 注册,无需模型轮次即可分派
添加后台工作ctx.jobs 注册,job_* 工具收集/停止
限制进程ctx.sandbox 后端,消费方在启动进程前包装 argv
拦截请求/工具/轮次agent/*tools/* 事件
添加模型可见上下文agent.inject(),落到下一次获准的请求
添加 UI 集成驱动 ctx.agents,从 session/event 渲染
生成会话标题注册唯一的 ctx.sessionTitle 提供方
fork 活跃会话ctx.sessions.fork(source, boundary, childSessionId)

9.7 本章小结

下一章,三个实战案例,把前面所有知识串起来。

第 10 章 实战案例:把知识串成能力

案例 A:让 Harness 自动修复失败的测试

场景:你有一个出问题的仓库,想快速知道 agent 能不能自己定位并修复。

做法(无头模式,适合 CI 和本地一次运行):

dsh --profile headless "run the test suite, analyze the failures, fix them, then rerun to confirm"

看点

进阶:把这条命令放进 GitHub Actions,PR 时自动让 agent 试修一遍并附上报告。

案例 B:用 Python SDK 批量处理多个仓库

场景:10 个仓库都要"升级依赖并跑通测试",人工逐个做很费时。

做法(第 5 章批量例子的完整版):

from pathlib import Path
from deepseek_harness import DeepSeekHarness

repos = {
    "svc-auth": "Upgrade all dependencies to latest compatible, run tests, fix issues.",
    "svc-billing": "Upgrade all dependencies to latest compatible, run tests, fix issues.",
    "svc-notify": "Upgrade all dependencies to latest compatible, run tests, fix issues.",
}

with DeepSeekHarness(
    provider="deepseek-official",
    model="deepseek-v4-flash",
    cwd="/workspaces",
    session_root="/sessions",
    cordis=Path("minimal.cordis.yml").resolve(),
) as harness:
    for repo, task in repos.items():
        result = harness.run(task, session_id=repo)
        print(f"### {repo}\n{result.final_response}")

看点

案例 C:给 Harness 加一个"文档检索"工具

场景:你有一个内部知识库,想让 agent 回答问题时能查文档,而不是瞎猜。

思路:写一个工具插件,execute 里调用你的检索服务(比如向量库或 Elasticsearch),返回命中片段。模型会在需要时自动调用它。

骨架

import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'

export const name = 'docs-search'
export const inject = ['tools']

export function apply(ctx: Context) {
  ctx.tools.register(defineTool({
    name: 'search_docs',
    description: 'Search internal docs and return relevant snippets.',
    parameters: {
      query: { type: 'string', required: true, description: 'Search query' },
      limit: { type: 'number', description: 'Max results, default 5' },
    },
    output: {
      schema: { type: 'array', items: { type: 'string' } },
      render: (_args, value) => [{ type: 'text', text: value.join('\n---\n') }],
    },
    async execute(args) {
      // 在这里调用你的检索服务
      return fetchSearch(args.query, args.limit ?? 5)
    },
  }))
}

关键设计

效果:agent 遇到"某某接口怎么用"时,会先 search_docs,再基于检索结果回答,比裸模型的幻觉率低一个量级。

案例 D(彩蛋):做一个"自动化研究循环"

社区里已经有 Loops(auto-research) 类插件:让 agent 自己规划步骤、反复搜索、产出报告。原理其实就是本书第 9 章说的——**在 agent/* 事件上挂一个"续跑"策略**:只要目标未完成且预算未耗尽,就让轮次继续。

想动手的话,从 docs/agent-lifecycle.mddocs/subsystems/core.md 读起,那里有续跑机制的完整说明。

本章小结

下一章:生态、坑和 FAQ——让你少走弯路。

第 11 章 生态、坑与 FAQ:少走弯路

11.1 生态现状:你正站在起跑线上

发布两天,GitHub Star 突破 10 万(据公开报道),dsh-plugin 话题下的插件仓库 24 小时内就攒到 288 个。社区里已经能看到这些方向的插件:

官方社区入口:

对你意味着什么:现在做插件,竞争者少、被看到概率大;而且生态刚起步,缺什么插件,你就有机会补什么

11.2 必须知道的坑

坑 1:开发者预览版,兼容性会变

官方原话:正在快速迭代,未来将出现破坏兼容性的变更。对策:

坑 2:Python SDK 暂不支持 Windows

官方示例的持久 PTY 后端需要 POSIX 环境。Windows 用户用 WSL、Linux 服务器或容器。

坑 3:git 安装插件的 prepare

从 GitHub 装 TypeScript 插件时,没有构建产物会加载失败;pnpm ≥10 默认拒绝运行 prepare 脚本。对策在第 8 章:作者给 prepare,用户显式 allowBuilds只对可信源码授权

坑 4:危险的全权限

SDK 最小示例和某些组合默认 danger-full-access:Bash 和编辑器能改任何路径。永远在可丢弃的环境里跑,或换上严格的沙箱策略。

坑 5:Key 管理

API Key 在 Web UI 里是只写的(存 $DSH_HOME/.credentials.yaml)。但脚本和 CI 里,别把 Key 写进代码或提交到 git,用环境变量或密钥服务。

坑 6:模型与推理档位

DeepSeek 适配器默认路由是 deepseek-official,默认模型 deepseek-v4-flash / deepseek-v4-pro,默认上下文窗口 100 万 token,输出上限默认 256,000 token,推理档位 off | high | max(默认 high,thinking 默认开启)。需要按部署调这些时,改配置而不是改代码。

11.3 FAQ 十问

Q1:没有 API Key 能玩吗?
能启动界面,但不能跑任务。Key 从 DeepSeek 开放平台申请。

Q2:MISSING_CREDENTIAL 是什么?
模型路由没配好凭据:去设置里存 Key,或提供被引用的环境变量。

Q3:UNKNOWN_MODEL 怎么办?
选择已配置的模型;自定义提供方则需要添加缺失的模型。

Q4:端口被占用了?
dsh web --port 8080--port 是 web 应用的参数,放在 dsh 自己的参数之后)。

Q5:能接别的模型吗?
能。设置里可添加 Anthropic、OpenAI 等目录提供方,或自定义 OpenAI 兼容端点。自定义提供方的 Provider ID 是永久的,改名要新建再删除。

Q6:自定义提供方的视觉模型不认图片?
手动录入的模型默认按纯文本处理;在 $DSH_HOME/settings.yaml 给该模型加 input: [text, image]

Q7:插件改了配置没生效?
配置变更会触发热重载;但 patch 是整行替换 config,检查你是否重述了目标行的所有键。

Q8:日志在哪?
会话日志是 JSONL(Python SDK 场景在 session_root 下);Key 在 $DSH_HOME/.credentials.yaml;机器级配置在 $DSH_HOME/cordis.patch.yml

Q9:Windows 能跑 Web UI 吗?
能,Web UI 和源码构建在 Windows 没问题;受限制的是 Python SDK 官方示例。

Q10:想给官方提反馈?
GitHub Discussions;给插件仓库加 dsh-plugin 话题提升被发现概率。

11.4 本章小结

最后一章,给你一张继续深入的地图。

第 12 章 学习路径与资源:从这本书到"你能维护它"

12.1 推荐学习顺序

第一周 · 会用

第二周 · 会改

第三周 · 会懂

第四周 · 会分享

12.2 官方文档地图(都在仓库 docs/ 下)

想看什么去哪
架构总览docs/architecture.zh.md
Cordis 入门docs/cordis-primer.zh.md
Cordis 动手教程docs/cordis-tutorial/(7 课)
Agent 生命周期/时序docs/agent-lifecycle.zh.md
工具执行流水线docs/tool-execution-pipeline.zh.md
能力 seamdocs/capability-seams.zh.md
事件生产/消费映射docs/event-producer-consumer.zh.md
配置字段全集docs/config-catalog.zh.md(自动生成)
子系统详解docs/subsystems/(30+ 个)
实操手册docs/cookbook/(加包、加工具、加 LLM 适配器等)
术语表docs/glossary.zh.md

12.3 值得动手的仓库目录

12.4 给想持续跟进的你

12.5 结语

DeepSeek Harness 目前还只是 0.1 的开发者预览版。但框架级的东西往往就是这样:先理解它的人,定义它之后的模样。

你现在手里的这本书,就是一张门票。剩下的路,靠你在 apply(ctx) 里一笔一笔写出来。

附录里有命令速查表和术语表,随时翻。

第 13 章 综合实战:搭一个属于自己的 Agent 工作台

前面 12 章你学了安装、CLI、SDK、插件开发。这一章把它们拧成一台"机器":一个每天自动产出代码变更摘要 + 周报的个人 Agent 工作台。做完它,你就不是"会用 dsh",而是"在用它干活"了。

13.1 目标与成品

场景:你在维护一个仓库,希望每天自动生成一份"昨天改了什么、有什么风险点"的摘要,周五再汇总成周报。

成品

  1. 一个自用插件 weekly_report(注册一个面向模型的工具);
  2. 一个专属 profile(把插件装进去);
  3. 一个 Python 调度脚本(每天跑一个无头任务,报告存进 reports/)。

13.2 先搭目录骨架

mkdir -p agent-workbench/{workspace,sessions,my-plugin/src}

三个目录各司其职:

目录用途
workspace/agent 干活的地方:放一个仓库的克隆(或把现有项目复制进来)
sessions/会话日志(JSONL),出事可回放
my-plugin/你的插件项目
安全约定:workspace 必须是"弄坏了也不心疼"的副本。dsh 的 agent 会真实改文件。

13.3 插件:weekly_report 工具

创建 my-plugin/src/weekly.ts

import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
import Schema from '@deepseek-ai/schemastery'

export const name = 'weekly-report'
export const inject = ['tools']

export interface Config {
  author: string
  repoName: string
  outputDir: string
}

export const Config: Schema<Config> = Schema.object({
  author: Schema.string().required(),
  repoName: Schema.string().required(),
  outputDir: Schema.string().required(),
})

export function apply(ctx: Context, config: Config) {
  ctx.tools.register(defineTool({
    name: 'weekly_report',
    description: 'Generate a markdown weekly report from structured change data.',
    parameters: {
      dateRange: { type: 'string', required: true, description: 'e.g. 2026-08-10 to 2026-08-16' },
      commits: { type: 'string', required: true, description: 'One commit subject per line' },
      filesChanged: { type: 'string', description: 'One file path per line' },
      riskNotes: { type: 'string', description: 'Anything risky or worth attention' },
    },
    output: {
      schema: { type: 'string' },
      render: (_args, value) => [{ type: 'text', text: value }],
    },
    async execute(args) {
      const lines = [
        `# ${config.repoName} 周报`,
        ``,
        `- 作者:${config.author}`,
        `- 周期:${args.dateRange}`,
        ``,
        `## 提交列表`,
        args.commits.split('\n').filter(Boolean).map(c => `- ${c}`).join('\n') || '- 无',
        ``,
        `## 变更文件`,
        args.filesChanged?.split('\n').filter(Boolean).map(f => `- ${f}`).join('\n') || '- 无',
        ``,
        `## 风险与注意`,
        args.riskNotes || '- 无',
      ]
      return lines.join('\n')
    },
  }))
}

注意两点:

13.4 加载与调试

创建 my-plugin/cordis.yml(路径换成你的绝对路径):

- insert:
    - id: weekly
      name: '/absolute/path/to/agent-workbench/my-plugin/src/weekly.ts'
      config:
        author: '你的名字'
        repoName: 'my-project'
        outputDir: '/absolute/path/to/agent-workbench/reports'

先看配置树,再启动:

pnpm dsh web --patch ./my-plugin/cordis.yml --dump-config
pnpm dsh web --patch ./my-plugin/cordis.yml

在 Web UI 里发这样一个任务:

Inspect the workspace repo. Run git log --oneline -20 to see recent commits,
then use the weekly_report tool with the last week's data and save the result
to the configured output directory.

模型会自己跑 git、整理数据、调用 weekly_report。打开报告目录,你的第一份 Agent 生成的周报就在那了。

13.5 打包进专属 profile

临时 --patch 只适合调试。把它转成 bundle,装进一个叫 workbench 的 profile:

my-plugin/package.json

{
  "name": "dsh-weekly-report",
  "version": "0.1.0",
  "type": "module",
  "main": "src/weekly.js",
  "files": ["src/weekly.js", "cordis.patch.yml"],
  "dsh": { "bundle": { "patch": "./cordis.patch.yml" } }
}
main 指向的是编译后的 JS 产物(用 tsc/tsdown 从 weekly.ts 构建),示例中为简洁省略了构建步骤;开发期直接走 --patch 加载 TS 源码即可。

cordis.patch.yml 里的插件行改成按包名引用:

- insert:
    - id: weekly
      name: dsh-weekly-report
      config:
        author: '你的名字'
        repoName: 'my-project'
        outputDir: '/absolute/path/to/agent-workbench/reports'

安装并验证:

dsh plugin --profile workbench add ./my-plugin
dsh --profile workbench --dump-config
dsh --profile workbench

以后 dsh --profile workbench 就是你的"工作台"——自带周报能力。

13.6 Python 调度脚本:每天自动跑

agent-workbench/run_daily.py

from datetime import date, timedelta
from pathlib import Path

from deepseek_harness import DeepSeekHarness

root = Path("/absolute/path/to/agent-workbench")
last_week = f"{date.today() - timedelta(days=7)} to {date.today()}"

task = (
    "Inspect the workspace. Use git log to list commits from the last 7 days, "
    "then call weekly_report with that data and save the markdown to the "
    "configured output directory. Report the file path when done."
)

with DeepSeekHarness(
    provider="deepseek-official",
    model="deepseek-v4-flash",
    cwd=str(root / "workspace"),
    session_root=str(root / "sessions"),
    cordis=Path("minimal.cordis.yml").resolve(),
) as harness:
    result = harness.run(task, session_id=f"daily-{date.today()}")
    print(result.final_response)

然后用系统的定时任务(macOS launchd、Linux cron、Windows 计划任务)每天早晨执行:

0 9 * * * cd /absolute/path/to/agent-workbench && /path/to/python run_daily.py >> daily.log 2>&1

三个约定再强调一次:

13.7 安全与生产化清单

13.8 下一步扩展

13.9 本章小结

到这里,你已经有能力搭建属于自己的 Agent 工作台了。附录里的命令速查和术语表,随时回来翻。

附录 A 命令速查表

安装与启动

# 一行启动 Web UI(首次会自动下载)
npx @deepseek-ai/dsh web

# 全局安装后直接使用
npm install -g @deepseek-ai/dsh
dsh web

# 源码构建运行
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install
pnpm run build
pnpm dsh web

常用命令

命令作用
dsh web启动 Web UI(默认 http://127.0.0.1:3080
dsh --profile headless "任务"无头模式跑一次性任务
dsh --profile web --dump-config打印组合后的配置树
dsh --dump-default-config打印默认配置
dsh web --patch ./xxx/cordis.yml叠加配置层启动
dsh plugin --profile demo add ./pkg安装插件包到 profile
dsh plugin --profile demo remove pkg从 profile 移除
dsh plugin --profile demo add github:you/pkg从 GitHub 安装
dsh --help启动器帮助
dsh web --helpWeb 应用帮助

Python SDK

python -m pip install deepseek-harness-sdk
export DEEPSEEK_API_KEY=sk-...
# export DEEPSEEK_BASE_URL=http://127.0.0.1:8000/v1
# export DSH_MODEL=deepseek-v4-flash

关键路径

内容位置
凭据(只写)$DSH_HOME/.credentials.yaml
机器级配置$DSH_HOME/cordis.patch.yml
Profile 目录$DSH_HOME/profiles/<name>/
会话日志session_root(SDK)下的 JSONL

附录 B 术语表

术语含义
Harness把模型接到世界上的那层"接线系统":工具、文件、命令、日志、审批
dshDeepSeek Harness 的命令行名称与缩写
Plugin(插件)导出 apply(ctx) 的能力单元,一切皆插件
Context(ctx)服务容器,通过 ctx.<key> 访问服务
inject插件声明服务依赖的方式
Bundle(组合包)带一个配置层的 npm 包,dsh.bundle 声明
Profile可启动的具名装配,dsh.profile 声明 bundle 顺序
Patch(补丁层)按 id 插入或替换配置行的 YAML 层
Seam(能力接缝)可替换能力:定义 + 实现 + 使用
Service Definitionseam 中声明接口的角色
Service Providerseam 中实现接口的角色
Consumerseam 中使用能力的角色(通常是面向模型的工具)
Agent Loop(智能体循环)驱动"模型请求 + 工具调用"的循环,本身也是插件
Turn(轮次)零个或多个步骤,从领输入到不再欠工作
Step(步骤)一次模型请求 + 它调用的工具
Waterfall 事件中间件式事件,监听器须 next() 委托
会话事件追加到日志的持久事实
模型可见即已记录dsh 的运行时不变量:模型看到的都必须能从日志重建
Headless(无头模式)一次性跑任务并退出的 profile
danger-full-access无沙箱限制的权限配置,只能在隔离环境使用
$DSH_HOMEHarness 主目录,存放凭据、配置与 profile
Cordisdsh 底层的插件框架
Schemastery插件配置的 schema 定义库
MCPModel Context Protocol,模型上下文协议
E2B远程沙箱提供方(官方 POC 示例)
HMR热重载,配置变更自动替换插件实例

附录 C 参考资料

官方

底层框架

背景资料

提示:开发者预览期信息变化快,一切以官方仓库为准;本书中与仓库不一致之处,请相信官方。

附录 D 实测记录

本附录记录本书写作过程中的真实运行验证,供读者放心参照。

D.1 环境

项目
实测日期2026-08-17
Node.jsv24.19.0(满足官方 >=24.0.0 要求)
包管理器pnpm 11.21.0(pnpm dlx 等价于 npx
dsh 包@deepseek-ai/dsh(与官方 README 一致)

D.2 验证项

1. 启动 Web UI

pnpm dlx @deepseek-ai/dsh web --port 3099

结果:终端输出 dsh web: http://127.0.0.1:3099;用 curl 访问返回 HTTP 200,页面正常响应。

2. 启动器帮助(与第 4 章一致)

dsh --help

结果:输出 dsh: boot a DeepSeek Harness profile — an ordered stack of plugin-bundle patch layers under your own overrides.,包含 --profile--patch--dump-config--dump-default-configwebplugin 等选项与命令,与第 4 章命令表完全对应。

3. 配置树(与第 2 章一致)

dsh --profile web --dump-config

结果:输出分层配置树,可以看到:

D.3 未验证项与原因

D.4 给读者的建议

你的环境只要满足 Node 22.19+ 或 24+,按第 3 章步骤操作即可复现上述结果。如遇差异,优先以官方仓库 README 和 Release 说明为准。