从零认识 DeepSeek Harness

中文工程导读 · 从基础开始

从零认识
DeepSeek Harness

从“这个项目能做什么”开始,逐步理解任务流程、插件架构和工程目录。

12 章正文 · 约 1 万汉字 · 适合初学者

开始阅读

依据 2026-10-20 工作区编写 · 非官方工程导读

从零认识 DeepSeek Harness 书籍封面

前言:从哪里开始读

这本书写给第一次接触这个工程的你。你不需要先会 TypeScript,也不需要先理解智能体框架。读完后,你应该能说明这个项目有什么用、一次任务怎样完成、主要目录分别负责什么,以及遇到问题时该去哪里找依据。

本书以当前工作区中的 DeepSeek Harness 为对象,依据日期为 2026 年 10 月 20 日。仓库根 package.json 的版本是 0.2.1-rc.2。项目处于开发者预览阶段,具体界面、配置和接口可能继续变化。阅读后续版本时,请以那个版本的 README 和对应文档为准。

我们会反复使用一个例子:你让智能体“阅读一个项目,并写一份适合新人的介绍”。这个例子既需要模型理解语言,也需要程序读取文件,适合用来观察整个工程如何协作。

本书前半部分介绍用途和使用流程,后半部分带你认识工程。第一次阅读可以按顺序读;如果你暂时不准备写代码,读完前六章后,可以直接看故障排查和学习路线。附录中的术语表可以随时查阅。

书中出现的使用步骤和命令以仓库现有文档为依据。本次编写没有启动完整应用,也没有调用真实模型 API,因此这些步骤是阅读与操作指引,不是本次环境的实测报告。涉及费用、认证和运行结果时,请以自己的实际环境为准。

第一章 这个工程到底做什么

1.1 先理解三个不同的东西

大语言模型是一种根据输入生成输出的模型。你给它一段文字,它可以继续写作、回答问题,也可能提出调用工具的请求。模型本身并不因为看见一个文件名,就自动拥有你电脑上那个文件的内容。

智能体是围绕目标持续推进工作的程序。它把模型生成、工具执行、结果反馈和下一步决策连接起来。例如,它可以先请求查看目录,再读取 README,最后根据这些真实内容写出介绍。

Harness 是组织这一过程的运行框架。DeepSeek Harness 的缩写是 dsh。它提供模型接入、工具管理、任务循环、会话记录、权限交互和界面等能力,让模型能够在一个受配置约束的程序环境中工作。

这三个概念要分清。换一个模型,可能影响推理、速度和输出质量;换一个工具实现,可能影响文件从哪里读取;换一个界面,可能影响你怎样发起任务。它们承担不同的职责。

1.2 它适合哪些工作

从使用指南来看,智能体可以读取和编辑工作区文件、执行命令、委派工作,并维护计划。对于一个代码仓库,你可以让它整理结构、查找某项能力的实现位置,或者在明确范围内修改代码。

对初学者而言,最合适的第一项任务是阅读。比如:“请介绍这个项目的用途、主要目录和启动入口。每个判断都给出对应文件路径。”这能让你同时检查答案是否可信,以及工具是否读取了相关材料。

当你开始要求修改时,任务要有明确的结果。比如:“为这一段说明补上术语解释,并告诉我修改了哪个文件。”完成后,你仍然需要检查文件差异。智能体给出完成说明,并不自动说明内容正确。

1.3 一切皆插件意味着什么

项目 README 用“一切皆插件”概括其架构。模型适配器、工具注册表、会话日志,甚至默认的智能体循环,都是由插件提供的。

插件是一段由框架管理的功能代码。它可以向系统提供服务,也可以监听事件或注册工具。不同插件通过约定的接口协作,配置决定实际加载哪些插件。

这给项目带来很强的可组合性,同时也提高了阅读难度:一个功能往往跨越多个包。你需要先认识各自职责,再追踪它们如何连接。本书会逐步建立这种阅读方法。

1.4 一个判断练习

假设你发出“介绍这个仓库”,模型却回答了一份与实际目录不符的介绍。可能的原因包括没有读取文件、工作区选择错误,或者模型误解了工具结果。单看最后一句回答,无法确定原因。

这一点解释了为什么这个工程需要工具记录和会话日志:你需要知道它实际接收了什么、尝试了什么,以及哪些操作得到了结果。

