Preset Composition
不同会话可以拥有不同能力的 Agent。
每会话一份 cordis.yml 风格的组合清单。
问题
到目前为止,所有 Agent 共用同一套工具和提示词。但现实需求千差万别:
- 代码 Agent 要 bash、文件、LSP;写作 Agent 要 web 搜索、文档工具;
- 每个会话同时运行,互不干扰——一个进程里既有一个"代码助手"又有一个"文档助手";
- 不同 Agent 甚至要有不同的身份(persona)。
Agent 的组合应该按会话可配置——每个会话声明自己要哪些工具、哪些提示词区块,harness 只给这个会话挂载这些。
解决方案
每会话一个组合清单(预设),挂载进该 Agent 的作用域:
// agent.cordis.yml —— 一个预设 = 一个目录 + 一份组合清单
// - id: tools:code name: '@dsh/tool-bash'
// - id: tools:fs name: '@dsh/tool-fs'
// - id: persona name: '@dsh/persona' config: { text: "你是代码助手" }
const presets = new Map(); // sessionId -> 该会话要挂载的插件清单
async function mountPreset(agent, presetDir) {
const rows = await loadPresetYaml(presetDir); // 读 agent.cordis.yml
for (const row of rows) {
// 在 agent.ctx 作用域注册——只对这个 Agent 可见
await mountRow(agent.ctx, row);
}
}
工作原理
第 1 步:作用域是核心——每个 Agent 有独立的 agent.ctx,在其中注册的工具/区块只属于它,同名工具遮蔽全局。
第 2 步:预设目录结构。一个预设 = 一个目录,内含 agent.cordis.yml(组合清单)。harness 启动 Agent 时按会话解析预设并挂载:
// 会话 A → 预设 code-agent:bash + fs + 代码 persona
// 会话 B → 预设 writer-agent:web + 文档 persona
// 同一个进程,两个 Agent,能力互不重叠
第 3 步:守卫——预设不得发布进程全局服务(那会撞车)。组合规则:跨会话设施是进程单例,留在宿主组合里;预设只携带"一个 Agent 贡献给注册表的东西"。
试一下
运行 s18:
- 定义两个预设(一个带 bash,一个不带),开两个会话各用其一
- 在带 bash 的会话问"列出文件",在不带 bash 的会话问同样问题——观察工具差异
- 给两个会话配不同的 persona,确认系统提示词各自生效
观察重点:作用域遮蔽的粒度——同名工具在不同会话可以有不同的实现吗?提示词区块呢?
以下内容基于 packages/preset 的核查。DSH 的预设族:
| 包 | 角色 |
|---|---|
agent-presets |
预设词汇、文件系统发现(可信根 + 用户根)、受守卫的按 Agent 挂载(ctx.agentPresets) |
persona |
人格作为一个可组合行——预设不只换工具,还换身份 |
一、预设的组成
每个预设是一个目录,内含 agent.cordis.yml;部署自带的预设放在 apps/cli/config/agent-presets/(那个目录列表就是名册)。组合拆分原则:注册表与跨会话设施是进程单例留在宿主组合;预设携带一个 Agent 贡献给它们的东西。预设如果命名了一个发布进程全局服务的行,挂载时被拒绝——而不是与下一个会话冲突。
二、与"一切皆插件"的关系
预设挂载的是同样的 Cordis 插件,只是作用域不同:agent.ctx 注册 → 该 Agent 专属(s02 注册表、s10 区块的遮蔽语义在这里显式化)。同一份 tool-bash 代码,可以同时是全局工具、会话 A 的工具、会话 B 没有的工具。
一句话:预设 = "按会话组合 Agent"的声明式机制。工具、persona、提示词区块都是可组合行,挂载在会话作用域里。