跳转至

SOP:PostToolUse 守卫 skills 命中面收窄(2026-10-02)

1. 背景

posttooluse-guard/skill-edit-guard.py 在写操作命中 ~/.trae-cn/skills/ 任意文件时, 统一提醒「改动 skill / 全局规则会使上游前缀缓存失效(约 0.35 元/次),请开新会话」。

但既有实测结论(measured-baselines.md 第十二节)与该提醒矛盾:只有 SKILL.md 的 frontmatter description 进每轮 system prompt 的技能索引,skill 正文与 references/ 是「按需加载」,改它们不进每轮前缀、不触发缓存重建。旧实现属错误归因,会诱导一次 无谓的新会话(重注入 HANDOFF + 重建上下文同样花钱)。本轮先复测确认,再收窄判据。

2. 目标

  1. 以同会话逐步 cache_miss 复测「改 references/ 是否触发前缀重建」,给出可复现证据;
  2. 守卫改为:skills 下仅当「文件名为 SKILL.md 且改动文本含 description:」时提醒; references/、skill 正文(非 SKILL.md)一律静默;
  3. 14 项离线回归全过,且不破坏既有四类提醒(user_rules / commands / hooks.json 分档文案)。

3. 前置条件

  • 只读取证工具:cost_audit/selfcheck.py、logs/usage.jsonl(只经脚本聚合,不读原文);
  • 守卫脚本可写;hooks.json 不需改动(脚本改动即时生效,改 hooks.json 才须重启 Trae);
  • 复测窗口须覆盖整个任务(selfcheck --since/--until 带秒)。

4. 影响范围

  • 涉及组件:posttooluse-guard/skill-edit-guard.py(层 3 守卫)一处;不涉及代理服务、不动 hooks.json;
  • 无需停服、无窗口期约束;token-proxy 服务与在跑作业不受影响;
  • 影响面仅限「写操作后的提醒文案与触发条件」;漏报的最坏后果是少一次提醒,不影响任何功能路径。

5. 分步操作

实现体:posttooluse-guard/skill-edit-guard.py(改动落在该文件,下列序列供阅读与复现)。

步骤 1:复测「改 references/ 是否触发前缀重建」

在本会话内对 ~/.trae-cn/skills/ops-execution-efficiency/references/measured-baselines.md 做一次真实 SearchReplace 编辑,然后逐步看 cache_hit_tokens / cache_miss_tokens。

坑:miss 单值不能直接判「重建」——读入一个大文件、写一次报告,都会让下一步的 miss 接近该内容大小(新内容首次入上下文,一次性未命中)。判据是 hit 是否回落: 前缀重建表现为 hit 骤降 + miss ≈ prompt 全量(如 364,998 那条)。

python3 - <<'PY'
import json
rows = [json.loads(l) for l in open('logs/usage.jsonl') if l.strip()]
conv = rows[-1]['conv_key']
for r in [x for x in rows if x.get('conv_key') == conv]:
    print(r['ts'], 'hit=%s miss=%s out=%s' % (r['cache_hit_tokens'], r['cache_miss_tokens'], r['completion_tokens']))
PY

预期:本会话 hit 从 6,784 单调递增到 127,104、无回落;改动 references/ 的那一步 miss=1,584,跨用户轮次后的首个请求 miss=2,339——均在 1~2 K 量级,不构成重建。

步骤 2:备份原脚本

高危动作前先留原文件,位置与命名见全局规则;本主题台账 backup/README.md。

# 高危:这是覆盖前唯一的回滚点
TS=$(date +%Y%m%d-%H%M%S)
mkdir -p hook-skill-desc-20261002/backup/posttooluse-guard
cp -p posttooluse-guard/skill-edit-guard.py \
      "hook-skill-desc-20261002/backup/posttooluse-guard/skill-edit-guard.py.bak.$TS"
ls -l hook-skill-desc-20261002/backup/posttooluse-guard/

预期:列出 1 个 .bak.<时间戳> 文件,大小与原脚本一致(12,456 B)。

步骤 3:改判据(四处)

  1. 常量:删 WATCHED_DIRS,新增 SKILL_FILE = "SKILL.md"、DESCRIPTION_RE、 CONTENT_KEYS;SKILLS_DIR / RULES_DIR / COMMANDS_DIR 保留;
  2. 新函数 _touches_description(tool_input):任一文本字段出现行首 description: 即判为 「可能改了 frontmatter」;
  3. 命中收集:user_rules / commands / hooks.json 照旧;skills 下改为 basename == SKILL_FILE and _touches_description(tool_input) 才计入;
  4. 文案:prefix_hits 一句改为「改动全局规则或 skill 的 frontmatter(description 等, 二者均进每轮 system prompt)…」。

