DeepSeek Harness 从入门到精通

目录

入门篇

  • 第 1 章 认识 DeepSeek Harness
  • 第 2 章 环境准备与安装
  • 第 3 章 第一次运行:Web UI 快速上手
  • 第 4 章 核心概念速览

进阶篇

  • 第 5 章 模型与提供方配置
  • 第 6 章 工作区、会话与数据
  • 第 7 章 权限、审批与沙箱
  • 第 8 章 内置工具全景
  • 第 9 章 CLI 与 headless 自动化
  • 第 10 章 Profile 与插件管理
  • 第 11 章 Skills 技能系统
  • 第 12 章 Python SDK

精通篇

  • 第 13 章 Cordis 与插件开发基础
  • 第 14 章 开发一个工具
  • 第 15 章 服务与依赖注入
  • 第 16 章 事件系统
  • 第 17 章 LLM 适配器
  • 第 18 章 打包与发布插件
  • 第 19 章 架构深入:扩展点与事件域
  • 第 20 章 综合实战:从零构建一个插件

附录

  • 附录 A 命令速查
  • 附录 B 常用环境变量
  • 附录 C 术语表
  • 附录 D 官方资源与 FAQ

第一部分 入门篇

第 1 章 认识 DeepSeek Harness

1.1 它是什么

DeepSeek Harness(命令行工具名为 dsh)是 DeepSeek AI 官方开源的 agent harness(智能体框架)。它不是一个"聊天网页",而是一套让你构建、运行和定制 AI 智能体(Agent)的完整运行时:

  • Agent 可以读取和编辑工作区文件、运行 shell 命令、搜索代码、访问网页;
  • Agent 可以委派子任务给 subagent、运行工作流、维护计划;
  • 你可以在 Web UI 中交互,也可以用命令行做无人值守任务,或用 Python SDK 把它嵌进自己的程序。

它最鲜明的口号是 “Everything is a Plugin”(一切皆插件):模型适配器、工具注册表、会话日志、权限策略,乃至 agent 主循环本身,全部都是插件。这意味着你不需要改框架源码,就能通过配置和插件替换任何一环。

1.2 技术底座:Cordis

Harness 的插件体系由开源框架 Cordis 驱动。Cordis 是一个小型插件运行时,核心约定只有五条:

  1. 插件是实现能力(Service)的对象,可以是一个带 apply(ctx) 的函数、对象或 Service 子类;
  2. 上下文(ctx)是服务的容器,服务占据稳定的 ctx.<key>(如 ctx.toolsctx.llm);
  3. 通过 inject 声明服务依赖,框架保证依赖就绪后才加载你的插件;
  4. 类型化事件用于插件间通信,有 emitparallelserialwaterfall 等分发模式;
  5. 注册是可逆的副作用,插件卸载时自动清理。

这套约定贯穿全书:入门时你只需要感知它的存在,精通篇会完全展开。

1.3 当前状态与版本

  • 开源协议:MIT。
  • 版本阶段:开发者预览(v0.1 系列,当前仓库版本 0.1.0-rc.7),破坏性变更随时可能发生
  • 社区:GitHub Discussions 是反馈与提问的官方渠道;插件仓库可打上 dsh-plugin 话题便于被发现。

1.4 你能用它做什么

场景做法
个人编程助手Web UI 选一个工作区,自然语言提需求,Agent 读代码、改代码、跑测试
无人值守自动化dsh --profile headless "任务描述",一次性跑完并输出结果
把 Agent 嵌入自己的产品Python SDK(JSON-RPC stdio)驱动
接入自有模型/网关添加自定义 Provider(OpenAI 兼容端点)或编写 LLM 适配器
扩展能力开发工具插件、服务插件、Skill,甚至替换文件系统/沙箱/子代理提供方

第 2 章 环境准备与安装

2.1 环境要求

跑 Web UI 与 CLI 只需要 Node.js:

  • Node.js:^22.19.0>=24.0.0(官方 package.json 的 engines 声明);
  • 包管理器:日常使用不需要;若从源码构建,需要 pnpm(仓库使用 pnpm@11.7.0)。

用 Python SDK 时的额外要求:

  • Python 3.10 或更高;
  • Git;
  • 平台:Linux x64、Linux arm64,或 macOS 14+ 的 arm64(SDK 内置运行时暂不支持 Windows agent)。

2.2 最快路径:一条命令启动

npx @deepseek-ai/dsh web

命令会启动 Web UI,默认地址为 http://127.0.0.1:3080。首次运行会自动初始化 web profile。启动时所在的目录将作为默认文件系统位置;Web UI 里还需要你手动添加并选择一个工作区(见第 3 章)。

2.3 从源码运行

如果你想看文档、跑示例或开发插件,克隆仓库:

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

仓库根目录的 docs/ 就是官方文档站点源码,也可以本地跑 pnpm run docs:dev 预览。

提示:本教程写作时已将官方仓库克隆到 D:\code\deepseek-harness-test\deepseek-harness-master,你可以随时查阅其中的 docs/packages/examples/

2.4 常见安装问题

症状处理
npx 找不到包确认 Node ≥ 22.19;必要时 npm install -g @deepseek-ai/dsh 后直接用 dsh
从源码构建报 Node 版本错误升级 Node 到 22.19+ 或 24+
pnpm install 卡住确认网络可达 npm registry;国内网络可配置 registry 镜像
端口 3080 被占用Web 应用通常支持 --port 等自有参数,例如 dsh web --port 8080--port 属于 web 应用而非启动器)

第 3 章 第一次运行:Web UI 快速上手

3.1 启动与登录界面

npx @deepseek-ai/dsh web