本章自测:模型负责什么?框架负责什么?参考答案是:模型生成回答和调用意图,框架组织请求、执行工具、记录过程并处理交互;具体能力还取决于所加载的插件。

第二章 第一次使用前的准备

2.1 认识运行环境

Node.js 是运行这个项目 JavaScript 程序的环境。npm 是常见的包管理工具,npx 可以运行 npm 包提供的命令。pnpm 是本仓库采用的包管理工具,源码开发时会用到它。

仓库当前声明的 Node.js 版本范围为 ^22.19.0 或 >=24.0.0。这表示支持从 22.19.0 起的 22.x,以及 24.0.0 及之后的版本;23.x 不在这个声明范围内。不要只根据“版本号更大”判断兼容性。

终端是输入命令的程序窗口。当前目录表示终端此刻所在的位置,很多命令会以它为起点查找文件。浏览器则负责显示 Web 界面。终端里运行的服务结束后,浏览器页面即使还开着,也不代表服务仍然可用。

2.2 普通使用与源码开发

如果只想体验产品,根 README 给出的入口是:

npx @deepseek-ai/dsh web

按文档说明,这会启动 Web UI,默认地址为 http://127.0.0.1:3080。本机启动通常会打开默认浏览器。实际访问时应看终端打印的地址;通过 SSH 启动还涉及本地端口转发,不能把远端打印的地址直接当成本机服务。

如果你要研究或修改当前仓库,源码路径是安装依赖、构建,再启动:

pnpm install
pnpm run build
pnpm dsh web

这些命令需要在仓库根目录执行。安装会准备依赖,构建会生成运行需要的产物,最后一条命令启动应用。文档明确说明,pnpm dsh web 使用已经构建的产物,不会替你重新构建。

本书没有执行上述安装和启动命令,也没有为了写书改变依赖。你真正操作前,可先阅读仓库 README.zh.md 中的“运行”和 docs/development.zh.md 中的环境说明。

2.3 模型服务与密钥

框架需要连接模型提供商。模型 API 密钥是一项认证凭据;能打开 Web 页面,并不等于已经能向模型发送请求。

当前 Web 使用指南给出的基本流程是进入“设置 → 模型”,填写 DeepSeek API 密钥并保存。其他提供商或自定义网关见模型配置指南。选择模型时,要确保提供商、模型 ID 和协议相互对应。

这里有三个常被混淆的项目:API 地址决定请求发到哪里,协议决定如何组织请求,模型 ID 决定请求哪个模型。地址能连通,不意味着协议或模型 ID 一定正确。

密钥不要贴进任务内容、截图或 Git 提交。仓库的凭据服务负责管理凭据引用;应用可用的权限和凭据也会影响工具执行。API 调用的费用以提供商的规则为准,本书不承诺免费额度。

2.4 工作区和权限

工作区是你希望智能体处理的项目位置。按当前 Web 指南,全新界面还没有选中工作区,需要先添加再选中,随后才能输入任务。

工作区选择与安全隔离不是一回事。架构文档说明,工作目录影响用户上下文与执行路径,但不会自动改变原始项目身份、沙箱可写根目录或现有进程的目录。

本工程能够执行模型生成的命令并加载插件。官方安全说明明确指出,它尚未接受安全审计,沙箱和审批也不保证隔离。第一次练习请使用备份或可丢弃的项目副本,并检查拟执行操作。这里的提醒直接关系到这个工程实际拥有的文件与进程能力。

第三章 完成你的第一个阅读任务

3.1 写一个可检查的请求

选好模型与工作区后,可以发送下面这段练习提示。它是供你复制使用的任务示例,并非程序配置:

我是刚接触这个项目的新手。请阅读根 README 和主要目录说明,写一份中文项目介绍。先解释项目能做什么,再列出五个重要目录。每个结论给出文件路径;没有找到依据的内容请明确说明。本次只做阅读和说明。

这个请求包含读者、材料、输出顺序和依据要求。它能减少空泛介绍,也方便你核对结果。请求中的“只做阅读”表达你的任务范围;真实工具权限仍由配置与执行策略控制。

3.2 观察中间过程

智能体可能先查看目录,再读取说明文件,必要时继续查找源码。你可以观察工具请求和返回内容,判断它是否选对了位置。

