Task 3 完整版:拆解 DSH 的"一切皆插件"原则(上)
作业 A:Host Plane / Agent Plane 分层图

这张图可以当成餐厅的平面图来看,一共两层楼:
- 一楼后厨(Host Plane,宿主层) :全店只有一套------炉子、冰柜、仓库、采购渠道、保安。在 DSH 里就是:工具登记簿(Tool Registry)、总机(Model Route,决定请求发给哪家 AI)、存档室(Persistence,会话记录落盘)、保安系统(沙箱+审批)、传菜调度(Subagent Registry)等等。行话叫 process-singleton:这套东西的寿命 = 整个进程,所有会话共用。
- 楼上包间(Agent Plane,会话层) :每开一个新会话,就像开一个新包间。服务员把某份菜单(Preset)送进包间------这叫"挂载",而且在建包间那一刻就定死了,中途不换菜单。
图里还标了 Standard 包间的10 项家当 。作业要求至少标 8 项,我标满了 10 项。
每行都回答三个问题:这道菜归哪桌(作用域)?谁提供的(后厨还是菜单自带)?客人能不能点(模型看不看得见)?
| # | 组合树项(专业名) | 名字 | 解释 | 源码插件 | 归宿(生命周期归属) |
|---|---|---|---|---|---|
| 1 | Persona / Instructions | 角色与开场白 | 服务员的人设和口头禅 | dsh-persona · dsh-agent-instructions | 跟随 Agent/Preset(跟着菜单走) |
| 2 | Filesystem | 文件柜 | 让 AI 能读写文件 | tool-fs · tool-fs-search | 工具随 Preset;执行落 Host filesystem service + policy(菜单给"能点"资格,真正开柜子的是后厨) |
| 3 | Shell | 终端 | 让 AI 能敲命令行 | tool-pwsh(win32)· tool-bash(非 win32) | 执行落 Host subprocess executor + 沙箱(命令在后厨沙箱里跑) |
| 4 | Search / Web | 联网搜索 | 让 AI 能上网查资料 | tool-web | 执行落 Host web service/provider |
| 5 | Skills | 技能册 | 开场先念一遍"本店有这些特色菜" | skill-filesystem · tool-skill | Catalog 注入会话(册子内容送进包间) |
| 6 | Plan / Goal | 计划本 | AI 做事前列的计划 | planning group · tool-goal · command-goal | isolate: planMode(只归本桌,3 号绝不让 5 号看见) |
| 7 | Context / Compaction | 聊天摘要 | 聊太长时的压缩存档 | compaction-basic · command-compact · tool-result-pruner | isolate: compaction + toolResultPruner(只归本桌) |
| 8 | Subagent | 小弟系统 | AI 派子任务给别的 AI | tool-subagent (spawn/fork) · tool-subagent-control | isolate: workflowEngine;执行落 Host Subagent Registry(调度归本桌,小弟在后厨统一登记) |
| 9 | Workflow | 工作流 | 固定流程自动化 | workflow-ptc · tool-workflow(tool-ralph 禁用) | delegation group 内(只归本桌) |
| 10 | Interaction | 互动按钮 | 问用户、列待办、展示结果 | tool-ask-user · tool-todo · tool-present | 跟随 Agent/Preset(跟着菜单走) |
四类归属(表 3-1):1、5 = 角色与提示词(Skills 兼工具);2、3、4、9、10 = 面向模型的工具 + Host 能力适配;6、7 = 会话级状态;8 = 工具 + 状态 + Host 适配混合。
同一个东西,"能不能被点到"和"在哪儿被做出来"是两回事------菜单决定客人看得见什么,后厨决定活儿在哪儿干、怎么被管着。
作业 B:改 Agent Plane 还是改 Host Plane?------两类需求,两种改法
一、书上怎么分
| 需求 | 改哪儿 | 怎么说 |
|---|---|---|
| "这桌别上写文件这道菜" | 菜单层(Agent Plane) | 把菜从这桌菜单上撤了就行,后厨连锅都没动 |
| "所有菜必须换地方做------全搬去中央厨房" | 后厨层(Host Plane) | 这是动后厨本身,跟哪桌点什么无关 |
判别法压缩成一句话:改"客人看到的菜单"→ 菜单层;改"这活儿到底谁干、在哪儿干、被谁管着"→ 后厨层。
二、我自己的四个例子
以下例子都在本机上跑过: DSH v0.2.0-rc.2 环境(Windows)。
例 1(Agent Plane):「让模型少看到工具、换种方式用工具」
需求 :把模型可见的 26 个原生工具压缩成 1 个 run_code,让模型写 TypeScript 编排调用。
判别:这是"模型看到什么、以什么方式看到"------典型的 Agent Plane。底层 Tool Registry 一个都没动。
证据(源码 diff):直接 diff npx 缓存里两份 Preset 声明:
text
standard.patch.yml vs ptc.patch.yml
1 file changed, 10 insertions(+), 4 deletions(-)
其中 4 处改动是 id/order 元数据;实质性差异只有两处:
- 新增一个插件块(工具呈现方式):
yaml
- id: tool-presentation
name: '@deepseek-ai/dsh-agent-tool-presentation'
config:
mode: ptc
- 给
workflow-ptc和tool-workflow两个插件加了disabled: true。
这就是教材自测题 3 的答案实证:PTC 与 Standard 的源码差异小到只有一段呈现配置 ;改变的是"当前 Agent 如何看到和使用其中的工具",不是重新立了一套工具系统。(注:教材写的是 mode: code,本机 rc.2 版本实际是 mode: ptc------教材自己也提醒了"以当前版本实际声明为准"。)
运行时证据:Task 2 实验 1 的 request/header 取证------同一进程下 standard 会话 26 个 model-facing 工具,ptc 会话只有 1 个(run_code)。Tool Registry 是同一个,呈现截然不同。
例 2(Agent Plane):「让某个会话"看不见"某项能力」
需求:一个会话里根本不出现技能(skills)系统。
判别 :还是"模型看到什么"------把 skill-filesystem / tool-skill 插件从该会话的 Preset 组合里去掉即可,磁盘上的技能文件不用动。
证据(本次实验 task3-exp1,详见第四节) :我在工作区写了一个探针技能 task3-scope-probe,然后三个新会话问同一个问题:
- Standard 会话:技能目录注入里有探针 → 正确列出
- PTC 会话:技能目录注入与 Standard 逐字相同 (1099 字符)→ 正确列出(注意:PTC 的 model-facing 工具只有 run_code,但技能目录仍以
<available_skills>注入------"有哪些能力"和"以什么方式用"又一次分离) - Minimal 会话:没有 skill-filesystem 插件 → 技能目录根本不存在,回复"当前没有可用的技能。"
文件躺在共享磁盘上,能力却没挂载------教材 3.2.1 那句"不能只根据文件或目录的位置来判断",被这个探针实锤了。
例 3(Host Plane):「所有会话的模型请求都换一个端点」
需求 :DSH 对 DeepSeek API 的请求从 /v1 切到 /anthropic。
判别 :Model Route(模型路由)是 Host Plane 的 process-singleton 能力(表 3-1"Host 能力适配项:Host 提供,Agent 消费")。改它影响的是这个进程里的所有 Session,跟任何单个会话的能力组合无关。
证据(Task 2 的 404 排障实录) :用户级环境变量 DEEPSEEK_BASE_URL=https://api.deepseek.com/v1 会让所有会话的请求拼出 /v1/messages → 404;在启动脚本里进程内覆盖为 /anthropic 后,之后新建的所有会话一次性修复。如果这是 Agent Plane 的事,我得给每个会话分别改------显然不是。这正是"改 Host 层一处,全部会话生效"的体验。
例 4(Host Plane):「工作区写入被 ACL 拒绝------修的是沙箱,不是会话」
需求:模型在 DSH 沙箱里写工作区文件报访问拒绝。
判别 :沙箱策略(sandbox-policy / windows-acl)在 Host Plane。Task 2 实验 2 的标准模式首轮就踩到了:目录 ACL 缺 WRITE_OWNER,模型用预置技能 diagnose-windows-sandbox-acl 修复后,后续所有会话在这个工作区都不再遇到该问题。修的是执行边界本身(Host),不是某个会话的工具清单(Agent)。
两个例子放一起看
| 例 1/2(Agent Plane) | 例 3/4(Host Plane) | |
|---|---|---|
| 改什么 | 会话挂载哪些插件、以何种呈现 | Provider / Route / 沙箱策略 |
| 影响面 | 仅当前会话(其他会话不受干扰) | 进程内所有会话 |
| 需要重启会话吗 | 新建会话挂新 Preset 即可 | 通常动环境/配置/基础设施 |
| 反过来想 | 就算 Session A 加了 26 个工具,Session B 的清单纹丝不动 | 就算 Minimal 把工具压到 1 个,Host 沙箱照样管着它 |
三、Scope 三问 × 源码里的 isolate 声明
教材 3.2.2 说动态组合必须同时回答三个问题:这项能力属于谁?谁可以看到和使用它?它应该跟随谁的生命周期一起创建和销毁?
这不是抽象说教------standard.patch.yml 里直接写着答案(isolate 字段就是作用域声明):
yaml
planning group: isolate: { planMode: true } # plan 状态只属于本会话
compaction group: isolate: { compaction: true,
toolResultPruner: true } # 压缩状态按会话隔离
delegation group: isolate: { workflowEngine: true } # 工作流引擎按会话隔离
minimal.patch.yml 里同样有:
yaml
persistent-shell group: isolate: { terminals: true } # 持久终端按会话隔离
如果反过来,把本应属于当前会话的服务错误注册到全局,就会发生教材说的:变成所有 Session 共享、还可能与其他会话的同名服务冲突------即自测题 2 的场景「会话 A 新增的 Tool 影响到会话 B」,问题出在注册的作用域错了(把 Agent Plane 的会话级能力注册到了 Host/全局层),而不是"分层"本身出了问题。
四、实验记录:task3-exp1「同一份技能文件,谁把它挂载为能力?」
实验设计(控制变量):
- 同一个 DSH 进程,新建三个会话:Standard / Minimal / PTC
- 同一问题逐字发送:「请列出你当前可用的全部技能(skills):只列出技能名称和一句话用途,不要调用或执行任何技能,也不要做其他事情。」
数据:
| 指标 | t3-standard | t3-minimal | t3-ptc |
|---|---|---|---|
| model-facing 工具数 | 26 | 1(pwsh) | 1(run_code) |
| 系统提示词(system 消息) | 约 6KB(JSON 6178B) | 46 字符 | 29799B(JSON) |
<available_skills> 注入 |
有(1099 字符,含探针) | 无 | 有(与 Standard 逐字相同,含探针) |
| 探针名在轨迹中出现 | 2 次(注入 + 回复) | 0 次 | 2 次(注入 + 回复) |
| 工具调用 | 0 | 0 | 0 |
| 回复 | 列出 2 技能含探针 | "当前没有可用的技能。" | 列出 2 技能含探针 |
三个发现:
- 能力可见性 ≠ 文件可见性。探针文件在共享工作区磁盘上三会话都能"读到"(Host 文件系统),但只有挂载了 skill-filesystem 插件的会话(standard、ptc)把它变成"能力"(目录注入 + skill 工具);minimal 连目录注入都没有。
- PTC 与 Standard 共享同一个 Host 技能 Provider:两者注入逐字相同(1099 字符)------同一批能力,不同呈现,教材 3.4 的现场版。
- 工具清单按会话冻结:三个会话并存于同一进程,request/header 各自记了 26/1/1 份工具清单,互不影响------图 3-1(一个 Host、多 Session、各自 Preset)的现场版。
会话并排(侧边栏三个新会话):