终端会打印访问地址(默认 http://127.0.0.1:3080),浏览器打开即可。全新安装不会选中任何工作区,因此选择工作区之前,会话输入框不可用

3.2 配置模型

打开设置 → 模型,找到 DeepSeek 卡片,输入 DeepSeek API 密钥 并保存。

几个值得注意的设计:

  • 密钥是只写的:保存后页面只会收到脱敏描述符,永远不会把明文密钥回传给你;
  • 密钥存储在 $DSH_HOME/.credentials.yaml,settings 里只保留凭据引用;
  • 模型路由立即生效,不需要重启服务器;
  • 之后也可以随时增删提供方、换模型,模型选择器里选中的模型会成为新会话的默认值。

3.3 选择工作区

点击选择工作区,添加你启动 dsh 时所在的项目目录(或任意你希望 Agent 操作的目录),然后选中它。Agent 后续的读写、搜索、跑命令都围绕这个工作区展开。

3.4 发送第一个任务

随便找个仓库,然后发送:

Summarize this repository and identify its main packages.

你会看到 Agent 开始工作:读取文件、运行命令、维护计划。如果当前权限策略要求审批,Web UI 会先询问你(比如某个写操作或高权限命令),你批准后它才继续。

建议的第一个任务也可以更具体,例如:

阅读这个仓库的 README 和目录结构,用中文总结项目用途、技术栈和主要模块,并指出入口文件在哪里。

3.5 新手清单

  1. Node.js 版本满足要求;
  2. npx @deepseek-ai/dsh web 成功启动;
  3. 设置里配置了可用的 API Key;
  4. 选择了工作区;
  5. 发送任务并观察 Agent 的"读文件 → 跑命令 → 修改 → 汇报"过程;
  6. 遇到弹窗审批时,先理解它要做什么再点允许。

第 4 章 核心概念速览

这一章用最小篇幅建立全书共用的词汇表。更精确的定义见附录 C。

4.1 会话(Session)、轮次(Turn)、步骤(Step)

  • 会话:一段持久化的对话与工作记录,以事件日志(JSONL)的形式落盘。
  • 轮次(Turn):会话对一条已接纳输入的一次"排空"过程:模型与工具反复工作,直到不再欠任何工作为止。
  • 步骤(Step):一次模型请求 + 该响应引发的工具执行。一个轮次包含零到多个步骤。
  • Round:承载一个轮次的外层策略迭代(例如 Goal Round),Round 计数归策略所有,不是每个轮次都算。

4.2 Agent 与 Scope

  • Agent 是运行中的智能体实例;一个活跃的 Agent 就是它自身 scope 的 key。
  • scope 是"按 agent 划分的注册单位":工具、提示词片段、监听器要么全局可见,要么归属于某一个 agent。同名时最具体的(scope 内的)会遮蔽全局项——这是为不同 agent 定制 persona 和工具变体的机制。

4.3 工具(Tool)

工具是模型可以调用的能力单元,每个工具都有 namedescription 和 JSON Schema parameters。内置工具见第 8 章。

4.4 权限三件套:沙箱模式 + 审批策略 + 权限预设

  • 沙箱模式(SandboxMode)read-only(只读)、workspace-write(可写工作区)、danger-full-access(完全访问)。
  • 审批策略(ApprovalPolicy)ask(问用户)、never(永不询问、一律拒绝)。
  • 权限预设(Permission Preset):把"沙箱模式 + 审批策略"打包成具名选项。默认表:
预设沙箱模式审批策略适用
workspace-writeworkspace-writeask日常使用(标准模式)
danger-full-accessdanger-full-accessnever可丢弃环境/CI 中的全权模式

两者的组合不匹配任何预设时,派生值为 custom(仅展示,不可作为切换目标)。

4.5 Profile、Bundle、Patch、Plugin

  • Profile$DSH_HOME/profiles/<name> 下的具名"可启动组合",由若干 bundle 按顺序叠放而成。webheadless 是随发行版提供的模板。
  • Bundle(组合包):附带一个配置层的 npm 包,回答"这个包贡献什么配置"。
  • Patch(补丁层):按 id 定位配置项并替换其整个 config,或插入新配置项。
  • Plugin(插件):导出 apply(ctx) 的模块,是能力的最小单位。

4.6 事件的三层含义

文档里"事件"有三类,别混:

  • 会话事件:追加到日志、必须持久化的事实(turn/*step/*tool/*user/messageassistant/* 等),通过 session/event 广播。
  • Agent 事件(agent/*:携带活跃 Agent 的实时扩展点,用于观察/拦截进行中的工作。
  • 能力事件(fs/*tools/*telemetry/* 等):给某个能力 seam 附加策略和适配器的实时事件。

第二部分 进阶篇

第 5 章 模型与提供方配置

模型配置都在 设置 → 模型 页面完成,修改下一次请求即生效,无需重启。

5.1 配置 DeepSeek 官方模型

打开设置 → 模型,DeepSeek 卡片上输入 API 密钥保存即可。默认 DeepSeek 路由是纯文本的 chat-completions。

5.2 添加目录提供方(Anthropic / OpenAI / 其他)

选择添加提供方,选取 Anthropic、OpenAI 等提供方并输入 API 密钥。已安装的目录会提供端点、协议与模型列表。

注意原生认证类提供方的差异:

提供方需要什么
BedrockAWS 凭据与区域
VertexADC(Application Default Credentials)项目
Azureapi-version
CodexOAuth

这些提供方只填 API 密钥字段是配不起来的

5.3 添加自定义提供方(OpenAI 兼容网关)

公司网关、自建服务器、不在目录里的服务,用添加自定义提供方

  1. 填小写 Provider ID(永久标识,请求、已保存会话、模型默认值、凭据引用都依赖它,之后不可改名);
  2. 填显示名称、基础 URL、API 协议、凭据;
  3. 至少添加一个模型;
  4. 保存前可以先在模型目录里点获取可用模型,它会对当前草稿的基础 URL 发起 GET /models 探测;选中的候选只更新草稿,保存才落库。

Provider ID 需要改名的正确姿势:添加新提供方,再删除旧的。

5.4 图片输入(模态声明)

自定义提供方的模型默认按纯文本对待,因为框架无从询问端点支持哪些模态。给未声明图片能力的模型附图片,会在发送前被拒绝。手动录入的视觉模型需要在 $DSH_HOME/settings.yaml 中声明 input

llm-pi-ai:
  providers:
    my-gateway:
      apiKeyEnv: GATEWAY_API_KEY
      api: openai-completions
      baseURL: https://gateway.example/v1
      models:
        - id: legacy-chat
        - id: vision-preview
          input: [text, image]
  • input 接受 textimage,只作用于该模型;
  • 整条路由上的模型都收图时,可用 defaultInput: [text, image] 作为回退值(默认 [text]);
  • 目录提供方要收窄某模型,写在 modelOverrides 下,以模型 id 为键;
  • 这两个字段是对端点的断言而非检查:声明了端点其实不支持的图片能力,请求会被提供方拒绝。

5.5 常见报错与排查

错误/现象原因与处理
MISSING_CREDENTIAL未配置密钥。到模型页存密钥,或提供被引用的环境变量
UNKNOWN_MODEL模型未配置。换一个已配置模型,或给自定义提供方补上
获取可用模型返回 401密钥不对。模型发现走 OpenAI 兼容的 GET /models;不支持该端点的服务请手动输入模型
图片发送前被拒模型未声明图片模态,给模型加 input: [text, image]
带图请求被提供方拒绝模型声明了端点实际不支持的图片能力,从 input/defaultInput 移除 image,并开启新会话(旧会话日志里残留的附件会反复触发同一问题)

第 6 章 工作区、会话与数据

6.1 工作区(Workspace)

dsh 进程把启动时所在目录作为默认文件系统位置。Web UI 中可以添加并选择多个工作区;Agent 的读写边界(沙箱的 workspace-write 模式)以会话的不可变 cwd 为根。

6.2 会话与日志

每次会话都有一份持久化的 JSONL 事件日志:模型请求、工具调用、审批、标题等都以事件形式落盘。日志是模型所见上下文的唯一来源(“模型可见即已记录”),因此:

  • 会话可以回放、恢复、fork;
  • 工具调用结果、审批记录都可以追溯;
  • 想给 Agent 加模型可见的输入,必须先有对应的会话事件类型。

6.3 Harness Home($DSH_HOME

Harness home 默认是 ~/.dsh,可用环境变量 DSH_HOME 覆盖。重要文件:

路径内容
$DSH_HOME/profiles/<name>/每个 profile:package.json(含 dsh.profile manifest)、cordis.patch.yml(用户 patch 层)
$DSH_HOME/cordis.patch.ymlhome 级 patch 层(所有 profile 共享的机器本地偏好)
$DSH_HOME/settings.yaml用户设置文档(如模型模态声明)
$DSH_HOME/.credentials.yaml凭据(只写、脱敏存储)
$DSH_HOME/.env用户级环境层
$DSH_HOME/skills/用户技能目录(其 .system 子目录会被跳过)

6.4 环境变量加载顺序

产品 CLI 会冻结"继承环境 > 项目 .env > 用户 $DSH_HOME/.env"的环境快照:继承环境优先,.env 文件不覆盖继承值。启动目录的 .env 优先于 harness home 的 .env


第 7 章 权限、审批与沙箱

这一章解决一个核心问题:如何让 Agent 能干活的同事不越界

7.1 沙箱模式

沙箱只管控文件系统效果

模式含义
read-only拒绝一切写(只允许 /dev/null 这类必要 sink)
workspace-write允许在工作区根目录及后端承诺的临时区域下写入
danger-full-access绕过隔离,直接 spawn 原始 argv

不同平台的后端:Linux 用 bwrap/Landlock,macOS 用 Seatbelt,Windows 用 ACL 受限令牌。强制完整性分 full/partial:Windows ACL 后端与较老内核 ABI 存在 partial 情形,要求绝对边界的场景需要留意。

7.2 审批策略

策略行为
ask(默认)把问题发给应答者链(Web UI 弹窗、ACP 桥接等),无人应答时 fail-closed 为拒绝
never确定性拒绝一切询问,适合 CI/无人值守

审批结果只有四种:allowed-once(一次性授权)、rejectedcancelledunavailable;除 allowed-once 外全部按拒绝处理。每次询问都会记一对审计事件(approval/asked / approval/decided),只进日志、不进模型 transcript。

7.3 在 Web UI 中切换权限

UI 提供 Permissions 选择器,对应"权限预设":

  • workspace-write:可改工作区文件 + 需要时问你(日常推荐);
  • danger-full-access:全权 + 从不询问(仅限可丢弃环境、容器、CI)。

预设只是把两个独立旋钮(沙箱模式、审批策略)打包,实际执行仍然由两个旋钮各自的规范 setter 写入。

7.4 给 Agent 的实用建议

  • 第一次跑陌生任务,先用 workspace-write + ask,看清楚每个审批再放行;
  • 跑不可信代码/脚本,改用 read-only 或让代码在容器/沙箱工作区里执行;
  • CI 或定时任务用 never + danger-full-access 时,请把工作区设在可丢弃的 checkout 或容器内;
  • Windows 上沙箱强制是 partial,不要把 Windows 当绝对隔离边界。

第 8 章 内置工具全景

工具是模型可见的能力单元,其 schema 由系统提示词组装给模型。下表是随产品发布的主要工具(packages/*/tool-*),默认配置下模型看到的名称如下:

类别工具名说明
Shellbash执行 shell 命令;支持 run_in_background 后台运行
ShellpwshWindows 组合的 PowerShell 方言
文件str_replace_editor查看/创建/唯一字面量替换/按行插入
文件read write edit read_image文件系统工具族;读写遵循先读后写策略
搜索glob grep基于随包 ripgrep 的发现工具,无需宿主机装 rg
委派subagent委派自包含任务(continuable,可后台)
委派subagent_fork一次性 fork 子 agent(one-shot)
委派控制list_agents send_message interrupt_agent管理后台 subagent
子级汇报report进程内可继续子级向父级汇报(仅子级内部可见)
编排workflow大规模编排 subagent 的纯 JS 脚本(agent/pipeline/parallel/phase 钩子)
编排ralph面向不可变目标的前台全新 agent 迭代(Ralph 循环)
任务管理todo_write会话所有的结构化任务清单(整表替换)
后台任务job_list job_output job_kill统一管理后台 bash/PTY/subagent
Webweb_search web_fetch联网搜索与抓取(需要 ctx.web 提供方)
技能skill按名字加载 skill 指令
规划exit_plan_mode规划模式下提交计划、获批后退出规划模式
代码模式run_code以 TypeScript 程序调用工具(Code Mode)
提问ask_user_question暂停工具调用,向用户提 1-3 个问题
终端terminal_open terminal_send terminal_read terminal_list terminal_close terminal_signal持久终端工具族(需显式启用)
语言服务lsp结构化 LSP 查询(需提供方)
会话查询session_search session_trace session_event_read session_event_search session_event_trace只读会话/事件查询(需显式启用)
计划schedule_create schedule_delete schedule_list定时任务(需显式启用)
目标create_goal get_goal update_goal同会话目标管理
自省cordis_define cordis_run cordis_stop cordis_undefine cordis_inspect_*动态 Cordis 包管理(需显式启用)

完整 JSON Schema 见官方 工具 Schema 目录。工具的实际可见集合由你的组合配置决定,未加载的包不会出现在模型目录中。


第 9 章 CLI 与 headless 自动化

9.1 入口模式

命令作用
dsh --profile <name>启动 $DSH_HOME/profiles/<name> 下的具名 profile
dsh --profile headless "job"运行一个全新的持久化会话,打印最终答案并退出
dsh web--profile web 的别名
dsh plugin --profile <name> <pnpm args>在 profile 目录内转发给 pnpm 管理插件
dsh --version / -V版本号
dsh -h启动器自身帮助(未指定 profile 时)

9.2 启动器参数 vs 应用参数

启动器只解析自己的 flag,第一个不认识的 token 之后全部属于应用

dsh --profile web --port 8080        # --port 属于 web 应用
dsh --profile headless "run tests"   # 任务字符串
dsh web --help                       # web 应用自己的帮助
dsh --help                           # 启动器自己的帮助

常用启动器 flag:

dsh web --patch ./extra.yml          # 追加一个 patch 覆盖层(可重复)
dsh web --dump-config                # 打印组合后的配置树并退出(含用户层与 --patch)
dsh web --dump-default-config        # 只打印 bundle 层,不含用户层

--dump-config 打印出的任何条目,都可以由你自己的 patch 按 id 覆盖。调试"某个功能从哪里来"时这是第一件武器。

9.3 headless:无人值守跑一个任务

DEEPSEEK_API_KEY=sk-xxx dsh --profile headless "检查这个仓库,修复失败的测试"

行为特点:

  • 接受一个非空任务字符串,创建一个全新持久化会话;
  • 执行完打印最终的 assistant 文本并退出;
  • 会话日志按 JSONL 持久化,可以回放;
  • 首次使用会自动初始化 headless profile。

9.4 插件管理命令

dsh plugin --profile demo add ./my-plugin        # 安装本地插件包
dsh plugin --profile demo add github:you/hello-plugin
dsh plugin --profile demo remove dsh-hello-plugin
dsh plugin --profile demo why <package>          # 查看依赖关系

dsh plugin 会把 pnpm 子命令原样转发到 profile 目录执行。首次使用某个 profile 名时会自动初始化(以 @deepseek-ai/dsh-base 为第一个 bundle)。


第 10 章 Profile 与插件管理

10.1 两个核心概念、两种 manifest

概念是什么manifest
Bundle(组合包)附带一个配置层的 npm 包,回答"这个包贡献什么"package.jsondsh.bundle 指向 patch 文件
Profile$DSH_HOME/profiles/<name> 下可启动的组合,回答"由哪些 bundle 按什么顺序组成"dsh.profilebundles 列表

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

10.2 层叠加顺序

生效配置在空根之上按此顺序逐层组合:

  1. profile 的 dsh.profile.bundles 所列各 bundle 的 patch,按列表顺序(先是 @deepseek-ai/dsh-base);
  2. profile 自己的 cordis.patch.yml
  3. home 级 $DSH_HOME/cordis.patch.yml(各 profile 共享);
  4. 每个 --patch <path> overlay,按命令行顺序。

两条重要规则:

  • 后应用的层按行胜出
  • patch 替换目标行的整个 config,不做键级深度合并——覆盖时未改的键也要重述。

10.3 一个最小 bundle 长什么样

hello-plugin/
├── package.json       # 声明 dsh.bundle
├── cordis.patch.yml   # 该 bundle 贡献的配置层
└── index.js           # 插件模块
{
  "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" } }
}
export const name = 'hello-plugin'
export function apply() {
  console.log('[hello-plugin] plugin loaded!')
}
- insert:
    - id: hello
      name: dsh-hello-plugin