如果权限策略要求审批,界面会先向你询问。审批前应看清操作对象与范围。要求读取一个目录,与允许运行任意命令,所涉及的能力不同。

阅读过程中,模型可能发现 README 没有解释某个细节,接着读取相关包的说明。这是一个合理的查证过程。反过来,如果它没有读取资料就断言某个内部接口存在,你可以让它补充证据。

3.3 怎样验收一份项目介绍

先检查最容易核对的事实:目录是否真实存在,所说的启动命令是否来自当前 README,某个包的职责是否与自己的 README 一致。

再检查解释是否适合你。如果文章把大量包名堆在一起,却没有解释它们之间的关系,可以要求它用一次任务的执行过程重写。技术名词准确和初学者能读懂,需要同时满足。

最后检查不确定性。良好的介绍应该区分“在文件里读到了什么”和“推测可能怎样实现”。只有查看源码或运行验证,才能支持更具体的行为断言。

3.4 逐步增加任务难度

第一轮只问用途与目录,第二轮再问一次任务怎样完成,第三轮才定位某个具体实现。这样每一轮都有可验证的目标,也容易发现自己从哪一步开始不理解。

如果第一次就要求“全面分析所有代码并修复所有问题”,工作范围会很难判断。你可以把大目标拆成明确结果,同时保留最终目标:先掌握入口,再查一个子系统,最后做一项小改动。

本章练习:打开介绍中引用的三个文件,自己各读一段。如果内容和结论对不上,请追问具体依据,而不是先修改工程。

第四章 一次任务内部怎样运行

4.1 从用户输入到模型请求

你发送的文字会进入智能体的输入处理过程。框架准备系统提示词、工具说明和相关历史,再由模型适配器发送请求。

系统提示词包含运行规则与能力说明。工具说明告诉模型有哪些工具以及各工具接受什么参数。历史消息使模型了解之前发生了什么,但可见历史由程序根据会话记录组织,不是把整个硬盘或所有旧记录直接交给模型。

例如“介绍项目”这项任务开始时,模型通常还不知道 README 的全部内容。它需要通过已提供的上下文或后续工具结果获得文件信息。

4.2 Step 与 Turn

项目把一次模型请求以及它引发的工具调用称为一个 step,可译作“步骤”。一个 turn,可理解为一轮处理,可能包含多个步骤,也可能在输入被拒绝等情况下没有步骤。

典型过程是:模型请求读取文件,框架执行工具并记录结果,模型在下一步读取结果并继续回答。因此,一个用户问题可能产生多次模型请求。

下面的简图省略了重试、取消和失败分支,只用于建立顺序感:

用户输入
   ↓
整理提示词、历史与工具说明
   ↓
向模型发送请求
   ↓
模型回答或请求工具
   ↓
执行工具,记录结果
   ↓
需要继续时再次请求模型
   ↓
本轮结束

这个循环解释了两个现象:第一,回答可能比普通聊天耗时更长;第二,工具返回并不总是任务结束,模型可能还要整理结果或继续查询。

4.3 流式输出和最终记录

流式输出是生成过程中逐渐显示文本或状态。用户可以在完整回答结束前看见内容,但显示中的片段与已经持久保存的完整消息存在区别。

架构文档说明,进程内实时事件提供增量更新;完整的紧凑输出流在结算时进入会话记录。若进程在结算前突然丢失,不能假定屏幕上刚出现的片段已经持久保存。

这也是排查问题时要区分界面显示、模型请求、工具结果和持久记录的原因。它们相互关联,承担的职责却不同。

4.4 失败与取消

模型请求可能因认证、网络或提供商限制失败,工具也可能因参数、权限或文件不存在而失败。某一步失败,并不能直接推出所有先前操作都被撤销。

假设某工具已经修改了文件,而后续模型请求中断,文件修改可能已经存在。恢复前应查看文件状态和工具结果。对结果未知的写入,不要立刻重复执行。

本章自测:为什么工具执行完以后还可能出现下一次模型请求?因为模型需要读取工具结果,决定继续工作或组织最终回答。

第五章 会话记录为什么重要

5.1 Session 记录发生的事实

Session 是本工程中记录一次持续交互的重要对象。它包含输入、助手消息、工具调用和工具结果等事件。它比你在聊天窗口中看见的文字更接近底层工作事实。

