跳转至

SOP — 动作类型全覆盖的强制合并(merge-coverage-all-20261006)

1. 背景

用户提问:「增删改查算得上你所有动作类型吧,都应用上强制合并了么?结合现有数据」。

实测(logs/req_shape.jsonl 全量 16591 步 / 4485 个有工具名步):

  • 现有动作不止增删改查:19 种工具调用中,Skill 244 · TodoWrite 132 · AskUserQuestion 63 · CheckCommandStatus 25 · Task 22 · PureShowWidget 8 · run_mcp 7 · StopCommand 2 · GetDiagnostics 1 = 504 次(占工具调用 7.6%) 不属四类。
  • 规则侧:「查 / 取数 / 改-验 / 改」有条款;「增(Write)」「删(DeleteFile)」无专项条款, 编排 / 计划 / 等待 / 交互 / 展示既无条款也无分列口径。
  • 口径侧:step_profile.py 旧分列只有 5 类,其他 在未合并块中一度不可定性 (旧碎步组口径下 其他 999 组 / 6169 步,其中混有旧记录缺工具名的噪声)。

需求:尽可能覆盖所有操作类型;能合并的合并、不能的串行;且要能强制。 用户选定「三层全覆盖」+「语义类粒度」。

2. 目标

  1. 规则层:rule-agent-efficiency.md 新增「动作类型 × 合并性」全覆盖矩阵(12 类 + 可合并性 + 强制语)。
  2. 口径层:cost_audit/step_profile.py 分列扩为动作全集 12 类,每类带可合并性,并给出 「可并类合计」;旧 5 类聚合保留作跨轮可比。
  3. Hook 层:新增 merge-guard/merge-guard.py(PreToolUse),对高置信的「本可合并却逐步来」 序列注入一行极短提醒;hooks.json 挂载。
  4. 验收:D 组「工具调用」必报「可并类合计」并点名 取数命令 / 改-验循环 / 增 / 删。

3. 前置条件

  • 本机 token_proxy 工程可写;C 类变更授权(声明即执行,备份即授权)。
  • 无在跑业务受影响;无需停服。
  • 改动生效条件:hooks.json → 重启 Trae;全局规则 → 开新会话。

4. 影响范围

项 内容
主机 / 组件 本机(192.168.31.250);token_proxy 工程、~/.trae-cn/ 配置
是否停服 否(代理 8788 不受影响)
窗口期 无;但重启 Trae 会中断当前会话
影响面 ① 规则文件进每轮 system prompt(改动使前缀失效,≈0.35 元/次);② Hook 在每次工具调用前执行(新增一条进程调用)

5. 分步操作

步骤 1:备份(前置,未备份不得执行)

三处被改文件先备份到本主题 backup/。