10.4 从 GitHub 安装:构建脚本这道坎

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

git 安装拉的是源码,不会运行 build。因此:

  • 作者要提供自包含的 prepare 脚本(pnpm 在 git 安装后运行它,产出可直接加载的入口);
  • 用户要在 profile 的 pnpm-workspace.yaml 里显式允许构建:
allowBuilds:
  dsh-hello-plugin: true

请如实看待这个授权:它允许该包在安装时于你的机器上执行代码,且不在 Agent 沙箱内。只对源码可信的包授权,并尽量锁定 commit(github:you/hello-plugin#<sha>)。

如果不想让用户做授权,就分发构建产物:发布到 npm(dsh plugin add your-package),或交付 tarball(pnpm packdsh plugin add ./your-package-0.1.0.tgz)。


第 11 章 Skills 技能系统

Skill 是可选的指令包:给模型提供"遇到某类任务时该怎么做的专业知识/流程",按需加载,不占对话常驻上下文。

11.1 存放位置与优先级

本地提供方按 rank 顺序扫描(rank 越小优先级越高):

Rank来源根目录
100project-dsh<项目根>/.dsh/skills
200project-agents<项目根>/.agents/skills
300customConfig.customSkillDirs 配置
400user-dsh$DSH_HOME/skills
500user-agents$AGENTS_HOME/skills
600bundledConfig.bundledSkillDir(需配置)

项目根是包含 .git 的最近祖先目录;找不到时用当前 cwd。用户 $DSH_HOME/skills.system 子目录会被跳过。

11.2 Skill 的两种文件形态

skill 名字必须是 kebab-case(^[a-z0-9]+(?:-[a-z0-9]+)*$):

  • 目录包:<name>/SKILL.md
  • 扁平文件:<name>.md

示例 .dsh/skills/my-checklist/SKILL.md

---
description: 发布前按清单逐项检查。
---

当需要发布版本时,按以下步骤操作……

支持的前置元数据:

frontmatter 键作用
description简短路由说明,模型目录只展示 name + description
disable-model-invocationtrue 时模型不可调用(仅供人类命令等场景)
user-invocable是否对人类调用面可见(省略默认 true

11.3 模型如何使用 Skill

  • 会话首次观察到非空 skill 目录时,系统会注入一份持久化的目录消息(只含 name + description,绝不包含正文或路径);
  • 模型通过 skill({ name }) 工具按需加载正文;正文变更会反映到下一次调用,但不会改写已注入的目录;
  • 提供方读取正文时按调用 agent 的 cwd 解析,因此 skill 可以感知工作区。

11.4 给项目装 Skill 的推荐流程

  1. 在项目根建 .dsh/skills/(或 .agents/skills/);
  2. 每个 skill 一个目录 + SKILL.md,写清楚触发时机(description/whenToUse)与步骤;
  3. 若涉及脚本/模板,放同目录并让正文引用相对路径;
  4. 在会话里触发场景,观察目录是否注入、工具是否加载正文。

第 12 章 Python SDK

Web UI 之外的程序化入口。SDK 通过 JSON-RPC stdio 驱动 Harness 子进程,把"跑一个 agent 任务"变成普通 Python 函数调用。

12.1 前置要求与安装

  • Python 3.10+、Git;
  • Linux x64 / Linux arm64 / macOS 14+ arm64(内置运行时暂不支持 Windows agent);
  • DeepSeek 兼容的 API 端点与凭据;
  • 一个隔离的 workspace(Agent 可以改文件)。
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

12.2 设置凭据与运行内置示例

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.'
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."

脚本会打印 assistant 的最终回复;会话目录会收到包含模型请求与工具调用的 JSONL 日志。

12.3 在自己程序中使用

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)

要点:

  • DeepSeekHarness 延迟启动内置运行时,退出上下文管理器时回收;同一 harness + session id 会保留该会话的 Bash 进程状态(工作目录、导出的变量、shell 函数);
  • 独立任务用新的 session id;只有需要延续同一段对话时才复用;
  • 示例组合(minimal.cordis.yml)的工具只有持久 bashstr_replace_editor,Bash 超时 300 秒,编辑器输出上限 16,000 字符,关闭了上下文压缩,沙箱为 danger-full-access——只能在可丢弃的 checkout 或容器里跑
  • 这个组合是刻意的最小集:没有 skill、没有一次性 bash、没有任务工具、没有压缩。需要更多能力,改用自己的 cordis.yml 组合即可。

第三部分 精通篇

第 13 章 Cordis 与插件开发基础

13.1 插件就是函数

在 Harness 中,插件是导出 apply 函数的 TypeScript 模块:

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

export const name = 'hello-plugin'

export function apply(ctx: Context) {
  // 在这里注册能力。
}

这是完整配置——没有框架启动代码。插件描述自己的贡献,cordis.yml 负责组合应用。

13.2 三种插件形态

// 1. 函数形式(最常见)
export function apply(ctx: Context) {}

// 2. 对象形式
export default {
  name: 'my-plugin',
  inject: ['tools'],
  apply(ctx: Context) {},
}

// 3. 类形式(需要对外提供服务时)
import { Service, type Context } from '@deepseek-ai/cordis'

export default class MyService extends Service {
  static inject = ['tools']
  constructor(ctx: Context) {
    super(ctx, 'myService')
  }
}

13.3 用 --patch 把本地插件加载进 Web UI

在仓库根目录创建 scratch-plugin/src/my-plugin.ts(内容同 13.1),再创建 scratch-plugin/cordis.yml

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

插件路径必须是绝对路径(patch 只贡献配置,不改 loader 解析模块的基准目录)。启动:

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

打开 http://127.0.0.1:3080,终端会打印 [hello-plugin] plugin loaded!

13.4 生命周期与自动清理

通过 ctx 注册的一切(事件监听、工具、定时器)在插件卸载时自动清理。需要手动释放的资源用 ctx.effect()

export function apply(ctx: Context) {
  ctx.effect(() => {
    const timer = setInterval(() => console.log('heartbeat'), 5000)
    return () => clearInterval(timer)  // 插件卸载时执行
  })
}

13.5 声明依赖

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

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

框架保证 apply 执行时注入的服务全部就绪;服务缺失时插件会等待而不是报错。

13.6 配置与 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 默认值
}