项目的重要原则是“模型可见,就必须有日志依据”。模型请求的历史由会话日志投影而来。这里的投影,指根据记录计算出某种用途需要的数据,例如模型历史或界面状态。

这个原则使恢复、分支、转录和诊断能够围绕同一批持久事实展开。它也约束插件开发者:新增模型可见输入时,需要考虑怎样让它被记录和重建。

5.2 恢复不等于外部世界回到过去

恢复会话可以让程序基于保存的记录继续处理,但外部文件、网络服务和正在运行的进程都可能已经变化。

例如昨天记录中有 README 的内容,今天文件可能已经更新。继续旧会话时,如果任务依赖当前状态,就应重新读取关键文件。历史能说明当时看见了什么,不能保证现在仍然相同。

同样,重放日志并不意味着自动重新执行所有命令。理解记录与执行的区别,可以避免把浏览历史误当作再次运行任务。

5.3 存储格式与升级

仓库将会话逻辑事件与具体持久化后端分开。JSONL 是一种一行一个 JSON 记录的文本形式,适合按记录处理;项目也有相应的持久化实现和格式迁移规则。

初学者不需要手动编辑底层会话文件。现有规则强调已提交的格式世代不能随意移动、覆盖或删除;读取和写入升级分别有明确流程。

如果未来你参与开发,凡是修改持久事件字段,都需要查看 docs/session-format-status.md 和持久化类型变更审查说明。普通界面文字调整与持久格式变更,维护责任相差很大。

5.4 上下文压缩

模型能接收的输入有长度限制。上下文压缩相关能力用于处理较长的任务历史,让后续请求在容量约束下继续工作。

压缩后的材料不等于原文逐字保留。对于文件路径、约束和待办事项等关键内容,后续工作仍需核对实际来源。你可以让智能体在继续修改前重新读取相关文件。

本章练习:把“日志记录了工具成功返回”和“文件此刻仍保持当时内容”写成两句话。前一句涉及历史事实,后一句需要检查当前文件。

第六章 插件之间怎样协作

6.1 Cordis 管理插件

Cordis 是这个工程使用的插件框架。它负责挂载插件、管理服务依赖、分发事件,以及在插件卸载时清理相应资源。

先认识上下文 ctx。它是插件访问共享服务的入口。比如 ctx.tools 指向工具服务,ctx.llm 指向模型服务,ctx.sessions 指向会话服务。阅读源码时遇到这些名称,可以先问“这里在使用哪一项服务”。

插件通过 inject 声明依赖。依赖服务就绪后,插件才可以启动。这样,插件间的关系由依赖表达,而不是靠猜测某个文件先执行。

6.2 服务调用与事件

服务调用适合直接请求一项能力,例如让文件系统读取某个路径。事件适合通知或介入一个过程,例如在工具执行前检查策略。

本工程区分持久的 Session 事件、带有实时 Agent 的 agent 事件,以及文件系统或工具等能力事件。不是每个实时通知都自动成为持久日志。

某些事件采用 waterfall,即环绕中间件语义。监听器接到 next 后,可以调用它继续下游,也可以在自己有决策权时直接返回。只是观察或补充信息的监听器若忘记调用 next,可能阻止后续处理。

作为读者,你暂时只需知道:事件名说明扩展的位置,分发模式决定监听器如何协作。真正编写时必须阅读事件的声明与文档。

6.3 可逆的注册

注册工具、监听事件或启动资源,都会对运行中的系统产生影响。工程要求这些注册作为可撤销的副作用管理,常见入口是 ctx.effect() 与 ctx.on()。

例如插件卸载后,它注册的工具不应继续留在列表里,监听器也不应继续处理消息。相关资源要在正确时机释放,尤其要保证使用者结束工作后再关闭它所依赖的资源。

这解释了为什么插件开发不只是“写一个函数”。你还需要考虑功能何时出现、何时消失,以及中途卸载时怎样收尾。

6.4 Service Definition、Provider 与 Consumer

项目用三个角色描述一项可替换能力。Service Definition 定义接口;Provider 实现接口;Consumer 使用接口。文档把这项完整的能力组合称为 capability seam。

以文件读取为例,定义说明怎样读取路径,提供方负责实际访问本地或远端文件,工具消费者把这种能力开放给模型。消费者依赖定义,而不应绑定某个具体实现。

这些角色不要求永远拆成三个包。是否拆包取决于它们是否需要独立演进。理解角色,比先记住所有包名更有帮助。