坑:Write 整篇写一份 SKILL.md 时必然含 description: 行,即使值未变也会命中—— 单次 PostToolUse 事件取不到旧值,无法比对,此为该判据的已知局限(见第 8 章)。

# 查看实际改动(与备份逐行比对)
diff -u hook-skill-desc-20261002/backup/posttooluse-guard/skill-edit-guard.py.bak.20261002-140208 \
        posttooluse-guard/skill-edit-guard.py
python3 -m py_compile posttooluse-guard/skill-edit-guard.py && echo "compile OK"

预期:diff 只出现上述四处;compile OK。

步骤 4:跑离线用例

python3 hook-skill-desc-20261002/verify_guard_cases.py

预期:14 项全 PASS,末尾打印「失败 0 项」。

6. 验证

# 用例 预期 实测(2026-10-02 14:04)
V1 规则改动(user_rules) 提醒含「开新会话」 通过
V2 命令改动(commands) 提醒含「无需为此开新会话」 通过
V3 hooks.json 改动 提醒含「重启 Trae」 通过
V4 只读工具(Read) 静默 通过
V5 备份文件(*.bak.<ts>) 静默 通过
V6 非监视路径(/tmp/*) 静默 通过
V7 references/ 改动 静默(本次新增判据) 通过
V8 skill 正文改动(SKILL.md 非 frontmatter) 静默(本次新增) 通过
V9 skill 目录下非 SKILL.md 静默 通过
V10 SKILL.md 的 description: 改动 提醒 通过
V11 整篇 Write SKILL.md 提醒(可忽略,见第 8 章) 通过
V12 非对象 JSON([]) 退出码 0、stderr 空、静默 通过
V13 空 stdin 同上 通过
V14 同会话去重(第二次) 静默 通过

全部用例同时满足两条红线:退出码 0、stderr 为空。 实测命令:python3 hook-skill-desc-20261002/verify_guard_cases.py。

待补的真机证据:本次收窄的「正向」对照(改 SKILL.md 的 description → 下一轮 全量重建)沿用 2026-10-01 的旧实测,本轮未重跑;在新会话里做一次即可闭环。

7. 回滚

  • 备份位置:hook-skill-desc-20261002/backup/posttooluse-guard/skill-edit-guard.py.bak.20261002-140208, 台账见 hook-skill-desc-20261002/backup/README.md(同时登记 docs/HANDOFF.md 的备份);
  • 回滚触发条件(量化):① 用例回归出现 FAIL;② 新判据导致该提醒时静默(线上漏报, 例如改 user_rules 未提醒);③ 脚本出现非 0 退出码或 stderr 非空。
# 高危:覆盖现存脚本;脚本即时生效,无需重启 Trae
cp -p hook-skill-desc-20261002/backup/posttooluse-guard/skill-edit-guard.py.bak.20261002-140208 \
      posttooluse-guard/skill-edit-guard.py
python3 -m py_compile posttooluse-guard/skill-edit-guard.py && echo "rollback OK"

回滚后验证:重跑第 6 章用例表——V1~V6、V10~V14 应仍全过,V7~V9 应回到「提醒」(旧行为)。

8. 风险说明

风险 说明与缓解
判据基于文本关键词 description: 若某 skill 的 frontmatter 写法异常(无 description: 行),改它会漏报;缓解:漏报代价是一次提醒,不影响功能;hooks.json / user_rules 路径不受影响
整篇 Write SKILL.md 误报 必然含 description: 行,即使值未变也提醒(取不到旧值比对);缓解:文案与 docstring 已写明「此时可忽略」,且整篇重写 SKILL.md 属低频动作
改 references/ 时文本恰好含 description: 字样 会被判为 frontmatter 改动而提醒;缓解:路径在 references/ 下时不看内容(代码先判文件名是否为 SKILL.md),实际不受影响
真机正向对照未重跑 「改 description → 全量重建」沿用旧实测;缓解:已登记为 HANDOFF 3.2 待观察项,新会话补一次即可
改动触发当前会话前缀重建 本脚本不在 hooks.json、也不进每轮前缀,改它不需要开新会话(与改规则不同)

来源:/home/rain/Documents/services/token_proxy/hook-skill-desc-20261002/SOP.md | 最后更新:2026-10-02 14:05(由 build/sync.py 同步,请勿在站点侧修改)