在 patch 里传配置:

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

规则:

  • 不要导出普通对象当 Config(不满足 Standard Schema 接口);
  • 配置在加载时校验,不合法就明确失败,绝不静默跳过;
  • 凡是不同部署可能取不同值的参数,都必须做成配置字段——检验标准:能否在 cordis.yml 改它而不用改代码?
  • 配置变更会触发 HMR 热替换:卸载旧实例、加载新实例,注册全部自动清理。

第 14 章 开发一个工具

工具是模型能调用的能力。用 @deepseek-ai/dsh-toolsdefineTool 定义:

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}!`
    },
  }))
}

要点:

  • defineTool 根据 parameters 推导并校验 argsexecute 收到的参数已类型安全;
  • execute 返回 output.schema 声明的规范值output.render 再把它转成面向模型的内容块;
  • inject: ['tools'] 让 Cordis 等待工具注册表就绪。

启动后让模型调用:Use the greet tool to greet Ada. 模型会调用 greet 并收到 Hello, Ada!

进阶主题(详见官方 adding-a-tool cookbook):嵌套 schema、规范化输出值、后台工作、策略钩子、Code Mode 与 UI 卡片。


第 15 章 服务与依赖注入

15.1 服务是什么

服务是挂载在 ctx 上的命名能力:ctx.tools(工具注册表)、ctx.llm(LLM 服务)、ctx.agents(Agent 服务)都是服务。任何插件都可以提供服务供他人使用。

15.2 提供服务:Service 类

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

export default class MetricsService extends Service {
  static inject = ['llm']  // 服务也可以依赖其他服务

  constructor(ctx: Context) {
    super(ctx, 'metrics')  // 'metrics' 就是服务名
  }

  record(event: string, value: number) {
    // ...
  }
}

消费方声明 inject = ['metrics'] 后即可 ctx.metrics.record(...)

15.3 类型声明(声明合并)

declare module '@deepseek-ai/cordis' {
  interface Context {
    metrics: MetricsService
  }
}

15.4 依赖行为

  • 必需依赖inject 声明,服务不存在则插件不加载;
  • 可选依赖:不声明,用 ctx.get('metrics') 在调用处查询(可能为 undefined);
  • 运行期间必需服务消失 → 依赖它的插件自动 dispose;服务重新出现 → 插件自动重载。

15.5 服务隔离

同一个服务可以有多个实例,不同插件组看到不同实例:

- id: group-a
  name: '@deepseek-ai/cordis-plugin-group'
  group: true
  isolate:
    shell: true
  config:
    - name: '@deepseek-ai/dsh-bash-local'
      config:
        timeoutMs: 5000
    - name: './src/plugin-a.ts'

- id: group-b
  name: '@deepseek-ai/cordis-plugin-group'
  group: true
  isolate:
    shell: true
  config:
    - name: '@deepseek-ai/dsh-bash-local'
      config:
        timeoutMs: 60000
    - name: './src/plugin-b.ts'

plugin-aplugin-b 各自看到自己组内的 Bash 实例,互不影响——这是按 agent/preset 定制能力集合的基础。


第 16 章 事件系统

事件是插件间松耦合通信的核心。选对事件域是大多数扩展的第一次决策。

16.1 四种分发模式

模式是否 await顺序返回值
emit按注册顺序观察
bail-按顺序,首个非空返回值短路
serial按顺序执行,首个非空值终止后续
waterfall按顺序,每个监听器可包装下游

waterfall 是"环绕中间件":必须调用 next() 传递给下游,不调用即短路整条流水线(这是拦截/网关的设计手段)。

16.2 基本用法

// 监听
ctx.on('event-name', (payload) => { /* ... */ })

// 触发
ctx.emit('event-name', payload)

16.3 类型安全的事件

import '@deepseek-ai/cordis'

declare module '@deepseek-ai/cordis' {
  interface Events {
    'my-plugin/ready': (payload: { id: string }) => void
    'my-plugin/check': (input: string) => boolean | undefined
    'my-plugin/transform': (input: string, next: () => Promise<string>) => Promise<string>
  }
}

16.4 Harness 事件命名

  • Cordis 事件遵循 namespace/actionagent/stepagent/requestagent/request-errortools/resultsession/event
  • turn/*step/*tool/calltool/resultcompaction/*持久化会话事件,不是同名 Cordis 事件;要观察它们请监听 session/event 并检查 event.type
  • 监听器也是 effect:插件卸载时自动移除。

16.5 示例:工具日志插件

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

export const name = 'tool-logger'

export function apply(ctx: Context) {
  ctx.on('tools/result', (exec, result) => {
    console.log(`[tool] ${exec.name}(${JSON.stringify(exec.arguments)})`)
    const text = result.content
      .map(block => block.type === 'text' ? block.text : '')
      .join('')
    console.log(`[tool result] ${text.slice(0, 100)}`)
  })
}

第 17 章 LLM 适配器

接入新模型提供方 = 实现一个继承 LlmAdapter、实现 stream() 的类。

17.1 最小实现

import type { Context } from '@deepseek-ai/cordis'
import Schema from '@deepseek-ai/schemastery'
import { LlmAdapter, type GenerateOptions, type StreamChunk } from '@deepseek-ai/dsh-llm'

class MyAdapter extends LlmAdapter {
  private apiKey: string

  constructor(apiKey: string) {
    super()
    this.apiKey = apiKey
  }

  async *stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
    // 1. 把 options.messages 转成提供方格式
    // 2. 调用流式 API
    // 3. 把响应转成 StreamChunk
  }
}

export interface Config {
  apiKey: string
  providers: string[]
}

export const Config: Schema<Config> = Schema.object({
  apiKey: Schema.string().required(),
  providers: Schema.array(Schema.string()).required(),
})

export const name = 'my-llm-adapter'
export const inject = ['llm']

export function apply(ctx: Context, config: Config) {
  const adapter = new MyAdapter(config.apiKey)
  ctx.llm.registerAdapter(config.providers, adapter)
}

17.2 StreamChunk 协议

stream() 按协议生成分片:

import { CallId, type StreamChunk } from '@deepseek-ai/dsh-llm'

async function* exampleChunks(): AsyncIterable<StreamChunk> {
  // 1. 每个内容块先发 block-start
  yield { type: 'block-start', index: 0, blockType: 'text' }

  // 2. 文本走 text-delta
  yield { type: 'text-delta', index: 0, text: 'Hello' }
  yield { type: 'text-delta', index: 0, text: ' world' }

  // 3. block-end 携带完整块
  yield {
    type: 'block-end',
    index: 0,
    block: { type: 'text', text: 'Hello world' },
  }

  // 4. 工具调用块
  yield { type: 'block-start', index: 1, blockType: 'tool-call' }
  yield {
    type: 'tool-call-delta',
    index: 1,
    id: CallId('call-123'),
    name: 'bash',
    argumentsDelta: '{"command":"ls"}',
  }
  yield {
    type: 'block-end',
    index: 1,
    block: {
      type: 'tool-call',
      id: CallId('call-123'),
      name: 'bash',
      arguments: '{"command":"ls"}',
    },
  }

  // 5. usage 必须在 finish 之前
  yield { type: 'usage', usage: { inputTokens: 100, outputTokens: 50 } }

  // 6. finish 必须是最后一个分片
  yield { type: 'finish', reason: { kind: 'stop' } }
  // 或 { kind: 'tool-calls' } 请求执行工具
}

关键规则:每个 block-start 都有对应 block-endindex 从 0 递增;tool-call-deltaargumentsDelta 是原始 JSON 文本增量;finish 最后;usagefinish 前。

17.3 在 cordis.yml 中使用

- id: my-llm
  name: './src/my-llm-adapter.ts'
  config:
    apiKey: !!js process.env.MY_API_KEY
    providers:
      - my-provider

- id: agent-loop
  name: '@deepseek-ai/dsh-agent-loop'
  config:
    agents:
      - id: main
        provider: my-provider
        model: my-model-v1

17.4 进阶与错误处理

  • 覆写 resolveModel(provider, model, signal?) 返回确切的提供方/模型身份与可选元数据;异步查询必须响应 signal
  • 能公布模型选项时覆写 listModels()
  • 不支持的字段抛带稳定 code 的 LlmError不要静默丢弃
  • 每个 HTTP 请求合并 attributionHeaders() 并传递 options.signal
  • 完整参考实现:packages/llm/llm-deepseek/(DeepSeek API)与 packages/llm/llm-pi-ai/(Pi AI),对照二者可看到同一套契约在不同 SDK 上的落地。

第 18 章 打包与发布插件

把本地插件变成可安装的 bundle(第 10.3 节的结构),然后:

# 在包含 hello-plugin 的目录中
dsh plugin --profile demo add ./hello-plugin

首次使用会初始化 profile,pnpm 链接该 checkout,并因包声明了 dsh.bundle 而把它追加进 dsh.profile.bundles。先验证再启动:

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

卸载:

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

让 bundle 持有自己的命令行参数

定义了可运行应用的 bundle 挂载一个普通提供方插件:

- id: hello-startup
  name: 'dsh-hello-plugin/startup'

该插件 inject = ['cmdlineArgs'],用 @deepseek-ai/dsh-cmdlineparseCmdline 解析启动器之后的参数,再把自己的服务提供出去。受参数配置的行注入该服务:

- id: my-app
  name: '@example/my-app'
  inject: [myAppStartup]
  config:
    port: !!js ctx.myAppStartup.port ?? 8080

这样加应用专属 flag 无需修改启动器。

发布三选一

方式用户安装是否需要构建授权
发布 npm(pnpm publish 时构建好 lib/dsh plugin add your-package
交付 tarball(pnpm packdsh plugin add ./pkg-0.1.0.tgz
GitHub 直装dsh plugin add github:you/hello-plugin是(allowBuilds

第 19 章 架构深入:扩展点与事件域

19.1 组合树

运行中的 dsh 是一棵插件树,由启动时按序叠加的各层组成。没有需要打补丁的"特权内核":扩展 dsh 的方式就是把插件挂载到其他插件旁边。

核心包一览:

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

19.2 轮次流程(Turn Flow)

turn/start
  领取输入 → 组装提示词与工具 schema
  → agent/pre-step(可改写/拒绝)
  → step/start → 追加 user/message → 派生历史
  → agent/request → llm/stream → assistant/* 消息
  → tool/call → tools/pre-execute → tools/execute → tools/post-execute → tool/result
  → step/end(工具要求继续或新输入到达则下一步)
  → agent/turn-stopping
turn/end

agent/pre-stepagent/requestllm/streamtools/* 是 waterfall 事件,监听器必须 next() 委托;agent/turn-stopping 是 serial 事件。

19.3 能力 Seam:可替换能力的三角色

一个 seam 包含三种角色:

  1. Service Definition:声明接口的 Cordis 服务(如 ctx.shell);
  2. Service Provider:实现(如 dsh-bash-localdsh-bash-sandbox);
  3. Consumer:消费方(如 dsh-tool-bash)。

这正是"换一个提供方就换掉整个产品行为"的原因:把文件系统与进程提供方指向远程沙箱,Bash、PTY、LSP 一起搬过去。

19.4 新行为该放哪:扩展点速查

目标机制
添加模型提供方ctx.llm 注册适配器
添加面向模型的能力ctx.tools 注册;schema 加入提示词组装
会话拥有不同能力集合组装 agent preset;服务行用 isolate realm
添加 shell 执行注册 ctx.shell 后端
添加持久终端注册 ctx.terminals 后端 + dsh-tool-terminal
添加用户命令ctx.commands 注册(/command,不走模型轮次)
添加后台工作ctx.jobs 注册;job_* 工具负责收集/停止
添加文件系统访问或策略注册 ctx.fs 提供方,或监听 fs/* 事件
限制进程使用 ctx.sandbox 后端;消费方 spawn 前包装 argv
拦截请求/工具/轮次使用 agent/*tools/* 事件
添加模型可见上下文agent.inject(),落到下一次获准请求
添加 UI/编辑器集成驱动 ctx.agents 并从 session/event 渲染
添加持久会话状态扩展 SessionEventMap;从日志渲染和回放
fork 活跃会话ctx.sessions.fork(source, boundary?, childSessionId?)
把注册限定到单个 agent使用该 agent 的 agent.ctx

第 20 章 综合实战:从零构建一个插件

把前几章串起来,做一个"带配置、带工具、带事件日志、可打包"的完整插件。

20.1 目标

给 Web UI 加一个 note 工具:把模型记下的要点追加到工作区的 NOTES.md,同时用事件日志记录每次调用。配置项:文件名与追加模式。

20.2 插件代码 scratch-plugin/src/note-plugin.ts

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

export const name = 'note-plugin'

export interface Config {
  file: string
  appendNewline: boolean
}

export const Config: Schema<Config> = Schema.object({
  file: Schema.string().default('NOTES.md'),
  appendNewline: Schema.boolean().default(true),
})

export const inject = ['tools']

export function apply(ctx: Context, config: Config) {
  ctx.tools.register(defineTool({
    name: 'note',
    description: `Append a note to ${config.file}.`,
    parameters: {
      text: { type: 'string', required: true, description: 'The note text to append' },
    },
    output: {
      schema: { type: 'object', properties: { lines: { type: 'number' } } },
      render: (_args, value) => [
        { type: 'text', text: `Appended to ${config.file} (${value.lines} line(s)).` },
      ],
    },
    async execute(args) {
      const line = config.appendNewline ? `${args.text}\n` : args.text
      // 真实实现应通过 ctx.fs 写入;此处为演示直接使用 Node API。
      const { appendFileSync } = await import('node:fs')
      appendFileSync(config.file, line)
      return { lines: line.split('\n').length - 1 }
    },
  }))

  ctx.on('tools/result', (exec, result) => {
    if (exec.name === 'note') {
      console.log(`[note-plugin] ${exec.arguments.text}`)
    }
  })
}

20.3 加载并验证

# scratch-plugin/cordis.yml
- insert:
    - id: note
      name: '/absolute/path/to/deepseek-harness/scratch-plugin/src/note-plugin.ts'
      config:
        file: 'NOTES.md'
pnpm dsh web --patch ./scratch-plugin/cordis.yml

在 Web UI 发送:Please note down: remember to run the linter before committing. 然后检查 NOTES.md 与终端日志。

20.4 打包成 bundle 并安装

按第 10.3 节的目录结构把插件整理成 note-plugin/,补上 dsh.bundle manifest 与 cordis.patch.yml(把 name 换成包名 dsh-note-plugin),然后:

dsh plugin --profile demo add ./note-plugin
dsh --profile demo --dump-config
dsh --profile demo

20.5 打磨清单

  • 所有可调参数进 Config,有默认值;
  • 无效配置在加载时响亮失败(用 Schema 表达约束);
  • 文件写入走 ctx.fs(不要裸用 Node API),让沙箱/权限策略能约束它;
  • 工具调用失败返回结构化错误,不要吞;
  • 发布前 pnpm pack 或发布 npm,避免用户被 allowBuilds 卡住。

附录

附录 A 命令速查

# 启动
npx @deepseek-ai/dsh web                  # Web UI,默认 http://127.0.0.1:3080
dsh web --port 8080                       # 换端口(--port 属于 web 应用)
dsh --profile <name>                      # 启动具名 profile
dsh --profile headless "task"             # 一次性任务,打印结果后退出

# 配置检查
dsh web --dump-config                     # 打印组合后的配置树
dsh web --dump-default-config             # 只打印 bundle 层
dsh --version                             # 版本

# 插件管理(转发给 pnpm)
dsh plugin --profile demo add <pkg>       # 安装
dsh plugin --profile demo remove <pkg>    # 卸载
dsh plugin --profile demo why <pkg>       # 依赖关系

# 开发(源码 checkout 内)
pnpm install && pnpm run build            # 构建
pnpm dsh web --patch ./scratch/cordis.yml # 带本地 patch 启动

附录 B 常用环境变量

变量用途
DSH_HOMEHarness home,默认 ~/.dsh
DEEPSEEK_API_KEYDeepSeek API 密钥
DEEPSEEK_BASE_URL覆盖默认端点(OpenAI 兼容代理)
DSH_MODELPython SDK 默认模型
DSH_SYSTEM_PROMPTPython SDK 示例组合的系统提示词
DSH_BUNDLED_SKILL_DIR随包 skill 目录配置的环境变量形式
AGENTS_HOME用户级 agents skill 目录的基准路径($AGENTS_HOME/skills

附录 C 术语表(节选)

  • seam:可替换能力 = Service Definition + Service Provider + Consumer 三角色。
  • scope:按 agent 划分的注册单位;活跃 agent 即自身 scope 的 key。
  • shadowing:scope 内同名注册替换全局注册。
  • turn / step / round:轮次 / 步骤 / 外层策略迭代。
  • bundle / profile / patch:配置层 / 具名组合 / 覆盖层。
  • skill:可选指令包,按需加载。
  • Ralph 循环:面向不可变目标、每轮全新子会话的工作流策略。
  • Goal / Goal Round:附着在会话上的持久目标及接纳的续行周期。

附录 D 官方资源与 FAQ

官方资源

  • 仓库:https://github.com/deepseek-ai/deepseek-harness
  • Web UI 指南:docs/user/guide/index.md
  • 模型配置:docs/user/guide/providers.md
  • Python SDK:docs/user/guide/python-sdk.md
  • 插件开发:docs/user/develop/basic/framework/practice/
  • Cordis 教程:docs/cordis-tutorial/(无需 API Key 的动手练习)
  • 工具目录:docs/tool-catalog.md;配置目录:docs/config-catalog.md
  • 架构与子系统:docs/architecture.mddocs/subsystems/

FAQ

Q:dsh webdsh --profile web 有区别吗?
没有,web 是别名。

Q:改模型配置要重启吗?
不用,模型路由下一次请求生效。改 profile/home 的 cordis.patch.yml 会触发 HMR 热重载;修改 settings.yaml 中的模态声明等,按所属插件的 applies 时机生效。

Q:patch 里覆盖某一行,为什么我的其他配置丢了?
patch 按 id 替换整行 config,不是深合并。覆盖时必须重述该行需要的每一个键。

Q:插件路径必须绝对吗?
本地 patch 里的模块路径必须绝对(或从配置目录可解析);打包成 bundle 后按包名引用。

Q:Windows 上能用 Python SDK 吗?
内置运行时暂不支持 Windows agent(持久 PTY 需要 POSIX 终端),建议在 Linux/macOS 上跑 SDK;Windows 上请用 Web UI/CLI(pwsh 工具)。

Q:为什么模型看不到我新加的 skill?
skill 目录注入发生在会话第一次观察到非空完整目录时;检查目录位置(项目根 .dsh/skills)、kebab-case 命名、SKILL.md 形态、disable-model-invocation,并确认模型有 skill 工具。

Q:GitHub 装的插件加载失败?
大概率是 git 安装没有构建产物:作者需要提供 prepare 脚本,用户需要 allowBuilds 授权(见 10.4),或改用 npm/tarball 分发。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

万猫学社

您的鼓励将是我创作的最大动力。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值