第七章 用一张目录地图认识仓库

7.1 先看顶层

根 README 是产品入口,package.json 说明包管理器、运行环境和脚本。AGENTS.md 是面向工程代理的协作约定;它会引用更详细的架构、测试和文档规则。

packages 保存按能力分组的工作区包,apps 保存应用相关工程。docs 保存架构、用户指南、子系统参考和开发流程。website 将仓库文档投影成文档网站,不是另一套可独立编辑的事实来源。

python 包含 Python SDK 与运行时相关内容,native 保存原生系统扩展源码。scripts 包含生成器和检查程序,snapshots 包含特定会话驱动测试的预期记录,benchmarks 关注性能。

vendor 是固定版本的第三方源码。它有自己的同步和修改政策,不适合当作初学者练手区。.agents 保存代理工作流、技能和工程决策资料;其中归档记录是历史材料,不能直接当作当前规则。

7.2 packages 的分组方式

包通常位于 packages/组名/包名。npm 名称一般是 @deepseek-ai/dsh-名称。文件夹、发布包名和 ctx 服务名不一定完全相同,查找时要区分。

可以先把常见分组按阅读问题关联起来:

你想弄懂的问题 先看的分组
任务为什么继续或结束 core
请求怎样发给模型 llm
如何读写文件与运行命令 fs、shell、subprocess
会话怎样保存、检索 session、session-query
怎样组装并启动应用 boot、bundle
界面怎样显示和交互 host、client、api
权限和用户询问怎样处理 interaction、sandbox、credentials
外部能力怎样接入 mcp、skill、web

这张表是定位入口,不是完整包目录。完整分组以 packages/README.zh.md 为准,每个组的 README 再列出直接所属包。

7.3 从一个功能向下读

假设你想研究文件读取,先看 packages/fs/README.zh.md,确认有哪些包,再看相关工具包和提供方的 README,最后定位源码。

假设你想研究任务循环,先看 docs/architecture.zh.md 的流程,再看 core 组和 agent-loop 的说明。先有流程概念,再读类型和方法,会更容易理解每段代码的目的。

这种阅读方式能限制范围。一个包引用另一个包时,只在它影响当前问题时继续追踪,不需要把整个依赖树一次读完。

7.4 怎样识别应用与界面两侧

Host 侧负责运行和服务能力,Client 侧负责浏览器中的界面体验,两者通过协议交换数据。文件操作通常发生在拥有文件访问能力的一侧,而不是因为浏览器显示了目录就直接由浏览器执行。

所以,远程使用时应先明确服务运行在哪台机器、工作区属于哪台机器。界面打开在你的电脑上,并不能单独决定工具执行位置。

本章练习:为“我看不到某次工具结果”找出两个可能入口。可以先读工具执行相关文档确认结果,再读 host/client 相关资料确认它怎样展示。

第八章 配置怎样组装出应用

8.1 Profile 是启动组合

Profile 是一套命名的应用组合。当前架构说明列出 web、headless、sdk、sdk-minimal 和 acp 等随项目提供的模板。

web 提供浏览器应用,headless 提供没有服务器的一次性执行方式,sdk 提供 SDK 服务入口,acp 提供自动化协议入口。sdk-minimal 是仓库明确维护的最小 SDK 组合,有自己完整的插件树。

支持的 Node 应用通过 dsh profile 启动。不要因为某个包中有入口文件,就把它当作另一个受支持的独立应用启动器。

8.2 Bundle 是可叠加的分发层

Bundle 包含配置条目以及它们挂载的代码。Profile 通过有序 bundle 叠加,构成基础插件树。

在一般组合中,dsh-base 提供共同能力,其他应用 bundle 增加 Web、headless 或 SDK 等入口。sdk-minimal 是明确的例外,它不应用 dsh-base。

这解释了为什么两种入口可以共享很多能力,又具有不同的启动和交互方式。你读配置时应先看 profile 叠了哪些层,再找最终生效的条目。

8.3 Patch 的覆盖规则

架构文档中的层次顺序是:profile 的有序 bundles、profile 自己的 cordis.patch.yml、home patch,最后是命令行指定的 overlays。后面的层可以覆盖前面。

一个重要细节是:按条目 id 覆盖时,会替换整份 config。初学者容易把它理解成“只改一个小字段,其余自动合并”,从而意外丢掉原配置中的内容。

