Skip to content

Latest commit

 

History

History
444 lines (370 loc) · 43.6 KB

File metadata and controls

444 lines (370 loc) · 43.6 KB

v2.29 同角色顾问 ROI 度量册(第三次拆册,2026-09-05)

计划 v2.29 步 2「ROI 度量先行:不达标即止步入账」的冻结登记。母册链: EVAL-SET.md → EVAL-SET-M5-3.md → EVAL-SET-M5-CLOSE.md(303 行)→ 本册。度量定义唯一权威 = .ccm/similar-spec-2026-09-05.md(本机件,§三 词袋 / §四 PPMI / §七 评估)。本册每个数字直读 contracts/eval/similar-sample-v1.json(样本)、contracts/eval/similar-oracle-v1.json(oracle)与 cli/tests/it/eval_similar_review/sample.json(仲裁记录),由渲染脚本一次写出后冻结;第二代(留出集, 「留出集复测」节)同形三件带 -v2 后缀;重放门 = CI cargo test --test it -- eval_similar_precision::。本册与三册前身同入冻结集 (frozen_set.rs:不扫芯片、不生成、退出引文门),行号引文一律不写。

仪器(cli/tests/it/similar_replay.rs,常驻 --ignored 腿,release 跑)

五语料各自成库:自仓走产品 walk + scratch 索引(refreshed_index 把词袋写进 bag / df 两表〔步 3,schema 16〕→ similar::reader::Reader 读回每个单元的袋 → 与 similar::file_bags 对同一文本现算的袋逐词对拍,漂移即拒);四份 crosscheck 夹具目录被 ce.toml 排除在自仓外,各自单独成库。排序走持久化的 Postings / Cooc 读者,并与同一批袋建的内存 Corpus / Table 逐位断言相同(一条排序路、两个倒排来源)。每个单元对本库其余单元查两臂 top-k:裸臂 = 自身袋按通道乘子;扩展臂 = 裸臂 + 每个 拼出的词通道词的 top-m PPMI 邻词(权重 ≤ 一半,邻词只计分不成证据)。候选 = 两臂 top-k 并集,每候选一行 六通道命中整数 + 形状全等位 + 同角色位 + 同文件位 + 两臂名次与分数。同角色位 = (N ≥ 1 ∧ C ≥ 1) ∨ (N ≥ 2 ∧ 形状全等), 与步 5 将落在 Haskell CE.Similar 的合取同式(步 2 只在 Rust 镜像以便度量)。

常量 值 常量 值
SIMILAR_REV 1 k(top-k) 5
k1 6/5 b 3/4
idf 定点位 8 分数定点位 16
W_UNIT(查询权重单位) 256 TERM_CAP(PPMI 每单元词上限) 96
MIN_COOC 2 MIN_PPMI(8 位定点 = 2 bit) 512
PPMI_CAP(= 4 bit) 1024 PPMI_SCALE(= 8 bit,扩展权重 = w·min(ppmi, cap)/scale) 2048
TOP_M 3 文档归属 LEAD_GAP / HEAD_GAP 3 / 2
样本配额 self(role 1 / role 0) 35 / 35 样本配额每夹具 7 / 6

这些常量随样本冻结(constants 对象),CI 门 similar_oracle_consistent 断言它们等于仪器当下的常量—— 改一个常量即换一套仪器,旧 oracle 随之作废。

全量普查(树 143cfe0,dirty = true:similar 模块本身在度量时尚未提交;四份夹具是钉住的上游切片,自仓行按文件 sha 锚定)

语料 单元 裸臂 top-1 role=1 扩展臂 top-1 role=1 两臂 top-1 相同 裸臂 top-1 同文件 平均袋长 PPMI 截断单元
self 5378 1114 1092 3968 1376 76 17
go 92 57 54 73 85 90 0
python 125 46 44 98 100 140 0
rust 426 131 123 320 343 109 3
typescript 32 3 3 32 18 150 0

读法:role 位在自仓只对约五分之一的 top-1 亮起,在 typescript 夹具(32 单元)只亮 3 次——夹具太小, 样本配额取不满属实。扩展臂改变的 top-1 是少数(两臂相同列),PPMI 的作用只能在仲裁后看精度差。go 行是 2026-09-26 重量后的读数 (树 a5ac21f,dirty;Go 的单元宇宙随计划 v2.30 步 5b 第 3 / 25 条变,见「Go 语料重量与重仲裁」节),typescript 行是 2026-09-27 重量后的读数(04e9723 之上的 5b-6 工作树,dirty;TS 模块级 const / let / var 成单元,见「TypeScript 语料重量与重仲裁」节), 其余三行仍是 143cfe0 的。

样本(118 个查询 / 695 个候选对〔首冻 700;Go 语料 2026-09-26 重量后 71 对,见「Go 语料重量与重仲裁」节〕,contracts/eval/similar-sample-v1.json)

每库把裸臂 top-1 按 role 位分两层,各层按 sha256("similar|corpus|path|key|nth") 的字典序取前缀到配额—— 秩序由身份决定、与分数无关,仲裁者看不到分数(RM18)。

语料 查询 其中 role=1 候选对
self 70 35 424
go 13 7 71
python 13 7 78
rust 13 7 77
typescript 9 3 45

仲裁(cli/tests/it/eval_similar_review/sample.json)

