deepseek-harness 学习手册
子系统巡礼M5 · 图鉴
每页 5 min

图鉴

subagent 家族、Ralph、goal / plan / preset、skill、沙箱与信任、credentials 分层。

最后核对于 v0.1.1-rc.2 · commit b150a551b8 · 2026-08-22

图鉴式的一页:每个子系统统一四问——这是什么 → 解决什么问题 → seam 怎么切 → 源码坐标。目的是让你在需要的时候查得到,而不是逼你现在读完。

读法建议:先扫「seam 怎么切」那一栏。看一个项目怎么切它的能力边界,比看它实现了什么功能,信息量大得多。

5.1 subagent 家族:把别人家的 agent 当子进程

11 个包,是全仓第三大的分组。ctx.subagents 上可以同时挂多个具名 provider——它们的差异之大,正是这个 seam 的价值所在:

Provider子代理是什么
subagent-spawn-in-process进程内起一个全新的孩子
subagent-fork-in-process进程内从父代理已完成的历史分叉一个孩子
subagent-acp通过 ACP 起一个进程外的孩子
subagent-dsh-sdk通过 TypeScript SDK 起一个进程外的 harness 孩子
subagent-codex起一个真实的 Codex app-server 孩子
subagent-claude-code通过官方 Claude Agent SDK 起一个真实的 Claude Code 孩子

「进程内新起一个孩子」和「驱动一个别家产品的完整 agent」,在这个接口后面是同一件事。architecture.md 拿这个当 seam 价值的例证:「subagent provider 的差异之大不亚于此,从一个全新的子代理,到另一个产品里的一次被委派的 turn。」

依赖闭包上的诚实

Codex 和 Claude Code 这两个包是独立的可选 bundle,不进 dsh-base。所以 @deepseek-ai/dsh 的默认生产依赖闭包里,既没有这两个 provider,也没有 Claude Agent SDK,也没有 Codex 的 wrapper 和平台负载。装了才有,删了就只撤走它自己那一份私有运行时闭包。

5.2 Ralph:一个「没有改 loop」的编排策略

ralph({ objective, maxRounds? }) 给一串全新的子代理一个不可变的目标,一轮一轮跑下去。每一轮起一个新孩子,孩子拿到的只有:不可变目标、当前轮次与上限、一句「共享工作区才是权威」的指令,以及上一轮的结构化交接报告。父对话和之前孩子的会话都不会被种进去。

这一页真正要讲的不是 Ralph,是它的定位

README 第一段就把话说死了:它「以一个普通插件的形式,在 ctx.workflowEnginectx.subagents 之上演示了一种专门化的编排策略agent-loop 里没有加任何 Ralph 模式或 fresh-agent 循环,同会话的 goal 域也保持独立。」

这是对 M2 那条断言的又一次检验——一个足够特殊的编排策略,能不能不碰主循环就实现出来?能,那这个架构就是真的。

几个设计细节值得单看:

5.3 goal / plan / preset:三种「状态」,三种切法

4 个包 · ctx.goals goal —— 事件溯源的目标状态 附在已有 session 上的一个持久完成目标,有带修订号的 active / paused / blocked / complete 相位和一个轮次上限。它是状态,不是调度器,也不是另一段对话;session log 仍然是它的真相来源。

最有意思的是 goal activation:这个「允许再跑一轮」的许可是进程本地的,刻意不进持久重放——所以 resume 和 fork 之后,必须有一次人授权的恢复动作,才会重新开始自动工作。
packages/goal/
1 个包 · ctx.planMode plan —— 被记录下来的协作状态 「plan 模式是被记录的、按 agent 的协作状态,而不是一个通用的模式注册表或能力 seam。」

这句话本身就是一次克制的示范:并不是所有东西都该做成 seam。决策记录的分类是 simplification——它是把一个更泛化的设计收窄回来的结果。
packages/plan/plan-mode/
2 个包 · ctx.agentPresets preset —— 按 session 组合 agent 一个 preset 就是一个装着 agent.cordis.yml 的目录。把它挂在某个 agent 的 scope context 下,这个 session 就有了自己的工具和 prompt section,而其它活着的 session 各保各的——一个进程里可以同时跑几个组合完全不同的 agent。

护栏也写死了:一个 preset 如果指名了一行会发布进程全局服务的插件,会在挂载时被拒绝,而不是允许它和下一个 session 撞车。
packages/preset/
4 个包 · ctx.skills skill —— provider 中立的技能目录 定义(skill)+ 本地文件系统发现(skill-filesystem)+ 内置徽章技能(skill-badge)+ 面向模型的目录与加载器(tool-skill)。

README 里点了它的定位:「这个能力留在核心控制主干之外,可以用本地、内嵌或远程的 provider,而不改变面向模型的契约。」——同样的技能概念,Claude Code 是产品内建,这里是一个可换后端的 seam。
packages/skill/

5.4 沙箱与信任:一条画得很清楚的线

这里最值得学的是分层的方式

5.5 credentials:配置里放引用,不放密钥

三个包,切法很干净:credentials(引用与记录的 seam)、credentials-local(环境变量与本地文件 provider)、authorization(那些必须问人才能拿到凭据的流程,比如 OAuth)。

核心规则一句话:「配置携带的是引用,不是密钥值。」consumer 在自己的操作边界上解析这些引用。而两个 seam 的关系被规定得很死——「一个 authorization 流程写出一条凭据记录,并以它为键;这两个 seam 在记录这里相遇,别处不相遇。」

层次上,托管凭据住在 $DSH_HOME/.credentials.yaml;留在 .env 里的凭据仍然是一个优先级更低的兜底。这个「不强制迁移、但明确排序」的处理方式,比一刀切要现实得多。

5.6 还有一批,按需查

13 个包 session —— 持久化 / 投影 / 标题 / 遥测 全仓第二大分组。JSONL 和 SQLite 两个持久化后端、投影与投影缓存、三种生成 session 标题的策略、OTel 遥测。全部派生自同一条日志流(见 M3.2)。 packages/session/
10 个包 shell —— seam 的教科书样本 glossary 直接拿它当 seam 的标准例子:dsh-shell 是 Service Definition,bash-local / bash-sandbox 是 provider,tool-bash 是 Consumer;pwsh 一侧完整对称。base bundle 按平台用 disabled: !!js process.platform === 'win32' 各挂一套。 packages/shell/
7 个包 fs —— 文件能力 + 观测策略 定义、本地实现、沙箱围栏、观测策略,加三个面向模型的工具(tool-fstool-fs-searchtool-str-replace-editor)。fs-observation-policy 是个容易被忽略的角色:它让「模型有没有真的读过这个文件」变成可查询的事实。 packages/fs/
3 个包 hooks —— 别人家扩展模型的方言适配 共享协议库 + Claude Code / Codex 两个桥接。定位写得很清楚:规范的扩展面是 harness 自己的类型化拦截点,桥接只是把外部 shell hook 协议翻译过来 packages/hooks/
目录

本页

M5 · 图鉴