创造模式(cordis)详解:用来造 Agent 的 Agent
DSH 唯一一个能够"自我引用"的 Agent 模式:具备标准模式的全部能力,并额外携带 Cordis 工具集—— 模型可以直接读、查、挂载、运行、停止、卸载当前 DSH 进程里的实时运行时,还能创作全新的 Agent preset。
01它是什么
官方定位一句话:"用于创建自定义 Agent preset:具备标准模式的全部能力,并提供运行时检查、插件实验和 preset 创作指导。"
| 组成部分 | 内容 | 与标准模式的关系 |
|---|---|---|
| persona | 扩展版系统提示词:讲解 Harness/Cordis 架构、双平面、创作纪律 | 改写(多一大段) |
| agent-instructions | 上限 64KB | 相同 |
| shell / fs / jobs / goal / plan / compaction / delegation / ask-user / todo / web | 标准模式的全部行 | 逐字相同 |
| tool-cordis | 五个自引用工具(inspect / define / run / stop / undefine) | 新增 |
| skill-filesystem + tool-skill | 技能注册,指向 preset 自带目录 skills/ | 改写 |
| 随附技能 × 2 | editing-cordis-compositions、cordis-plugin-development | 新增 |
创造模式 = 标准模式 + 自引用工具集 + 创作技能 + 扩展人格,其余一字未动。
02核心心智模型:双平面(Two Planes)
任何能力改造,先回答"这东西归哪个平面"。
| HOST 组合(宿主平面) | AGENT PRESET(预设平面) | |
|---|---|---|
| 包含 | 注册表本身(tools、systemPrompt、agents、agent-loop、sessions);跨会话的东西(持久化、会话查询、存储、设置、凭据、遥测);沙箱与审批栈、模型路由、子代理注册表及其后端 | 一个会话向注册表贡献的东西:它的工具插件、persona 与提示词段落、压缩策略 |
| 实例数 | 每进程一份 | 每会话一份(随会话拆卸) |
| 判定标准 | 是否必须跨会话共享 | 是否只属于这一个会话 |
subagents 注册表要回答宿主跨会话查询 → 预设只贡献委派工具,注册表与后端留宿主。
isolate realm 会把服务发进进程全局 realm——第二个挂载该 preset 的会话直接冲突,挂载即被拒。
真正拥有的服务,要把提供者与所有消费者一起包进一个带 isolate 的组:
- id: delegation
name: cordis:group
group: true
isolate:
workflowEngine: true # true = 每次挂载一个私有 realm
config:
- id: tool-workflow
name: '@deepseek-ai/dsh-tool-workflow'
反过来,preset 只消费不拥有的宿主能力(tool-bash、tool-jobs、tool-goal)必须摆在 realm 之外,否则解析不到宿主实例。
03五个核心工具(Cordis 工具集)
底层是 dsh-cordis-host-runner 的 dynamicCordisRunner 服务:定义注册表 + node:vm 沙箱 + fiber 生命周期 + 浏览器广播。
| 工具 | 动词性质 | 做什么 |
|---|---|---|
| cordis_inspect | 只读 | 实时运行时报告:服务、全部存活插件 fiber、已注册工具、本会话动态包;what:"api"/"events"/"client" 配合精确 name 拿到完整契约 |
| cordis_define | 登记 | 语法预检后登记一个包(name、purpose、host 半 code 和/或浏览器半 client)。不执行;铸出 dyn-<n> id |
| cordis_run | 执行 | vm 沙箱中求值 host 半,浏览器半投递给每个打开的网页;对已运行包重跑 = 重新投递当前版本 |
| cordis_stop | 卸载 | host 半 dispose 到完全停稳并从页面撤回浏览器半;定义保留,可再次运行 |
| cordis_undefine | 删除 | 必要时先 stop,再忘掉定义;卡片作为已卸载记录留在会话里 |
生命周期模型
动态包的生命边界
- 只存在于共享 DSH 进程内存;跨轮次保持活跃,可能影响同进程的其他会话
cordis_stop/cordis_undefine/ 工具集卸载 / DSH 重启后消失- 不创建插件文件、不安装包、不改
cordis.yml或个人/项目配置、不能自动转正 - 每个动词以会话为界:一个包只在定义它的会话里可见、可控
ctx.fs、ctx.web、ctx.bash),
globalThis 写入保持局部——但 host-realm 辅助函数使逃逸成为可能,获准服务会影响实时运行时。
唯一可配参数在 runner:vmTimeoutMs(默认 5000ms,host 半同步求值上限)。
cordis_inspect 的信息来自哪里
what:"api"/"events"渲染生成式 API 目录(与docs/subsystems同一次 AST 遍历产出)——模型读到的与仓库文档不可能偏离- 报告与活的服务存储取交集:谁在跑由 store 回答,能做什么由目录回答
- 只展示可调用方法、只点名包实际够得到的
ctx键(injectable分类)
04两个随附技能
4.1 editing-cordis-compositions —— 组合创作指南
适用:创建/修改 preset、增删插件行、判定归属平面、校验能否挂载、诊断"挂载了但没贡献"。核心纪律:
- 禁区:绝不编辑/删除随附 preset(升级会覆盖;改坏
cordis会禁用创作本身);改随附先复制 - 先定平面(第 02 节)
- 从复制开始:
copy(from, id, name)是唯一创作写入 - 编辑文件:副本
preset.yml写 name/description;agent.cordis.yml逐行改,守 realm 规则 - 挂载校验:
standingKeyFor(id)是权威检查
4.2 cordis-plugin-development —— 动态插件开发手册(420 行)
适用:创建/修改/调试/扩展动态插件(Host 服务与事件、Client 槽位与主题 UI、包私有 Client→Host 调用、动态工具、版本更新、审批失败、运行时诊断)。工作流浓缩:
cordis_inspect拿当前 Host/Client 的 Provider、方法与 schema- 先查再写:精确查询要用的 Service / Event / Builtin / Slot / Theme token / Tool,绝不凭名字猜 API
- 设计第一个 Package → 写纯 JS(
code.host/code.client,无 JSX、无 import、无 TS,Client 用React.createElement)→cordis_define cordis_run激活;不要在同一个回合里等审批/浏览器结果——awaiting-approval/starting后结束当前工具流,等系统回报- 排查用
cordis_stop(暂停不删);彻底不要才cordis_undefine
版本语义:pluginId 稳定实例 · packageId 不可变代码版本 · pluginRunId 每次激活尝试。run 激活 / update 换版本 / run current 回滚——永远不覆写失败的 Package,新定义一个再 update。
05与标准模式对比:多出来的全部能力
| 能力 | 标准模式 | 创造模式 |
|---|---|---|
| 查看实时服务/事件/工具目录 | ✗ | ✓ cordis_inspect |
| 临时挂载实验插件(不落盘) | ✗ | ✓ cordis_define + cordis_run |
| 动态注册一个工具,下一轮就可用 | ✗ | ✓ harness.registerTool |
| 浏览器半 UI(投递给所有打开页面) | ✗ | ✓ code.client + Slot 注册 |
| 创作新 Agent preset | ✗ | ✓ copy + 编辑 + standingKeyFor |
| 会话级"插件实验场" | ✗ | ✓ 动态包(进程内存、重启即失) |
| 信任级别 | system(随附) | system,但会话 = shell 访问 |
06完整案例一:用创造模式创建"文档审查模式" preset
场景:给团队做一个精简的 docs-review 预设——只做文档/代码审查,不需要子代理、工作流、Ralph、Web 搜索、目标等重型能力,但要保留文件读取、检索、Shell、todo 与提问工具。
[调用 skill] editing-cordis-compositions
→ 获知:创作 = 复制 + 编辑;校验 = standingKeyFor;根目录 = $DSH_HOME/.agent-presets/
[调用] cordis_inspect what:"api" name:"agentPresets"
→ 确认 agentPresets 签名:list() / read(id) / copy(from,id,name?) /
standingKeyFor(id) / remove(id) / resolve(id) ...
Agent 需要工具来调用 agentPresets 服务。按技能指引,用动态包临时注册一个工具给自己用(纯内存、用完即卸):
// cordis_define —— code.host
return {
name: 'preset-tools',
inject: ['agentPresets', 'tools'],
apply(ctx) {
const harness = ctx.get('harness') // Builtin
harness.registerTool(ctx, harness.defineTool({
name: 'preset_check',
description: '挂载校验一个 preset(按 id)',
parameters: { id: { type: 'string', required: true } },
output: { schema: { type: 'string' },
render(_a, v) { return [{ type: 'text', text: v }] } },
async execute(args) {
try {
await ctx.agentPresets.standingKeyFor(args.id)
return 'mounted OK'
} catch (error) { return error.message }
},
}))
},
}
cordis_run 后,下一个模型步骤就多了一个 preset_check 工具。
探针用完必须 stop + undefine——它是探针,不是要留下的能力。
① 复制(唯一创作写入,宿主侧执行,无需沙箱升级):
[调用] preset_check { id: "standard" } → "mounted OK" # 确认基线可挂载
preset_copy { from: "standard", id: "docs-review", name: "文档审查模式" }
→ 已创建 $DSH_HOME/.agent-presets/docs-review/
(agent.cordis.yml + preset.yml,权限 0o600/0o700,符号链接已解引用)
② 编辑副本(副本根目录在会话工作区外,第一次写会被文件沙箱拦——需对该命令带 sandbox_permissions 升级重试一次,用户批准;写入要批量):
# preset.yml
name: 文档审查模式
description: 面向文档与代码审查的精简 Agent:文件读写、检索、Shell、todo 与提问工具,
无子代理/工作流/Ralph/Web 搜索/目标。
# agent.cordis.yml:从 standard 副本删行
# 保留:persona、agent-instructions、tool-bash/tool-pwsh、tool-fs、tool-fs-search、
# tool-jobs、skill-filesystem、tool-skill、tool-todo、tool-ask-user、
# planning 组、compaction 组
# 删除:tool-goal、tool-web、delegation 组(subagent / workflow / ralph)
③ 挂载校验(权威检查):
[调用] preset_check { id: "docs-review" } → "mounted OK"
standingKeyFor(id) 会真实组装插件子树(与会话启动同款,只是没有 agent),拒绝四种失败:
包解析不到、配置非法、行未激活(waiting for service)、服务发布进根 realm(process-global service)。
只应在编辑完成后跑一次:成功会安装一个常驻代际(活到进程结束),失败则自动清理。
cordis_stop # 停掉 preset-tools 包
cordis_undefine # 忘掉定义
Agent 总结:docs-review 已就绪且通过挂载校验。请在"新建会话"的 preset chip 里选择"文档审查模式",
确认工具列表(应只有文件读写、检索、Shell、todo、提问 + 计划/压缩,无重型工具)。
07进阶案例二:动态插件实验(不落盘的能力试做)
场景:先试做一个"一键统计当前项目代码行数"的工具,验证效果后再决定是否做成正式 preset。
cordis_define
name: 'loc-counter'
purpose: '统计当前工作区各语言代码行数'
code.host:
return {
name: 'loc-counter',
inject: ['bash'],
apply(ctx) {
const harness = ctx.get('harness')
harness.registerTool(ctx, harness.defineTool({
name: 'count_loc',
description: '用 git ls-files + wc 统计代码行数',
parameters: {},
output: { schema: { type: 'string' }, render(_a, v) { return [{ type: 'text', text: v }] } },
async execute() {
const r = await ctx.bash.exec({ command: 'git ls-files | xargs wc -l | tail -5' })
return r.stdout
},
}))
},
}
cordis_run ──► 运行中;下一个模型步骤出现 count_loc 工具
[调用] count_loc ──► 得到统计结果
cordis_stop # 效果即刻消失,定义保留待改造
cordis_stop 干净回收——
这是创造模式区别于其他三模式的核心价值。
08常见坑与最佳实践
| 坑 | 正确做法 |
|---|---|
| 直接编辑随附 preset(升级会覆盖;改坏 cordis 会禁用创作本身) | 先 copy() 再改副本 |
| preset 里发布服务不带 isolate realm(第二个会话挂载即冲突) | 提供者 + 消费者一起包进 isolate 组;纯消费行留在 realm 外 |
| 把宿主能力(subagents 注册表等)搬进 preset | 注册表留宿主,preset 只贡献工具行 |
用 list() 的 broken 字段当校验 | broken 只是形状检查;standingKeyFor 才是真挂载校验 |
| 技能代码里用 ctx.x 却不声明 inject | 要么 inject: ['x'],要么 ctx.get('x') + 判空 |
| 动态包想"转正" | 动态包不能自动转正;按常规开发流程写本地/项目/仓库插件 |
| 浏览器半挂起无人应答 | 无页面连接时带浏览器半的包会挂起直到回合取消;host-only 包不受影响 |
| 把会话历史当持久存储 | 动态包定义只存在于进程内存;会话日志只存元数据不存代码 |
最佳实践速记
- 先
cordis_inspect查真实契约,再写代码 - 探针插件用完即
cordis_stop+cordis_undefine - 创作 preset:复制 → 批量编辑 →
standingKeyFor校验 → 交给用户开真实会话确认工具列表 - 把创造模式当"调试台 + 装配车间",日常编码回到标准 / PTC
09一句话总结
创造模式 = 标准模式的全部能力 + 一面照向自己的镜子(Cordis 工具集)+ 一本创作手册(两个技能)+ 一份"你正在运行在什么之上"的人格。
它是 DSH 插件化哲学的最终形态:一切皆插件,连"造插件的 Agent"本身也是一个 preset。