cd ~/Documents/services/token_proxy
T=$(date +%Y%m%d-%H%M%S); D=merge-coverage-all-20261006/backup
mkdir -p "$D/cost_audit" "$D/trae-cn/user_rules"
cp -p cost_audit/step_profile.py "$D/cost_audit/step_profile.py.bak.$T"
cp -p "$HOME/.trae-cn/user_rules/rule-agent-efficiency.md" "$D/trae-cn/user_rules/rule-agent-efficiency.md.bak.$T"
cp -p "$HOME/.trae-cn/hooks.json" "$D/trae-cn/hooks.json.bak.$T"
chmod 600 "$D"/cost_audit/*.bak.* "$D"/trae-cn/user_rules/*.bak.* "$D"/trae-cn/*.bak.*

预期:三个 .bak.<时间戳> 落盘、权限 600。失败判断:任一 cp 非 0 → 停止,不继续。 实现体:本步为纯 shell(无编排、步骤 ≤3 类),不另出脚本。

步骤 2:step_profile.py 分列扩为动作全集

把模块级常量由「只读 / 写」两个白名单扩为 TOOL_KIND(工具 → 语义类)映射,新增 KINDS(12 类中文名)、MERGEABILITY(可合并性)、LEGACY_MAP(新类 → 旧 5 类)、 EDIT_KINDS;重写 classify_group()(优先级:改-验 > 取数 > 编辑 > 单一类); 新增 legacy_of / merge_legacy / mergeable_totals / fmt_legacy 四个辅助函数; 输出层加「可并类合计」与「旧口径 5 类聚合」两行。

坑:KINDS 的键被 merge_groups / unmerged_blocks 的 by_kind 与两处打印函数共用, 扩键时四处自动跟随;但 rule_compliance.py 复用了本模块的 load / group_by_conv / last_msg / tools_of,不得改动这四个函数的签名(本次未动)。

# 语法与实跑(跑通即语法正确;重复运行结果应一致)
python3 cost_audit/step_profile.py --top 100000 | tail -n 12

预期:尾部出现 分列(动作全集)、可并类合计、旧口径 5 类聚合 三行。 失败判断:KeyError / NameError → 用第 7 章回滚后修正重跑。

步骤 3:规则新增「动作类型 × 合并性」矩阵

在 ~/.trae-cn/user_rules/rule-agent-efficiency.md 的「执行方式」表之后插入子节 (12 类 + 可合并性 + 合并方式 + 强制语 + 验收口径),并同步改写 D 组「工具调用」行的分列要求。

坑:本文件进每轮 system prompt,改动使前缀缓存失效(【实测】≈0.35 元/次), 故改完须开新会话;不要为省事在同一会话反复改。

python3 -c "import pathlib;t=pathlib.Path.home()/'.trae-cn/user_rules/rule-agent-efficiency.md';s=t.read_text(encoding='utf-8');print('动作类型 × 合并性' in s, '可并类合计' in s)"

预期:输出 True True。失败判断:任一 False → 改动未落盘,检查编辑器是否保存。

步骤 4:新增 merge-guard.py

新建 merge-guard/merge-guard.py(PreToolUse):

  • 判据(全部基于 tool_input 的高置信形态): A 连续 ≥3 次「查-读取」且参数互不相同;B 同一文件连续 ≥2 次 Read; C 同一文件连续 ≥2 次 SearchReplace;D 连续 ≥2 次 DeleteFile。
  • 「分属不同步」用相邻调用间隔近似(STEP_GAP_SEC=1.5s,小于即视为同一步并行、已合并)。
  • 限量:每会话最多注入 INJECT_MAX=2 次、每条判据至多一次(提示进入上下文后每轮重放)。
  • 任何异常静默 exit 0,绝不阻断工具调用。

坑:PreToolUse 退出码 2 等价 deny,一次误判就废掉本轮调用——故脚本内任何异常都必须吞掉; 缺 session_id 时无法跨调用追踪,直接返回而不注入。

python3 -c "print(open('merge-guard/merge-guard.py',encoding='utf-8').read().count('INJECT_MAX'))"

预期:≥2(定义 1 处 + 使用 1 处)。失败判断:文件缺失或语法错 → 用单测(第 6 章)暴露。

步骤 5:挂载 hooks.json

在 PreToolUse 数组末尾追加一条 {"matcher": "*", ...},命令为 [ -f "$s" ] && python3 "$s" || true; exit 0 形式(脚本缺失时静默失效,不影响工作区)。

坑:JSON 结构层级易错(本轮首次编辑即多了一层 ],被 json.tool 拦下)—— 改完必须先校验再重启 Trae。

python3 -m json.tool "$HOME/.trae-cn/hooks.json" > /dev/null && echo "hooks.json OK"

预期:hooks.json OK。失败判断:报 Expecting ',' delimiter 等 → 按第 7 章回滚 hooks.json 重改。

步骤 6:重启 Trae(使 Hook 生效)

hooks.json 在启动时读取,改后须重启 Trae。重启前先完成第 6 章离线验证,避免带病加载。

6. 验证

项 命令 预期
hooks.json 合法性 python3 -m json.tool ~/.trae-cn/hooks.json >/dev/null 无输出、rc=0
口径跑通 python3 cost_audit/step_profile.py --top 100000 出现「可并类合计」行
口径幂等 连跑两次 tail -n 12 后 diff 无差异
Hook 判据 A/B/C/D 见下方离线单测 第 3/2/2/2 次调用各注入一次
Hook 不误报 同一步并行(间隔 <1.5s)连续 3 次 Read 不注入
Hook 限量 同会话连续触发 A、C、D 仅前 2 次注入,第 3 次不注入
Hook 不阻断 喂非法 JSON / 缺 session_id rc=0、无输出
新 Hook 实时生效 重启 Trae 后正常一轮多步任务 出现「【合并提醒】」且不误报(待观察)

离线单测(本轮实测 8 项全通过;用后即清状态,不污染真实会话):

# 单测用独立 session 前缀 test-*,跑完删除 merge-guard/state/test-*
ls merge-guard/state/ 2>/dev/null | grep -c "^test-"   # 预期 0(已清理)

7. 回滚

改动 回滚动作
cost_audit/step_profile.py 覆盖 backup/cost_audit/step_profile.py.bak.20261006-230942 回原路径
~/.trae-cn/user_rules/rule-agent-efficiency.md 覆盖 backup/trae-cn/user_rules/rule-agent-efficiency.md.bak.20261006-230942,并开新会话
~/.trae-cn/hooks.json 覆盖 backup/trae-cn/hooks.json.bak.20261006-230942,并重启 Trae
merge-guard/merge-guard.py rm -f merge-guard/merge-guard.py + 从 hooks.json 删对应条目 + 重启 Trae

回滚触发量化(任一达标即回滚):① Hook 出现误报(对必须串行的步骤反复提醒)或每会话注入 超 2 次;② step_profile.py 抛异常或分列数字与旧 5 类聚合对不上;③ 重启 Trae 后工具调用异常。

备份存放位置:本主题 backup/,逐条登记见 backup/README.md。

8. 风险说明

风险 缓解
Hook 误报(依赖链被当成独立序列) 只报高置信形态(参数互不相同 / 同一文件重复);宁漏不误报
每条提醒每轮重放推高输出成本 文本 ≤90 字;每会话 ≤2 次、每判据一次
matcher: "*" 与既有 4 条 PreToolUse 并存 JSON 校验 + 重启后观察;既有条目不受影响
全局规则改动使前缀缓存失效 改完开新会话(≈0.35 元/次,一次性)
「可并类合计」把有依赖的步骤也算进去 口径只归类、不判依赖;命中后须人判并逐条说明理由(规则已写明)

附录:交付物清单

文件 说明
merge-coverage-all-20261006/SOP.md 本文件
merge-coverage-all-20261006/backup/README.md + 3 个 .bak.* 备份台账与备份文件
merge-guard/merge-guard.py 新增 Hook(PreToolUse,高置信合并提醒)

来源:/home/rain/Documents/services/token_proxy/merge-coverage-all-20261006/SOP.md | 最后更新:2026-10-06 23:13(由 build/sync.py 同步,请勿在站点侧修改)