SOP:cache_probe 探测体改用「本次请求前缀」+ 踩坑账降噪¶
- 主题目录:
probe-prefix-fix-20261010/|实施日期:2026-10-10|实施人:Agent(用户授权) - 实现体:
proxy.py(新增CacheProbe._probe_snap(),CacheProbe.check()换一行调用)、cost_audit/baseline_check.py(actionable()增设输出偏离的可行动类白名单) - 关联:
cache-probe-20261008/SOP.md(探针本体)、docs/BASELINE.md§2.1、cost_audit/ops_incidents.md
1. 背景¶
2026-10-10 00:20 实测一次漏拦:会话空闲 75 分 24 秒后,探针确实触发了,却报「缓存有效」放行, 紧接着的真实请求未命中 94%(命中 21760 / 372479)。
根因是探针探错了对象:旧口径把上一次请求的快照前缀发给上游(_send_probe(..., snap, ...)),
它只能回答「上次那段前缀还在不在」。本次请求的前缀已变(tools 定义指纹 629b34da81 →
f1fe0bd02a,27 个工具的名字与顺序不变),于是探针那个旧前缀照样命中 99.7%,
而真实请求在 tools 段断开,只命中 system 那约 21760 token。
同日用户判定 wrapup 回吐的两条踩坑为噪音:跑|中 输出 3711 tok、跑|未知 输出 5280 tok。
它们是模型自己写的取证脚本与说明,out_tok 与操作规模无因果关系。
2. 目标¶
- 探测体改用本次请求的
messages/tools;上次快照只用来算空闲时长与 chars/token 比值。 - 离线用例覆盖「前缀已变」形态,并给出可证伪的反例。
- 踩坑账「输出」偏离只在编辑类(
改/增)入账,其余类只沉淀基准。
3. 前置条件¶
- 已备份
proxy.py/cost_audit/baseline_check.py/cache-probe-20261008/offline_verify.py(见 §7); ./venv/bin/python -m py_compile proxy.py cost_audit/baseline_check.py cache-probe-20261008/offline_verify.py通过;- 离线验证 15/15 通过(
cache-probe-20261008/offline_verify.py); - 重启代理会打断在途流式回答:选无在跑任务时执行。
4. 影响范围¶
| 项 | 内容 |
|---|---|
| 主机 | 本机(dell-R620) |
| 组件 | proxy.py(探针探测体)、cost_audit/baseline_check.py(收尾分析器) |
| 服务 | systemd 用户服务 token-proxy.service(8788);proxy.py 需重启才生效 |
| 是否停服 | proxy.py 需重启(约 1~2 s);baseline_check.py 是离线脚本,下次收尾即生效 |
| 窗口期 | 无在跑任务时 |
| 影响面 | 全局:所有经该代理的会话,但只在「该会话空闲 ≥1 h 后的下一次请求」上动作 |
| 成本 | 前缀一致时探测花费不变;前缀已变(罕见)时探测多花约 0.03~0.05 元/次,换来正确判定 |
5. 分步操作¶
步骤 1:探测体改用本次请求前缀(proxy.py)¶
_prepare_body() 早已把本次请求的前缀挂在 meta(_probe_messages / _probe_tools,见
proxy.py 第 1690 行),因此不再需要把上次快照传进 _send_probe。
新增 CacheProbe._probe_snap():本次 messages / tools + 上次快照的 chars / prompt_tokens
(这两项只用于反推 chars/token 比值,与本次前缀长度无关)。本次前缀缺失时退回上次快照。
cd /home/rain/Documents/services/token_proxy
grep -n '_probe_snap\|probe_snap = self._probe_snap' proxy.py
预期:三处命中——方法定义 1 处、check() 里构造 1 处、_send_probe( 的实参 1 处。
坑:chars / prompt_tokens 不能改用本次请求的字符数——那两个字段是上一次请求的实测值,
比值近似即可;缺了会退化成 2 字符/token 的保守估算(截断会更短,不影响判定方向)。
步骤 2:离线用例覆盖「前缀已变」(cache-probe-20261008/offline_verify.py)¶
U12:同会话先发 tools=Read 的请求建快照,隔 3 s 再发 tools=Grep 的请求,断言探测体里的 tools
是 Grep(本次)而非 Read(快照),且上游判 miss 时返回 500。U12r 为反例:断言旧口径下
探测体必是 Read,证明该断言可被证伪。
cd /home/rain/Documents/services/token_proxy
./venv/bin/python cache-probe-20261008/offline_verify.py
预期:结果:共 15 项,失败 0 项。
坑:断言取 tools 而不是消息正文——探测体从第 0 条起按 token 预算截断,末尾新增的消息
可能被截掉,用消息正文断言会假失败。
步骤 3:踩坑账降噪(cost_audit/baseline_check.py)¶
actionable() 增设白名单 ACTIONABLE_OUT_CLASSES = ("改", "增"):kind == "输出" 且操作类
不在白名单内的异常,只沉淀基准、不入账。同时同步 ops_incidents.md 头部口径与
append_incidents() 写新文件时的表头文案。
cd /home/rain/Documents/services/token_proxy
./venv/bin/python cost_audit/baseline_check.py --selftest
预期:selftest PASS(含新增两条断言:跑类输出偏离不入账、编辑类输出偏离入账)。
步骤 4:重启代理使改动生效¶
systemctl --user restart token-proxy
sleep 2
curl -s http://127.0.0.1:8788/healthz # 预期 {"status":"ok","routes":["ds","glm","mi"]}
tail -n 3 /home/rain/Documents/services/token_proxy/logs/proxy.log
预期:启动行含 缓存探测=开启(空闲>=3600s, 探前 50000 tok, 命中<90% 拦截, 超时 15s)。
注意:本步会打断当前正在进行的流式回答。
6. 验证¶
| 项 | 命令 | 第 1 遍 | 第 2 遍(幂等复验) |
|---|---|---|---|
| 语法 | ./venv/bin/python -m py_compile proxy.py cost_audit/baseline_check.py cache-probe-20261008/offline_verify.py |
PYCOMPILE-OK | 同 |
| 离线用例 | ./venv/bin/python cache-probe-20261008/offline_verify.py |
15 项 0 失败 | 15 项 0 失败 |
| 收尾分析器自测 | ./venv/bin/python cost_audit/baseline_check.py --selftest |
selftest PASS | selftest PASS |
| 服务 | curl -s http://127.0.0.1:8788/healthz |
{"status":"ok",...} |
同 |
线上生效验证(需真实场景,不主动制造):
grep '缓存探测' /home/rain/Documents/services/token_proxy/logs/proxy.log | tail -n 5
预期:平日无输出;出现空闲 ≥1 h 的会话后,该行给出 conv=… 空闲=…s 命中=x/y。
失败判据:若再次出现「探针报高命中、紧邻的业务请求却大面积 miss」,说明本次修复未生效,
按 §7 回滚并重新归因。
7. 回滚¶
- 备份位置:
probe-prefix-fix-20261010/backup/,逐条登记见backup/README.md。
只回退探针修复(保留降噪):
cd /home/rain/Documents/services/token_proxy
cp -p probe-prefix-fix-20261010/backup/proxy.py.bak.20261010-003113 proxy.py
systemctl --user restart token-proxy
curl -s http://127.0.0.1:8788/healthz # 预期 {"status":"ok",...}
只回退降噪(保留探针修复):
cd /home/rain/Documents/services/token_proxy
cp -p probe-prefix-fix-20261010/backup/cost_audit/baseline_check.py.bak.20261010-003113 cost_audit/baseline_check.py
更快停用探针(不动代码):把 config.yaml 的 cache_probe.enabled 改为 false 并重启。
回滚后验证:/healthz 返回 200;logs/usage.jsonl 不再新增 "probe": true 记录。
8. 风险说明¶
| 风险 | 缓解 |
|---|---|
| 前缀已变时探测本身要付一次 miss(约 0.03~0.05 元) | 只在空闲 ≥1 h 且前缀已变时发生;换来的是不再漏拦(本次一次漏拦就白付 0.35 元级) |
| 真失效时拦截,用户需压缩后重试 | 与 cache-probe-20261008/SOP.md §8 同口径:阈值 0.90 留有余量,压缩后重建量更小 |
tools 定义随 Trae 端更新而变,会带来一次拦截 |
拦截可重试(回一句「继续」);根因已查明结案:WebSearch 的 description 内嵌当天日期(Today's date is YYYY-MM-DD),跨日必变,diff 证实 27 个工具里仅此一处、两版长度相同;证据与旧定义样本见 tools-hash-20261010/。2026-10-10 用户裁定:保留拦截、不做内容归一(宁可被拦一次后自行压缩或新开,也不想几乎整额付掉重建费) |
| 降噪后「跑」类输出异常不再入账 | 该维度确无入参因果;命令文本长度可由 shape.cmd.texts 另判,与输出 token 无关 |
附录:本次证据¶
| 时刻 | 记录 | 内容 |
|---|---|---|
| 2026-10-09 23:04:40 | logs/usage.jsonl |
上一轮最后一条,命中 371200/371674(99.9%) |
| 2026-10-10 00:20:04 | logs/usage.jsonl |
探测 probe=true,命中 73984/74200(99.7%)→ 放行 |
| 2026-10-10 00:20:14 | logs/usage.jsonl |
真实请求命中 21760/372479(5.8%) |
| 同时刻 | logs/req_shape.jsonl |
tools_hash 629b34da81 → f1fe0bd02a;27 个工具名与顺序不变;system 与前 8 条消息指纹一致 |
来源:
/home/rain/Documents/services/token_proxy/probe-prefix-fix-20261010/SOP.md| 最后更新:2026-10-10 01:08(由build/sync.py同步,请勿在站点侧修改)