Agent 预设与 Persona
一个 Agent 不只是"模型 + 几个默认工具":它有一个固定组合:工具、系统提示词段、投影单元,外加它的人格。DSH 用两层机制承担这个:Agent 预设(preset)与 Persona。
| 机制 | 管什么 | 载体 |
|---|---|---|
| Agent 预设(preset) | 该 agent 的完整能力组合 | 目录 + 一个 agent.cordis.yml |
| Persona | 该 agent 的人格/系统提示词 | 一个可组合的系统提示词行 |
1. Agent 预设(agent-presets)
一个 preset 是一个目录,里面放一个 agent.cordis.yml。@deepseek-ai/dsh-agent-presets 在进程内把它只挂载一次(standing mount),每个命名了它的会话通过 agent scope 的父链加入它。
- 每个会话各 key 自己状态,但共享同一份挂载:"每个 preset 的组合只存在一次,覆盖所有加入它的 agent"
- 作用域解析链:
agent → preset → global(就近遮蔽最远) - 挂载的工具/提示段/投影单元对每个其下 agent 可见,兄弟 preset 对其保持 deaf
ctx.agentPresets.defaultId // 未指名时挂载的 preset id
await ctx.agentPresets.list() // 当前所有 preset(含 broken,带 reason)
await ctx.agentPresets.resolve(id?) // 按 id,缺省 defaultId
await ctx.agentPresets.mount(agentCtx, id?) // 组成一个 agent 的 preset
await ctx.agentPresets.composeFrom(agentCtx, parentCtx) // 加入另一 agent 已在运行的标准组合
await ctx.agentPresets.read(id) / copy(...) / remove(id) // 读原文 / 复制建新 / 删除(本地授权)
发现不缓存:list()/resolve() 每次重读根,运行中新增的 preset 立即可见。损坏的目录会以 broken 列出(带原因),而不是被跳过。
2. Persona
@deepseek-ai/dsh-persona 是一个可组合的人格行。部署人格由 dsh-system-prompt 作为自己的 config 无条件注册(进程恰有一个);这个包就是给单个 agent shadow 部署人格的那一行。
# 在 preset 的 agent.cordis.yml 里
- id: persona
name: '@deepseek-ai/dsh-persona'
config:
text: |
你是一个专注代码审查的助手。语气直接、给出可执行的修改建议。
complete: false # false=shadow; true=成为唯一系统提示词段
includeRuntimeContext: true # false=隐藏此 agent 的全部动态上下文快照
text是模板,支持{{…}}组,在渲染时(非组装时)对已注册提示词变量严格解析complete: true时,组装仍解析上下文/工具/变量/协作监听器,但之后只保留这一个 persona 段:没有其他身份/工具指引能追加includeRuntimeContext: false时不求值此 scope 的 context provider,并丢弃 assembly listener 追加的 context;它不关闭拥有沙箱、审批、委派状态的服务
只能挂在 agent 作用域内(preset 挂载提供的):在作用域外挂会与
deployment:persona注册冲突并 loud fail。这不该绕过:部署人格已有主人,这一行的意义就是为单个 agent 遮蔽它。
3. Plan 模式(plan-mode)
@deepseek-ai/dsh-plan-mode 是 per-agent 的规划协作状态 + 部署所有的指导段。它和 persona 一样是一段可组合进系统提示词的文本,区别在于 persona 是持续的人格行,plan 段只在 plan mode 激活时出现在 prompt order 50,未激活贡献零 token。
- 入口/出口是命令:
/plan [message]选中 plan mode(裸/plan直接进;非空参数先选中、再经agent.steer()作为下一步普通用户消息提交);/plan off退出(参数off被精确保留)。命令与其终端结果都不进模型历史。 exit_plan_mode是唯一的退出工具:schema 两种状态都稳定注册,execute 只在 plan 激活时接受,且只在用户经ctx.userQuestions精确批准后才离开。它声明plan-reviewpresentation intent,把Approve作为批准标签——有能力的 UI 会把它呈现成决策而非通用提问。- 状态只写日志:
plan/mode事件({ active: boolean })是 log-only、整值替换;foldPlanMode从会话日志直接恢复,所以 resume、fork、compaction 都拿回状态。fork 子继承已记录的 plan 状态,新 spawn 从 inactive 开始。 section是必填非空的 config;未知键 load 时即失败。它不接受任意命名模式、工具过滤、沙箱设置或批准策略。
口径一致:plan mode 是软指导,不是模型路由。它不切换模型、不做 plan/execute 双路由(见 多模型 与 模型路由);沙箱模式与批准策略独立强制、不读也不写 plan 状态。要真正"限制"执行,得单独配 sandbox + approval。默认挂载(base);Web 下 plan-mode 移到 preset 层,standard preset 的
planninggroup 以 entry-local realm 携带它——plan 状态本就是 per-agent 的,这才是正确生命周期。
一个完整 preset 长什么样
~/.dsh/.agent-presets/reviewer/ # 用户自己作的 preset 根($DSH_HOME/.agent-presets)
└── agent.cordis.yml
# agent.cordis.yml —— 该 preset 的标准组合
- id: persona
name: '@deepseek-ai/dsh-persona'
config:
text: 你是一个代码审查助手。只读重构建议,不实施。
- insert:
- id: tools
name: '@deepseek-ai/dsh-tools'
- id: lsp-tool
name: '@deepseek-ai/dsh-tool-lsp'
会话选择这个 preset → 只挂一次,mount 的组合对每个加入的 agent 有效。
验证
# 看用户 preset 根
ls ~/.dsh/.agent-presets/
dsh web --dump-config | grep agentPresets
# 会话里看 persona/系统提示词段
zstdcat ~/.dsh/sessions/*/*/session.jsonl.zstd | grep -E 'persona|preset' | head
下一步
- 插件解剖:preset 也是一段组合
- 子 Agent 与并行:子 Agent 可以有自己的 preset/persona