SOP:req_shape 命令特征埋点(C2)+ 「重启不中断输出流」定死 + HANDOFF 过时表述修正¶
主题目录:task-side-metric-20261008/|执行时间:2026-10-08|类型:C 类变更(改 proxy.py + 全局规则 + 项目档案)
1. 背景¶
三件事同轮处理,前两件有共同病因——结论没有被实测校验,却长期当事实用:
- C2:收尾自查的「任务侧效率」行无机检。规则早已注明:现有日志(
usage/req_shape/tool_timing)均不含命令文本(req_shape.shape只有工具名),代理侧取不到「本轮是否跑部署器 / 远程批量」的客观判据 → 该口径「有机检之名、无验收之实」。要升机检,须先在proxy.py记命令特征。 - 「重启代理会打断在途流式回答」是错的。该结论写进了 HANDOFF §4 红线、规则《攒批重启》理由①、
BASELINE.md§3 命令速查,但从未实测。用户 2026-10-08 实测推翻,并指出「影响好多次」。 - HANDOFF 两处过时表述:§3.2 首行仍写「写入前置 v4」(已是 v7);§3.1 过账行仍写「总账 1.1 待办型仅 1 项 #32」(#32 已关闭、1.1 已清空)。
2. 目标¶
req_shape.jsonl的shape新增cmd字段:{n, len, remote, deployer, heads},不落命令全文;执行步有值、非执行步为null。- 「重启不中断输出流」在 HANDOFF / 规则 / BASELINE 三处同步为正确结论,并留痕为已证伪 #14。
- HANDOFF 两处过时表述清零。
3. 前置条件¶
proxy.py改动前必须备份 + 离线验证(HANDOFF §4 红线)。./venv/bin/python可用(proxy.py依赖yaml);系统python3用于只读取数。- 重启代理:
systemctl --user托管(run.sh与 systemd 互斥)。 - 执行前检查:
bash systemctl --user is-active token-proxy # 预期 active test -x ./venv/bin/python && echo venv-ok python3 -m py_compile proxy.py && echo compile-ok
4. 影响范围¶
| 项 | 内容 |
|---|---|
| 主机 | 本机 dell-R620(单机) |
| 组件 | proxy.py(重启即生效);全局规则 rule-agent-efficiency.md(每步重放);项目档案 HANDOFF.md / BASELINE.md |
| 是否需停服 | 需重启代理;实测不会中断在途输出流(本次二次实证:重启后本会话继续正常) |
| 日志影响 | req_shape.jsonl 每行 shape 多一个 cmd 键(向后兼容:旧记录无该键,读取端须用 .get) |
| 隐私 | 只落条数 / 长度 / 两个布尔 / 前 3 个首词,不落命令全文(有 U9 用例断言) |
5. 分步操作¶
步骤 1:备份¶
TS=$(date +%Y%m%d-%H%M%S)
D=/home/rain/Documents/services/token_proxy/task-side-metric-20261008/backup
mkdir -p "$D/token_proxy/docs" "$D/trae-cn/user_rules"
cp -p docs/HANDOFF.md "$D/token_proxy/docs/HANDOFF.md.bak.$TS"
cp -p docs/BASELINE.md "$D/token_proxy/docs/BASELINE.md.bak.$TS"
cp -p proxy.py "$D/token_proxy/proxy.py.bak.$TS"
cp -p ~/.trae-cn/user_rules/rule-agent-efficiency.md "$D/trae-cn/user_rules/rule-agent-efficiency.md.bak.$TS"
步骤 2:改 proxy.py(C2)¶
三处:
- 常量区新增
EXEC_TOOLS/REMOTE_HINTS/DEPLOYER_HINTS/CMD_HEAD_LIMIT; - 新增
_arguments_command_text()与_tool_command_features()(只输出特征,不输出原文); build_shape_fingerprint():默认值加"cmd": None,在"从尾部向前找最近一次工具调用"处一并取cmd。
坑:arguments 是 JSON 字符串且可能不是合法 JSON(工具调用被截断等)——解析失败必须返回空串、按"无命令"处理,不得抛错(U8 用例锁死)。
坑:heads 要切 ; / && / || / | / 换行,且去重 + 上限 3(U10 用例锁死)。
隐私:函数内可取原文,但返回值只含聚合量;U9 断言特征 JSON 里不含原文子串。
步骤 3:改全局规则(重启结论)¶
rule-agent-efficiency.md 的《同一共享服务的多处改动》行:理由由两条改为一条,并显式标注旧理由①已证伪:
理由只剩明确成立的一条:重启清掉内存态(
thinking_stages/ 缓存探测快照)→ 该会话的空闲保护缺席一次。 旧理由「重启打断在飞的流式请求、客户端须重发」已于 2026-10-08 由用户实测推翻——重启不会中断输出流,禁止再用它当理由。
步骤 4:改项目档案¶
HANDOFF.md§4 红线:「重启代理会打断在途流式回答」→「重启代理不会中断在途输出流(2026-10-08 用户实测,推翻旧结论)」;HANDOFF.md§3.2 删 v4 过时行;§3.1 过账行改为「1.1 待办型已清空」;BASELINE.md:§3 命令速查注释同步;§2.1「已证伪结论」表追加 #14(留痕,不得静默删);§5 修订记录加一行。
步骤 5:离线验证¶
./venv/bin/python task-side-metric-20261008/offline_verify_cmd_features.py # 15 项(含 6 个反例)
python3 -m py_compile proxy.py
步骤 6:重启代理¶
systemctl --user restart token-proxy && echo "[高危] 已重启 token-proxy"
sleep 3
python3 -c "import urllib.request;print(urllib.request.urlopen('http://127.0.0.1:8788/healthz', timeout=5).status)"
实测记录:本次重启后本会话未中断,命令正常返回、对话继续 → 与"不中断输出流"结论一致(第二次实证)。
6. 验证¶
| # | 判据 | 命令 | 期望 |
|---|---|---|---|
| V1 | 纯函数正反例 | ./venv/bin/python task-side-metric-20261008/offline_verify_cmd_features.py |
末行 全部通过(15 项) |
| V2 | 语法 | python3 -m py_compile proxy.py |
rc=0 |
| V3 | 服务健康 | 见步骤 6 的 healthz | 200 |
| V4 | 真机落字段 | python3 -c "import json;lines=open('logs/req_shape.jsonl').read().splitlines();s=json.loads(lines[-1])['shape'];print('cmd' in s, list(s.keys()))" |
True 且 keys 含 cmd |
| V5 | 执行步有值 / 非执行步为 null | 见下 | RunCommand 步 cmd 非空;SearchReplace/Read 步为 null |
V5 取数(也是「任务侧效率」的只读消费端):
python3 -c "
import json
lines=open('logs/req_shape.jsonl').read().splitlines()
for line in lines[-5:]:
s=(json.loads(line).get('shape') or {})
print('tool=', s.get('tool_names'), '| cmd=', json.dumps(s.get('cmd'), ensure_ascii=False))
"
实测结果(重启前后同工具对比,直接证明新代码生效):
tool_names= ['RunCommand'] | cmd= null ← 重启前(旧代码)
tool_names= ['RunCommand'] | cmd= {"n": 1, "len": 316, "remote": 0, "deployer": 0, "heads": ["echo","systemctl","sleep"]} ← 重启后
tool_names= ['SearchReplace'] | cmd= null ← 非执行步
幂等复验:V1 连跑两遍,两遍 全部通过。
7. 回滚¶
TS=20261008-153344
D=/home/rain/Documents/services/token_proxy/task-side-metric-20261008/backup
cp -p "$D/token_proxy/proxy.py.bak.$TS" /home/rain/Documents/services/token_proxy/proxy.py
cp -p "$D/token_proxy/docs/HANDOFF.md.bak.$TS" /home/rain/Documents/services/token_proxy/docs/HANDOFF.md
cp -p "$D/token_proxy/docs/BASELINE.md.bak.$TS" /home/rain/Documents/services/token_proxy/docs/BASELINE.md
cp -p "$D/trae-cn/user_rules/rule-agent-efficiency.md.bak.$TS" ~/.trae-cn/user_rules/rule-agent-efficiency.md
systemctl --user restart token-proxy && echo "[高危] 已回滚并重启"
回滚后验证:shape 不再含 cmd 键;HANDOFF §4 红线恢复旧措辞。
注意:req_shape.jsonl 是不可再生日志,回滚不动它——已写入的 cmd 字段保留(新字段向后兼容,无读取端会因此报错)。
8. 风险说明¶
| 风险 | 缓解 |
|---|---|
改 proxy.py 影响流式转发 |
改动只在 build_shape_fingerprint 内加一个纯聚合字段,不碰任何转发 / 流式路径;离线 15 项 + 真机 healthz 200 双证 |
解析 arguments 抛错导致请求失败 |
全路径 try/except + 结构判定,异常一律返回空串/None(U8 三类反例锁死) |
| 落盘体积膨胀 | 每步仅增 ~60 字节(5 个聚合字段);实测单条 cmd JSON 约 60~80 字节 |
| 泄露命令内容 | 只落条数 / 长度 / 两个布尔 / 前 3 首词;U9 断言不含原文;heads 也只在首词层面(如 curl,不含 URL) |
| 规则改动打碎前缀缓存 | 已知代价(常驻规则每步重放);本次为必要修正,改后本会话即生效 |
9. 追加变更(同日下午):cmd 指标接入 wrapup.py¶
C2 只做了埋点,消费端仍缺 → 收尾自查的「任务侧效率」依然靠自判。本节把消费端接上(明细见 docs/SOP.md 第五十九节):
| 项 | 内容 |
|---|---|
| 新增 | cost_audit/wrapup.py:task_side_stats()(汇总 cmd)+ should_report_task_side()(机检触发) |
| 输出 | 仅当本轮有远端 / 部署器命令时追加:命令步 / 命令调用 / 远端步 / 部署器步 / 重复 heads;纯本地轮不输出 |
| 规则 | 《任务侧效率》节表头改为机检可得五字段;旧「条件触发(自判)」废除;「上限须知」同步 |
| 备份 | wrapup.py.bak.20261008-153826、rule-agent-efficiency.md.bak.20261008-153826 |
| 验证 | 用例 13 项 ×2 全通过;反例端到端(纯本地轮 → 不输出)+ 正例端到端(造 3 行 remote=1 → 输出且数值正确) |
仍不可机检(如实):SSH 建连数(无连接数埋点)、精确「可省重复」(cmd 只存首词)。
9.1 首次真机使用即暴露的判据缺陷(已修)¶
现象:接入后第一次跑收尾自查,本轮明明只有本地命令,却报「部署器步 = 1」。
根因:DEPLOYER_HINTS 原用朴素子串 "deployer" in text 匹配。命中的那条命令是我构造测试数据的 heredoc,内含字面量 "deployer": 0——命令里"提到"关键词 ≠ "跑了"该类命令。
修复:新增 _hint_hit(),改为按 token / 路径段边界匹配(空白切 token、/ 切段,任一段以关键词开头才算命中);REMOTE_HINTS 同步去掉尾空格(与段匹配冲突,曾致 3 项用例失败)。
对照证据(真机,同形态命令、修复前后):
| 时间 | 命令形态 | 旧代码 | 新代码 |
|---|---|---|---|
| 15:39:47 | heredoc 内含 "deployer": 0 |
deployer=1 ❌ 误报 |
— |
| 15:42:36 | python3 -c "print({'deployer': 0, 'remote': 1})" |
— | deployer=0, remote=0 ✅ |
用例补强:U12 / U13(字面量不得命中)+ U14 / U15 / U15b(路径段与 token 命中)。
方法论:这正是「判据要过反例」的价值——离线用例第一版没覆盖"命令里提到关键词"这一形态,靠真机首次使用才暴露。
迭代快照:proxy.py.bak.20261008-154154(含 cmd 但未落 _hint_hit 的中间态)。回滚点统一为 proxy.py.bak.20261008-153344(原始无 cmd 版)。
9.2 收尾自查「费用」口径改为当轮(2026-10-08 用户要求)¶
诉求(原话):「我不想要总计的命中、未命中输出、输出思考占比,我想要的就当轮任务的」。
改法:wrapup.py 费用格——金额仍给「本轮 / 本会话」两个数(对照仍有价值),
但四个百分数(命中 / 未命中 / 输出 / 思考占输出比)一律取本轮;规则《收尾自查》表与说明行同步。
坑(量纲):这四个百分数是金额占比(不是 token 占比),故本轮需按峰谷逐条取价算三段金额;
首版改动曾把 token 数(cur_hit)传给 cur_share,量纲错、会得出无意义百分数,已修正并加注释。
验证(异源对照,完全一致):
| 来源 | 结果 |
|---|---|
wrapup.py 输出 |
0.279/2.748 | 52%/10%/38%/70%,129 |
独立复算(自写脚本按 IDLE_SECONDS=900 切本轮) |
步=11 hit=3,605,632 miss=14,048 out=13,333 reason=9,371 思考占输出=70% |
| 手验金额占比 | 命中 0.1442 / 未命中 0.0281 / 输出 0.1067 = 0.279 → 52% / 10% / 38% ✓ |
幂等:两遍输出一致。备份:wrapup.py.bak.20261008-154745。
9.3 「命中 / 未命中」改为输入侧独立 100%(2026-10-08 用户要求,紧接 9.2)¶
诉求(原话):「我要的是命中/未命中 这个合起来是 100%,这个是输入侧的,不需要你完全统计…… 你现在的统计是将输出侧的也加进来算了」。
根因(9.2 埋下的):9.2 只把口径从「本会话」改到「本轮」,分母没动——仍是总成本(命中+未命中+输出),
于是命中+未命中+输出 = 100%,输出被折进了「输入侧」的 100% 里。用户看到的 53%/9%/38%/63% 中,
38% 是输出占总成本的比,与前两项不同分母,所以看起来「命中+未命中 ≠ 100%」。
改法(wrapup.py 费用格):
- 命中% / 未命中% 分母收窄为输入侧金额(cur_cost_hit + cur_cost_miss)→ 两项合计 100%;
- 去掉原「输出%」(不再折进同一分母);
- 保留「思考占输出比」(输出侧 reasoning_tokens / completion_tokens,与输入侧不同分母)。
- 规则《收尾自查》四处同步:费用行、形态示例行、形态说明行、B 组异常判据 +「输入侧占比 + 未命中归因」行。
验证(异源复算 + 幂等,完全一致):同刻同数据 —— wrapup.py 输出 0.194/0.352 | 42%/58%/69%,15;
独立脚本按同一 active_tail 复算 命中42% 未命中58% 合计100% 思考占输出69%,逐项一致;两遍输出一致。42+58=100 ✓。
备份:wrapup.py.bak.20261008-155628(同批含规则 / SOP)。
10. 追加变更(当晚):HANDOFF 落点核验从答复撤出、改挂写前拦截¶
动因(2026-10-08 用户裁定):原「每轮带出」把落点核验打进给用户的收尾答复,用户原话: 「我想的是你自己闭环,不需要我参与,这是写给你看的」——该行既不占用户版面、也不该由用户裁定; 同时不能因此留下漏洞(此前用户已纠过:旧触发点挂在「本轮动 HANDOFF」上,越线时靠「不写」即可绕过核验)。
改法:
1. cost_audit/wrapup.py:删除每轮输出段;随之删除唯二调用点所在的 handoff_path() / handoff_status() /
常量 HANDOFF_MAX_UP / 未使用的 from pathlib import Path(净减 1 285 B)。
2. handoff-landing-guard/handoff-landing-guard.py:新增 _landing_hint() —— 在判定要拦时用 importlib
加载 handoff_precheck 并 scan(),把「该落候选 N 行 / 合计 B」并进 deny 理由。异常 / 取不到 → 返回空串
(宁漏不误,绝不因此放行或崩溃)。
3. skill writing-handoff:验收行两处改为「落点结论不进答复」。
验证:
| 项 | 命令 | 结果 |
|---|---|---|
| 既有离线用例 | python3 task-side-metric-20261008/offline_verify_handoff_gate.py |
11 通过 / 0 失败 |
| 正例(探针 20.6 KB + 净增 400 B) | printf '{…}' \| python3 handoff-landing-guard/handoff-landing-guard.py |
deny,理由含「落点核验:候选 360 行 / 合计 20700 B」 |
| 幂等复验(换 session 重跑) | 同上 | 输出逐字一致 |
坏样本(脚本复制到 /tmp,同目录无 cost_audit) |
同上 | 仍 deny,理由不含「落点核验」→ 静默降级、不误放行 |
| 死代码 / 语法 | grep -n 'handoff_status\|handoff_path\|HANDOFF_MAX_UP\|pathlib' cost_audit/wrapup.py;py_compile |
无命中;编译通过 |
| 答复侧 | python3 cost_audit/wrapup.py |
输出不再有「HANDOFF 落点」行 |
残留(已知):_landing_hint 每次被拦都会重扫一遍 HANDOFF(~9 KB,量级可忽略),未做缓存。
「该写却完全没写」这一情形无机器兜底(Trae 无「最终答复」事件)。
备份:TS 20261008-222232(两个脚本)。台账:task-side-metric-20261008/backup/README.md。
补充(同日 22:34):第三种 —— 写时顺带报轮次(用户裁定「加第三种」)
reason is None(放行)分支新增 _offer_ctx():输出 allow + additionalContext,内容
「【HANDOFF 写前】距上次更新已 N 轮 | 当前 X KB | 落点核验:候选 K 行」;越线时 _turn_txt() 并入 deny 理由。
新增 _epoch() / _turn_txt()(倒读 req_shape.jsonl 尾部 256 KB、遇 ts <= mtime 即停,
按不同 turn 值计数)/ _emit_allow_ctx();去重键 ctx_key = 轮次文案 + 水位档(512 B),
写在既有 state/<session> 里。
| 用例 | 期望 | 实测 |
|---|---|---|
| 健康水位 8 KB + 净增 400 B | allow + additionalContext 含轮次 |
✅ 「距上次更新已 36 轮 | 当前 8.0 KB | 候选 0 行」 |
| 同 session 第二次同场景 | 空输出(去重) | ✅ 输出为空 |
| 偏高 9.5 KB + 净增 | deny + 含轮次 | ✅ 「当前 9.5 KB 已在偏高水位…距上次更新已 36 轮」 |
坏样本:脚本隔离到 /tmp(无 logs/、无 cost_audit/) |
仍放行、不崩 | ✅ 文案自动省去轮次与候选,只剩「当前 8.0 KB」 |
| 既有 11 项用例 | 全过 | ✅ 11 / 0 |
边界:轮次是提示值(turn 客户端生成、可能复用),不参与任何拦截判定;「该写却完全没写」仍无机器兜底。
11. 追加变更(当晚 23:11):拦截额度 999 → 3(每个操作拦三次就放行)¶
动因:用户拿另一会话(6ac59a3fc1804934)的截图来问「是不是硬拦没放行导致任务没完成」。
查底层日志(logs/guard_events.jsonl):该会话 22:47–22:59 被拦 45 次、同一操作被拦 4 遍,
最后一次工具调用停在 22:59:27 → 任务确实被拦停。根因是上午「上强度」把 DENY_MAX 改成 999
(实质无上限),且 deny_repeat 分支不读该额度。
改动:DENY_MAX 999 → 3;denied list → dict(存每指纹已拦次数,旧格式兼容读作 1);
deny_repeat 加额度判定,满 3 次即 allow_fp 放行。
验证:连发 6 次同操作 → deny → deny_repeat → deny_repeat → 放行 ×3(日志一致);
offline_verify_strict_guards.py 31/0;verify_allowfp.py 5/0;
offline_verify_guard_i.py 5/1(改前改后一致,既有失败)。
备份:TS 20261008-231109。明细:docs/SOP.md 第七十五节。
来源:
/home/rain/Documents/services/token_proxy/task-side-metric-20261008/SOP.md| 最后更新:2026-10-08 23:12(由build/sync.py同步,请勿在站点侧修改)