仲裁者:codex exec gpt-6-astra (reasoning max, read-only sandbox), batches of 24 queries; the prompt carried identities and verbatim source only — no scores, no role bits, no arm placements (RM18)。每批一份 packet 带每个单元的逐字源码(path:start-end key#nth 头), 仲裁者必须读源码后才答;词表 = same_role(两单元在各自程序里扮演同一角色)/ related(同一机制的不同腿、 调用方与被调方、同族不同职)/ unrelated;另标 clone(代码本身近似相同——同角色的平凡形,单独计一档); why ≥ 40 字符否则合并脚本拒收。转录修复 2 处(仲裁者抄错 rank 哈希、 仅当唯一的 ≥ 16 位十六进制前缀匹配时按名修复,逐条列在记录的 transcription_repairs)。

判决 候选对
same_role 178
related 348
unrelated 169
其中 clone = true 64

(首仲裁 177 / 355 / 168 / clone 67;Go 13 题 2026-09-26 重量重仲裁,见「Go 语料重量与重仲裁」节;TypeScript 9 题 2026-09-27 重量重仲裁, 见「TypeScript 语料重量与重仲裁」节。)

结果(contracts/eval/similar-oracle-v1.json summary,CI 门从行重导必须相等)

p@1 = 该臂第 1 名被仲裁为 same_role 的比例(分母 = 该臂有答案的查询);hit@5 = 该臂 top-k 里至少一个 same_role;role 位混淆 = 同角色位对全部候选对的判决(tp / fp / fn / tn)。不报 recall:oracle 只知道 它见过的候选,没有一个单元的全部同角色伙伴,分母不存在。

语料 查询 p@1 裸臂 p@1 扩展臂 p@1 裸臂 role=1 p@1 裸臂 role=0 p@1 裸臂非 clone hit@5 裸臂 hit@5 扩展臂
all 118 66/118 = 55.9 % 64/118 = 54.2 % 39/59 = 66.1 % 27/59 = 45.8 % 40/92 = 43.5 % 75/118 = 63.6 % 77/118 = 65.3 %
self 70 40/70 = 57.1 % 38/70 = 54.3 % 24/35 = 68.6 % 16/35 = 45.7 % 27/57 = 47.4 % 45/70 = 64.3 % 46/70 = 65.7 %
go 13 8/13 = 61.5 % 9/13 = 69.2 % 4/7 = 57.1 % 4/6 = 66.7 % 6/11 = 54.5 % 9/13 = 69.2 % 10/13 = 76.9 %
python 13 4/13 = 30.8 % 3/13 = 23.1 % 3/7 = 42.9 % 1/6 = 16.7 % 3/12 = 25.0 % 4/13 = 30.8 % 4/13 = 30.8 %
rust 13 7/13 = 53.8 % 7/13 = 53.8 % 5/7 = 71.4 % 2/6 = 33.3 % 3/9 = 33.3 % 8/13 = 61.5 % 8/13 = 61.5 %
typescript 9 7/9 = 77.8 % 7/9 = 77.8 % 3/3 = 100.0 % 4/6 = 66.7 % 1/3 = 33.3 % 9/9 = 100.0 % 9/9 = 100.0 %
语料 候选对 role 位 tp fp fn tn role 位精度 role 位对候选的召回
all 695 100 65 78 452 100/165 = 60.6 % 100/178 = 56.2 %
self 424 70 30 49 275 70/100 = 70.0 % 70/119 = 58.8 %
go 71 9 16 7 39 9/25 = 36.0 % 9/16 = 56.2 %
python 78 6 12 2 58 6/18 = 33.3 % 6/8 = 75.0 %
rust 77 9 7 8 53 9/16 = 56.2 % 9/17 = 52.9 %
typescript 45 6 0 12 27 6/6 = 100.0 % 6/18 = 33.3 %

扩展臂的 role=1 / role=0 / 非 clone 切片同在 summary(p_at_1_widened_*),本表只列裸臂切片以免两表同形。

读数(2026-09-05 首仲裁的池 118 / 700;Go 重量后的当下读数见「Go 语料重量与重仲裁」节)

  • 裸臂 top-1 的三分:same_role 67 / related 40 / unrelated 11(118 查询)——顶 1 与查询「同角色或同机制」占 107/118; 67 个同角色顶 1 里 29 个是 clone(平凡形)、34 个与查询同文件。样本按 role 位对半分层,故 67/118 不是自仓 全量的 p@1(自仓 role=1 的 top-1 只占 1114/5378);分层各自的数才是可外推的:role=1 顶 1 同角色 39/59、 role=0 顶 1 同角色 28/59。
  • 42/118 个查询在 700 个候选里没有一个 same_role——这些单元在两臂的 top-5 里都没有同角色伙伴被翻出来 (或本就没有);hit@5 74/118 的另一面是:在 76 个「有同角色候选出现」的查询里,裸臂把它放在第 1 名的有 67 个, 藏在第 2–5 名的 9 个。
  • PPMI 扩展臂(m = 3):27/118 个查询的顶 1 被扩展改变;配对看,裸臂对、扩展臂错 6,裸臂错、扩展臂对 2, 其余 110 个两臂同对(61)或同错(49);hit@5 +1(74 → 75)。在这份 oracle 上,联想扩展对顶 1 是净负(−4/118), 对 top-5 覆盖持平。ppmi_capped_units 自仓 17 / rust 3 / 其余 0。
  • 同角色位的替代合取(对 700 个已仲裁候选,精度 / 对候选的召回):spec 形 (N≥1∧C≥1)∨(N≥2∧形状全等) 0.612 / 0.571;N≥1 0.402 / 0.695;N≥1∧C≥1 0.612 / 0.480;(N≥1∧C≥1∧形状)∨(N≥2∧形状) 0.658 / 0.412; N≥1∧C≥2 0.651 / 0.316;spec ∧ 非同文件 0.678 / 0.333;spec ∨ (D≥2∧形状) 0.483 / 0.627。没有一个替代形 同时高于 spec 形的精度与召回——收紧买精度赔召回、放宽反之。
  • typescript 夹具退化:26 单元里 role=1 顶 1 只有 3 个,抽到的 9 个查询顶 1 全是 clone(非 clone 子集 0/0), 该语料的行只作存在证明,不作精度证据。

裁定(步 2 权限内:阈值、m、k1、b)

  1. k1 = 6/5、b = 3/4 不动——只量过这一组,没有换组的证据;SIMILAR_REV 1 的输入即此。
  2. 同角色合取维持 spec 形——替代形无一双优(上表);步 5 落 Haskell CE.Similar 时逐字沿用, 本册这张表是它的对拍基线。
  3. 地板 60 %(similar_oracle_floors,只升不降):role=1 顶 1 同角色 39/59 = 66.1 %、hit@5 74/118 = 62.7 %、 同角色位对候选的精度 101/165 = 61.2 %,三数各向下取整到一成 = 60 %;重冻结的 oracle 落到线下即是更差的仪器, 门红而不是改数。没有预登记的「达标线」——计划 v2.29 步 2 把线留给度量本身,故这里给的是回归地板,不是验收线。
  4. PPMI 的 m 与默认臂、以及「是否值得往步 3 走」:两者都超出仪器能自答的范围——前者是 #4 的产品形态 (用户三裁点名「稀疏检索 + 仓内 PPMI 联想」),后者是验收线的解释——按 2026-08-07 用户指令经 AskUserQuestion 呈裁。
  5. 用户裁定(2026-09-05):① 让 gpt-6-astra 以本 oracle 为靶尝试调优,能显著增强就采、不能就按此精度继续 步 3;② PPMI 不按「默认开 / 关」二选一,改为从数学与模型两个角度探索更优雅的联想方案再定;③ 顺带回答 「只报不判的顾问怎样才能被更高效地用起来」——调优与探索的结果另起一节追记,本册以上数字是它们要打败的基线。

调优与联想探索(追记 2026-09-05:用户裁定 ①②③ 的结果)

仪器 = cli/tests/it/similar_tune.rs(+ similar_tune_parts/,--ignored,release 跑;gpt-6-astra max 写、本仓第一方复跑;v2.33 W3 起退役,复活配方在「复跑」节): 在冻结 oracle 的候选池上重排——每个配置只对已仲裁的候选重新打分排序(未仲裁的新候选另行导出、不冒充标签), 84 个打分配置 + 16 个同角色谓词;p@1 / hit@5 口径同上表,配对 W : L = 与基线相比顶 1 由错变对 : 由对变错。 生产打分代码与冻结 JSON 一字未动(仪器结束时断言 oracle 文件与读入时逐字节相等)。

先修一个身份缺陷:自仓行的 sha 冻结时算的是本机工作树的字节,而那棵树是混行尾的(CRLF / LF 各一部分)—— 干净检出上 codex 只能对上 13/70 个自仓查询。行身份改为 CRLF 折叠成 LF 后的 sha(identity_sha,仪器与评测器同一所有者; 词袋从不看见 \r,夹具语料在 LF 树上逐字节回放已证),自仓 494 行里 83 行(29 个文件)按同一文件的 LF 字节重识别, 其余行本就是 LF sha;17 行所指的 2 个文件(cli/src/similar/bag.rs, cli/src/similar/stem.rs)在冻结后、提交前又被改过, 原文本不在任何提交里,身份按实保留;复跑时 2 个查询整池跳过、其余池里 6 个候选跳过。四夹具语料 e300386 已钉 LF,一行不动。

语料 冻结查询 / 候选对 复跑对上 部分池 空池
self 70 / 424 68 / 407 2 0
go 13 / 76 13 / 76 0 0
python 13 / 78 13 / 78 0 0
rust 13 / 77 13 / 77 0 0
typescript 9 / 45 9 / 45 0 0
all 118 / 700 116 / 683 2 0

天花板:116 个对上的查询里只有 76/116 个池里存在 same_role 候选——完美重排最多把 p@1 从 66/116 提到 76/116; 调优前写下的显著线「全样本与 role=1 子集各 ≥ +8 且 W ≥ 2L 且无语料变差」在 role=1 子集上按构造就不可达,此处如实记下,不改线。

配置(含义) p@1 role=1 role=0 非 clone hit@5 W : L role=1 W : L hit@5 W : L
baseline(基线 = 生产 BM25(k1 6/5、b 3/4、六通道乘子)) 66/116 39/57 27/59 38/88 74/116 0 : 0 0 : 0 0 : 0
field_binary(按通道分别做长度归一(BM25F 单域特例)+ 查询 tf 截 1) 69/116 40/57 29/59 39/86 76/116 6 : 3 1 : 0 2 : 0
binary_query(只把查询 tf 截 1(重复语法不压过意图)) 68/116 39/57 29/59 40/88 76/116 2 : 0 0 : 0 2 : 0
cap2_query(查询 tf 截 2) 68/116 39/57 29/59 40/88 75/116 2 : 0 0 : 0 1 : 0
field(只按通道分别做长度归一) 64/116 39/57 25/59 35/87 76/116 2 : 4 1 : 1 2 : 0
weighted_jaccard(idf 加权集合 Jaccard(对称重叠)) 67/116 40/57 27/59 37/86 75/116 2 : 1 1 : 0 2 : 1
khalf_bthree(k1 = 1/2) 68/116 38/57 30/59 41/89 75/116 3 : 1 0 : 1 1 : 0
k0(k1 = 0(只看有无)) 61/116 36/57 25/59 37/92 76/116 2 : 7 0 : 3 2 : 0
kbase_b0(b = 0(不做长度归一)) 53/116 32/57 21/59 34/97 76/116 2 : 15 0 : 7 2 : 0
lm100(Dirichlet 语言模型 μ = 100(词通道)) 60/116 37/57 23/59 38/94 73/116 1 : 7 0 : 2 1 : 2
cosine_squared(余弦平方) 63/116 37/57 26/59 37/90 75/116 1 : 4 0 : 2 1 : 0
role_first(先按 role 位再按分数) 67/116 39/57 28/59 39/88 75/116 1 : 0 0 : 0 1 : 0
no_s(去掉结构通道 S) 65/116 38/57 27/59 39/90 72/116 1 : 2 0 : 1 1 : 3
names(只用名字通道) 55/116 35/57 20/59 34/95 75/116 1 : 12 0 : 4 2 : 1
structure(只用结构通道) 49/116 31/57 18/59 28/95 75/116 3 : 20 2 : 10 2 : 1
ppmi_v1(PPMI 扩展臂 = spec §四原形(m 3、≥ 2 bit、≤ 半权重)) 62/116 37/57 25/59 38/92 75/116 2 : 6 0 : 2 2 : 1
ppmi_m1(PPMI m = 1) 65/116 39/57 26/59 39/90 74/116 1 : 2 0 : 0 1 : 1
ppmi_mass4(PPMI 扩展总量 ≤ 拼写词权重的 1/4) 66/116 39/57 27/59 39/89 74/116 0 : 0 0 : 0 0 : 0
ppmi_tie(PPMI 只在裸分相等时决胜) 66/116 39/57 27/59 38/88 74/116 0 : 0 0 : 0 0 : 0
ppmi_rerank5(PPMI 只重排裸臂前 5) 65/116 38/57 27/59 39/90 74/116 2 : 3 0 : 1 0 : 0
translation_quarter(Berger–Lafferty 翻译模型(PPMI 邻词分 1/4 质量,Dirichlet 平滑)) 58/116 35/57 23/59 35/93 75/116 1 : 9 0 : 4 2 : 1
rocchio5(Rocchio 伪相关反馈(裸臂前 5 的质心)) 63/116 37/57 26/59 38/91 75/116 2 : 5 0 : 2 1 : 0
rm3_style5(RM3 形伪相关反馈(前 5)) 65/116 38/57 27/59 40/91 75/116 2 : 3 0 : 1 1 : 0
callee_second(二阶联想 词 → 被调 → 词(路径权 ≤ 1/4)) 66/116 39/57 27/59 38/88 74/116 0 : 0 0 : 0 0 : 0

全部 84 行在仪器输出 all.md(每个语料与「去掉某语料」各一张同列表)。最好的一行是 field_binary:p@1 69/116(基线 66/116,配对 6 : 3), role=1 40/57(基线 39/57),hit@5 76/116(基线 74/116)——+3 / +1,离显著线远。

去掉的语料 用其余四语料选出 在去掉那份上 p@1 配对 W : L
self field_binary 41/68 5 : 3
go binary_query 7/13 0 : 0
python field_binary 4/13 0 : 0
rust field_binary 7/13 0 : 0
typescript field_binary 9/9 0 : 0

联想家族(用户裁定 ②,数学与模型两个角度):

配置 p@1 W : L hit@5 W : L 新候选槽(top-5 里未仲裁的身份) 池内打分 μs 全库检索 μs
ppmi_v1 62/116 2 : 6 2 : 1 0 495862 590837
ppmi_m1 65/116 1 : 2 1 : 1 17 3781 508450
ppmi_mass4 66/116 0 : 0 0 : 0 1 3433 598720
ppmi_tie 66/116 0 : 0 0 : 0 0 3513 614477
ppmi_rerank5 65/116 2 : 3 0 : 0 0 3595 612406
translation_quarter 58/116 1 : 9 2 : 1 281 138352 3342725
rocchio5 63/116 2 : 5 1 : 0 27 5282 516114
rm3_style5 65/116 2 : 3 1 : 0 34 4304 532338
callee_second 66/116 0 : 0 0 : 0 13 4625 531045
baseline 66/116 — — 0 2600 458707

时间是全部对上查询的串行总和(同一进程、通用打分器),只用来比相对代价,不是 ce similar 的冷 / 暖延迟。

同角色谓词(对冻结的 700 个候选对,用冻结证据,与覆盖无关):16 个谓词里只有一个弱 Pareto 优于 spec 形—— spec ∧ 2N ≥ QN(命中的名字词至少占查询名字词的一半)精度 101/151 对 spec 的 101/165, 池内召回同为 101/177;去掉的 14 个误报全在 go / python / rust 夹具,自仓与 typescript 一个不动。 它是查询方向的展示过滤器,在同一份样本上选出、没有留出集佐证,不进 spec;步 5 第二份样本仲裁后先测它再定。

裁定(调优与联想,2026-09-05)

  1. 打分不动:SIMILAR_REV 1 维持。 84 个配置里没有一个过显著线;最好的两行(按通道分别归一 + 查询 tf 截 1;单独的 查询 tf 截 1)的赢面几乎全在自仓(上表按语料的配对列:自仓 5 : 3 与 2 : 0、go 1 : 0 与 0 : 0,python / rust / typescript 一个查询不动); 五折留出选择每一折都选出这两行之一,但在留出的那份语料上除自仓外一个查询也没赢回来——同一份样本上选出的赢面,出了这份样本就不见了。 这两个配置与 spec ∧ 2N ≥ QN 谓词一并记为候选:步 5 第二份样本仲裁出来后在留出集上先测,测过才进 spec、才动 SIMILAR_REV。
  2. PPMI 联想不做默认排序臂,做显式的「联想视图」。 数学上,单元内共现的 PPMI 量的是搭配(open 与 close、parse 与 render 常同现),不是可替换(fetch 与 load 恰恰不在同一单元里出现)——一阶共现估计不了同义;模型上,翻译模型、伪相关反馈、 二阶「词 → 被调 → 词」都在同一份 oracle 上被测过:顶 1 无一净正,翻译模型与 Rocchio 还是净负;把联想只用来决胜(ppmi_tie) 或限总量(ppmi_mass4)能把损失归零,但一个查询也赢不回来。联想真正带来的是新候选(ppmi_m1 17 槽、二阶 13 槽、RM3 34 槽 ——top-5 里出现了未仲裁的身份),那是发现,不是排序。故 spec §四 的数学不动(共现对的存法由步 3 实测定:不建 cooc 对表, 边际进 df.marg、对在查询时从 bag 行推导——见 PERF-BUDGET 步 3 节),§六 三面把扩展臂改为 opt-in(ce similar --widen / MCP widen / GUI 开关),扩展来的候选单独标「联想」且永不计证据、永不亮 role 位; 默认臂 = 裸臂。这不是「关掉 #4」——RAG 的稀疏检索是默认臂本身,联想是它的第二页。
  3. 行身份 = LF 折叠 sha,写进仪器。 自仓行按此重识别(表内数字),similar_fixture_rows_replay_byte_for_byte 与评测器读同一个 identity_sha;两个在冻结后改过的文件按实保留旧身份,第二份样本把它们重新纳入。冻结任何以内容 sha 为身份的记录前,身份函数 必须先对检出方式不变。
  4. 显著线的构造缺陷如实记账:在冻结的候选池上重排,role=1 子集最多只能多对 4 个查询,「≥ +8」按构造不可达;下次先算天花板再写线。
  5. 只报不判的顾问怎样才被用起来(用户裁定 ③)——三面之外,顾问的价值在它出现的时机:
    • 写之前问:MCP similar_units 接受草稿文本(--text),代理在准备写新函数、想清楚它的目的之后、落键之前调一次; 命中 role = 1 就先读那两段源码再决定复用 / 改造 / 另写。插件 README 与 .claude 提示词里放这一句约定,比任何钩子都便宜。
    • 写之后一行:Stop 审计只查本会话新增单元的 top-1,role = 1 才写一行「新单元 ↔ 已有单元」(feed 加性键 similar, observe 档只记不拦),同一 (会话, 新单元, 伙伴, 两侧 sha) 只提示一次;索引冷 / 陈旧 = 「不可用」而不是「没有伙伴」。
    • 看得见证据:三面都并排给六通道命中、形状位、role 位与来源(直接重叠 / 联想),点开即两段源码对照;不用红色、不计分、不进 ce check。
    • 记录后果:给每次提示一个可选回执 reused / kept_distinct / consolidated / irrelevant,以两侧 sha 记;它们是下一份仲裁样本 的来源(也是留出集),不喂给打分器、不改判决。度量 = 每次有用动作花了几次查看、重复提示率、冷 / 暖延迟。
    • 不做的:PreToolUse 预算装不下一次检索(spec 立场 5 不变);不在每次按键上查;不对未改动 / 早已存在的单元主动提示; 不把「同文件相邻」当语义证据。

留出集复测(步 5 前置,2026-09-05:三个候选在第二份样本上先测再定)

步 2 裁定把 field_binary / binary_query / spec ∧ 2N ≥ QN 三个候选留给「第二份样本的留出集」。第二份样本 = 同一仪器、同一配额、同一秩序,只跳过 v1 oracle 仲裁过的 118 个 rank(CE_SIMILAR_SAMPLE_GEN=2;仪器读冻结的 v1 oracle 取排除集,故留出集可从两份冻结件重导,与 v1 零重叠由门 similar_oracle_consistent 断言)。树 c704d71(步 3 已提交, dirty = 步 5 前置的测试改动),cli/src/similar/bag.rs 与 stem.rs 的单元这次按当下身份纳入。typescript 夹具只有 3 个 role=1 的 top-1 且全在 v1 里,该层抽到 0 属实(配额不从另一层补)。

语料 查询 其中 role=1 候选对
self 70 35 405
go 13 8 76
python 13 7 73
rust 13 7 83
typescript 6 0 30

仲裁同 v1(codex gpt-6-astra max、五批并行、只给身份与逐字源码),记录 cli/tests/it/eval_similar_review/sample-v2.json, 转录修复 1 处;判决:same_role 159 / related 361 / unrelated 147,其中 clone 42 (首仲裁 151 / 379 / 138 / 39;Go 13 题 2026-09-26 重量重仲裁,见「Go 语料重量与重仲裁」节;TypeScript 6 题 2026-09-27 重量重仲裁,见「TypeScript 语料重量与重仲裁」节)。

语料 查询 p@1 裸臂 p@1 扩展臂 p@1 裸臂 role=1 p@1 裸臂 role=0 p@1 裸臂非 clone hit@5 裸臂 hit@5 扩展臂
all 115 44/115 = 38.3 % 40/115 = 34.8 % 29/57 = 50.9 % 15/58 = 25.9 % 27/97 = 27.8 % 69/115 = 60.0 % 66/115 = 57.4 %
self 70 27/70 = 38.6 % 26/70 = 37.1 % 18/35 = 51.4 % 9/35 = 25.7 % 17/60 = 28.3 % 46/70 = 65.7 % 44/70 = 62.9 %
go 13 3/13 = 23.1 % 3/13 = 23.1 % 3/8 = 37.5 % 0/5 = 0.0 % 3/13 = 23.1 % 4/13 = 30.8 % 4/13 = 30.8 %
python 13 5/13 = 38.5 % 5/13 = 38.5 % 4/7 = 57.1 % 1/6 = 16.7 % 3/11 = 27.3 % 6/13 = 46.2 % 6/13 = 46.2 %
rust 13 5/13 = 38.5 % 2/13 = 15.4 % 4/7 = 57.1 % 1/6 = 16.7 % 3/11 = 27.3 % 8/13 = 61.5 % 7/13 = 53.8 %
typescript 6 4/6 = 66.7 % 4/6 = 66.7 % 0/0 4/6 = 66.7 % 1/2 = 50.0 % 5/6 = 83.3 % 5/6 = 83.3 %
语料 候选对 role 位 tp fp fn tn role 位精度 role 位对候选的召回
all 667 89 90 70 418 89/179 = 49.7 % 89/159 = 56.0 %
self 405 55 53 50 247 55/108 = 50.9 % 55/105 = 52.4 %
go 76 11 15 6 44 11/26 = 42.3 % 11/17 = 64.7 %
python 73 11 15 3 44 11/26 = 42.3 % 11/14 = 78.6 %
rust 83 12 7 1 63 12/19 = 63.2 % 12/13 = 92.3 %
typescript 30 0 0 10 20 0/0 0/10 = 0.0 %

读数(2026-09-05 首仲裁的池 115 / 668;当下读数见「Go 语料重量与重仲裁」节):

  • 留出集比 v1 低一档,且低在每个切片:p@1 46/115(v1 67/118)、role=1 顶 1 30/56 = 53.6 %(v1 66.1 %)、非 clone 顶 1 29/98 = 29.6 % (v1 42.7 %)、role 位精度 86/177 = 48.6 %(v1 61.2 %);hit@5 60.0 %(v1 62.7 %)持平。v1 的顶 1 里 clone 占 29/67,留出集 17/46—— 平凡形少了一截,但非 clone 切片也同降,故 v1 读数偏乐观不只是 clone 的功劳;此后引用顾问精度以两代并列,门两代各守各的地板。
  • 46/115 个查询的池里没有 same_role(v1 42/118);69 个有的里裸臂放在第 1 名 46、藏在第 2–5 名 23(v1 9)——留出集上「找得到但排不到顶」的 查询多了一倍,这是打分侧还有余地的唯一信号,但下表说明现有候选没有兑现它。
  • PPMI 扩展臂:27/115 顶 1 被改变,配对 2 : 6(v1 2 : 6 完全同形),hit@5 −3;步 2 裁定 ②(联想改 opt-in 视图)在留出集上再次成立。
  • role 位在 go 夹具召回 8/8、精度 8/24:名字与被调各命中一次的合取对 Go 的短单元过松;rust 相反(12/19 精度、12/13 召回)。

三个候选在留出集上(评测器 CE_SIMILAR_ORACLE=2,115 查询 / 668 对全部对上、零部分池;表落 cli/target/similar-tune-v2/):

配置 p@1 role=1 role=0 非 clone hit@5 W : L role=1 W : L hit@5 W : L 按语料配对 W : L(self / go / python / rust / ts)
baseline 46/115 30/56 16/59 29/98 69/115 0 : 0 0 : 0 0 : 0 —
field_binary 49/115 31/56 18/59 31/97 68/115 5 : 2 1 : 0 0 : 1 3 : 2 / 0 : 0 / 1 : 0 / 1 : 0 / 0 : 0
binary_query 48/115 30/56 18/59 30/97 68/115 6 : 4 3 : 3 0 : 1 2 : 4 / 0 : 0 / 1 : 0 / 3 : 0 / 0 : 0
谓词 v1 精度 v1 池内召回 留出集精度 留出集池内召回
spec (N≥1∧C≥1)∨(N≥2∧形状全等) 101/165 101/177 86/177 86/151
spec ∧ 2N ≥ QN 101/151 101/177 83/159 83/151(少的 3 个全在 go)

同一套 84 个配置在两份样本上的方向并不一致:v1 上最差的三行 k0(k1 = 0)、lm100、translation_quarter(配对 2 : 7 / 1 : 7 / 1 : 9) 在留出集上成了最好的三行(各 52/115、配对 10 : 4);五折留出选择在留出集上每折选出的配置各不相同(binary_query / lm100 / translation_quarter / k0 / translation_quarter)。±6 个查询的赢面在这个样本量上是噪声,两份样本各自「选出」的赢家出了那份样本就换人。

裁定(留出集复测,2026-09-05)

  1. 三个候选一个不采,SIMILAR_REV 1 与 spec 合取原样进步 5。 预登记的线是「全样本与 role=1 子集各 ≥ +8 且 W ≥ 2L 且无语料变差」: field_binary +3 / +1(W 5 : L 2,hit@5 −1)不过 +8;binary_query +2 / 0 且自仓 2 : 4 变差;谓词 spec ∧ 2N ≥ QN 在 v1 上是弱 Pareto (召回不动、精度 +),在留出集上少 3 个真阳(go),不再 Pareto——它是 v1 那份样本的形状,不是规则。field_binary 两代都 +3、方向一致, 记为唯一有留出集佐证的正向候选,但幅度在噪声带内;步 6 三面落地后若回执样本(裁定 5「记录后果」)攒到第三代 oracle,再测一次即定。
  2. 地板第二代 40 %(similar_oracle_floors,GENERATIONS 表按代给地板):role=1 顶 1 53.6 %、hit@5 60.0 %、role 位精度 48.6 % 三数取最小向下 取整到一成;v1 的 60 % 不动(各代各守,一代重冻结落线即红)。
  3. 留出集是仪器的常设通道:CE_SIMILAR_SAMPLE_GEN=<n> 抽第 n 代(跳过更早各代 oracle 的 rank),CE_SIMILAR_ORACLE=<n> 让评测器对第 n 代重排(评测器已退役,按「复跑」节复活后才有这条通道), 门对每一代断言:与更早各代零重叠、holdout_of 点名前一代、常量 = 当下常量、夹具行逐字节回放。第三代起同一条路,不再另写。

Go 语料重量与重仲裁(2026-09-26,计划 v2.30 步 5b 第一小批)

起因:第 3 条(Go 分组形参按名计数,a, b int = 2)与第 25 条(包级 const / var 一名一单元)改了 Go 夹具的单元宇宙—— units.go 59 → 92;样本的 13 + 13 个查询不变(秩序由身份决定、与分数无关,不重抽),但它们的候选表全变,活门 similar_fixture_rows_replay_byte_for_byte 红;v2 两个查询换了键(appendIfNotPresent/1 → /2、configEnvVar/1 → /2),rank 随身份 重导、按 (path, start_line) 重新就位。候选对 v1 76 → 71、v2 77 → 76;v2 那 13 题里 role=1 的顶 1 7 → 8。

做法:同一仲裁者(codex exec gpt-6-astra, reasoning max, read-only sandbox),每代一批 13 题,packet 只给身份与逐字源码(RM18); 147/147 答案落在包里的候选上、零转录修复、why 全过 40 字地板。合并:oracle 两册 Go 行整体替换、summary 由门的 metrics 重导、 加 rearbitrated 记录;样本两册 Go 行按冻结 rank 就位换成活行,units.go / summary.go 重计,加 remeasured 记录;审阅记录两册 Go 行替换(700 → 695、668 → 667)加 rearbitrated。上面「结果」与「留出集复测」两节的表已就地改成当下的 summary(门从行重导必须 相等),动的只有 all 与 go 两行;两节的「读数」散文是首仲裁池上的读数,当下值在下面。

代 行(候选对) p@1 裸臂 p@1 裸臂 role=1 p@1 裸臂 role=0 p@1 裸臂非 clone hit@5 裸臂 hit@5 扩展臂 role 位精度 role 位对候选的召回
v1 all(700 → 695 对) 67/118 → 68/118 39/59 → 39/59 28/59 → 29/59 38/89 → 39/89 74/118 → 75/118 75/118 → 77/118 101/165 → 100/165 101/177 → 100/180
v1 go(76 → 71 对) 7/13 → 8/13 4/7 → 4/7 3/6 → 4/6 5/11 → 6/11 8/13 → 9/13 8/13 → 10/13 10/25 → 9/25 10/13 → 9/16
v2 all(668 → 667 对) 46/115 → 45/115 30/56 → 29/57 16/59 → 16/58 29/98 → 28/98 69/115 → 69/115 66/115 → 66/115 86/177 → 89/179 86/151 → 89/160
v2 go(77 → 76 对) 4/13 → 3/13 4/7 → 3/8 0/6 → 0/5 4/13 → 3/13 4/13 → 4/13 4/13 → 4/13 8/24 → 11/26 8/8 → 11/17

地板(similar_oracle_floors):v1 60 %——role=1 顶 1 39/59 = 66.1 %、hit@5 75/118 = 63.6 %、role 位精度 100/165 = 60.6 %(首仲裁 101/165 = 61.2 %;离地板 0.6 个百分点, 下一次夹具的单元宇宙再动就可能落线——届时是事件不是数字);v2 40 %——role=1 顶 1 29/57 = 50.9 %、hit@5 69/115 = 60.0 %、role 位精度 89/179 = 49.7 %。两代都立住。

当下读数(与两处「读数」同口径):

  • v1:裸臂 top-1 三分 same_role 68 / related 38 / unrelated 12(首 67 / 40 / 11);68 个同角色顶 1 里 clone 29、同文件 35; 40/118 个查询的池里没有 same_role(首 42),78 个有的里裸臂放在第 1 名 68、其余 10(首 76 / 67 / 9);扩展臂配对:裸对扩错 5、 裸错扩对 3(首 6 : 2),两臂同对 63 / 同错 47,hit@5 75 → 77(首 74 → 75)——联想扩展对顶 1 仍是净负(−2/118); 判决三分 same_role 180 / related 341 / unrelated 174,clone 67(首 177 / 355 / 168 / 67);替代合取(对 695 个候选,精度 / 对候选的召回): spec 形 0.606 / 0.556(首 0.612 / 0.571)、N≥1 0.410 / 0.700、N≥1∧C≥1 0.606 / 0.461、(N≥1∧C≥1∧形状)∨(N≥2∧形状) 0.637 / 0.400、N≥1∧C≥2 0.663 / 0.306、spec ∧ 非同文件 0.678 / 0.328、spec ∨ (D≥2∧形状) 0.478 / 0.611——仍没有一个替代形同时高于 spec 形的精度与召回。
  • v2:三分 45 / 55 / 15(首 46 / 57 / 12);46/115 个查询的池里没有 same_role(首 46),69 个有的里顶 1 45、其余 24(首 69 / 46 / 23); 配对 6 : 2(首 6 : 2)、同对 39 / 同错 68,hit@5 69 / 66(首 69 / 66);go 夹具 role 位精度 11/26、召回 11/17 (首 8/24、8/8——「合取对 Go 的短单元过松」仍成立,召回不再是 100 %);判决 same_role 160 / related 353 / unrelated 154,clone 39(首 151 / 379 / 138 / 39); 替代合取(对 667 个候选):spec 形 0.497 / 0.556(首 0.486 / 0.570)、N≥1 0.374 / 0.762、N≥1∧C≥1 0.484 / 0.469、(N≥1∧C≥1∧形状)∨(N≥2∧形状) 0.590 / 0.431、N≥1∧C≥2 0.589 / 0.412、spec ∧ 非同文件 0.495 / 0.281、spec ∨ (D≥2∧形状) 0.463 / 0.594。

评测器(84 配置 / 16 谓词 / 留出集三候选)两节的表不重跑:它们支撑的裁定(三候选不采、SIMILAR_REV 1 不动)已经落地;要复核就按 「复跑」节的复活配方把评测器取回,在当下的 oracle 上重排。

TypeScript 语料重量与重仲裁(2026-09-27,计划 v2.30 步 5b-6)

起因:5b-6 把 TS 模块级 const / let / var 按规则收进声明域(解构的每个绑定名各一单元,函数值已由函数抽取器命名的不再造第二个), TS 夹具的单元宇宙 units.typescript 26 → 32——多出的六个全在 benchmarks/object.ts(三个 Benchmark.Suite、三个 z.object 形状),三个 locale 文件 在模块级只有函数值的 const error,早已是单元 error/0。样本的 9 + 6 个查询不变(秩序由身份决定、与分数无关,不重抽),object.ts 的 5 + 3 个查询候选集变了 (它的 (anonymous)/0 同文件兄弟进了前五、顶掉 locale 的 error/0),locale 的 4 + 3 个查询候选集不变、只换分数与次序(同一对的证据整数也随语料变: bg → ota error/0 的 hits [1,1,0,0,35,1] → [1,2,0,0,39,1]),活门 similar_fixture_rows_replay_byte_for_byte 红;无查询换键。候选对 v1 45 → 45、 v2 30 → 30;role=1 的顶 1 v1 3 → 3、v2 0 → 0。

做法:同一仲裁者(codex exec gpt-6-astra, reasoning max, read-only sandbox),每代一批(9 题 / 6 题)——按「复跑」节的规则重仲裁整个语料, 不只是候选集变了的查询;packet 只给身份与逐字源码(RM18);75/75 答案落在包里的候选上、零转录修复、why 全过 40 字地板。与冻结判决对照(只记不裁): v1 45 对里 28 同 / 6 改判 / 11 是新候选,v2 30 对里 20 同 / 3 改判 / 7 新。合并:oracle 两册 TS 行整体替换、summary 由门的 metrics 重导、 rearbitrated 改成列表加 typescript 一条;样本两册 TS 行按冻结 rank 就位换成活行,units.typescript / summary.typescript 重计,remeasured 改成 列表加一条;审阅记录两册 TS 行替换(45 → 45、30 → 30)加 rearbitrated。上面「结果」与「留出集复测」两节的表已就地改成当下的 summary(门从行重导必须 相等),动的只有 all 与 typescript 两行;「Go 语料重量与重仲裁」节的「当下读数」是 Go 重量后、TS 重量前的读数,all 的当下值以本节为准。

代 行(候选对) p@1 裸臂 p@1 裸臂 role=1 p@1 裸臂 role=0 p@1 裸臂非 clone hit@5 裸臂 hit@5 扩展臂 role 位精度 role 位对候选的召回
v1 all(695 对) 68/118 → 66/118 39/59 → 39/59 29/59 → 27/59 39/89 → 40/92 75/118 → 75/118 77/118 → 77/118 100/165 → 100/165 100/180 → 100/178
v1 typescript(45 对) 9/9 → 7/9 3/3 → 3/3 6/6 → 4/6 0/0 → 1/3 9/9 → 9/9 9/9 → 9/9 6/6 → 6/6 6/20 → 6/18
v2 all(667 对) 45/115 → 44/115 29/57 → 29/57 16/58 → 15/58 28/98 → 27/97 69/115 → 69/115 66/115 → 66/115 89/179 → 89/179 89/160 → 89/159
v2 typescript(30 对) 5/6 → 4/6 0/0 → 0/0 5/6 → 4/6 2/3 → 1/2 5/6 → 5/6 5/6 → 5/6 0/0 → 0/0 0/11 → 0/10

地板(similar_oracle_floors):三个发表数一个没动——v1 role=1 顶 1 39/59 = 66.1 %、hit@5 75/118 = 63.6 %、role 位精度 100/165 = 60.6 %; v2 29/57 = 50.9 %、69/115 = 60.0 %、89/179 = 49.7 %。两代都立住;TS 夹具的 role=1 查询只有 3 + 0 个、role 位候选只有 6 + 0 个,动不了这三个数。

当下读数(与两处「读数」同口径,由合并后的 oracle 行重导;括号里是 Go 重量后的上一读数):

  • v1:裸臂 top-1 三分 same_role 66 / related 40 / unrelated 12(68 / 38 / 12);66 个同角色顶 1 里 clone 26、同文件 33;40/118 个查询的池里没有 same_role(40),78 个有的里裸臂放在第 1 名 66、其余 12(68 / 10);扩展臂配对:裸对扩错 5、裸错扩对 3(5 : 3),两臂同对 61 / 同错 49,hit@5 75 → 77 ——联想扩展对顶 1 仍是净负(−2/118);判决三分 same_role 178 / related 348 / unrelated 169,clone 64(180 / 341 / 174 / 67);替代合取(对 695 个候选, 精度 / 对候选的召回):spec 形 0.606 / 0.562(0.606 / 0.556)、N≥1 0.410 / 0.708、N≥1∧C≥1 0.606 / 0.466、(N≥1∧C≥1∧形状)∨(N≥2∧形状) 0.637 / 0.404、 N≥1∧C≥2 0.663 / 0.309、spec ∧ 非同文件 0.678 / 0.331、spec ∨ (D≥2∧形状) 0.478 / 0.618——仍没有一个在两轴上同时胜过 spec 形。
  • v2:三分 44 / 56 / 15(45 / 55 / 15);46/115 个查询的池里没有 same_role(46),69 个有的里顶 1 44、其余 25(45 / 24);配对 6 : 2(6 : 2)、同对 38 / 同错 69,hit@5 69 / 66(69 / 66);判决 same_role 159 / related 361 / unrelated 147,clone 42(160 / 353 / 154 / 39);替代合取(对 667 个候选): spec 形 0.497 / 0.560(0.497 / 0.556)、N≥1 0.374 / 0.767、N≥1∧C≥1 0.484 / 0.472、(N≥1∧C≥1∧形状)∨(N≥2∧形状) 0.590 / 0.434、N≥1∧C≥2 0.589 / 0.415、 spec ∧ 非同文件 0.495 / 0.283、spec ∨ (D≥2∧形状) 0.463 / 0.597。
  • typescript 夹具:v1 9 题顶 1 三分 7 / 2 / 0(同角色顶 1 里 clone 6、同文件 3),9 个池都有 same_role,hit@5 9 → 9,判决 18 / 19 / 8、clone 16, role 位精度 6/6、召回 6/18;v2 6 题三分 4 / 2 / 0,1 个池没有 same_role,hit@5 5 → 5,判决 10 / 20 / 0、clone 11,role 位 0/0、召回 0/10。

门(cli/tests/it/eval_similar_precision.rs,非 ignored;GENERATIONS 表列出每一代与其地板)

  • similar_oracle_consistent(每代):常量 = 仪器当下常量;词表;≥ 100 个查询;rank 升序且由身份重导;每候选 truth 在词表、clone 为布尔、why 过地板;summary.all 与每语料块 = 从行重导的度量(eval_similar_precision_parts,与合并脚本同式); 第二代起 holdout_of 点名前一代,且没有一个 rank 被更早的一代仲裁过。
  • similar_fixture_rows_replay_byte_for_byte:四份夹具语料按当下代码各重量一次,每一代冻结行的候选表(身份、六通道 整数、形状位、role 位、同文件位、两臂名次与分数)逐字节回来,且行所指文件的身份未变。行身份 = 文本 CRLF 折叠成 LF 后的 sha256 (identity_sha,仪器与评测器同一所有者,检出方式不能改变一行是谁)。自仓行只按身份锚定不重量 (自仓文件一动 df 就动,重量无意义)。
  • similar_oracle_floors:本册两个「裁定」节定下的地板由该腿执行(v1 60 %、留出集 40 %)。
  • 样本 ≺ 判决子树的出处腿(RM2,姊妹族 dedup_provenance 同形)待步 5 core/app/CE/Similar 落地后加: assert_subtree_postdates 对空子树按名拒绝(「空子树让门空转」),步 2 加不了。

复跑

cd cli && cargo test --test it -- eval_similar_precision::
export CE_CORE_BIN=…   # 自仓索引刷新要核
cd cli && cargo test --release --test it -- --ignored similar_replay::similar_replay --nocapture
CE_BLESS=1 …           # 重冻结样本(常量改动后);CE_SIMILAR_PACKET=<file> 另写仲裁 packet
CE_SIMILAR_SAMPLE_GEN=2 CE_BLESS=1 …   # 抽第二代(留出集:跳过 v1 oracle 的 rank)→ similar-sample-v2.json

评测器退役(2026-10-04,v2.33 W3,主会话裁):它重排的是生产里的 Rust BM25 / PPMI 排序,那份排序已经搬进核(rank/1),Rust 里不再有它调的东西;它在 W3 里改挂测试子仓的冻结预言机 unit/w3_oracle/similar.rs,违反了子仓自己的架构规则(it/ 不读 unit/),于是按名删除。结论不变:三候选不采、SIMILAR_REV 1 不动、field_binary 是唯一有留出集佐证的正向候选、待第三代 oracle 再测(要测就先复活)。复活只读子仓历史、不写任何索引——f623abf 是子仓里最后一个带它的提交:

git -C cli/tests archive f623abf it/similar_tune.rs it/similar_tune_parts | tar -x -C cli/tests
git -C cli/tests show f623abf:unit/w3_oracle/similar.rs > cli/tests/it/similar_tune_parts/oracle.rs

再删掉取回的 similar_tune_parts/mod.rs 里 mod oracle; 上面那行 #[path = "../../unit/w3_oracle/similar.rs"](mod oracle; 就读旁边那份拷贝,它经仪器自己的 mod lib 取库),在 cli/tests/it/main.rs 加回 mod similar_tune; / mod similar_tune_parts;,然后跑 cargo test --release --test it -- --ignored similar_tune::similar_tune --nocapture(表落 cli/target/similar-tune-v1/,CE_SIMILAR_ORACLE=2 对第二代重排、表落 similar-tune-v2/)。公式就是冻结预言机:取回的那份与现行 unit/w3_oracle/similar.rs 只差文件头注释与三行 use,函数体逐字节相同。跑完用 rm -r 删掉取回的文件、去掉那两行 mod。

仲裁批处理(prompt / packet / 合并脚本)在会话 scratchpad,不入库;重仲裁 = 换仲裁者重跑合并脚本, 样本不动;仪器的单元宇宙一变(2026-09-26 Go 先例)= 该语料的样本行重量 + 只重仲裁那一语料,其余语料的行与仲裁不动。