SOP:效率迭代(merge-guard 判据 S + 优化关键词提档)¶
主题目录:
efficiency-iteration-20261011/;变更日期 2026-10-11。改动对象:merge-guard/merge-guard.py、config.yaml、proxy.py,以及既有用例脚本thinking-mode-control-20261010/offline_verify.py。
1. 背景¶
本轮用户提出两件相关的事,都是「效率优先」的落地:
- 固化候选要有必然触发点。前一次变更收尾时,
docgen/backup_ledger.py被分 4 次调用(本可--backup a b c一次传全)。但merge-guard既有判据都要求「紧邻连续」(_detect按相邻同工具计数),而这 4 次调用之间夹着Read/Edit,任何判据都不覆盖,即缺必然触发点。用户裁定走「加判据」方案(而非仅靠自觉的规则约定)。 - 效率/成本迭代类请求用 high 档。用户在关思考后感到判断力不足,要求「涉及效率与成本迭代」的请求自动升 high,因为这类内容需要判断力;效率优先于成本。
2. 目标¶
- 新增
merge-guard判据 S:同一 python 脚本在窗口内被RunCommand调用 ≥2 次 → 强制拦截,提示「确认是否支持一次传多个参数」; config.yaml的thinking.escalate新增optimize_hints(8 词),命中即升 high;proxy.py的档位判定新增「优化关键词」分支,reason记为optimize;- 既有判据与三档结构不变。
3. 前置条件¶
- 可写
merge-guard/merge-guard.py、config.yaml、proxy.py;备份已生成(见第 7 章); ./venv/bin/python(跑代理与 thinking 用例)、系统python3(跑 Hook 与判据用例);systemctl --user可用。
4. 影响范围¶
- 组件:
token-proxy(systemd --user,0.0.0.0:8788)+merge-guardPreToolUse Hook; - 代理需重启(改了
config.yaml/proxy.py);Hook 脚本改动即时生效,无需重启 Trae; - 影响面:① 所有
RunCommand调用都会过判据 S(只认python[3] <脚本>.py形态);② 所有/ds请求的提档判定多一组关键词; /glm、/mi仍由skip_models原样放行,不受影响。
5. 分步操作¶
步骤 0:备份¶
cd /home/rain/Documents/services/token_proxy
python3 docgen/backup_ledger.py efficiency-iteration-20261011 --backup merge-guard/merge-guard.py config.yaml proxy.py
预期:输出三行「备份 …」;台账 efficiency-iteration-20261011/backup/README.md 增三行。
步骤 1:merge-guard 新增判据 S¶
八处改动(同一文件):头注补判据 S 说明、常量段加 SCRIPT_WINDOW_SEC / SCRIPT_MIN / SCRIPT_KEEP、PROMPTS 与 DENY_PROMPTS 各加 "S"、ENFORCE_MARKS 加 "S"、新增 _script_of()、主流程加 script_ts 台账与判定、state 保存加 script_ts。
坑:判据 S 必须按窗口内累计计数,不能沿用 _detect 的「相邻同工具」口径——那正是旧判据覆盖不到本轮场景的原因(调用之间夹了别的工具)。同时要保留「分属不同步」检查:同一步里并行的两条同脚本调用属已合并,不应计。
cd /home/rain/Documents/services/token_proxy
./venv/bin/python -m py_compile merge-guard/merge-guard.py && echo COMPILE_OK
步骤 2:config.yaml 与 proxy.py 加优化关键词¶
config.yaml:escalate 段 hints 后新增 optimize_hints(优化 / 迭代 / 效率 / 成本 / 压成本 / 脚本化 / 合并碎步 / 固化),并同步顶部策略注释(三类 → 四类信号)。
proxy.py:DEFAULT_ESCALATE 加 optimize_hints;parse_escalate_config 的键元组加该项;_apply_thinking_signals 在疑难关键词之后加分支(reason = "optimize");启动摘要加「优化 N 词」。
坑:parse_escalate_config 的键元组漏加会让配置项被静默忽略(配置有、代码不读),表现为「词表写了但不生效」。
cd /home/rain/Documents/services/token_proxy
python3 -c "import yaml; yaml.safe_load(open('config.yaml')); print('YAML_OK')"
./venv/bin/python -m py_compile proxy.py && echo COMPILE_OK
步骤 3:用例脚本补 optimize 覆盖¶
thinking-mode-control-20261010/offline_verify.py:临时配置加 optimize_hints: ["优化", "迭代"];新增 U12(优化词命中 → high)与 U12r(普通叙述 → 保持 low)。
步骤 4:离线验证¶
cd /home/rain/Documents/services/token_proxy
./venv/bin/python thinking-mode-control-20261010/offline_verify.py | tail -2
./venv/bin/python thinking-mode-control-20261010/offline_verify.py | tail -2
预期:两遍均 18/18 通过,逐条一致。
判据 S 用假事件直接喂脚本验证(merge-guard/state/ 下用独立 session,跑完即清):
cd /home/rain/Documents/services/token_proxy/merge-guard
python3 - <<'PY'
import json, os, subprocess, time
def call(cmd, sess):
ev = {"session_id": sess, "agent_id": "solo_agent", "hook_event_name": "PreToolUse",
"tool_name": "RunCommand", "tool_input": {"command": cmd}}
p = subprocess.run(["python3", "merge-guard.py"], input=json.dumps(ev), capture_output=True, text=True)
out = p.stdout.strip()
if not out:
return "静默"
return json.loads(out).get("hookSpecificOutput", {}).get("permissionDecision", "?")
def fresh(s):
for f in os.listdir("state"):
if f.startswith(s):
os.remove(os.path.join("state", f))
CASES = [
("st11", "S1 同脚本第2次", "python3 docgen/backup_ledger.py t1 --backup a", "python3 docgen/backup_ledger.py t2 --backup b", True),
("st12", "S1r 不同脚本", "python3 docgen/a_script.py x", "python3 docgen/b_script.py y", False),
("st13", "S1r2 python -c", 'python3 -c "print(1)"', 'python3 -c "print(2)"', False),
("st14", "S2r 同一步并行", "python3 docgen/same.py a", "python3 docgen/same.py b", False),
]
for sess, name, c1, c2, expect_deny in CASES:
fresh(sess); call(c1, sess)
if not name.startswith("S2r"):
time.sleep(2)
got = call(c2, sess)
good = (got == "deny") if expect_deny else (got != "deny")
print(("PASS " if good else "FAIL ") + name + " 实测=" + got)
fresh(sess)
PY
预期:4 项全 PASS(1 正例 + 3 反例:不同脚本 / python -c / 同一步并行)。
坑(本轮实测踩到):判据写好后第一次跑,判定语句用子串 "应拦" in name,而 "不应拦" 也含该子串 → 四例被误判为 3 FAIL。判据自身要先过反例,改成分支判断后 4/4。
步骤 5:重启代理¶
systemctl --user restart token-proxy ; sleep 2 ; systemctl --user is-active token-proxy
预期:active。Hook 脚本改动即时生效,无需重启 Trae。
6. 验证¶
- 启动日志(
logs/proxy.log,为二进制流须加-a)应含:
grep -a "思考注入" logs/proxy.log | tail -1 | sed 's/.*思考注入=/思考注入=/; s/| 结构指纹.*//'
预期:思考注入=默认={"type": "enabled"}/low | 升档={"type": "enabled"}/high | 最低={"type": "disabled"} | 触发=失败3次/8步·保持10步·疑难12词·优化8词·手动1上1下 | 跳过=glm-5.3*,mimo-* | 拒绝兜底=开(实测 2026-10-11 13:52 已见)。
usage.jsonl逐步的thinking_level/thinking_reason:普通步low/default,优化词命中为high/optimize,疑难词为high/hint。- 判据 C 回归(确认新判据未破坏旧判据):同文件连续两次
Edit,第二次应deny;实测 PASS。 - 观察期:看
optimize触发的频次与成本。要知情:8 个词在效率/成本类对话里高频,实际会显著抬高 high 档占比与成本;若过高,收词或降低触发面。
7. 回滚¶
- 备份位置与台账:
efficiency-iteration-20261011/backup/README.md; - 文件:
merge-guard/merge-guard.py.bak.20261011-134742、config.yaml.bak.20261011-134742、proxy.py.bak.20261011-134742; - 步骤:
cd /home/rain/Documents/services/token_proxy
cp efficiency-iteration-20261011/backup/merge-guard/merge-guard.py.bak.20261011-134742 merge-guard/merge-guard.py
cp efficiency-iteration-20261011/backup/config.yaml.bak.20261011-134742 config.yaml
cp efficiency-iteration-20261011/backup/proxy.py.bak.20261011-134742 proxy.py
systemctl --user restart token-proxy ; sleep 2 ; systemctl --user is-active token-proxy
- 回滚触发判据(任一即回滚):① 启动摘要缺「优化8词」或服务非
active;② thinking 用例未达18/18;③ 判据 S 空转率过高(被拦后原样重发,guard_events.jsonl里action=allow_fp)。 - 只退一半:若只是判据 S 误伤,可把
SCRIPT_MIN调大或把"S"从ENFORCE_MARKS移除(降为提醒);若只是词表太重,删减optimize_hints即可,不必回滚代码。
注意:用例脚本 offline_verify.py 已随本轮在原位更新(新增 U12 / U12r),不在备份范围;整体回滚时需一并改回。
8. 风险说明¶
| 风险 | 缓解 |
|---|---|
| 判据 S 误伤真依赖链(第二次调用依赖第一次输出) | 只认同一脚本路径且要求「分属不同步」;DENY_MAX 1 次即放行,误判最多浪费一次往返;文案写明「确需分次则原样重发」 |
| 优化词表过宽、逼近全程 high | 词表在配置里,增删只需改 config.yaml + 重启,不必动代码;观察期看 optimize 占比再收 |
| 配置项被静默忽略(键元组漏加) | 启动摘要会打印「优化 N 词」,N=0 即说明没读到;用例 U12 可捕获 |
| 判据自身写错(本轮已发生一次) | 判据必须配反例、跑两遍同结果;本次反例为「不同脚本 / python -c / 同一步并行」 |
9. 追加变更:拆分 merge-guard 拦截额度(2026-10-11 同日)¶
背景:用户追问「hook 额度用完后是降级提醒还是完全不生效」,并指出「拦截应以操作为粒度,不是整个会话共用额度」。核查确认:旧口径 DENY_MAX = 1 同时约束两个维度(同一操作指纹、每判据每会话),于是某判据在本会话拦过一次后永久退化(退提醒 → 提醒额满 INJECT_MAX = 2 → 静默),表现为「再也不拦」。
目标:拆成两个独立常量,让「该拦就拦」与「重发放行」同时成立。
| 常量 | 含义 | 取值 |
|---|---|---|
DENY_MAX_FP |
同一操作(指纹)上限,重发即放行 | 1(保持原保护,防拦死) |
DENY_MAX_MARK |
每判据每会话上限,None = 不限 |
None |
改动的四处:常量定义与注释、指纹分支(< DENY_MAX_FP)、每判据分支(DENY_MAX_MARK is None or ...)、头注「三重放行机制」。
验证(独立 session 喂假事件,连跑两遍同结果):
| 用例 | 期望 | 实测 |
|---|---|---|
| R1 判据 A 首次命中 | deny | deny |
| R2 判据 A 换目标后再次命中 | deny(旧口径下会退化为提醒) | deny |
| R3 同指纹重发 | 放行 | 静默 |
旁证(同日实测):本次收尾过程中判据被真机触发两次——判据 S 拦下「同一验证脚本 30 min 内第 2 次调用」、判据 R 拦下「窗口内已 3 次编辑」。旧口径下这两次都不可能出现(额度早已耗尽),即拆额度生效的现场证据。
踩坑(三处):
- 首版用判据 C 验证,但窗口内编辑达 3 次会先触发判据 R 并把同指纹入册,第 2 个文件于是被当「重发」放行,误判为 FAIL。改用判据 A(不依赖指纹、不受 R 干扰)后 3/3。教训:验证某判据时,须排除同窗口内其他判据的干扰。
- 验证命令本身被判据 T 拦下(8 段只读命令无输出上限),改为把逻辑写成脚本文件再执行。
- 判据 S 与幂等复验相冲:判据要求「每项判据跑两遍」,而判据 S 恰好拦「同一脚本 30 min 内第 2 次调用」——第二遍会被自家 Hook 拦下;脚本报错后重试同样中招(首次调用已被登记)。处置:把复验写成「一个脚本内部跑两遍并对比结果」,落在主题目录
verify_deny_scope.py,用新路径执行(新脚本路径不触发 S)。
风险:① 判据额度放开后,同一判据可在一次会话内多次拦截,新增往返;指纹维度仍为 1 兜底。② 判据 R 对「同一条消息里并行发多个编辑」可能误判为「逐步来」——若 Trae 逐个触发 Hook,间隔会大于 STEP_GAP_SEC,此时并行批会被算成分属不同步。原样重发即放行,不阻断。
回滚:backup/merge-guard/merge-guard.py.bak.20261011-140608;或只把 DENY_MAX_MARK 改回 1(不整体回滚)。
9.1 收窄:判据 S 降为提醒 + 排除固定脚本(同日实测后修正)¶
动因:判据 S 上线当天即被真机命中 3 次,全部是误伤:
| 命中对象 | 为何是误伤 |
|---|---|
cost_audit/wrapup.py |
规则强制「每轮收尾必跑」 |
docgen/backup_ledger.py |
变更脚手架,不同主题不同批次、本就不可合并 |
| 幂等复验脚本 | 判据要求跑两遍,第二遍必然触发 |
根因:判据只能看到「同一脚本被再次调用」,看不到「该脚本是否支持批量、这两次是否真可合并」。两处收窄:
- 新增
SCRIPT_EXEMPT = ("wrapup.py", "backup_ledger.py"),_script_of()命中即返回空串(视为不适用); - 从
ENFORCE_MARKS移除"S",由强制降为提醒,DENY_PROMPTS["S"]一并删除。
验证(verify_judge_s_scope.py,3/3 通过):
| 用例 | 期望 | 实测 |
|---|---|---|
wrapup.py 第 2 次调用 |
静默(被排除) | 静默 |
backup_ledger.py 第 2 次调用 |
静默(被排除) | 静默 |
| 普通脚本第 2 次调用 | 提醒、不阻断 | allow |
结论:判据 S 保留为建议级,命中只注入一行提示,由模型自行判断是否真能批量。若后续要恢复强制,须先解决「该脚本是否支持一次传多个参数」的判定问题(当前判据无法得知)。
回滚(9.1 部分):backup/merge-guard/merge-guard.py.bak.20261011-141043。
9.2 再扩排除名单:cost_audit/ 整目录(2026-10-11 用户裁定)¶
动因:9.1 只按脚本名排除了两个脚本,但 cost_audit/ 下都是日常反复调用的常驻工具(agg.py / selfcheck.py / handoff_precheck.py / action_mining.py / step_profile.py 等),同属「设计所需」,逐个列名必漏。
做法:新增 SCRIPT_EXEMPT_DIRS = ("cost_audit/",),在 _script_of() 里按路径片段匹配(兼容相对与绝对路径)。原有的按名名单保留(wrapup.py / backup_ledger.py),因为可能出现 cd cost_audit && python3 wrapup.py 这类不含目录片段的写法。
验证(verify_judge_s_scope2.py,5/5 通过):按名排除 2 项静默、按目录片段排除 2 项(相对路径 + 绝对路径)静默、普通脚本第 2 次仅提醒。
回滚(9.2 部分):backup/merge-guard/merge-guard.py.bak.20261011-142059。
9.3 全部判据降为提醒(2026-10-11 用户裁定,方案 A)¶
动因:用户指出两点。① merge-guard 的判据全是效率目的,没有安全目的,用「拒绝」纠正效率建议属语义错位;② 「删文件 / 写覆盖」是用户明确要求的正常动作,不该被拦。实测支持:当天 4 次强拦全部被原样重放,净亏约 0.05 元(每次 deny 多一个完整请求)。
做法:
ENFORCE_MARKS清空为set()。deny 机制与DENY_PROMPTS完整保留,恢复强制只需把判据字母填回该集合。INJECT_MAX由 2 提到 99。旧值 2 是跨判据总计数,叠加「每条判据每会话至多提醒一次」(fired),实际效果是整会话只轮到 2 条判据;提到 99 后每条判据都能各提醒一次。提醒文本约 100 tok,重放成本不到 0.000002 元/次,不构成约束。
验证(verify_all_remind.py,4/4):判据 A / C / T / S 命中后全部返回 allow(提醒),无一 deny。
回滚(9.3 部分):backup/merge-guard/merge-guard.py.bak.20261011-144456;或把判据字母填回 ENFORCE_MARKS(不整体回滚)。
遗留(未解决):提醒的效果无埋点。deny 有 allow_fp(被拦后原样重放)作为「无效」指标,提醒没有对应量,因此「提醒到底有没有改变行为」目前不可测。可加的代理指标:提醒后 N 步内同一判据是否再次命中(再次命中即视作提醒无效,与 deny 的空转率同型)。
9.4 新增「提醒无效」埋点(2026-10-11 用户裁定,补 9.3 的遗留)¶
动因:9.3 标出的盲区,用户要求补上,让 deny 与提醒能用同一把尺子量。
做法:
- 新增
REMIND_CHECK_SEC = 600(10 分钟观测窗); - state 增
remind_ts({判据: 最近一次提醒的时间戳}); - 注入提醒时记
remind_ts[mark] = now; - 命中判据时,若该判据已提醒过且距上次提醒在窗内 →
_log_event(..., "remind_ineffective")。
验证(verify_remind_instrument.py):同一脚本连调 3 次,返回 ['allow','allow','静默'],台账落 remind 与 remind_ineffective 各至少 1 条,PASS。
已知偏差(引用结论时必须带上):同一判据在窗内再次命中,未必等于「没听提醒」,也可能是不同批次的新违规(例如 K 判据的只读检索本就反复出现)。故这是代理指标,与 allow_fp 同型,不宜当作精确的「有效率」。
回滚(9.4 部分):backup/merge-guard/merge-guard.py.bak.20261011-145111。
9.5 新增判据 H:内联 heredoc 脚本化提醒(2026-10-11 用户裁定,方案 C)¶
动因:用户问「单段命令无输出上限」这条提醒该不该做。先核实数据:
req_shape.jsonl全量 21,473 条 shape,能配对(第 i 步cmd↔ 输出last_tool.len、且上一步为 RunCommand)的 506 对;last_tool.len最小 306 / 中位 1,042 / p90 3,218 / 最大 19,306 字符,≥8000 仅 11 条;- 11 条原文清一色是
cd … && python3 - <<'PYEOF' …内联取证 与python3 cost_audit/wrapup.py收尾报表 —— 大输出是功能必需,加| head会砍掉要的信息。
结论:原「单段命令无输出上限」提醒的两种取法(逐次 / 聚合)都只指向不该动的命令,作废。真信号是:大输出源集中在反复内联 heredoc 取证,而判据 S 只认 python[3] <脚本>.py、对内联形态完全失明。遂改口径(方案 C),补判据 H。
做法(merge-guard/merge-guard.py):
- 新增
INLINE_KEY = "<inline-python>"桶键哨兵; - 新增
_inline_of():认python[3] [- ]<<TAG(heredoc),不认-c/-m/<脚本>.py; - 新增
_bucket_of():S/H 共用分桶 —— 真脚本 →(路径, "S"),内联 →(INLINE_KEY, "H"); - S 主流程改用
_bucket_of,hit = (mark, n),mark 随桶类型取"S"/"H"; - 共用
SCRIPT_WINDOW_SEC/SCRIPT_MIN/SCRIPT_KEEP与同一script_ts台账,靠桶键区分、互不干扰计数;mark="H"独立去重(每会话 H 至多提醒一次); - 新增
PROMPTS["H"](脚本化文案);H 不在ENFORCE_MARKS,故只提醒、不加DENY_PROMPTS(避免死代码)。
验证(verify_judge_h.py,内部跑两轮对比):5/5 通过、两轮一致 —— 内联第 2 次 → allow/「脚本化」;仅 1 次、-c 两次、脚本+内联混用 → 静默;普通脚本第 2 次 → allow/「批量接口」(S 回归)。
风险:内联取数很常见,SCRIPT_MIN=2 可能偏灵敏;但每会话 H 至多提醒 1 次,且 9.4 埋点会量化其 remind_ineffective,可据实再收。
回滚(9.5 部分):backup/merge-guard/merge-guard.py.bak.20261011-151922。
附录:交付物清单¶
| 文件 | 说明 |
|---|---|
SOP.md |
本文档 |
backup/merge-guard/merge-guard.py.bak.20261011-134742 |
改前 Hook 脚本(新增判据 S 之前) |
backup/merge-guard/merge-guard.py.bak.20261011-140608 |
改前 Hook 脚本(拆额度之前) |
backup/config.yaml.bak.20261011-134742 |
改前配置 |
backup/proxy.py.bak.20261011-134742 |
改前实现体 |
backup/README.md |
备份台账 |
verify_deny_scope.py |
拆额度幂等复验脚本(内部跑两遍对比,两遍均 ['deny','deny','静默']) |
verify_judge_s_scope.py |
判据 S 收窄验证(排除固定脚本 + 普通脚本仅提醒,3/3 通过) |
verify_judge_s_scope2.py |
判据 S 排除名单扩展验证(按名 + 按 cost_audit/ 目录,5/5 通过) |
verify_all_remind.py |
全部判据降为提醒的验证(A/C/T/S 命中均返回 allow,4/4 通过) |
verify_remind_instrument.py |
「提醒无效」埋点验证(3 次连调 + 台账落账,PASS) |
verify_judge_h.py |
判据 H 验证(S/H 分桶不串 + S 回归,5/5 ×2 轮一致) |
backup/merge-guard/merge-guard.py.bak.20261011-151922 |
改前 Hook 脚本(新增判据 H 之前) |
backup/merge-guard/merge-guard.py.bak.20261011-145111 |
改前 Hook 脚本(加埋点之前) |
backup/merge-guard/merge-guard.py.bak.20261011-144456 |
改前 Hook 脚本(清空强制判据之前) |
backup/merge-guard/merge-guard.py.bak.20261011-142059 |
改前 Hook 脚本(扩排除名单之前) |
backup/merge-guard/merge-guard.py.bak.20261011-141043 |
改前 Hook 脚本(收窄判据 S 之前) |
thinking-mode-control-20261010/offline_verify.py |
既有思考用例,本轮增 U12 / U12r(原位) |
来源:
/home/rain/Documents/services/token_proxy/efficiency-iteration-20261011/SOP.md| 最后更新:2026-10-11 15:19(由build/sync.py同步,请勿在站点侧修改)