因此,编辑某条已有覆盖项时,要保留仍然需要的其他配置。不要把不完整示例直接当成现有配置的全文替代。具体字段以插件配置目录和对应包 README 为准。

8.4 为什么本书不给一大段通用配置

配置依赖版本、已安装插件、服务名称和实际使用的提供商。一段看似完整的 YAML,如果与当前 profile 不匹配,反而增加排查难度。

对小白更合适的顺序是先使用界面配置模型,再通过插件 README 了解某一个条目。等你能解释它挂载什么、依赖什么、怎样撤销,再尝试手动修改。

本章自测:Profile、Bundle 和 Patch 各做什么?Profile 命名应用组合,Bundle 提供可叠加的配置与代码,Patch 修改最终组合中的条目。

第九章 工具、技能、MCP 与子智能体

9.1 工具是可执行能力的入口

模型通过工具名称和参数表达操作请求。框架需要查找工具、处理策略、执行实现,并把结果返回给后续步骤。

工具执行流水线包括执行前、执行中和执行后的扩展点。权限检查、策略与报告可以在这些位置协作。模型提出某个调用,并不等于该调用已经成功执行。

读结果时也要区分内容与状态。例如“文件不存在”是一次操作失败的结果;它不说明模型没有发出请求,也不说明会话整体必然结束。

9.2 技能提供任务方法

Skill 通常提供某类任务的专用说明与资源。模型加载后,可以按照该方法组织工作。技能加载与实际工具执行是两件事。

例如一项文档技能可以要求先确认读者,再核对来源,最后做格式检查;真正读取文件和生成文件,仍需要相应工具。

所以,技能不能替代访问权限,也不能因为写着“已验证”就使某次操作自动得到验证。你仍应关注本次任务实际执行了哪些检查。

9.3 MCP 接入外部工具

MCP 即 Model Context Protocol。本工程的 MCP 相关能力把外部服务器的工具接入到工具体系中。

这扩大了可用能力,也增加了外部依赖:服务器是否运行、认证是否有效、工具参数是否匹配,都可能影响任务。遇到问题时要分清是 dsh 本身、连接层,还是外部服务的故障。

安装第三方工具或插件前,应了解它能接触什么数据、向哪里发送请求,以及谁维护它。它的能力来自运行代码与配置,不能只凭名称判断。

9.4 子智能体与后台任务

子智能体用于委派相对独立的工作,例如分别阅读两个模块,再由主智能体整合。委派依赖相应提供方与工具,不是所有组合都具备完全相同的能力。

后台任务机制则关注任务运行与作业控制。把工作放到后台,并不意味着已经成功,也不意味着可以不收集结果。

对于读者,你可以重点检查三点:任务交代是否完整,子任务是否真的结束,最终结论是否核对过。多个智能体给出相同观点,也不能代替对源码的检查。

第十章 想改一点代码时怎么入手

10.1 先选一个小而完整的目标

第一次贡献可以从纠正一处有证据的说明、补充一个明确缺失的解释,或在维护者指引下修复局部问题开始。

如果涉及 packages 下的代码,仓库要求先读架构文档;涉及状态、生命周期、远程服务或界面组件时,还要读 defensive-patterns 对应说明。先理解约束,可以减少“局部能跑、组合后出错”的修改。

开始前看清工作区已有改动,避免覆盖他人的工作。修改后重新查看差异,确认只改了目标文件。

10.2 TypeScript 与接口

TypeScript 在 JavaScript 基础上增加类型信息。接口描述一项能力需要哪些输入和输出,编译器据此发现许多不匹配。

类型检查通过,不等于文件一定存在、网络一定成功或用户输入一定有效。仓库强调:在进程、文件、网络、模型工具 JSON 等外部输入位置验证数据;在已经受类型约束的同进程内部调用中,信任类型并保持职责清楚。

先读公开接口,再读提供方实现,最后看消费者如何使用,是理解能力模块的好顺序。

10.3 测试分工

单元测试关注局部行为,端到端测试关注真实连接过程,记录会话回放关注模型或用户可见的交互结果。不同证据回答不同问题。

仓库的真实 API 测试依赖密钥,缺少密钥时会跳过;跳过不代表真实连接已验证。无密钥记录回放也不能证明某家提供商此刻网络可用。

