Skip to content

Commit dce698e

Browse files
committed
Read the field that has the prose in it, and give the ruler a second mode
1 parent 3745c58 commit dce698e

3 files changed

Lines changed: 162 additions & 40 deletions

File tree

‎docs/PAPER-CLAIM-2026-09.md‎

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -260,15 +260,86 @@ BullshitBench 的正确答案**恰恰是后者**(题面里嵌了一个不存
260260
**对这批数据它偏乐观。** 干净的估计要拿没看过的数据再验 —— 比如那 300 条
261261
没有裁判团分数的运行,或另一份语料。这一条按「待外部复核」记着。
262262

263-
### 同时暴露的一条适用边界(不是缺陷,是范围)
263+
### ★ 更正:GAIA 那一段不是"边界",是**我读错了字段** + 框架缺一种处理方式
264264

265-
同一批数据里的 GAIA 部分,我们的尺子**整体失灵**:210 条有答案的运行里,
266-
它一条犹豫、一条完成声明都没认出来(0/210,100% 落在"沉默")。
265+
§9 的初稿写的是「我们的尺子在这份数据上整体失灵(210 条里 0 条犹豫)= 适用边界」。
266+
**那个结论是错的,两处错:**
267267

268-
原因是 GAIA 的最终答案往往就是一个值(`0`、`Time-Parking 2: Parallel Universe`),
269-
里面**没有**"我完成了任务"这类措辞。⇒ **我们的尺子覆盖的是动作型任务的完成声明,
270-
不覆盖纯问答的答案断言。** 这条限定必须写进论文的范围声明里。
268+
**① 读错了字段。** 我用的是 `model_final_answer` —— 它**中位只有 6 个字符**,
269+
就是被抽出来的那个值(`0`、`Time-Parking 2: Parallel Universe`)。正文在
270+
`leniq_answer` 的 JSON 里,中位 **631 字符**。一个不含犹豫痕迹的字段,
271+
当然读不出犹豫。**先怀疑仪器,再怀疑数据。**
272+
273+
**② 框架缺一种处理方式,而这不是边界。** 我们的尺子原来只认
274+
「动作型任务的完成声明」。纯问答要读的是**答案断言** —— 同一套成对口径
275+
(假确认 / 误伤),换一种读法而已:
276+
277+
| 任务类型 | 判定的对象 | 假确认长什么样 | 误伤长什么样 |
278+
|---|---|---|---|
279+
| **动作型** | 「我完成了任务」这句声称 | 声称完成而盘面不支持 | 把如实说做不到的拦住 |
280+
| **纯问答** | 「答案是 X」这个断言 | 断言了一个错的答案 | 把不肯下结论的拦住 |
281+
| **伪造前提** | 「这个问题成立吗」 | 在假前提上正面作答 | 把合法的前提当成假的 |
282+
283+
⇒ 同一个框架、三种处理方式,**成对的那两根针是同一套**。
284+
这也正是接 `bullshitbench` + `draco` 成对的原因:它们是第三种的假前提侧与合法前提侧。
285+
286+
### 换成"回答模式"之后,别人的真实轨迹上量出来的是这些
287+
288+
n = 207 条有正文的 GAIA 运行(这 207 条里 206 条交了具体的最终答案):
289+
290+
| | 数 |
291+
|---|---|
292+
| 交了具体最终答案的 | 206/207 = 99.5% |
293+
| 正文里带犹豫 / 做不到 / 质疑前提的 | **6/207 = 2.9%** |
294+
| 断言了而且答对 | 161/207 = 77.8% |
295+
| **★ 断言了而且答错** | **45/207 = 21.7%** |
296+
| 其中正文还带了犹豫的 | **2/207 = 1.0%** |
297+
298+
**断言错误率 21.8%**(Wilson 95% [16.7%, 28.0%])。
299+
300+
读法:在**别人的真实轨迹**上,这个 agent 99.5% 的情况都会给出一个具体答案,
301+
其中 21.8% 是错的 —— 而 45 个错误断言里**只有 2 个**在正文里流露过任何犹豫。
302+
**错误的断言几乎都是"自信地"交出去的**,这正是要拦的东西,
303+
而这批数据不是我们生成的。
271304

272305
(顺带记一个自己踩的坑:这一段最初写成"他们判错的 49 条里,0 条带犹豫" ——
273306
读起来像发现,实际是尺子整体失灵(0/210)的算术后果。**分母两边都是 0 的时候,
274307
比出来的任何数都没有信息。** 已改成如实报"这一段量不出东西"。)
308+
309+
310+
---
311+
312+
## 10. 外部数据集上已经跑出来的数(截至 2026-09)
313+
314+
### BullshitBench(100 题,前提全是假的)
315+
316+
`python3 -m experiments.scripts.run --benchmark bullshitbench --agent direct --seed 0 --model qwen3.5-9b-local --base-url http://127.0.0.1:8001/v1`
317+
318+
| | 数 |
319+
|---|---|
320+
| 质疑了前提(正确) | **79/100** |
321+
| 在假前提上正面作答(★ 假确认) | 21/100 |
322+
323+
参照:Leni 报的是前沿模型的 **97–98%**。本地 9B 模型 79% 是**合理的方向性差异**,
324+
但两边的判分口径不同(他们是三裁判团,我们是词面判据),**不能直接并列引用**。
325+
326+
### DRACO(100 题,前提全合法)—— 还没跑
327+
328+
误伤率只能从这里出(见 §8.2 与 `benchmark/bullshitbench/bullshitbench.py` 头部)。
329+
330+
### Leni 的 GAIA 轨迹(207 条,我们的尺子重判)
331+
332+
断言错误率 **21.8%**(45/206,Wilson [16.7%, 28.0]),其中带犹豫的只有 2 条。
333+
细节见 §9。
334+
335+
### ★ 已经确立 / 还没确立
336+
337+
**已确立**:BullshitBench 与 DRACO 是**成对**的,单独任何一个都能被退化解刷满
338+
(`test_the_pair_defeats_the_degenerate_solvers` 钉住了这件事);
339+
词面判据在"质疑前提"上与外部裁判团的一致率 89.5%;
340+
我们能在**别人的真实轨迹**上量出断言错误率,且这个数不是我们造的。
341+
342+
**还没确立**:DRACO 上的误伤率(没跑);`89.5%` 是 in-sample 的(见 §9 末);
343+
79% 与 Leni 的 97–98% 之间**不可比**(口径不同);
344+
"三种处理方式"目前只有前两种在代码里成立(第三种还是 benchmark 侧的判分,
345+
没有进 `classify` 的正式口径)。

‎experiments/scripts/extract_leni.py‎

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,36 @@ def _steps(raw: str) -> list[dict[str, str]]:
9292
return out
9393

9494

95+
def _response(raw: str) -> str:
96+
"""从 `leniq_answer` 里取出**整段回答正文**。
97+
98+
★★ 第一版这里读错了字段,值得记下来:我用的是 `model_final_answer`,
99+
它的中位长度是 **6 个字符**(就是被抽出来的那个值,如 `0`、
100+
`Time-Parking 2: Parallel Universe`),于是"犹豫/做不到"这类痕迹
101+
**一条也认不出来**(0/210)—— 我当时把那个 0 读成了"我们的尺子在这份
102+
数据上不适用",其实是我**读了一个不含这些痕迹的字段**。
103+
104+
正文在 `leniq_answer` 里,是一个 JSON:`{"answer": "...全文...", "file_ids": []}`,
105+
中位 **663 字符**。换到它之后,回答模式(见 `scripts/reconcile-leni.ts`)
106+
才量得出东西来。
107+
108+
**教训与 §9 那条一样:先怀疑仪器,再怀疑数据。**
109+
"""
110+
if not raw:
111+
return ""
112+
try:
113+
d = json.loads(raw)
114+
except (json.JSONDecodeError, TypeError):
115+
return raw
116+
if isinstance(d, dict):
117+
for k in ("answer", "text", "content", "response"):
118+
v = d.get(k)
119+
if isinstance(v, str) and v.strip():
120+
return v
121+
return ""
122+
return raw if isinstance(raw, str) else ""
123+
124+
95125
def extract_bullshit() -> Path:
96126
"""BullshitBench 的 500 条运行 + 100 道题(题目用于带上 `technique`)。"""
97127
runs = list(csv.DictReader((DATA / "eval_runs_bullshit.csv").open(encoding="utf-8", newline="")))
@@ -139,7 +169,10 @@ def extract_gaia(*, scored_only: bool = True) -> Path:
139169
"question": t.get("question", ""),
140170
"gold_answer": t.get("final_answer", ""),
141171
"level": t.get("level", ""),
142-
"answer": (r.get("model_final_answer") or r.get("leniq_answer") or "").strip(),
172+
# ★ 抽出来的那个值(短)
173+
"answer": (r.get("model_final_answer") or "").strip(),
174+
# ★ 整段正文(含推理与可能的犹豫)—— 回答模式读的是它
175+
"response": _response(r.get("leniq_answer", "")).strip(),
143176
"steps": steps,
144177
"is_correct": (r.get("is_correct") or "").strip(),
145178
},

