跳转至

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. 目标

  1. 探测体改用本次请求的 messages / tools;上次快照只用来算空闲时长与 chars/token 比值。
  2. 离线用例覆盖「前缀已变」形态,并给出可证伪的反例。
  3. 踩坑账「输出」偏离只在编辑类(改 / 增)入账,其余类只沉淀基准。

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 同步,请勿在站点侧修改)