Appearance
4.3 Agent Preset 与作用域组合
本章概览:如何让"不同会话拥有不同能力集合"——agent preset 的组合机制、
dsh-scope的作用域分层(agent → preset → global),以及子代理如何继承父组合。
preset 是什么
一个 preset 是存放一份 agent.cordis.yml 的目录。roster(ctx.agentPresets)在进程内以 standing scope 挂载一次;每个选择该 preset 的会话,其 agent 作用域 key 通过 dsh-scope 的父链父级化到该挂载上。
要点(详见 1.3 配置树):
- 发现即读盘:
list()/resolve()每次重读根目录; - 健康检查:组合缺失/不可加载的目录以
broken原因列出; - 默认 id:
ctx.agentPresets.defaultId,未命名时挂载的 preset。
出处:
packages/preset/agent-presets/README.md(deepseek-harness-src/packages/preset/agent-presets/README.md)
dsh-scope:作用域分层
dsh-scope(core/scope)是按 agent 划分作用域的注册原语。注册落进"调用上下文作用域所在的层":
- 宿主/仓库插件行 → global 层;
- preset standing 组合挂载的插件 → preset 层;
- 通过
agent.ctx的注册 → agent 层。
读取合并"global 层 + 查看者作用域的链",解析顺序 agent → preset → global(最近者遮蔽最远者,同层重名才按 rank 决定)。ctx.tools、ctx.systemPrompt、ctx.skills 等都按此形状分层。
出处:
packages/core/scope/README.md与packages/core/tools/README.md
mount 与 recompose
ctx.agentPresets.mount(agentCtx, id?):组合一个 agent——确保 standing 挂载(single-flight),并把 agent 的作用域 key 父级化到它,返回 preset 供调用方记录。唯一支持的调用点是 agent 工厂的setup(agentCtx)钩子:只有在那里,agent 尚未发布,被拒绝的组合会把整个创建回滚,而不是留下半组合的会话;ctx.agentPresets.recompose(agentCtx, id):把 agent 重链到另一个 preset 的 standing 组合。仅在 agent 未产生任何内容时有效(调用方负责检查);新挂载先确保成功再移链,失败保持原状。因为两个组合会向同一层注册同名工具,不能共存——recompose 先卸载旧子树再挂载新子树;- 组合代次:每个代次记录组合文件的 stamp(mtime + size);文件变更使下一会话起新代次,已加入的会话继续用自己运行的那代——运行中会话的组合在其文件变化/消失后仍然存活。
子代理继承:composeFrom 而非 mount
子代理通过 ctx.agentPresets.composeFrom(agentCtx, parentCtx) 绑定父代理的 standing 组合,绝不重新 mount:
- 所有模型可见行都在 agent 平面上——工具注册表的 global 层是空的,子代理若不绑定,将带着空工具注册表(且没有父代理的任何提示片段)到达模型;
- 重挂载父 preset 会产生两个问题:文件编辑后会给孩子不同代次;preset 被删除后孩子直接失败。绑定是同步的,这是进程内 subagent 驱动能在同步创建窗口内组合孩子的原因;
- 孩子把加入的 preset id 记录在自己的持久 header 上——冷读孩子历史时重建它实际运行过的组合,而非部署默认。
出处:
packages/preset/agent-presets/README.md(deepseek-harness-src/packages/preset/agent-presets/README.md)
会话运行哪个 preset
- 创建 header 记录会话开始时的 preset;
resolveSessionPreset(session)得出它运行的 preset——两者在空白会话切换后不同,所以恢复、fork、picker 摘要都走解析而非读 header; - 切换是会话事件:
agent-preset/selected在交换提交后追加——preset 决定模型看到的工具 schema 与提示片段,必须可从日志重建(模型可见 ⟺ 已记录); - 该提交事实还会以非作用域的 cordis 事件
agent-preset/selected(sessionId, agentPreset)(client-safe./types导出)重发,允许远程消费方使会话派生状态失效。
关键设计决策
| 决策 | 理由 |
|---|---|
| standing mount 一次、多会话共享 | 工具的挂载与提示片段只存在一份,会话以 Session/Agent 为 key 保持隔离 |
| 子代理绑定(composeFrom)而非挂载 | 同步、无组合失败模式、孩子与父运行同一代次 |
| 文件 stamp 决定代次 | 文件是唯一组合编辑器;stamp 把编辑带到后续会话 |
| 切换仅限空白会话 | 已有内容的会话重组合会重放历史到新工具集无法行动的局面 |
小结
preset + scope 分层让"能力集合"成为会话级变量:进程级组合包定产品形态,preset 定会话能力,agent.ctx 定个体差异,子代理继承父组合。这是 DSH 实现"子代理能力与父一致"的关键机制。
参考资料
- packages/preset/agent-presets/README.md — preset 组合(
deepseek-harness-src/packages/preset/agent-presets/README.md) - packages/core/scope/README.md — 作用域原语(
deepseek-harness-src/packages/core/scope/README.md) - docs/subsystems/scope.md — 作用域子系统页(
deepseek-harness-src/docs/subsystems/scope.md) - agent-scope-contexts Agent Note — 作用域安全非目标