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. 目标¶
- 规则层:
rule-agent-efficiency.md新增「动作类型 × 合并性」全覆盖矩阵(12 类 + 可合并性 + 强制语)。 - 口径层:
cost_audit/step_profile.py分列扩为动作全集 12 类,每类带可合并性,并给出 「可并类合计」;旧 5 类聚合保留作跨轮可比。 - Hook 层:新增
merge-guard/merge-guard.py(PreToolUse),对高置信的「本可合并却逐步来」 序列注入一行极短提醒;hooks.json挂载。 - 验收: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同步,请勿在站点侧修改)