From 7cc3fb09a5aa3de8dd6bf1e998a52dfa612f0549 Mon Sep 17 00:00:00 2001 From: LUOYN <3029295957@qq.com> Date: Wed, 21 Oct 2026 17:12:31 +0800 Subject: [PATCH 1/2] =?UTF-8?q?docs(evals):=20=E8=A1=A5=E8=AF=84=E6=B5=8B?= =?UTF-8?q?=E5=8D=AB=E7=94=9F=E8=A7=84=E5=88=99=EF=BC=88=E4=B8=AD=E6=80=A7?= =?UTF-8?q?=E5=8C=96=E6=A0=B7=E6=9C=AC=E3=80=81=E5=8E=9F=E5=A7=8B=E8=BE=93?= =?UTF-8?q?=E5=87=BA=E9=9A=94=E7=A6=BB=E3=80=81skill=20=E5=89=AF=E6=9C=AC?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- evals/runbook.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/evals/runbook.md b/evals/runbook.md index d11c7a0..fd54a05 100644 --- a/evals/runbook.md +++ b/evals/runbook.md @@ -14,6 +14,17 @@ Copy-Item -Recurse evals\fixtures\tiny-shop $env:TEMP\dcr-eval ``` > **不要把 `ground-truth.md` / `rubric.md` 放进被审目录**——被测模型看到标准答案,这次评测就废了。 +> +> **样本副本要做中性化**:仓库里 `evals/fixtures/tiny-shop/README.md` 写着「刻意植入缺陷」, +> 那句话等于给被测模型透题。复制到隔离目录后,把 README 换成**中性描述版** +> (去掉缺陷披露、去掉「怎么用」段),其余 4 个文件**逐字节**复制,SHA256 必须与仓库一致。 +> +> **副作用**:报告里引用 README 的行号会比仓库 fixture 差 1–2 行。 +> 标准答案只引用 `.go` 文件行号,不受影响;评分时**不要**因为 README 行号差一位就扣「表达质量」。 +> +> **被测上下文要能读到 skill 本体**:skill 装好后,约束里要写清「skill 副本在哪个路径」, +> 而不是禁止它读安装目录——它读不到就会去磁盘上乱找,实测捡到过一份过期旧副本, +> 导致那一轮按旧口径判案、整轮作废。要禁的是**仓库里的 `evals/` 目录**(那里才有标准答案)。 ## 跑三种问法 @@ -41,6 +52,10 @@ A 轮验的是**快模式本身**:合并判定必须由这个 skill 直接给 想看稳定性就跑 3 轮,取中位数。 - **样本很小**。约 150 行,读得完。漏报不能用「范围太大没读完」解释—— 如果模型说「未覆盖」,那是它自己的选择,要记下来。 +- **别把历次评测的原始输出放在样本目录旁边**。那些原始输出本身就是「答案」—— + 上一轮的评审全文躺在样本目录旁边时,有一轮直接把上一轮结论读进去了,那一轮作废重跑。 +- **原始输出目录与样本目录不要有父子关系**,也不要放在同一个父目录下—— + 被测上下文有可能往父目录翻,命中任何一份历史评审都会污染这一轮。 - **不是每次都要跑全三种问法**。改 skill 正文时跑 B+C; 只改 description 时重点跑 A(路由);改输出契约时重点看 C(结构)。 From 22ea56c02b1b336b9226510456a1189f956767b4 Mon Sep 17 00:00:00 2001 From: Luo-root <185462884+Luo-root@users.noreply.github.com> Date: Wed, 21 Oct 2026 18:29:12 +0800 Subject: [PATCH 2/2] =?UTF-8?q?docs(evals):=20=E6=8A=98=E8=BF=9B=E5=88=A4?= =?UTF-8?q?=E5=88=86=E7=BB=86=E5=88=99=20+=20=E8=AE=B0=E5=BD=95=20v4=20?= =?UTF-8?q?=E8=BD=AE=E5=9F=BA=E7=BA=BF=EF=BC=8825=20=E6=9D=A1=E5=8F=A3?= =?UTF-8?q?=E5=BE=84=EF=BC=8C=E5=86=BB=E7=BB=93=E6=8F=90=E4=BA=A4=207c1c4e?= =?UTF-8?q?a=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rubric.md:重写为「正文 + 打分细则(歧义点定死版)」八节,把每轮都会吵的判据写死——命中 1 含「在别的条目里顺带说对」、归类的分母是 finding 条目数、G12/G13 特例、证据的 E 原则、表达质量的最小对照例子判据、raw 公式与读分提醒。 baseline.md:追加 v4 轮(不改首轮 12 条口径的历史记录)。冻结提交 7c1c4ea,三种问法各 1 轮,原始输出留仓外。命中 15.5/13.0/19.5,误报全 0,归类 10.5/8.0/12.0,证据 4.93/4.73/4.20,raw 163.4/154.4/151.6。断言 ③ 按实测改写为「不成立」,并附 6 处分歧的口径与数字影响表。 Co-Authored-By: MiniMax-M3.1-Flash --- evals/baseline.md | 154 ++++++++++++++++++++++++++++++++++++++++++++-- evals/rubric.md | 144 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 286 insertions(+), 12 deletions(-) diff --git a/evals/baseline.md b/evals/baseline.md index be469cd..3af3278 100644 --- a/evals/baseline.md +++ b/evals/baseline.md @@ -2,9 +2,9 @@ 首次跑通 `evals/runbook.md` 的三种问法,记录结果作为后续改动的对照点。 -> **口径提示**:本文件记录的是**首轮 12 条 ground truth 口径**下的分数。随后 `ground-truth.md` 已扩充到 25 条(补进首轮实测发现的 13 条真缺陷,见 #14 / #15),`rubric.md` 的分母同步改为 25。 -> **本文件的分数与新口径不可直接比较**——要比较,必须按新口径重跑一轮。 -> 新口径下的重跑结果尚未产生,本文件暂按首轮事实保留。 +> **口径提示**:本文件上半部分是**首轮 12 条 ground truth 口径**下的分数,保留为历史事实、不覆盖。 +> 下半部分「v4 轮」是**25 条口径**下的重跑结果——改完标准答案、重跑、锁定判分口径之后才有资格谈发版。 +> **两轮分数不可直接比较**:GT 条目数(12→25)、归类分母、命中判据三样都变了。要比较必须按同一口径重跑。 **结论先行** @@ -199,4 +199,150 @@ B 轮提出的另一个角度更接近真实问题:承诺里提到的「订单 | `evals/rubric.md` | 评分表与五维口径 | | `evals/runbook.md` | 怎么跑一次评测 | | `evals/fixtures/tiny-shop/` | 样本(约 150 行 Go) | -| 仓外 `scratchpads\dcr-eval\runs\round-{A,B,C}.md` | 三轮原始输出,不入版本控制 | +| 仓外 `scratchpads\dcr-eval\runs\round-{A,B,C}.md` | 首轮三轮原始输出,不入版本控制 | + +--- + +# v4 轮(25 条口径 · 冻结提交 `7c1c4ea`) + +改完标准答案(G4 改判、GT 扩到 25 条、硬编码细则去矛盾)之后重跑的一轮。**这一轮的分数是 v1.1.0 的发版依据。** + +## 运行条件 + +| 项 | 值 | +|---|---| +| 日期 | 2026-10-21 | +| skill 版本 | 冻结提交 `7c1c4ea`(`fix/consistency-and-lint`)——三轮全程未改任何 skill 文件 | +| 被测上下文 | 三个全新子 Agent 上下文,互不污染,轮间不共享结论 | +| 样本 | `evals/fixtures/tiny-shop` 逐字节复制到仓外 `scratchpads\dcr-eval\tiny-shop`,README 中性化 | +| 隔离 | 只禁仓库 `evals/` 目录;skill 路径显式指向装线 `C:\Users\LUOYN\.minimax\skills\deep-code-review`(与仓库同 hash) | +| 原始输出 | 仓外 `scratchpads\dcr-eval-archive\round-{A,B,C}-v4.md`,**不入版本控制** | +| 输出指纹 | A `BA1FFA73…C691B4` / B `A13EA522…F48912` / C `3037B600…238DC`(SHA256) | + +这一轮之前有两次重跑作废:一次跑在「边改边测」的移动靶上,一次让子 Agent 读到了磁盘上的旧 skill 副本。两条都写进了 `evals/runbook.md` 的卫生规则。 + +## 结论先行 + +1. **误报三轮全 0**。包名/目录名这条三处都守住了边界(A 排除、B/C 进拍板且明写「不进必须修」),没有一条混进修复项。 +2. **命中 15.5 / 13.0 / 19.5(满分 25)**。C 轮(模块体检)最好,A 轮(快模式)次之,B 轮最低——B 掉的主要是 G14(单位不一致)这类**设计面**条目,快模式按契约本就不报,符合预期。 +3. **C 轮命中最高、raw 反而最低**(151.6 vs A 的 163.4)。差距全在证据:C 的证据均值 4.20,A 是 4.93。**C 靠「读完 + 读懂」拿命中,A 靠「跑探针」拿证据。** 想提分,方向是让全量轮也像快模式那样去跑探针。 +4. **「文档维度整体失灵」是错的说法**。三轮报出的文档类问题都落在 G3/G11/G14 上——说明模型**能**发现「文档与实现不一致」。真正系统性漏掉的是两种更窄的形态:**位置指代型**(G19:注释说「第二个返回值」,实际 bool 是第一个)和**承诺无对应对象型**(G4:godoc 里的「订单存储」在这个模块里根本不存在)。 +5. **共同漏报(作为条目报出 = 0)只有 3 条**:G4、G19、G22。另有 G23(`Order.ID` 零使用)三轮都没当成条目报出。G9/G25 的 0 是「同一根因只算一次」的结构性结果,不是能力漏报。 +6. **首轮票面的三条断言,第三条不成立**:① 硬编码维度**部分成立**(B、C 结论一致,但只有 B 明确区分「协议固定值 100 不算」,C 全文没提 100);② 包名处置**成立**;③ 「真正的文档缺陷仍报得出来」**不成立**——GT 点名的 G4/G19 三轮都没报出来,三轮报的文档类问题全落在 G3/G11/G14,不能充数。 + +## 汇总 + +| 维度 | A(快模式·合并判定) | B(全量·按七维审) | C(全量·模块体检) | +|---|---|---|---| +| 命中 | 15.5 / 25(62.0%) | 13.0 / 25(52.0%) | 19.5 / 25(78.0%) | +| 误报 | 0 条 | 0 条 | 0 条 | +| 归类合计 / 已报条目数 | 10.5 / 14(75.0%) | 8.0 / 15(53.3%) | 12.0 / 15(80.0%) | +| 证据强度均值 | 4.93(69/14) | 4.73(71/15) | 4.20(63/15) | +| 表达质量 | 14.1 / 14.5 | 15.1 / 15.5 | 15.1 / 15.5 | +| **raw** | **163.4** | **154.4** | **151.6** | + +**分母说明**:归类的分母是**报告实际交付的 finding 条目数**(必须修 + 建议修 + 拍板),不是 25——未报出的 GT 条目按 0 计进归类合计,所以归类同时受漏报拖累。表达质量的满分是「条目数 + 0.5」(C1 的加分)。 + +## 逐条对照 + +`命中/归类`。`—` 表示完全没涉及。 + +| GT | 级别 | A | B | C | 说明 | +|---|---|---|---|---|---| +| G1 零值 `Checkout` panic | P1 | 1/1 | 1/0.5 | 1/1 | B 报 P2,偏一级 | +| G2 `timeout` 形同虚设 | P1 | 1/1 | 1/1 | 1/1 | 三轮都给了零引用证据 | +| G3 `Code` 从未被读取 | P1 | 1/1 | 1/1 | 1/0.5 | C 判成拍板(GT 判缺陷) | +| G4 承诺没有对应对象 | P2 | 0.5/0 | 0.5/0 | 0/0 | A/B 只说了「模块内没有 IO」就判「承诺成立」;C 说的是镜像问题 | +| G5 批次中途失败计数残留 | P1 | 1/0.5 | 1/0.5 | 1/1 | A/B 报 P2,偏一级 | +| G6 两份元→分换算漂移 | P2 | 1/0.5 | 1/0 | 1/0 | B/C 都报 P0,偏两级;A 报 P1 最接近 | +| G7 `E_INVALID` 两个真相源 | P2 | 1/1 | 1/1 | 1/1 | 三轮都给了 `errors.Is` 恒假的论证 | +| G8 `refundWindow` 硬编码 | P2 | 1/1 | 1/0.5 | 1/0.5 | B/C 判成拍板(GT 判缺陷) | +| G9 折扣接口过度设计 | P2 | — | — | — | 同一根因并入 G3,不重复计分 | +| G10 测试盲区 | P2 | 1/0.5 | 0.5/0 | 1/0.5 | A/C 在覆盖行里点到了变体;B 只说了并发路径零覆盖 | +| G11 测试用字符串比较断言 | P2 | 1/0.5 | 1/0.5 | 1/0.5 | 三轮都写进了 E_INVALID 条目的证据与修法,但都无独立级别 | +| G12 `Percent` 无范围校验 | 拍板 | 0.5/0 | 0/0 | 1/0.5 | A 明确排除;B 完全未提;C 在别的拍板项里顺带提到 | +| G13 退款窗口从查询时刻起算 | P1 | 1/0.5 | 1/1 | 1/1 | A 没说业务前提;B/C 的拍板项问了「24h 是不是统一口径」 | +| G14 `Issue` 收元 / 返分 | P1 | 1/1 | — | 1/0.5 | B 漏报;C 判成 P2,偏一级 | +| G15 0 元退款被记成已退款 | P2 | — | — | 1/0.5 | C 判成拍板(GT 判缺陷) | +| G16 `Refunder{}` 零值 panic | P2 | 1/1 | — | 1/0.5 | B 漏报;C 与 G1 并成一条,宿主级别 P1 | +| G17 共享指针 + 非原子自增 | P1 | 1/1 | 1/0.5 | 1/0.5 | B/C 报 P2,偏一级 | +| G18 `issued` map 只增不减 | P2 | — | — | 1/0.5 | B 的性能维度写了「无无界缓存」——**与 G18 相反的覆盖声明**(不扣误报,但属假阴性) | +| G19 注释「第二个返回值」写反 | P2 | — | — | — | B 引了那段 godoc 去**背书**幂等语义,没核返回顺序 | +| G20 NaN/Inf 绕过校验 | P2 | — | 1/0.5 | 1/1 | A 漏报;B 报 P1 偏一级 | +| G21 两个计数口径不一致 | P2 | 0.5/0 | 1/1 | 1/0.5 | A 明确判为不报 | +| G22 状态持久性接缝缺失 | P2 | 0.5/0 | — | — | A 划进「本次没覆盖」;B 兼容性维度未扫;C 判「无发现」 | +| G23 `Order.ID` 零使用 | P3 | — | — | 0.5/0 | C 全文提及但判为「无注入面可报」 | +| G24 折扣先截断再减 | P3 | 0.5/0 | — | 1/0.5 | C 判成拍板(GT 判缺陷) | +| G25 折扣机制整体未接线 | P2 | — | — | — | 同一根因并入 G3 | +| **命中合计 / 25** | | **15.5** | **13.0** | **19.5** | | +| **归类合计** | | **10.5** | **8.0** | **12.0** | 未报出记 0 | + +## 证据强度明细 + +分母 = 该轮交付的全部 finding 条目数(含拍板项)。无一条触发「声称强度高于实际」或「已查过没问题没给验证方式」的扣分。 + +| 轮 | 条目与分数 | +|---|---| +| A | P1-1 换算重复 5、折扣未接线 5、timeout 5、nil Stats 5、计数器竞态 5、Issue 单位 5、窗口滑动 5、**P2-8 硬编码 4**、错误身份 5、零值 Refunder 5、计数残留 5、拍板1 5、拍板2 5、拍板3 5 → **69/14 = 4.93** | +| B | P0 换算 5、P1 折扣 5、timeout 5、窗口 5、NaN/Inf 5、P2 并发 5、错误路径 5、口径 5、零值 5、E_INVALID 5、**过度设计 3**、拍板1 5、拍板2 5、**拍板3 硬编码 3**、拍板4 包名 5 → **71/15 = 4.73** | +| C | P0 换算 5、P1 零值 5、窗口 5、**timeout 4**、计数器 5、P2 上界/NaN 5、并发 5、重复 5、**文档单位 2**、可观测性 4、测试盲区 4、拍板1 折扣 5、**拍板2 硬编码 2**、拍板3 幂等 5、**拍板4 包名 2** → **63/15 = 4.20** | + +**A 轮最强**:11 条 P1/P2 全部有探针。**C 轮最弱**:3 条只读码没跑探针(拍板2 硬编码、拍板4 包名、P2 文档单位),1 条只 grep 确认未覆盖。 + +## 表达质量明细 + +| 轮 | 说明 | +|---|---| +| A | P1-1…P1-7 各 1.0;**P2-8 硬编码 0.6**(讲清机制,只给了「改成 48h 或 7 天」的场景,没有对照例子);P2-9…P2-11 各 1.0;拍板1–3 各 1.0;**C1 +0.5**(`-race` 无告警 + 锁范围推理)→ **14.1 / 14.5** | +| B | P0、P1×4 各 1.0;P2 并发/错误路径/口径/零值/E_INVALID 各 1.0;**过度设计 0.6**(讲清机制,无对照例子);拍板1–4 各 1.0;**C1 +0.5**(8 goroutine × 300 次 `-race` 无告警)→ **15.1 / 15.5** | +| C | P0、P1 零值/窗口/计数器、P2 上界/并发/重复/文档/可观测性/测试盲区 各 1.0;**P1 timeout 0.6**(讲清机制,无对照例子);拍板1–4 各 1.0;**C1 +0.5**(`-race` 报告 PASS)→ **15.1 / 15.5** | + +## 首轮票面三条断言的复核结果 + +| 断言 | 结论 | 依据 | +|---|---|---| +| ① 硬编码维度:B、C 结论一致,且能区分「协议固定值 100 不算」与「业务政策值 24h 算」 | **部分成立** | B 明写「`100` 是协议固定值(分/元),按规则不算」,C **全文未提 100**——从报告文本无法证明它做了这个区分。两轮都把 24h 当业务政策值、都给「参数/配置项」两条路,结论一致 ✔ | +| ② 包名/目录名不进「必须修/建议修」 | **成立** | A 排除候选「按命名细则属口味,不报」;B「这条本身按规则属于命名口味,所以不进必须修」;C「只是风格统一建议,不影响正确性」 | +| ③ 真正的文档缺陷仍报得出来 | **不成立** | GT 点名的 G4、G19 三轮都没报出来。三轮报出的文档类问题全部落在 G3/G11/G14(外加 G1/G17/G7 的 godoc 缺口),不能充数 | + +**断言③ 的正确表述**(建议替换票面原文): + +> 三轮都能报出「文档与实现不一致」类问题(G3/G11/G14),但**位置指代型**(G19)与**承诺无对应对象型**(G4)两类文档缺陷仍系统性漏报。 + +## 共同漏报 / 共同命中 + +- **共同命中(命中 = 1,三轮全中,11 条)**:G1、G2、G3、G5、G6、G7、G8、G11、G13、G17、G21。真正的差别在**归类**:A 全部 1 分;B 的 G6=0、G13=0.5、G1/G5/G17/G20=0.5;C 的 G6=0、G13=0.5。 +- **共同漏报(命中 = 0,三轮全不中)**:G4、G19、G22(能力性漏报)+ G9、G25(结构性,按「同一根因只算一次」不重复计分)。**G23** 三轮都没作为条目报出,建议追加为实质共同漏报。 +- **部分触碰**:G10(A 1/0.5、B 0.5、C 1/0.5)、G12(A 0.5、B 0、C 1/0.5)、G24(A 0.5、B 0、C 1/0.5)、G23(仅 C 0.5)。 +- **单轮独有命中**:G14(G15/G16/G18/G20 同理)——A 漏 G18/G20,B 漏 G14/G15/G16/G18,**C 漏得最少**。 + +## 口径待裁定:6 处分歧与数字影响 + +判分细则已经写进 `evals/rubric.md`(「打分细则」八节),本轮基线按该细则算。下列 6 处是**判分规则本身的宽窄选择**,不是事实分歧——换个读法分数会变,列在这里是为了让基线可被挑战、可被复算。 + +| # | 分歧点 | 本基线采用 | 另一种读法 | 数字影响 | +|---|---|---|---|---| +| 1 | 在别的条目里顺带说对了,算不算命中 1 | **算 1**(细则 §一) | 算 0.5 | A 命中 −1.0;C 命中 −1.0~−2.0 | +| 2 | G4 三轮记 0 还是 0.5 | **A/B 各 0.5、C 0**(细则 §一:「只陈述了相邻事实」算 0.5) | 三轮全 0 | A 命中 −0.5;B 命中 −0.5 | +| 3 | G22 在 A 轮算不算 0.5 | **算 0.5**(「没有持久化层」是相邻事实) | 记 0 | A 命中 −0.5 | +| 4 | G13 归类:「谈没谈业务前提」的最低标准 | **B/C 记 1**(拍板项问了「24h 是不是全平台统一口径 / 按商户渠道区分」就算谈业务前提),A 记 0.5 | B/C 各 0.5 | B 归类 −0.5;C 归类 −0.5 | +| 5 | G8 被放进拍板时归类 | **0.5**(GT 判 P2 缺陷,报告判成拍板 → 偏一级) | 记 1 | B 归类 +0.5;C 归类 +0.5 | +| 6 | 表达质量「最小对照例子」的宽窄 | **保守档 0.6**(只有静态断言 + 场景描述记 0.6) | 宽松档记 1.0 | 三轮表达各 +0.4,raw 各 +0.4 | + +**为什么这样定**:第 1 条按 `worksheet` 明文(「独立成条、或作为别的条目里的一段正确陈述都算 1」),它消掉了「同一条 finding 里顺带提到的到底给 0.5 还是 1」这个每轮都要吵的问题。第 2、3 条选宽档,是因为「陈述了相邻事实」确实比「完全没涉及」多走了一步。第 4 条取「只要把窗口口径摆成需要人来定的问题就算谈业务前提」——B/C 的拍板项原话正是这个形状。第 6 条取保守档,因为「`NewRefunder(window time.Duration)`」这类签名形状是**修法**不是**对照例子**,读者看不出「原来会怎样、现在会怎样」。 + +**独立复算**:本轮分数由一个独立子 Agent 按同一份细则复算过一次(不参考本文件)。复算方在第 2、3、4 条上取窄档,得 raw **A 162.3 / B 153.4 / C 151.1**——与本基线相差 ≤ 1.7 分。**结论方向完全一致**:C 轮命中最高但 raw 最低、误报全 0、断言③不成立、G4/G19/G22 共同漏报。窄档的完整数字留在这份复算报告里,不入版本控制。 + +## 一处措辞过时(不计分) + +A、B 两轮仍写「编译器不报错(起个别名即可)」——这个理由在 `7c1c4ea` 已被实测纠正为 `shop redeclared in this block`(两个同名包一起 import 不起别名,编译器**会**报错)。**处置结论三处都正确**(都没进必须修/建议修),所以不计分;但说明模型在新措辞下仍会把理由压缩回旧说法。已开票(v1.2.0 措辞候选)。 + +## 方法论局限(v4 轮补充) + +- **样本与标准答案同一人写**:基线偏乐观,真实水平要打折看。 +- **每种问法只跑 1 轮**:没有方差估计。单轮差异小于 2 个百分点的结论不要当结论。 +- **快模式轮的 GT 覆盖天然不全**:G8–G11、G14–G25 多数属设计面与测试面,快模式按契约本就不报。拿 A 轮的命中分去比全量轮不公平。 +- **B 轮有一条假阴性**:性能维度写「无…无界缓存」,与 GT 的 G18 相反。不构成编造缺陷、不扣误报分,但它是覆盖声明失真。 +- **原始输出与评分表分离**:原始输出留在仓外(`scratchpads\dcr-eval-archive\`),版本控制里只有评分表——第三方可复算分数,但看不到原始报告全文。 +- **数值复算以 Go 1.24.0 windows/amd64 实跑为主**;`int64(NaN)` 属平台相关的未定义转换,不同架构结果可能不同。 +- **没有做变异对照**:没改坏代码去验证每条 finding 是否真能让测试变红,「finding 是真的」靠读码 + 探针实测,不是测试红。 diff --git a/evals/rubric.md b/evals/rubric.md index b81c8e7..ecc50b1 100644 --- a/evals/rubric.md +++ b/evals/rubric.md @@ -2,6 +2,8 @@ 对一次审查输出一份评分表。**评分对象是输出,不是「结论对不对」**——结论对但表达不可复核,同样扣分。 +> **本表正文 + 文末「打分细则」是评分的唯一依据。** 细则把容易产生分歧的判据写死了,第三方照着它能复算出同样的分数。当前基线见 `evals/baseline.md`。 + ## 五个维度 ### 1. 命中(recall) @@ -10,12 +12,14 @@ | 项 | 分数 | |---|---| -| 命中且触发路径正确 | 1 | -| 命中但触发路径含糊(只说「可能有问题」) | 0.5 | -| 漏报 | 0 | +| 把该缺陷的实质说对了 | 1 | +| 只碰到一半,或明确否认它 | 0.5 | +| 完全没涉及 | 0 | 满分 25(按 G1–G25 计;C1 不算命中,算第 5 项)。 +「说对了」的判定见文末细则一:独立成条、或作为别的条目里的一段正确陈述,都算 1。 + **首轮基线用的是旧版 12 条口径**(见 `baseline.md`),与本表的 25 条**不可直接比较**。改 GT 之后必须重跑一轮评测,才能得到同口径的基线。 ### 2. 误报(precision) @@ -69,7 +73,7 @@ ## 总分换算 ``` -raw = 命中 + 归类正确性 + 证据强度 + 表达质量 - 误报扣分 +raw = 命中 + 归类合计 + 证据均值 × 25 + 表达合计 − 误报扣分 ``` | raw | 评价 | @@ -82,18 +86,142 @@ raw = 命中 + 归类正确性 + 证据强度 + 表达质量 - 误报扣分 命中项满分 25,加上归类(25 项×1)、证据(25 项×~2)、表达(25 项×~0.7), 满 raw 约 100+。**换算时请按维度占比看,不要只看绝对值**——不同轮次之间本来就有波动。 +> **读分提醒**:证据项(均值×25)通常占 raw 的 70% 左右,单项独大。 +> 三轮之间的 raw 差异,先看是「命中更多」还是「证据更硬」造成的。 +> 另外**三个维度的实际分母不是 25**:表达与证据是按**报告交付的 finding 条目数**打分, +> 归类虽然按 25 条 GT 计和,但未报出的记 0,所以它同时受漏报拖累。 +> 报告里必须同时写「合计」和「分母」,否则百分比没法复算。 + ## 记录格式 ```markdown ### <日期> · · <问法> -- 命中:__/25(G__ 命中) +- 命中:__/25(G__ 命中;__ 条只碰到一半) - 误报:__ 条(__) -- 归类正确:__/25(按已报条目计:__) -- 证据强度:均值 __ -- 表达质量:__/25 +- 归类合计:__(分母:25 条 GT,未报出记 0);按已报条目:__/__ +- 证据强度:均值 __(__/finding 条目) +- 表达质量:__(分母:finding 条目数) - raw:__ - 备注:漏了 __(为什么没抓到);多报了 __(误判在哪) ``` 贴到 issue 里,或按 `runbook.md` 的建议归档。 + +--- + +# 打分细则(歧义点定死版) + +正文里写得含糊、每轮都会吵起来的地方,在这里定死。**打分时以本节为准。** + +## 一、命中(recall,满分 25) + +| 情形 | 分数 | +|---|---| +| 把该缺陷的**实质说对了**——独立成条,或作为别的条目/覆盖表里的一段正确陈述 | 1 | +| 只碰到一半(只陈述了相邻事实、没把它说成该缺陷);或明确否认它(声明它「不算问题」) | 0.5 | +| 完全没涉及 | 0 | + +补充: + +- **C1 不占命中**,它算表达质量的正向加分。 +- **同一根因只算一次**:折扣能力整体没接线(G3 / G9 / G25)只记最靠前的一条(G3),**命中与归类都不再计分**。 +- 「明确否认」与「没涉及」要分清:**报告讨论了这个缺陷的实质并判定它不是问题 → 0.5**;报告压根没谈这个方向 → 0。 + - 例(A 轮、B 轮):写「模块内也没有任何 IO 调用/没有存储写入路径,承诺成立」——这是 G4 事实的一半(模块里确实没有存储),但结论是「承诺没问题」而非「承诺没有对应对象」→ **命中 0.5、归类 0**。 + - 例(A 轮):在「本次没覆盖」里写「这 4 个文件是纯内存实现,持久化……不在本次范围内」——碰到了 G22 的相邻事实(没有持久化层),但把它划到范围外、没说这是缺陷 → **命中 0.5、归类 0**。 + - 例(C 轮):只写「godoc 里的只读承诺会让人以为 `Run` 是纯函数」——那是 G4 的镜像问题(承诺低估了副作用),没问「承诺指向的对象在不在」→ **G4 记 0**。 +- **只是引用了相关代码/文档但没评估那条性质** → 0。例如 B 轮写「与 `refund.go:24-26` 的文档一致」,那是引用 G19 那段注释去背书幂等语义,没有核对「第二个返回值」的说法 → **G19 记 0**。 + +## 二、归类正确性(满分 25) + +| 情形 | 分数 | +|---|---| +| 该条目拿到了自己的级别,且与 GT 一致 | 1 | +| 级别偏一级 | 0.5 | +| 级别偏两级及以上 | 0 | +| 没有自己的级别(只在别的条目里被顺带提到),但宿主条目级别与 GT 一致 | 0.5 | +| 没有自己的级别,且宿主条目级别与 GT 不一致 | 0 | +| GT 说「该拍板」而报告判成缺陷 | 0 | +| GT 说是缺陷(P 级)而报告判成「需要你拍板」 | 0.5 | + +补充: + +- **未报出的条目记 0**,所以这一项对漏报是二次惩罚。 +- **「按已报条目」的辅助值 = 归类合计 ÷ 报告交付的 finding 条目数** + (finding 条目数 = 必须修 + 建议修 + 拍板项;**不含**「已查过没问题」「本次没覆盖」)。 +- 另可给一个更贴近判级能力的辅助值:**归类合计 ÷ 命中条目数**(命中 ≥ 0.5 的 GT 条目数)。 + +## 三、误报(precision) + +| 情形 | 扣分 | +|---|---| +| 编造不存在的缺陷 | 每条 −2 | +| 把「不该报」清单里的候选报成**修复项**(进必须修 / 建议修 / P3 等任何修复分层) | 每条 −1 | +| 只把它放进「需要你拍板」,或明确排除 | 不扣 | + +**GT 之外的真实问题**:不扣分、不加分(它不是误报,只是不在 GT 里)。 +**漏报**在误报这一项**不扣分**,它在命中和归类两处已经计过了。 + +## 四、三条特例 + +1. **同一根因只算一次**:折扣能力整体没接线(G3 / G9 / G25)只记最靠前的一条(G3),其余**命中与归类都记 0**。 +2. **G12**(`Percent` 无范围校验,GT 要求进「需要你拍板」): + - 独立拍板项 + 给了两条路 + 建议 + 要你定什么 → 归类 1 + - 在别的拍板项里顺带提到(给了具体触发与修法) → 归类 0.5 + - 作为缺陷条目报出 → 归类 0 + - 明确排除(「等某个决策定了再一并处理」) → 命中 0.5、归类 0 +3. **G13**(退款窗口从查询时刻起算):判成 bug 或判成拍板**都算命中**; + 若报告把它说成「已经确认的 bug」,且**没有把「退款窗口该不该有 / 该由谁定」作为需要回答的问题提出来** → 归类 0.5。 + - 判据:报告里出现「这个 24 小时是全平台统一口径,还是按商户/渠道/品类区分」这类**需要用户或业务来定的问题**(写在拍板项里或明确提问)→ 归类 1; + 只是说它是「业务政策值」、应该做成参数或配置项 → **不算**谈业务前提,归类 0.5。 + +## 五、证据强度(1–5 级,取均值) + +| 级 | 含义 | +|---|---| +| 5 | 跑了测试 / 构造了最小复现 | +| 4 | 读了测试并确认覆盖或未覆盖 | +| 3 | grep 了全部调用方 | +| 2 | 读了完整函数 + 相邻实现 | +| 1 | 只读了 diff | + +- 取**全部上报条目**(含拍板项)的均值。 +- 声称强度高于实际做过的,每条 −1。 +- 「已查过没问题」里没有验证方式的,每条 −1。 +- **一条目的级别看它「中心主张」的验证强度**:该主张自己写在条目里的探针/实测算; + 同一份报告里已经为该主张**确立了前提**的探针也算(条目明引,或事实同一)。 + 反过来,条目只是复述别处跑过的、与该主张无关的探针,不给分。 +- **必须逐条列出明细**(哪条几分),否则不可复算。 + +## 六、表达质量(满分 = finding 条目数 + 0.5) + +| 情形 | 分数 | +|---|---| +| 用大白话讲清机制且配了最小对照例子 | 1 | +| 讲清机制没例子 | 0.6 | +| 只有结论 | 0.2 | +| 术语堆砌 / 行号当论据 | 0 | + +- 拍板项另加一条要求:给了「两条路 + 建议 + 要你定什么」才记 1。 +- 主动说清「哪块查过没问题」并给依据(对应 C1)→ 额外 +0.5。 +- **「最小对照例子」的判据**:报告里能指认一个**可复现的具体症状或并置对照**,二者之一即可—— + - 可复现的输入/输出(探针数字,如 `0.29 元 → 结算 28 分 / 退款 29 分`、`NaN → total=-9223372036854775808`); + - 「这里声明/承诺了什么」与「实际发生了什么」的并置(如「总=0 但 Processed=1」对上「期望 total=0、计数为 0」)。 + - 只有**静态断言 + 场景描述**(如「grep 全仓零调用点 + 想接入第二种策略时才发现这个抽象没在管事」、 + 「`timeout` 只出现在签名那一行 + 调用方以为这批最多跑 1 秒」)→ **0.6**。 +- **必须逐条列出明细**。 + +## 七、raw + +``` +raw = 命中 + 归类合计 + 证据均值 × 25 + 表达合计 − 误报扣分 +``` + +> **读分提醒**:证据项(均值×25)通常占 raw 的 70% 左右,单项独大。 +> 不要只看 raw 总数,要看维度占比(命中/25、归类/25、证据 1–5、表达/条目数)。 + +## 八、这套细则与首轮打分的口径差异 + +首轮(12 条口径)用的是「只在证据里点到记 0.5」的旧写法。本细则改成 +「**命中 1 = 实质是否说对**」,独立成条或在别的条目里顺带说对都算 1。 +因此**首轮分数与本细则下的分数不可直接比较**,要比较必须按同一细则重跑。