对一项小改动,应选择能覆盖实际变化的检查。不要因为某一个测试通过,就说整个工程已经全面通过。

10.4 构建与文档

源码是开发者编辑的内容,构建产物是应用运行和发布使用的内容。修改源码后,如果仍运行旧产物,看到的行为可能没有变化。

本仓库区分 build:artifacts 与 build:check。前者准备产物,后者检查依赖当前产物的测试和脚本;仓库要求在最新产物之后完成相应检查,才能声明开发工作完成。

代码修改还应同步影响到的说明与接口文档。若改变用户可感知的公开使用方式,还要遵循升级指南规则。这里介绍的是贡献流程,本书交付本身没有修改产品源码。

第十一章 遇到问题时怎样定位

11.1 先确认失败发生在哪一层

先问自己:命令有没有启动?浏览器有没有连上?模型有没有接到请求?工具有没有开始?文件有没有得到预期改变?这几个问题能把范围逐步缩小。

每次只验证一个假设。例如页面打不开时,先查看终端中的服务状态与地址,不要同时换模型、重写配置并重装依赖。多项变化叠加后,很难知道哪一步起作用。

下表是排查方向,不能代替实际错误信息:

现象 优先检查 判断依据
终端提示找不到命令 运行环境、包管理器和所在目录 命令是否实际可用
浏览器打不开页面 服务进程、终端打印地址、远程转发 是否存在可访问的服务
输入框不可用 是否选中工作区和有效模型 当前界面提示
模型请求失败 凭据、提供商、协议、模型 ID、网络 错误类别与响应信息
工具失败 参数、路径、权限、执行环境 工具返回的状态与内容
改了代码却没变化 是否重建、正在运行哪份产物 启动方式和产物来源

11.2 提问时给出最少但充分的信息

有效的问题报告包含:你想完成什么、使用什么启动方式、失败前的操作、具体错误、期望结果,以及与问题相关的版本和平台信息。

例如:“从源码启动 Web,选择工作区后发送阅读任务,模型请求返回认证错误。界面已保存密钥。我想确认实际使用的是哪个提供商。”这比“不能用了”更容易推进。

分享日志前,移除密钥、个人路径中的敏感部分和私有文件内容,但保留错误类别与关键结构。不要为了脱敏把所有诊断线索都删掉。

11.3 不确定是否写入成功时

如果命令在写入过程中断,先检查目标文件、版本差异和工具结果,再决定是否重试。网络断开、页面刷新或最终回答缺失,都不能单独说明写入未发生。

同样,智能体说“已完成”之后,也应检查目标文件是否存在、内容是否符合任务。执行事实、文件结果和最终说明要相互对应。

11.4 何时回到权威资料

界面操作先看用户指南;插件字段先看配置目录和包 README;循环与会话语义先看架构与子系统文档;开发规则先看 AGENTS.md 及其引用。

当资料与实际行为冲突时,保留复现步骤并继续核对当前源码。版本较新的页面或听起来熟悉的解释,都不能代替当前环境的证据。

第十二章 给小白的学习路线

12.1 第一阶段:能使用并判断结果

目标是完成一次项目阅读任务,并能打开引用文件核对结论。你只需要理解模型、工具、工作区和会话四个词。

建议读根 README、Web UI 使用指南和安全说明。实践后用自己的话写一段“我发了什么,它读取了什么,最后生成了什么”。

达成标准是:你能指出答案中的两项事实分别来自哪个文件,也能说明一次失败发生在模型层还是工具层。

12.2 第二阶段:能读懂运行过程

目标是解释一次输入为什么触发多次模型请求。阅读架构中的 turn flow,再对照一次真实任务的工具过程。

此时再学 step、turn、Session 和投影。无需先记完整事件列表,先理解它们记录或组织什么。

达成标准是:你能画出输入、模型、工具结果和下一步请求之间的顺序,并能解释历史记录为什么不保证当前文件状态。

12.3 第三阶段:能定位一个功能

选一个感兴趣的能力,比如文件读取或模型接入。按组 README、包 README、接口、实现、测试的顺序阅读。

把生词记成“词语、自己的解释、来源”三列。每次只深入解决当前问题需要的内容。

达成标准是:你能指出这个能力的定义、提供方与消费者,并说明最终是哪个 profile 或 bundle 把它装起来。