Standard 会话回复(列出探针技能):

Minimal 会话回复(无技能目录):

PTC 会话回复(run_code 呈现,技能目录照常注入):

五、一句话收束
DSH 的分层不是把代码切成两摞,而是把两类问题分开:"系统提供什么、由谁执行"收进 Host Plane 统一治理;"这个会话看到什么、怎么组合"留给 Agent Plane 按需挂载。 改需求之前先问一句:我改的是"模型看到的菜单",还是"后厨本身"?
作业 C:学习目标与自测作答
第一部分:学习目标 5 条逐条作答
目标 1:说清 4 套 Preset 不是 4 个独立 Agent,而是同一 Runtime 中的 4 种能力组合。
如果是 4 个独立机器人,就得有 4 个后厨、4 本登记簿,互相还看不见。实测恰恰相反:三个模式不同的会话同住一个店,点菜单 26/1/1 互不干扰------共享后厨、各点各菜,只有"一个 Runtime + 4 种组合"能解释。
目标 2:准确区分 4 个核心概念:Preset、Scope、Host Plane、Agent Plane。
- Preset = 菜单:这份声明决定了这桌挂哪些菜、怎么摆盘;
- Scope = 这道菜归哪桌:谁能点、跟哪桌一起撤;
- Host Plane = 后厨:全店一套的家当,寿命=整个店;
- Agent Plane = 这桌的菜和摆盘 :跟着包间生、跟着包间死。
关系链一句话:菜单经"归哪桌"挂进包间;后厨提供,包间消费。
目标 3:解释 Scope Context 的意义:会话级能力从哪里来,为什么动态组合必须有边界。
菜单可以按桌换,但菜单带进来的东西不能自动变成全店公物。否则 A 桌的私房菜上了大菜单,B 桌也能点(串味),甚至两家私房菜重名打起来(冲突)。后厨墙上那些"只跟本桌走"的告示,就是边界的落地。
目标 4:分析 Standard 的能力基线:每项能力属于哪个作用域、由谁提供、向谁开放。
别数有几道菜,要一道一道问:归哪桌?谁提供?谁能点?------就是作业 A 那 10 行表干的事。
目标 5:对比 PTC 的"同一批能力,不同呈现方式"与 Minimal 的"把 Harness 压薄"。
- PTC 改"怎么吃":26 道菜换成 1 个火锅,菜没少、吃法变了。省不省看任务:适合一锅炖的活儿才赚,不适合反而更费(实测 7 万 vs 4.8 万 token)。
- Minimal 改"给多少" :只上一道硬菜,须知压到 46 字------它是对照组 ,把干扰降到最低看模型"裸考"能考几分。但保安照常值班:后厨的规矩一条没少。
- 合起来:能力、吃法、给多少,是三个独立的旋钮。
第二部分:自测 3 题作答
Q1:DSH 的分层避免了哪两种极端?各自代价是什么?
极端一:全店共吃一盘菜(一切放全局)------A 桌加的菜 B 桌也有,串味、乱套,还可能重名打架。极端二:每桌自建一个后厨------重复建设,成本爆炸,没人管得住。DSH 选中间:后厨共享,菜单各点。
Q2:"会话 A 新增的 Tool 影响到会话 B"------问题出在哪一层?
怪传菜员把包间专属菜错登到了全店大菜单上------挂错了作用域,不是"分后厨和包间"这个设计错了。修法:把这道菜重新登回 3 号包间自己的小菜单,可见期和下架时间都跟着 3 号走。
Q3:PTC 与 Standard 在源码层面的差异有多小?改变的是什么?
经实测逐行比对:10 行增、4 行删 ,4 处是排版;实质差异 = 1 段"吃法说明"(火锅模式)+ 2 个停售标记。改的是菜怎么端上来 ,不是菜是什么------后厨登记簿同一本,Standard 服务员拿 26 张单,PTC 服务员拿 1 张火锅单,而两桌念的特色菜册一字不差(1099 字)。