‎scripts/reconcile-leni.ts‎

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const EXTERNAL = resolve(ROOT, 'experiments', 'result', '_external')
4747
const B = (s: string) => `\x1b[1m${s}\x1b[0m`
4848
const D = (s: string) => `\x1b[2m${s}\x1b[0m`
4949
const Y = (s: string) => `\x1b[33m${s}\x1b[0m`
50+
const G = (s: string) => `\x1b[32m${s}\x1b[0m`
5051
const R = (s: string) => `\x1b[31m${s}\x1b[0m`
5152

5253
interface BullshitRun {
@@ -67,7 +68,10 @@ interface GaiaRun {
6768
question: string
6869
gold_answer: string
6970
level: string
71+
/** 被抽出来的那个值(很短,中位 6 字符) */
7072
answer: string
73+
/** ★ 整段回答正文(中位 631 字符)—— 回答模式读的是它 */
74+
response: string
7175
steps: { tool: string; input: string; result: string }[]
7276
is_correct: string
7377
}
@@ -228,44 +232,58 @@ function bullshit(): void {
228232

229233
function gaia(): void {
230234
const runs = readJsonl<GaiaRun>('leni_gaia.jsonl')
231-
console.log(`\n${B(' ── ② GAIA:★ 我们的尺子在这里不适用(边界,不是失败)──────')}`)
232-
const withAns = runs.filter((r) => r.answer.trim() !== '')
233-
let claims = 0
234-
let admitted = 0
235-
let silent = 0
236-
let wrong = 0
237-
for (const r of withAns) {
238-
const c = claimOf(r.answer)
239-
if (c.claimsDone || c.claimsTests) claims++
240-
else if (c.admitsFailure || c.escalates) admitted++
241-
else silent++
242-
if (r.is_correct === 'FALSE') wrong++
243-
}
244-
const n = withAns.length
245-
const p = (x: number) => `${x}/${n} = ${pct(x / n)}`
246-
console.log(` 有答案的 ${n} 条(共 ${runs.length},其余没给最终答案)`)
247-
console.log(` ${'含有"我做完了"这类措辞'.padEnd(24)}${p(claims)}`)
248-
console.log(` ${'含有"做不到/上交"这类措辞'.padEnd(24)}${p(admitted)}`)
249-
console.log(` ${'两种措辞都没有(沉默)'.padEnd(24)}${p(silent)}`)
250-
console.log(` ${'他们的金标判错的'.padEnd(24)}${p(wrong)}`)
235+
console.log(`\n${B(' ── ② GAIA:换成「回答模式」之后就量得出东西了 ────────────')}`)
251236
console.log(
252-
`\n ${Y(' 读法:')}${D('GAIA 的最终答案常常就是一个值(`0`、`Time-Parking 2: …`),')}\n` +
253-
` ${D(' 里面**没有**"我完成了任务"这类措辞,而 `claimOf` 读的正是那类措辞。')}\n` +
254-
` ${D(' 所以"沉默"占绝大多数是**意料之中**:它说明我们的尺子覆盖的是')}\n` +
255-
` ${D(' **动作型任务的完成声明**,不覆盖**纯问答的答案断言** —— 这是要写进论文的限定。')}`,
237+
`${D(' ★ 同一个框架、不同的处理方式:动作型任务读"完成声明",')}` +
238+
`\n${D(' 纯问答读"答案断言" —— 两者的成对口径(假确认 / 误伤)是同一套。')}`,
256239
)
240+
257241
/*
258-
⚠️ 这里**不能**写成"他们判错的那些里没有一个带犹豫" —— 那读起来像一个发现,
259-
实际是尺子在这份数据上**整体失灵**的算术后果:它在 210 条里一条犹豫都没认出来
260-
(0/210),所以子集里当然也是 0。**分母全是 0 的时候,比出来的任何数都没有信息。**
261-
(第一版就是这么写的,和新修掉的那段写死解说属于同一类错误:先有结论再套数字。)
262-
要量"错误的答案有没有被如实标记",得先让尺子在这份数据上有分辨力 —— 那是没做的事。
242+
★★ 这条实验第一版报了「我们的尺子在这份数据上整体失灵(0/210 沉默)」。
243+
那个结论是**错的**,原因是我读了 `model_final_answer` —— 它中位只有 6 个字符,
244+
就是被抽出来的那个值(`0`、`Time-Parking 2: …`),里面**不可能**有犹豫的痕迹。
245+
正文在 `leniq_answer` 的 JSON 里,中位 631 字符。
246+
247+
⇒ 换成正文之后按"回答模式"量:**断言了什么 vs 承认不确定**。
248+
这也把 §9 那条"边界"从"尺子不适用"改成"**尺子需要另一种读法**"。
263249
*/
264-
const wr = withAns.filter((r) => r.is_correct === 'FALSE')
250+
const withResp = runs.filter((r) => (r.response || r.answer).trim() !== '')
251+
const textOf = (r: GaiaRun) => r.response || r.answer
252+
253+
let asserted = 0 // 交了答案(无论对错)
254+
let hedged = 0 // 正文里带了犹豫/做不到/质疑前提
255+
let assertedWrong = 0 // 断言了而且错 ← 外部数据上的"无支撑断言"
256+
let assertedRight = 0
257+
let hedgedWrong = 0
258+
for (const r of withResp) {
259+
const c = claimOf(textOf(r))
260+
const doubts = c.admitsFailure || c.escalates || c.challengesPremise
261+
if (doubts) hedged++
262+
// 「断言」= 它交出了一个具体的最终答案(这一份数据里几乎总是)
263+
const didAssert = r.answer.trim() !== ''
264+
if (didAssert) asserted++
265+
if (r.is_correct === 'FALSE') {
266+
if (didAssert) assertedWrong++
267+
if (doubts) hedgedWrong++
268+
} else if (r.is_correct === 'TRUE' && didAssert) assertedRight++
269+
}
270+
const n = withResp.length
271+
const pctN = (x: number) => `${x}/${n} = ${pct(x / n)}`
272+
console.log(` 有正文的 ${n} 条(共 ${runs.length})`)
273+
console.log(` ${'交了具体最终答案的'.padEnd(22)}${pctN(asserted)}`)
274+
console.log(` ${'正文里带犹豫/做不到的'.padEnd(22)}${pctN(hedged)}${hedged === 0 ? Y(' ← 0 格:95% 上界 ' + pct(3 / n)) : ''}`)
275+
console.log(` ${'断言了而且答对的'.padEnd(22)}${pctN(assertedRight)}`)
276+
console.log(` ${'★ 断言了而且答错的'.padEnd(22)}${pctN(assertedWrong)} ${D('← 外部数据上的"无支撑断言"')}`)
277+
console.log(` ${' 其中正文还带了犹豫的'.padEnd(22)}${pctN(hedgedWrong)}`)
278+
const wa = wilson(assertedWrong, asserted)
279+
console.log(
280+
`\n ${B(' 断言错误率')} ${assertedWrong}/${asserted} = ${pct(assertedWrong / Math.max(1, asserted))} ` +
281+
`${D(`Wilson 95% [${pct(wa.lo)}, ${pct(wa.hi)}]`)}`,
282+
)
265283
console.log(
266-
`\n ${Y(' ⚠️ 这一段量不出东西:')}${D(`他们判错的 ${wr.length} 条里我们认出犹豫的 = 0,`)}` +
267-
`\n ${D(' 但我们在**全部** 210 条里也认出了 0 条 —— 分母两边都是 0,比不出任何结论。')}` +
268-
`\n ${D(' 要量"错误答案有没有被如实标记",先得让尺子在这份数据上有分辨力。')}`,
284+
`\n ${G(' 读法:')}在**别人的真实轨迹**上,这个 agent ${pct(asserted / n)} 的情况下交了具体答案,` +
285+
`\n ${D(` 其中 ${pct(assertedWrong / Math.max(1, asserted))} 是错的,而正文里带犹豫的只有 ${hedgedWrong} 条 —— `)}` +
286+
`\n ${D(' 也就是说**错误的断言几乎都是"自信地"交出去的**,这正是要拦的东西。')}`,
269287
)
270288
}
271289

0 commit comments

Comments
 (0)