12.4 第四阶段:完成一个受控的小改动

修改前先写出你要改变的可观察结果;修改后用适当检查证明它。记录实际运行过的命令和结果。

若暂时不会写测试,可以先阅读相邻测试如何构造输入、观察输出,再请人帮助选择覆盖范围。不要把复制一段断言当作理解行为。

学到这里,你已经具备继续阅读工程的基础。下一步取决于兴趣:做产品功能就深入 client 与 host,做模型接入就深入 llm,做工具能力就深入接口与提供方,做自动化集成就看 SDK 指南。

附录 A 术语速查

术语 本书中的含义
Agent / 智能体 围绕任务组织模型与工具调用的运行实体
Harness 组织智能体运行、能力、记录和交互的框架
LLM 大语言模型
Provider 实现某项能力的提供方,具体含义取决于上下文
Plugin / 插件 由框架管理并贡献服务、事件或功能的代码
Cordis 本工程使用的插件框架
Context / ctx 插件访问服务和注册能力的上下文
Session 保存持续交互事实的会话
Event / 事件 表达发生事实或运行扩展点的数据与通知
Projection / 投影 根据记录计算出特定用途的数据
Step 一次模型请求及其调用的工具
Turn 一轮处理,可能包含零个或多个 step
Tool / 工具 模型可请求执行的能力入口
Skill / 技能 某类任务的方法说明与相关资源
MCP 外部工具服务器的接入协议
Profile 命名的应用组合
Bundle 包含配置条目及其代码的可叠加分发层
Patch / Overlay 对应用组合条目施加的配置修改层
Workspace 用户选定的项目工作位置
SDK 供其他程序使用的开发工具包
JSON-RPC 用 JSON 消息表达远程调用的协议形式
ESM 本工程采用的 JavaScript 模块系统
HMR 热更新相关机制,具体范围由配置决定
Disposer 释放注册或资源的清理函数
Snapshot 记录并比较特定预期结果的测试材料

附录 B 继续阅读的资料

以下路径均相对于当前仓库根目录。电子阅读器中打开网页链接需要联网;本书正文可以离线阅读。在线文档可能随项目更新,与本书依据的工作区存在差异。

产品入门

  • README.zh.md:运行入口、源码启动和项目定位。
  • SAFETY.zh.md:实际能力、实验性状态与安全限制。
  • docs/user/guide/index.zh.md:工作区、模型配置与首次任务。
  • docs/user/guide/providers.zh.md:提供商、凭据、模型与协议配置。
  • 在线文档:DeepSeek Harness 文档站。

工程导读

  • docs/architecture.zh.md:组合方式、任务循环、会话与扩展位置。
  • docs/cordis-primer.zh.md:上下文、依赖、事件和资源清理。
  • packages/README.zh.md:完整能力分组入口。
  • docs/development.zh.md:开发环境、构建和日常流程。
  • docs/testing.md:测试分工和证据要求。

进阶定位

  • docs/subsystems/:按子系统查找语义与类型参考。
  • docs/cookbook/:按目标查找扩展与贡献步骤。
  • docs/config-catalog.md:由源码生成的配置字段参考。
  • docs/session-format-status.md:当前会话格式状态。
  • python/README.zh.md:Python SDK 与运行时入口。
  • apps/cli/README.zh.md:其他命令行使用方式。
  • 项目源码:GitHub 仓库。

附录 C 阅读后的自检

试着不翻前文回答以下问题,再回到相关章节核对。

  1. 模型为什么不能仅凭一个路径就知道文件内容?
  2. 一个用户问题为什么可能触发多个 step?
  3. 工具被请求、工具执行成功、任务最终完成有何区别?
  4. 恢复会话为什么还可能需要重新读取文件?
  5. Profile、Bundle 与 Patch 怎样共同决定启动组合?
  6. 编辑覆盖项时为什么要留意整份 config?
  7. 哪个目录能帮助你找到文件系统能力的包?
  8. 写入操作结果未知时,为什么应先检查再重试?

参考要点:模型只能使用请求中提供的材料;工具结果往往需要后续模型处理;调用意图、执行结果和任务验收属于不同事实;外部世界会变化;profile 选取有序 bundle,patch 再修改条目;覆盖会替换完整配置;文件能力从 packages/fs 开始查找;重复写入可能叠加已经发生的操作。