背景 / Background
DeepCopilot 的 skill 系统中,skill-creator 是一个元 skill(meta-skill),按设计它应当在每次创建新 skill 之前承担质检与优化职责:
- 优化
description,提高未来触发命中率
- 校验 SKILL.md body 结构是否符合 skill 规范
- 对过长 / 未分层的 prompt 做精简
- 必要时跑 evals 验证
但 skill_create 工具在实现层 没有任何前置约束,模型可以直接把用户的 raw prompt 塞进 body 参数落盘,整个 skill-creator 流程被静默跳过。
复现 / Reproduction
来自一次真实会话(session-2026-05-24T13-53-49-129Z.log):
- 用户让模型"帮我创建一个 skill"
- 模型直接调用
skill_create({ name, body: <用户的原始 prompt> }),没有调用 skill_invoke({ name: "skill-creator" })
- 用户人工 review 才发现内容未经任何优化
模型事后自承认:
你给了我完整的 prompt → 我大脑里直接生成「内容已就绪,写入即可」→ 把 skill_create 当成了单纯的持久化工具 → 跳过了 skill-creator 的审查优化环节。本质上是我把「创建 skill」这个动作降格成了「写文件」,而省略了 skill 系统里 skill-creator 承担的是质检 + 优化的守门角色。
更糟的是,当用户追问"如何避免再发生"时,模型提出"在 .deepcopilot/hooks.json 里加一条 hook",但写下去的 JSON 用的是它自己编造的 schema:
{
"hooks": [{
"trigger": { "on_tool": "skill_create", "timing": "before" },
"action": { "type": "invoke_skill", "skill": "skill-creator" }
}]
}
而真实 schema(见 src/hooks.js)只支持 event + tool + run(shell 命令),根本没有 trigger / action / invoke_skill。这条 hook 在 loadHooks 里会因为 !hook.run 被直接 continue 跳过——等于没写。也就是说,"为了防止跳过守门"的修复,本身也是一次同款的"降格成写文件"事故。
根因 / Root cause
两层耦合的问题:
L1(主问题)skill_create 工具实现没有前置守门
- 见
src/tools/skill-tools.js:skill_create 接收 name + body 就直接写盘
- 没有任何字段要求"必须是经过 skill-creator 处理过的产物"
- system prompt / skill-creator SKILL.md 里也没有硬性规则强制模型先调用 skill-creator
L2(次问题)hooks 系统表达力不足
src/hooks.js 的 hook 只能跑 shell(hook.run)
- 无法表达"工具 A 调用前必须先调用工具/skill B"这种语义反射
- 模型尝试用 hooks 兜底但被架构限制(同时也暴露了模型对 schema 的幻觉问题)
影响 / Impact
- 质量风险:所有
skill_create 产物可能都没经过质检,长期累积污染 skill 库
- 静默失败:模型不会主动报告它"跳过了 skill-creator",只有人工 review 能发现
- 示范效应:其他元 skill 如果未来引入(例如
prompt-optimizer、agent-reviewer)会面对同样的绕过风险
期望行为 / Expected behavior
调用 skill_create 时必须满足以下任一条件,否则工具应拒绝执行并返回明确错误:
- 当前 turn 之前已经调用过
skill_invoke({ name: "skill-creator" }),或
skill_create 的 args 中显式带有 reviewed_by_skill_creator: true(或类似的校验字段,由 skill-creator 在其输出中产生)
被拒绝时的错误消息应包含:「请先调用 skill_invoke({ name: "skill-creator" }) 进行质检 / 优化后再创建 skill」。
候选方案 / Proposed solutions
方案 A:工具实现层硬守门(推荐,小改动、立即见效)
在 src/tools/skill-tools.js 的 skill_create 实现里加前置检查:
- 通过
run/session 对象读取本轮 turn 的工具调用历史
- 若历史中没有
skill_invoke 且 args 中没有 reviewed_by_skill_creator: true,直接返回工具错误
- 错误文本要明确指引下一步动作(让模型自动纠正)
优点:确定性、不依赖模型自觉、改动小
缺点:把策略硬编码在工具里;如果未来还有别的元 skill 守门需求要重复实现
方案 B:在 skill-creator SKILL.md + system prompt 里加硬约束(软约束,作补充)
- 在 skill-creator 的 SKILL.md 顶部加一段"任何 skill 创建必须经过本 skill"的强声明
- 在系统 prompt(
src/prompts/system.js)里追加规则:"before invoking skill_create, you MUST first invoke skill_invoke({ name: 'skill-creator' })"
优点:零代码成本
缺点:纯软约束,模型仍可能短路(本次事故就是绕过了"我应该……"的软约束)→ 建议 与方案 A 叠加使用,不要单独作为修复
方案 C:扩展 hooks 系统支持 invoke_tool / invoke_skill action(长期项)
扩展 src/hooks.js 的 schema:
这需要:
runHooks 能向调用方注入"前置工具调用"(不只是 shell output)
- tool-executor 在
before_tool hook 返回"需要先调用 X"时,把控制权让回 agent loop
- 新文档 + schema 迁移指引
优点:通用机制,未来所有"工具 A 前必须先调用 B"场景都能配置
缺点:架构改动较大,建议方案 A 落地后再开独立 issue 跟进
相关代码 / Related code
src/tools/skill-tools.js — skill_create 工具实现,守门应加在这里
src/hooks.js — 现有 hooks 系统(if (!hook.run) continue; 是限制点)
src/prompts/system.js — 软约束规则注入点
session-2026-05-24T13-53-49-129Z.log — 复现日志
验收标准 / Acceptance Criteria
备注
模型在本次事故中表现出的另一个 pattern——在不知道 schema 的情况下根据上下文猜测并写文件——本身也值得单独跟踪(可能需要在 system prompt 里加"未知 schema 必须先读源码确认"的元规则),但不在本 issue 范围内,建议另开。
背景 / Background
DeepCopilot 的 skill 系统中,
skill-creator是一个元 skill(meta-skill),按设计它应当在每次创建新 skill 之前承担质检与优化职责:description,提高未来触发命中率但
skill_create工具在实现层 没有任何前置约束,模型可以直接把用户的 raw prompt 塞进 body 参数落盘,整个skill-creator流程被静默跳过。复现 / Reproduction
来自一次真实会话(
session-2026-05-24T13-53-49-129Z.log):skill_create({ name, body: <用户的原始 prompt> }),没有调用skill_invoke({ name: "skill-creator" })模型事后自承认:
更糟的是,当用户追问"如何避免再发生"时,模型提出"在
.deepcopilot/hooks.json里加一条 hook",但写下去的 JSON 用的是它自己编造的 schema:{ "hooks": [{ "trigger": { "on_tool": "skill_create", "timing": "before" }, "action": { "type": "invoke_skill", "skill": "skill-creator" } }] }而真实 schema(见
src/hooks.js)只支持event+tool+run(shell 命令),根本没有trigger/action/invoke_skill。这条 hook 在loadHooks里会因为!hook.run被直接continue跳过——等于没写。也就是说,"为了防止跳过守门"的修复,本身也是一次同款的"降格成写文件"事故。根因 / Root cause
两层耦合的问题:
L1(主问题)
skill_create工具实现没有前置守门src/tools/skill-tools.js:skill_create接收name+body就直接写盘L2(次问题)hooks 系统表达力不足
src/hooks.js的 hook 只能跑 shell(hook.run)影响 / Impact
skill_create产物可能都没经过质检,长期累积污染 skill 库prompt-optimizer、agent-reviewer)会面对同样的绕过风险期望行为 / Expected behavior
调用
skill_create时必须满足以下任一条件,否则工具应拒绝执行并返回明确错误:skill_invoke({ name: "skill-creator" }),或skill_create的 args 中显式带有reviewed_by_skill_creator: true(或类似的校验字段,由 skill-creator 在其输出中产生)被拒绝时的错误消息应包含:「请先调用
skill_invoke({ name: "skill-creator" })进行质检 / 优化后再创建 skill」。候选方案 / Proposed solutions
方案 A:工具实现层硬守门(推荐,小改动、立即见效)
在
src/tools/skill-tools.js的skill_create实现里加前置检查:run/session对象读取本轮 turn 的工具调用历史skill_invoke且 args 中没有reviewed_by_skill_creator: true,直接返回工具错误优点:确定性、不依赖模型自觉、改动小
缺点:把策略硬编码在工具里;如果未来还有别的元 skill 守门需求要重复实现
方案 B:在 skill-creator SKILL.md + system prompt 里加硬约束(软约束,作补充)
src/prompts/system.js)里追加规则:"before invokingskill_create, you MUST first invokeskill_invoke({ name: 'skill-creator' })"优点:零代码成本
缺点:纯软约束,模型仍可能短路(本次事故就是绕过了"我应该……"的软约束)→ 建议 与方案 A 叠加使用,不要单独作为修复
方案 C:扩展 hooks 系统支持
invoke_tool/invoke_skillaction(长期项)扩展
src/hooks.js的 schema:{ "hooks": [{ "event": "before_tool", "tool": "skill_create", "action": { "type": "invoke_tool", "name": "skill_invoke", "args": { "name": "skill-creator" } }, "on_failure": "block" }] }这需要:
runHooks能向调用方注入"前置工具调用"(不只是 shell output)before_toolhook 返回"需要先调用 X"时,把控制权让回 agent loop优点:通用机制,未来所有"工具 A 前必须先调用 B"场景都能配置
缺点:架构改动较大,建议方案 A 落地后再开独立 issue 跟进
相关代码 / Related code
src/tools/skill-tools.js—skill_create工具实现,守门应加在这里src/hooks.js— 现有 hooks 系统(if (!hook.run) continue;是限制点)src/prompts/system.js— 软约束规则注入点session-2026-05-24T13-53-49-129Z.log— 复现日志验收标准 / Acceptance Criteria
skill_create时,若同轮 turn 未先经过 skill-creator,工具返回明确错误而非写盘备注
模型在本次事故中表现出的另一个 pattern——在不知道 schema 的情况下根据上下文猜测并写文件——本身也值得单独跟踪(可能需要在 system prompt 里加"未知 schema 必须先读源码确认"的元规则),但不在本 issue 范围内,建议另开。