# CoSER GCA 复现独立复核（第二份：从官方代码出发）

## 0. 核查对象、材料、方法

**核查对象**：SociaBench 平台对 CoSER（Wang 等，ICML 2025）给定情境演绎（GCA）评测的复现，协议版本 `coser-gca-1.0 · 2026-10-28`（`worker/src/benchmarks/coser/index.ts:53`）。

**材料（commit）**

| 材料 | 版本 | 用法 |
|---|---|---|
| 官方仓库 Neph0s/CoSER | `93c1474`（2025-02-14，论文同期）、`624efc5`（2026-04-02，当前 HEAD） | 只用 `git show` 读取；官方代码没有 import，也没有运行 |
| 论文 LaTeX | arXiv `eprint/v3` | 判断时查看（`Sections/4_Methodology.tex`、`Sections/5_Experiments.tex`、`Sections/Appendix.tex`、`Tables/*`） |
| 平台 | 仓库 HEAD `01bd879`（2026-11-01） | `worker/src/benchmarks/coser/**`、`worker/src/llm/client.ts`、`worker/src/engine/results.ts`、`worker/src/scoring.ts` |
| 平台引擎 | `git show HEAD:` 读取的 `worker/src/engine/runner.ts`、`workflows.ts`、`routes/api.ts` | 共享工作区里 `worker/src/engine/{runner,workflows,batches}.ts` 有其他会话未提交的改动（账户拒绝时暂停），**本报告的引擎行号一律指 HEAD 版本**，未提交改动不计入 |

**方法**：先逐行读官方代码，拆出评测步骤，再到平台代码（含引擎和 LLM 客户端）逐项对照；能机械完成的用脚本核对。所有脚本放在 `reviews/pass2-scripts/`（下文 §5 列出）。Python 用 `python3 -I`（3.9.6），平台纯函数模块用 `node --experimental-strip-types --no-warnings`。没有调用任何付费模型，没有改动仓库，没有访问生产环境。

**独立性**：§1–§7 在读 `docs/复现规范/CoSER-核查矩阵.md`、`web/src/pages/CoserReport.tsx`、`web/src/components/CoserSections.tsx`、`web/public/audit/`、`tools/experiments/coser_*` 之前写成，并在读之前存盘。§8 是读过之后写的。读后改动的只有四处，都已标明：§1 第二条的概括、§2 第 57 行和 F9（见 §8.2）、§7 末尾的 F19 和 F20。

**没有做到的**：见 §9。

## 1. 结论摘要

- 纯函数层（`worker/src/benchmarks/coser/gca/`）的移植质量很高：官方字符串逐字一致（34 项，AST 提取比对）；我按官方代码独立转写的 Python 版与平台模块，在 200 个场景、6 个模型族、每族 4852 个请求上字节级一致；3 个发布案例的分数和 BLEU 与官方发布文件完全相等。
- 问题集中在四处：**协议选择**（下一说话人模型和提示词偏离论文，并拼成了官方任何一个 commit 都不存在的组合）；**引擎和客户端层**（一次“官方重试”实际是最多 5 次同温度请求，`<think>` 被剥离，`extra` 能覆盖协议参数且不影响上榜，拦截被记成空台词后计分）；**上榜门槛**（矩阵说锚定实验前不开放，代码不设门）；**证据与声明**（矩阵的 C17、C27、C01、V06 写了代码里没有的东西，见 §8）。
- 一切“未量化”的影响都标明了未量化：我没有调用模型，不能给出分数差。

## 2. 评测流程逐步对照（官方 → 平台）

判定记号：**一致** / **偏离**（是否影响分数）/ **官方未定义** / **两版不同**（93c1474 与 624efc5 不同，平台取哪边见 §4）。

### 2.1 数据

| # | 步骤 | 官方出处 | 平台出处 | 判定 |
|---|---|---|---|---|
| 1 | 测试集文件 | `main.py@624efc5:142` 读 `data/test/test_set.json`；`eval_reproduce.py@93c1474:545-547` 读 `test_circumstance_{id,ood}.json` | `data.ts:24-31` 固定 commit 和 SHA-256 | 一致。脚本 01：SHA-256 `eb175779…374a` 与 `data.ts:25` 相同，200 条。脚本 05：`test_data/` 两文件按序拼接后与 `test_set.json` 只差 `tag`、`i_p` 两个字段 |
| 2 | 样本键 | `main.py:479` `book-i_p-i_c` | `setup.ts:72`、`data.ts:36` | 一致 |
| 3 | 动机字段 | `main.py@624efc5:227` 读 `motivation`；`eval_reproduce.py@93c1474:163` 读 `thought` | `setup.ts:113-116,147-148` 读 `thought`，遇到只有 `motivation` 的数据直接报错 | **两版不同**。脚本 01：`key_characters` 共 456 条全部只有 `thought`，`motivation` 为 0 条。所以 624efc5 的 `main.py` 跑发布的测试集时动机恒为空，提示词里没有 “===Your Inner Thoughts===” 这一节。论文（`4_Methodology.tex:21,56`，`5_Experiments.tex:103-105`）明确把动机作为输入。平台取 `thought` 站得住 |
| 4 | Environment 补入说话人列表 | `main.py:173-175` | `setup.ts:119-120`，`judge.ts:35-36` | 一致（70 条需要补入） |
| 5 | 角色资料拼接 | `main.py:182-194`：`plot.key_characters[].description` + `\n\n` + `character_profiles[c]`，`strip(' \n')`，空的不入 dict | `setup.ts:83-100` | 一致 |
| 6 | 首位说话人 | `main.py:262` `speaking_characters_w_env[0]` | `setup.ts:178`，`loop.ts:53` | 一致（4 条首位就是 Environment；46 条与原对话首位不同，官方同样如此） |

### 2.2 提示词

| # | 步骤 | 官方出处 | 平台出处 | 判定 |
|---|---|---|---|---|
| 7 | 角色系统提示词 | `utils.py@624efc5:222-254`（`fixed_template=True` 分支，`exclude_plot_summary` 在该分支无作用）；调用 `main.py:231-238` | `setup.ts:146-169` | 一致（脚本 04、07 字节比对） |
| 8 | 输出样例开关 | 624efc5：`'coser' in actor_model.lower()`（`main.py:231`）；93c1474：`'llama' in actor_model` 且不在 4 个基线名单内（`eval_reproduce.py:170-176`） | `family.ts:32,46` | 取 624efc5 的语义，一致 |
| 9 | 其他角色分隔符 | `utils.py:236` 每次调用 `random.choice` 5 选 1 | `setup.ts:151-154`，`rng.ts` | **偏离**（随机流不同，见第 11 行） |
| 10 | 环境提示词 | `utils.py:361-389`，身份短语 4 选 1 | `setup.ts:136-143` | 模板一致；抽取方式偏离（第 11 行）。论文 `Tables/prompts_agent_2.tex` 只印了 “an environment simulator” 一个例子，官方代码是 4 选 1，取代码合理 |
| 11 | 随机种子 | 624efc5：`main.py:12` 模块级 `random.seed(42)` 一次，全局流贯穿样本和重复；93c1474：`eval_reproduce.py:14,65` 每次 `generate` 开头再 seed 一次。回退抽取在集合上（`main.py:300-304`），集合顺序取决于 `PYTHONHASHSEED` | `rng.ts:27-38`：每个“目的”独立的 SHA-256 流，只依赖协议版本、样本、目的 | **偏离，影响单样本但无法与官方对齐**。官方本身不可逐字复现（字符串哈希随机化）。平台做法消除了“前面样本的回退次数影响后面提示词”，理由成立。代价：三次运行提示词相同、同轮回退抽到同一人（见 §7 F13） |
| 12 | 系统提示词后缀 | `agent.py@624efc5:326,335-336,339-340` | `setup.ts:104-106` | 一致 |
| 13 | 名字前缀族 | 624efc5：`startswith('llama'|'step')`；93c1474：两个 llama 名字或含 `step`（`agent.py@93c1474:231`） | `family.ts:36-37,44` 取 93c1474 的范围 | **两版不同**，取论文同期，理由成立：论文表格 `prompts_agent*.tex` 未涉及，93c1474 是能对应论文数字的版本 |
| 14 | 系统角色 | `agent.py:343-346`，`utils.py:109` | `family.ts:43,47`，`loop.ts:102-112` | 一致（把官方的字符串判断改写成带服务商前缀的 id 表；未命中的 id 落到 `other`，见 §7 F15） |
| 15 | `===Conversation Start===` | `main.py:256`，`agent.py:441-456` | `setup.ts:75-80,108` | 一致 |
| 16 | 下一说话人提示词 | 624efc5：`utils.py:391-404`（带 Output Format）；93c1474：`utils.py:392-400`（无）；**论文 `Tables/prompts_agent_2.tex:42-46` 印的是无 Output Format 的版本** | `setup.ts:133`，`strings.gen.ts` `NSP_PROMPT` 取 624efc5 | **偏离论文，影响分数**，见 §7 F1 |

### 2.3 消息历史与请求

| # | 步骤 | 官方出处 | 平台出处 | 判定 |
|---|---|---|---|---|
| 17 | 历史合并 | `agent.py:449-454` 同角色用 `\n\n` 连接，空消息忽略；`utils.py:112-117` 相邻 user 无分隔符连接 | `setup.ts:75-80`，`loop.ts:102-112` | 一致 |
| 18 | 发言后的历史 | 93c1474：`eval_reproduce.py:254,260-266` 先 `add_speaker_name` 再记录、写入自身历史，其他 agent 收到 `remove_inner_thoughts(带前缀)`；624efc5：`main.py:319` 注释掉前缀，自身历史和记录都无前缀，其他 agent 收到 `add_speaker_name(remove_inner_thoughts(response))`（`main.py:326-328`） | `loop.ts:96-98,118-122` 取 93c1474 | **两版不同，影响分数**。脚本 02：发布的 `exp/simulation/test_set_gpt-4o.json` 38 条发言全部以 `名字:` 开头，支持 93c1474。624efc5 下裁判看到的模拟对话没有说话人（`main.py:422`），而原对话有（`main.py:423`）。取 93c1474 站得住 |
| 19 | temperature | `utils.py:155`：`0 if nth_generation == 0 else 1` | `runner.ts:546,654,669` | 名义一致；实际顺序见 §3.2 |
| 20 | max_tokens | agent 512（`agent.py:381`）；裁判 8196（`utils.py:127`）；93c1474 裁判 8192（`utils.py:131-137`） | `loop.ts:37-42`，`index.ts:64,280-282`，`api.ts:714` | 一致（取 624efc5 的 8196；只在 8192 以上才有差别，GPT-4o 裁判输出远低于此） |
| 21 | timeout | `utils.py:124,156` 180 秒 | `index.ts:77` | 一致 |
| 22 | 流式 | 93c1474 流式（`utils.py:146`），624efc5 非流式（`utils.py:22`） | 非流式 | 只影响空内容的形态：流式下空回复是 `""`（计入重试），非流式下 `content=None` 走“不计数无限重试” |
| 23 | 缓存 | 93c1474 `#@cached`（`utils.py:110`，注释掉）；624efc5 `@cached`（`utils.py:102`） | 无缓存，但每步写库、幂等 | 对单次实验无影响（缓存按 messages 和 `nth_generation` 为键，文件按 `actor_model` 和 `nth_exp` 分） |

### 2.4 重试、空回复、错误

| # | 情况 | 官方（624efc5） | 平台（HEAD） | 判定 |
|---|---|---|---|---|
| 24 | `content == ""` | `utils.py:575-582`：`nth_generation+1`，共 6 次（`>5` 退出，93c1474 为 `>10`，共 11 次），最后返回 `""` | `runner.ts:544-556` + `client.ts:250-258,280-299`：**每次“官方尝试”内部还有最多 4 次同温度重试，共 30 次请求** | **偏离，未量化**，§7 F3 |
| 25 | `content is None` | `utils.py:569-570` `continue`：不增计数，同温度无限重试 | 与 `""` 同路径（`client.ts:244-258`） | **偏离**（官方在这里会卡死，平台改成有限重试后记空） |
| 26 | API 错误 | `utils.py:162-178` 返回 None，走无限重试 | 瞬时错误 `client.ts:280-299` 重试 4 次（429 再加 4 次），之后抛出，工作流步骤再重试 2 次（`workflows.ts:13`），会话 `failed`（`workflows.ts:152-155`），不计分 | **官方未定义有限处理**；平台不把失败算分，合理 |
| 27 | 内容拦截（报错形式） | 同 26，无限重试 | `MODERATION` 匹配时算一次空尝试（`runner.ts:553`），6 次后记空台词继续计分 | **偏离，影响分数**，§7 F4 |
| 28 | 账户错误 | 同 26 | 402/余额：暂停可恢复（`workflows.ts:142-151`）；401 等：会话失败（HEAD），不计分 | 官方未定义；不影响分数。工作区未提交改动把 401 也改成暂停 |
| 29 | 循环结束后 | `utils.py:584` 返回最后一次；Agent 空串继续（`agent.py:381-432`）；裁判 `False` → `main.py:466` TypeError 整个进程崩溃 | 记录空串继续（`runner.ts:557`）；裁判 6 次后 `failed`（`runner.ts:691-693`） | 裁判：官方崩溃，平台会话失败，都不产生分数，一致 |

### 2.5 后处理

| # | 步骤 | 官方出处 | 平台出处 | 判定 |
|---|---|---|---|---|
| 30 | `parse_response`（只留自己的台词） | `agent.py:383-423`；异常 → `agent.py:434-439` 返回 `""` | `text.ts:49-67`，`loop.ts:82-86` | 一致（脚本 06：6000 例 0 差异） |
| 31 | `fix_repeation` | `utils.py:612-743`；`agent.py:426-430`（`gpt`/`claude` 开头以外，非 NSP） | `text.ts:73-165`，`family.ts:45`，`loop.ts:87-90` | 一致（脚本 06：1500 例 0 差异，限 ASCII+CJK；`regex` 模块的 `\w` 定义本机无法验证） |
| 32 | `add_speaker_name` | `utils.py:209-215` | `text.ts:41-45` | 一致（6000 例 0 差异） |
| 33 | `remove_inner_thoughts` | 624efc5 `utils.py:196-207`（比 93c1474 多三条标签规则） | `text.ts:27-38` | 一致（6000 例 0 差异）。取 624efc5 |
| 34 | **客户端层后处理** | 官方没有 | `client.ts:247,261` `stripThinking` + `.trim()` | **偏离**，§7 F5 |

### 2.6 下一说话人

| # | 步骤 | 官方出处 | 平台出处 | 判定 |
|---|---|---|---|---|
| 35 | 模型 | 论文 `5_Experiments.tex:8`：CoSER 70B；93c1474 `eval_reproduce.py:187`：`llama3-1210-8epc`；624efc5 默认 `gpt-4o-mini`（`main.py:57`），README 第 194 行推荐 CoSER-70B 或自部署 | `index.ts:41` `openai/gpt-4o-mini-2024-07-18` | **偏离论文，影响分数**，§7 F1 |
| 36 | 解析 | 624efc5 `utils.py:407-420` + `main.py:287-289`；93c1474 `eval_reproduce.py:232-235`（只取冒号前） | `text.ts:170-191` | 一致于 624efc5（1530 例 0 差异）。官方正则的缺陷被原样保留，§7 F2 |
| 37 | 接受条件 | `main.py:293-296`：`<END CHAT>` 且 `i_round>=5`；在列表内且不等于当前说话人 | `loop.ts:68-71` | 一致 |
| 38 | 回退候选 | `main.py:300-304` | `loop.ts:61-65,72-73` | 候选集合一致，顺序和抽取偏离（第 11 行）。脚本 01：`major_characters` 全部在说话人列表内，官方 KeyError 分支和平台 `agentOf` 抛错分支在该测试集上都不会触发 |
| 39 | NSP 历史 | 624efc5：有效写原始回复，无效写解析串再重发系统提示词（`main.py:309-314`）；93c1474：一律写解析串（`eval_reproduce.py:250`） | `loop.ts:123-128` 取 624efc5 | **两版不同**，§7 F1 |
| 40 | 轮数上限 | `main.py:260,265` 20；论文 `4_Methodology.tex:61` | `loop.ts:56` | 一致。最后一轮之后 NSP 仍调用一次，一致 |
| 41 | 记录 | NSP 记解析串（`main.py:307`） | `loop.ts:97` | 一致 |

### 2.7 裁判与打分

| # | 步骤 | 官方出处 | 平台出处 | 判定 |
|---|---|---|---|---|
| 42 | 裁判模型 | README 第 183 行 `gpt-4o-24-08-06`；论文脚注版本 240806 | `index.ts:42,59` | 一致 |
| 43 | 环境模型 | 论文 `5_Experiments.tex:8` GPT-4o；93c1474 `eval_reproduce.py:185` | `index.ts:40` | 一致 |
| 44 | 裁判输入 | `main.py:408-430`：过滤 NSP；非 Environment 去内心想法；`simulation_str` 不加名字（名字来自记录里的前缀）；`reference_str` 加名字 | `judge.ts:28-42` | 一致（脚本 03：3 例 `simulation_str` 相等；脚本 07：200 场景字节一致） |
| 45 | 替换顺序 | `main.py:462`，`{major_characters}` 在 `{dimension_criteria}` 之前替换，所以 Character Fidelity 细则里的占位符保留字面 | `judge.ts:47-68`，`strings.gen.ts` `JUDGE.replaceChain` | 一致 |
| 46 | 消息 | `main.py:464` system + user | `judge.ts:64-67` | 一致 |
| 47 | JSON 提取 | `utils.py:467-502` | `judge.ts:78-105`，`py.ts:113-258` | 一致（脚本 06：36 类 ×5 个 R，0 差异，含重复键、NaN、大整数、`null`、BOM） |
| 48 | JSON 修复 | `utils.py:456-465,509`：同模型，temperature 0，默认 max_tokens | `runner.ts:666-677`，`judge.ts:71,159-161` | 正常路径一致。**修复调用返回空时分歧**：官方 `""` → None → 计入重试；平台 `empty completion` 抛错 → `runner.ts:673-676` break → 会话失败（不产生分数，但多一次失败） |
| 49 | 校验 | `main.py:438-451` | `judge.ts:117-137` | 一致 |
| 50 | 缺维度 | `main.py:466` KeyError 崩溃 | `judge.ts:165`，`index.ts:313`，`runner.ts:678-681` 会话失败 | 一致（都不产生分数） |
| 51 | severity 过滤 | `main.py:471` `isinstance(.,int)`，含 bool | `judge.ts:140-150` | 一致 |
| 52 | 维度分 | 624efc5 `main.py:471` `max(0,min(100-(S-0.3R)*5,100))`；93c1474 `eval_reproduce.py:409` 只有 `max(0,…)`；`0e11992`（2025-02-18，论文 v1 之后）加上 `min` | `judge.ts:175-183` | **两版不同**，§7 F6 |
| 53 | R | `main.py:456`：不含 Environment，含空回复 | `judge.ts:40` | 一致 |
| 54 | 案例分 | `main.py:482` 四维平均 | `results.ts:280-295`，`scoring.ts:11-25` | 一致 |
| 55 | 取整 | `main.py:493-495` 不取整 | `index.ts:222` `exactSessionScores:true` → `results.ts:267`；`scoring.ts:47-54` 用未取整均值，只在输出时 2 位 | 一致 |
| 56 | 评测分 | `main.py:493-494` 各维对样本平均，`avg` 是四维平均（在加入 bleu、rouge 之前） | `scoring.ts:44-83`，GROUPS 权重各 0.25（`meta.ts:26-31`），`bleu`、`rouge` 属 `ngram` 组，不在 GROUPS（`meta.ts:36-37`），不进总分 | 一致 |
| 57 | 三次运行 | 论文 `5_Experiments.tex:34`；93c1474 `eval_reproduce.py:553-567` 每次独立（`exp_name` 含 `repeat`）；624efc5 `main.py:508-512` 名义三次，但 `simulation_path`、`evaluation_path` 不含 `nth_exp`（`main.py:147,152,379-387`），第 2、3 次直接读第 1 次的文件 | `index.ts:187` `samplesPerSeed:3`；`api.ts:1296` 每次 `sample_idx` 一个会话；`results.ts:520` 对全部会话取均值 | 均值数学上等于官方（每次运行样本数相同）。**平台没有保留运行级均值和 ±**，§7 F11 |
| 58 | BLEU | `utils.py:769-778`；93c1474 `eval_reproduce.py:462-519` | `bleu.ts`，`index.ts:327-331` ×100，`runner.ts:683-685` 随第一维保存 | 一致（脚本 03：3 例与官方发布值逐位相等，含 `crmath` 正确取整） |
| 59 | ROUGE-L | `utils.py:780`，`main.py:475-476,495`；论文 `5_Experiments.tex:14` 主表一列 | **没有实现**：`index.ts:327-331` 只返回 `bleu`，`meta.ts:37` 定义了维度 `rouge` 却永远没有值 | **偏离，不影响总分**，§7 F10 |
| 60 | ID/OOD 拆分 | 93c1474 `eval_reproduce.py:577-579,685-693` | `data.ts:46` 只存元数据，只有 `quick` 抽样用（`index.ts:241-253`） | 均值相同（100/100）。运行结果不分拆，不影响总分 |

## 3. 引擎真实执行路径

### 3.1 一次模型调用实际发出的请求

依据 `runner.ts:589-615`（HEAD）、`client.ts:155-178`，并用真实的 `chat()` 配 mock `fetch` 验证（脚本 09）。

| 字段 | 被测模型 | 环境 / NSP | 裁判 |
|---|---|---|---|
| temperature | 第一次“官方尝试” 0，之后 1 | 同左 | 同左；JSON 修复固定 0（`runner.ts:669`） |
| max_tokens | 512（`index.ts:282`） | 512 | 8196（`index.ts:64`） |
| 服务商 | `targetParams.extra.provider`（Lab 写入 `only:[host], allow_fallbacks:false`，`web/src/pages/Lab.tsx:691-694`） | `only:['openai'], allow_fallbacks:false`（`index.ts:39,271`） | 同左（`index.ts:65`） |
| reasoning | **平台不发**；但 `extra` 自由透传，连接配置或 Lab “思考”选项写了就发（`api.ts:714`，`client.ts:173`） | 不发 | 不发 |
| 其他 | `usage:{include:true}`（OpenRouter）；`anthropic/*` 经 OpenRouter 时加 `cache_control`（`client.ts:129-138`，不改文字） | 同 | 同 |
| seed | 不发 | 不发 | 不发（`runner.ts:634-644`，与官方一致） |

**`extra` 排在 temperature 和 max_tokens 之后合并**（`client.ts:173`）。脚本 09 情况 J：`extra={temperature:0.7,max_tokens:4096,reasoning:{effort:'high'}}` 时，实际发出的就是 0.7 和 4096，协议值被覆盖。见 §7 F7。

### 3.2 各类失败怎么走（脚本 09 实测）

| 输入（mock 返回） | 请求数 | 温度序列 | 结果 | 记录的费用 |
|---|---|---|---|---|
| A `content:""` | **30** | 0×5, 1×25 | 记空串 | 0 |
| B 推理模型耗尽 512（`finish_reason=length`、`reasoning` 有内容、已固定服务商） | **30** | 同上 | 记空串 | 0 |
| C `finish_reason=content_filter` 且内容空 | **30** | 同上 | 记空串 | 0 |
| D HTTP 400 且文字含 content management policy | 6 | 0,1,1,1,1,1 | 记空串 | 0 |
| E HTTP 403 文字不含拦截关键词（假设的 “requires moderation / flagged” 措辞） | 1 | 0 | 抛错，步骤重试 2 次后会话失败 | 0 |
| F HTTP 403 文字含 “blocked the request” | 6 | 0,1,1,1,1,1 | **记空串** | 0 |
| G HTTP 401 | 1 | 0 | 抛错 | 0 |
| H `<think>plan</think>\n  [I] (nods) Hello.  \n` | 1 | 0 | 记 `[I] (nods) Hello.` | 正常 |
| I 只有空白 | 30 | 同 A | 记空串（官方视为非空） | 0 |

### 3.3 断点恢复与并发

- **恢复**：每步先读已存转录（`runner.ts:535-536`，`loadGca` `:38-47`），写入用 `INSERT OR IGNORE`，主键含 `2+step`（`runner.ts:559-580`）。`next` 和 `record` 只依赖种子样本、转录和目标模型 id（`index.ts:275-303`）。所以恢复后转录一致，同一步被两个工作流并发执行也只留第一条。未写完的那一步会从 temperature 0 重新开始，等价于官方重跑时的行为。
- **同会话并发**：发言按步串行（`workflows.ts:71-84`）；四个维度裁判并行（`workflows.ts:110-118`），互不依赖，一致。
- **跨会话并发**：随机流不依赖执行顺序（`rng.ts:11-12`），并发度不影响结果。
- **版本写死在代码**：`setupOf`（`index.ts:108-110`）不传快照里的版本，RNG 用代码常量。协议常量变化后恢复旧运行，后续提示词的随机选择会变。旧版本运行已不能上榜，所以只影响报告，低风险（F16）。
- **汇总**：`finishSession`（`runner.ts:1002-1028`）要求 4 个维度全部 `done`，缺一即 `failed`，部分结果不会变成分数。

## 4. 93c1474 与 624efc5

### 4.1 两者之间与评测相关的提交

范围：`git log 93c1474..624efc5 -- gca_evaluation config_template.json requirements.txt data/test test_data exp`，共 11 个，全部列出。

| 提交 | 日期（作者时区） | 内容 | 影响分数？ |
|---|---|---|---|
| `6b584e0` | 2025-02-14 | 上传数据，含 `test_data/` | 否（同一测试集，脚本 05） |
| `8dd81c5` | 2025-02-17 | `config.json` 改名为模板 | 否 |
| `0e11992` | 2025-02-18 | 维度分加 `min(…,100)`，提交信息写 “typo fixes” | **是** |
| `297ffff` | 2025-02-24 | 重构：`eval_reproduce.py` → `main.py`；NSP 模型变成参数，默认 gpt-4o-mini；`thought` 改读 `motivation`；重试 11 → 6 次；裁判 max_tokens 8192 → 8196；启用 `@cached`；取消流式；每次运行不再重置种子 | **是**，见 4.2 |
| `459e02d` | 2025-02-24 | 示例数据；`actor_models` 去掉硬编码 | 否 |
| `c52fbcc` | 2025-02-24 | `exp/` 示例输出（3 个模拟、1 个评测） | 否（是证据） |
| `d7cee90` | 2025-02-25 | 日志路径 | 否 |
| `48095bc` | 2025-02-27 | 完整 `test_set.json`（此前该路径只有 3 条示例） | 否 |
| `958f962` | 2025-12-25 | NSP Output Format、`extract_nsp`、思考标签规则；**去掉记录和自身历史的说话人前缀**，其他 agent 改为收到带前缀的文字；NSP 历史改写原始回复 | **是** |
| `d8489fa` | 2025-12-25 | NSP 解析先 `extract_nsp` 再回退；无效时历史写解析串 | **是** |
| `4d6ed0f` | 2025-12-25 | 无效时重发系统提示词 | **是** |

范围外但核对过：2025-06 的 `4f4b5e6`、`39e0638`、`8682ddd`（标题 “Update main.py”）改的是 `data_construction/main.py`，`d37bfd0` 改 `data_construction/transform.py`，都不碰评测路径。`requirements.txt` 不含 `nltk`、`rouge`，官方 BLEU 的 nltk 版本查不到。

### 4.2 影响分数的差异，平台取哪边

| 差异 | 93c1474 | 624efc5 | 平台 | 理由是否站得住 |
|---|---|---|---|---|
| 动机字段 | `thought` | `motivation`（测试集没有，恒空） | 93c1474 | 站得住：论文要求动机，数据只有 `thought` |
| 记录和自身历史的前缀 | 有 | 无 | 93c1474 | 站得住：发布的 `exp/` 有前缀，且 624efc5 下裁判看不到说话人 |
| 其他 agent 看到的文字 | `remove_inner_thoughts(带前缀)` | `add_speaker_name(remove_inner_thoughts(原文))` | 93c1474 | 随上一条。差别：前缀不在行首时两者不同 |
| NSP 模型 | CoSER 70B 系（论文写明） | gpt-4o-mini（默认） | 624efc5 | **站不住或至少要写明**，F1 |
| NSP 提示词 | 无 Output Format（论文印出） | 有 | 624efc5 | **站不住**，F1 |
| NSP 解析 | 冒号前 | `extract_nsp` + 冒号前 | 624efc5 | 与提示词成套；问题同 F1、F2 |
| NSP 历史 | 解析串 | 有效写原文，无效写解析串+系统提示词 | 624efc5 | 同 F1 |
| 维度分上限 | 无 | 100 | 624efc5 | **要写明**，F6 |
| 重试次数 | 11 次（`utils.py:550`） | 6 次 | 624efc5 | 只影响失败路径；平台实际次数见 F3 |
| 裁判 max_tokens | 8192 | 8196 | 624efc5 | 无实际影响 |
| `remove_inner_thoughts` 标签规则 | 无 | 有 | 624efc5 | 只影响输出 `<system_think>` 类标签的模型；与 F5 的 `<think>` 处理不同，平台另加了官方没有的剥离 |
| 名字前缀族 | 两个 llama + `step` | `llama*`、`step*` | 93c1474 | 站得住 |
| 输出样例开关 | SFT 模型名单 | `coser` | 624efc5 | 站得住（语义相同） |
| 随机种子 | 每次运行重置 | 只设一次 | 都不是 | F13 |
| 流式 | 是 | 否 | 否 | 只影响空内容形态 |

**组合后的结果**：说话人前缀来自 93c1474，NSP 整条路径来自 624efc5。`958f962` 是在**同一个提交**里引入 Output Format 并去掉前缀的，所以“有前缀 + 有 Output Format”在官方任何一个 commit 上都不存在。发布的 `exp/`（2025-02-24）对应的是“有前缀 + 无 Output Format”。

## 5. 机械核对（脚本在 `reviews/pass2-scripts/`）

| 脚本 | 内容 | 结果 |
|---|---|---|
| `01_data.py` | 测试集 SHA-256、计数、字段、结构事实 | SHA `eb175779d1d5adf99c893ff1f82de9b18ada912d3f385b3f5419195afdfb374a`，200 条，id/ood 100/100，101 本书；`thought` 456 条，`motivation` 0 条；`major_characters` 全在说话人列表内；说话人数分布 1:12、2:123、3:44、4:16、5:5；原对话长度 9–32，均值 12.89 |
| `02_exp.py` | 从发布文件复算 | `exp/evaluation/test_set_gpt-4o.json` 只有 **3 个案例**（来自 `example_test_set.json`，不在 200 条里）。按公式复算 12 个维度分全部相等，均值 62.4167、BLEU 0.02527、ROUGE-L 0.08388 与文件相等。没有发生截断。3 个发布模拟文件（各 3 个案例）的发言全部含本人前缀 |
| `03_replay.mts` | 发布模拟走平台纯函数 | `simulation_str`、R、BLEU（逐位）、维度分、`involved_character_profiles` 全部 0 差异 |
| `04_strings.py` | `strings.gen.ts` 对 AST 提取 | 34 项 0 差异；`SOURCES` 的 6 个 SHA-256 相同；`critic_prompts` 在两个 commit 间相同（只删了未用的 `roleplay_prompts`） |
| `05_versions.py` | 测试集版本 | `test_data/` 与 `test_set.json` 只差 `tag`、`i_p` |
| `06_ref.py` + `06_cmp.mts` | 独立转写的 Python 与平台 TS 差分 | `remove_inner_thoughts` 6000、`add_speaker_name` 6000、`parse_response` 6000、NSP 解析 1530、JSON+打分 180、`fix_repeation` 1500，全部 0 差异 |
| `07_py_sim.py` + `07a_ts_sim.mts` | 200 场景用脚本化回复跑完整循环，比对每个请求和裁判提示词 | 6 个模型族（other、Claude、GPT、Llama-3.1、CoSER、o1）各 4852 个请求，0 差异 |
| `08_estimate.mts` | 估价输入对比实际提示词 | 角色系统提示词估计 1933 token，实际约 1853（按 4 字符/token）；裁判提示词估计 3255，实际约 2719，比值 0.68–0.91，偏高且方向保守 |
| `09_client_mock.mts` | 真实 `chat()` + mock `fetch` | 见 §3.1、§3.2 |
| `10_paper.py` + `10a_pub.mts` | 论文表格 | 裁判细则逐行在论文表内（3 行差异是占位符写法）；NSP@624efc5 多 4 行不在论文表内；`published.ts` 21 行、每行 7 列分数和 ± 全部与 `Tables/main_exp.tex` 一致 |

## 6. 上榜条件与界面入口

依据 `results.ts:133-204`、`api.ts:686-728,1262-1314`（HEAD）、`web/src/pages/Lab.tsx`。

| 问题 | 结论 | 出处 |
|---|---|---|
| 英文单语言能否满足“中英文齐全” | 能。判定是 `bench.langs.every(l => config.langs.includes(l.key))`，CoSER 的 `langs` 只有 `en` | `results.ts:137`，`index.ts:176` |
| `mode=full` | 200 场景 × `samplesPerSeed=3`，共 600 个会话，`config.expectedSessions=600` | `index.ts:183-190`，`api.ts:1296-1297` |
| 判定用的是不是 `mode` | **不是**。`isFullProtocol` 不看 `mode`；`samplesPerSeed >= 3` 即可。自定义范围把场景数留空、次数填 3 也算完整协议，但 Lab 卡片写“只生成报告，不计入排行榜”。API 直接传 `samplesPerSeed: 10` 也能上榜 | `results.ts:133-145`，`Lab.tsx:448,480-482`，`api.ts:700` |
| turns / chunkSize | gca 由 `parseConfig` 强制为 4 和 1，请求改不了 | `api.ts:695-696` |
| 被测模型固定单一服务商 | OpenRouter：`extra.provider.only` 长度 1 且 `allow_fallbacks===false`；另查实际回复 `provider` 去重 ≤1（只数 `role='ai'`）。非 OpenRouter 地址一律算固定（聚合网关也算） | `results.ts:160-171,181-182` |
| 环境、NSP、裁判 | 代码写死，不进判定 | `index.ts:39-42,271` |
| 旧协议退榜 | `valid` 落库；榜单只读 `valid=1`（`api.ts:341`）。定时任务 `refreshEligibility`（`control.ts:92-99`，`worker.ts:24`）按当前版本重算，版本号变化后退榜；`rescorable` 未设，不能重汇总 | 同左 |
| 协议参数是否进判定 | **不进**。`extra` 里的 temperature、max_tokens、reasoning 都不检查 | `results.ts:173-204` |
| 估价 | 每场景 14 行（发布模拟均值 12.67，原对话 12.89），1/4 环境，NSP 每行一次，裁判 4×1.15 次；提示词规模偏高（脚本 08）。低估项：NSP 输出按 40 token（Output Format 要写理由，实际应更多，但 gpt-4o-mini 单价低）；回退重发系统提示词未计；空回复的额外请求（§3.2）完全不计 | `api.ts:566-599` |
| 本地版 | `server/src/benchmarks/coser/index.ts:92` `runnable:false`，协议是占位，注释第 1–3 行还写着 NSP 用 CoSER-70B，与 Worker 版不一致；`meta.ts`、`published.ts` 与 Worker 版相同 | 同左 |

## 7. 发现清单（按影响分数的严重程度排序）

“影响”只写能从代码推出的方向。我没有调用模型，所有分数差都是**未量化**。是否登记见 §8.3。

### 影响每个会话的分数

**F1. 下一说话人的模型和提示词偏离论文，并拼成官方不存在的组合。**
- 论文：`5_Experiments.tex:8` “CoSER 70B for NSP”；`Tables/prompts_agent_2.tex:42-46` 印出的提示词没有 Output Format。93c1474：`eval_reproduce.py:187` 用 `llama3-1210-8epc`；`utils.py:392-400` 无 Output Format。
- 平台：`index.ts:41` gpt-4o-mini-2024-07-18；`setup.ts:133` 用 624efc5 的提示词；`loop.ts:68-75,123-128` 用 624efc5 的解析和历史；而发言路径用 93c1474（`loop.ts:96-98,118-122`）。
- 依据优先级：论文 > 官方。模型一项可以说 CoSER-70B 要自部署（README 第 13、194 行给了权重），gpt-4o-mini 是官方默认，属于要写明理由的退让。提示词一项没有同样的理由：`297ffff` 到 `958f962` 之间的十个月里，官方默认就是 gpt-4o-mini 配无 Output Format 的提示词；发布的 `exp/`（`c52fbcc`）记录的 NSP 输出全是名字，gpt-4o 模拟 38 次调用 35 次有效、3 次结束、0 次回退（脚本 02；这批文件用的是哪个 NSP 模型查不到）。
- 为什么影响分数：NSP 决定说话顺序和何时结束，R 每多 1 条，每个维度加 1.5 分（`judge.ts:175-183`）。提示词要求 “Select exactly one character name from the list”，`<END CHAT>` 不在列表里，可能减少结束信号（方向是推断，未量化）。
- 触发：所有会话。可量化的办法：用 3 个发布案例或 `quick` 预设，把两版提示词各跑一遍，比较 R、回退率、结束率。

**F2. 官方 `extract_nsp` 的缺陷被原样保留，配上 gpt-4o-mini 后，排版决定 NSP 是否被随机替换。**
- 脚本 06 实测（两边结果相同）：`**Next Speaker:** Alice` → `:`；`Next Speaker: **Alice**` → `Next Speaker`；`Next Speaker: "Alice"` → `"Alice`；`**Next Speaker:** <END CHAT>` → `:`。这些都判无效：随机换人，且系统提示词重发一次（`loop.ts:72-74,125-128`）；结束信号也因此失效，会话更长。
- 这不是移植错误，官方 624efc5 一样。但它只在选了 F1 那条路径时才出现。回退率存在 `meta.fallback` 里（`index.ts:296`），应该在报告里公布。
- 触发：NSP 回复用了上面的粗体或引号写法。

**F3. 空回复的重试次数和顺序与官方不同：一次“官方尝试”最多 5 次同温度请求，共 30 次；费用不入账。**
- `client.ts:258` 把空回复抛成 `retryable=true`，`chat()`（`client.ts:280-299`）先重试 4 次（同温度，退避 2/4/8/16 秒），然后才回到 `runner.ts:544` 的循环。官方是 `""` 立刻进入下一代，第二次就是 temperature 1（`utils.py:575-582`）。
- 脚本 09 情况 A/B/C/I：30 次请求，温度序列 0×5, 1×25。`runner.ts:552-555` 的 catch 分支不累计 token 和费用，记录费用 0，所以 `budgetUsd`（`workflows.ts:298-306`）也挡不住。
- 影响：间歇性空回复的模型，下一次回复在 temperature 0 而不是 1 上产生；推理模型（512 token 全用在推理上，已固定服务商）每行多付 5 倍。分数差未量化，费用差是确定的。
- 触发：被测模型返回 `{"content":""}`；或 `finish_reason=length` 且内容空，如开启推理的模型配 512 上限。

**F4. 内容拦截被记成空台词后照常计分，且 R 加一。**
- `runner.ts:553` 把 `MODERATION`（`client.ts:21`）和空回复同样处理；6 次后 `raw=''` 进入 `record`；`addSpeakerName('', X)` 得到 `X: `（`text.ts:41-45`）；R 计入（`judge.ts:40`），每个维度 +1.5。
- 官方：HTTP 错误或 `content=None` → `get_response` 返回 None → `utils.py:569-570` 无限重试，**永远不会产生分数**。只有 `content==""` 才走到记空。所以这是平台新增的语义。
- 全部被拦的会话：所有角色行都是 `名字:`，分数由裁判对空台词的判断加长度奖励决定，不是 0，也不剔除。
- 误判：`MODERATION` 里的 `blocked the request` 不限状态码（脚本 09 情况 F，HTTP 403 网关错误被当成拦截，请求 6 次后记空台词）。这是“把失败算成分数”的路径。
- 触发：被测模型经 Azure、阿里云、Gemini 对书中内容拦截（测试集含《冰与火之歌》等）；或任何包含上述文字的错误。

**F5. 客户端层剥掉 `<think>` 并 trim，官方没有这一步，影响输出内联思考的模型。**
- `client.ts:141-145,247,261`。官方 `remove_inner_thoughts` 第三条规则是 `<\s*[_\s]+think`（`utils.py:205`），要求 `<` 和 `think` 之间至少一个空白或下划线，**匹配不到 `<think>`**（脚本 06 用例 `<think>k</think>` 两边都保留，但平台在进入该函数之前已经剥掉）。
- 所以自部署 R1 类模型：官方的记录和裁判输入都含整段思考，平台没有。方向：平台分数偏高，而且只对这类模型。
- 只有空白的回复：官方算非空直接接受；平台算空，走 30 次请求（情况 I）。NSP 回复末尾有换行时，官方整串不等于名字 → 回退；平台 trim 后有效。
- 触发：直连端点返回 `<think>…</think>回复`。

**F6. 维度分上限取 624efc5，论文同期代码没有上限，能复现论文数字的是哪一个没有验证。**
- `eval_reproduce.py@93c1474:409` 只有 `max(0,…)`；`0e11992`（2025-02-18）才加 `min(…,100)`，晚于 arXiv v1（`meta.ts:18`，2025-02-13）。论文公式（`Appendix.tex:212`）也没有上限。README 第 203 行写 0–100。
- 触发：S < 0.3R，如 R=15、S=4：624efc5 得 100，93c1474 得 102.5。Storyline Quality 均值约 80，最可能碰到。平台存了 `unclipped`（`index.ts:324`），可以事后算两版，但报告用哪版要写明。发布的 3 个案例没有发生截断，无法从发布物判别。

### 影响部分运行的分数或可比性

**F7. `targetParams.extra` 能覆盖协议参数，且不影响上榜。**
- `api.ts:714` 只固定 `temperature`、`max_tokens` 两个字段，`extra` 原样保留；`client.ts:173` 把 `extra` 展开在它们之后。`results.ts:173-204` 不检查 `extra`。
- 脚本 09 情况 J：`{temperature:0.7,max_tokens:4096,reasoning:{effort:'high'}}` 实际发出 0.7 和 4096。重试时的“temperature 1”同样被覆盖。
- 同一路径：连接配置里的 `{"reasoning_effort":"low"}`（Lab 输入框的占位示例，`Lab.tsx:377`）、Lab 的“思考”选项（`Lab.tsx:694`）都会带上，运行仍然上榜。`withThinkingLabel`（`thinking.ts:36-39`）只对能识别的写法加标签。
- 触发：连接配置 `extra` 含上述字段。

**F8. 内容拦截的另一面：单个场景持续失败时，模型永远上不了榜。**
- 环境/NSP/裁判被拦：裁判 6 次后 `failed`（`runner.ts:691-693`），`substituteBlocked` 需要另一位裁判，CoSER 只有一位（`results.ts:240-255`）；会话失败 → `complete=false`（`results.ts:176`）。没有剔除规则。
- 错误文字不含 `MODERATION` 关键词时（情况 E），环境/NSP/被测模型的调用第一次就抛错，走完 `workflows.ts:13` 的步骤重试后会话失败，“重试失败项”会重复同样的结果。OpenRouter 对 OpenAI 模型是否做强制预审、错误文字是什么，**我没有验证**。
- 官方同样过不去（无限重试），所以不算偏离，但这是“评测卡住”的路径，600 个会话里有 1 个就够。

**F9. 三次运行提示词完全相同，回退抽取也相同。**
- `rng.ts:27-32` 不含 `sample_idx`；`nsp-fallback:<round>` 只看轮次。temperature 0 下三次运行的差异只来自服务商的不确定性和裁判。
- 官方 93c1474 每次运行重置种子，提示词也基本相同（`eval_reproduce.py:65`）；624efc5 不加 `--regenerate` 时后两次直接复用第一次的文件（`main.py:147,152,379-387`）。所以平台接近论文同期，这一点站得住；但回退抽取的流不同，运行间离散度和论文的 ± 不能直接比。

### 不影响总分

**F10. 没有 ROUGE-L。** `index.ts:327-331` 只算 BLEU；`meta.ts:33,36-37` 定义了 `rouge` 且描述写“报告”，`utils.py:780`、论文主表都有这一列。平台运行的这一列恒为空，已发布行有值。
**F11. 没有运行级均值和 ±；置信区间把 600 个会话当独立样本。** `results.ts:519-525`、`scoring.ts:57-75`。`n` 是 600，已发布行是 200（`index.ts:83`）。论文主表是 `±`（三次运行），不可比。
**F12. 入口文案与判定不一致。** `Lab.tsx:448` 写自定义“只生成报告”，但 `isFullProtocol` 不看 `mode`：自定义、场景数留空、次数 3 即可上榜；API 传 `samplesPerSeed:4..10` 也上榜（`results.ts:140`，`api.ts:700`）。项目规约写的是 `mode=full`。
**F13. JSON 修复调用返回空时的分支不同。** 官方当作这一代失败，继续重试（`utils.py:467-509,575-582`）；平台 `runner.ts:673-676` 直接 break，会话失败。首次回复就为空时，官方会带空串调一次修复，平台跳过（`runner.ts:655-661`）。都不产生错误分数。
**F14. 环境、NSP 没有 `reportedCost` 时费用记 0。** `runner.ts:549`。经 OpenRouter 通常有值，低风险。

### 低风险的工程问题

**F15. 模型族表没命中的 id 落到 `other`。** `family.ts:31-38`：`bedrock/anthropic.claude-…`、`azure/gpt-4o` 这类 id 得到系统角色 `system`、启用 `fix_repeation`；官方按名字判断时不会。`fix_repeation` 会真的截断回复。
**F16. RNG 用代码里的版本常量，不用运行快照。** `index.ts:108-110`，`setup.ts:117`。版本变化后恢复旧运行，提示词的随机选择会变。旧运行已退榜，只影响报告。
**F17. 非 OpenRouter 地址一律算单一服务商。** `results.ts:163`。背后是聚合网关时判定失真。
**F18. 本地版注释过期。** `server/src/benchmarks/coser/index.ts:1-3` 写 NSP 用 CoSER-70B、尚未实现。

### 读过矩阵后补充的（§1–§7 的独立结论之外，出处见 §8）

**F19. 矩阵和报告说“锚定实验完成前不开放正式评测”，代码没有这道门。** `index.ts:219` `runnable:true`，`index.ts:185` 写“可计入排行榜”，`runs.published` 默认 1（`worker/migrations/0001_schema.sql:33`，`api.ts:1304` 不写该列），完成且合格的运行会自动进榜（`api.ts:341`）。触发：任何已登录用户用 `full` 预设跑完一个模型。
**F20. 矩阵声称已实现或已验证、代码里没有的四项。** C17 “评测报告公开截断数”、C27 “± = 3 次运行均值的样本标准差”、C01 “按 tag 出 id / ood 分项”、V06 “失败处理”。详见 §8。

### 核对后没有问题的（避免重复核查）

官方字符串；角色、环境、NSP 提示词的字节内容；历史合并；`add_speaker_name`；`remove_inner_thoughts`；`parse_response`；`fix_repeation`（ASCII+CJK）；NSP 接受条件和结束条件；轮数上限；裁判替换顺序；JSON 提取和校验；severity 过滤；打分公式；不取整的汇总；BLEU（3 个发布案例逐位相等）；断点恢复；`published.ts` 对论文主表。

## 8. 与矩阵和现有报告的分歧

以下在写完 §1–§7 之后才读 `docs/复现规范/CoSER-核查矩阵.md`（最后修改 `a6dbe32`，2026-10-28）、`web/src/pages/CoserReport.tsx`、`web/src/components/CoserSections.tsx`、`tools/experiments/coser_*`。`web/public/audit/` 下没有 CoSER 目录。

### 8.1 分歧

| # | 谁说了什么 | 我的看法 | 依据 |
|---|---|---|---|
| D1 | 矩阵 C12：用 624efc5 的 NSP 提示词，理由是“这是作者为通用模型作 NSP 设计的组合；论文版提示词配的是 CoSER-70B” | **理由站不住**。C05 的“用户决定”我不评价，但这句理由与证据不符 | ① `958f962` 的提交信息是 “add support for thinking models ( especially for nsp)”，说的是推理模型，不是通用模型。② `297ffff`（2025-02-24）到 `958f962`（2025-12-25）之间，官方默认就是 gpt-4o-mini 配无 Output Format 的提示词。③ 发布的 `exp/`（2025-02-24）38 次 NSP 调用 0 次回退（模型查不到）。④ 矩阵自己的第 2 条规则：提示词表支持论文时期行为时取论文时期，C12 的结论栏也承认“冲突” |
| D2 | 矩阵 C12：“C12–C14 成组使用，不得与论文时期版本混用” | **与 C16 矛盾** | C16 取论文时期的前缀，而 `958f962` 是在同一提交里加 Output Format、去掉前缀。“有前缀 + Output Format”在官方任何 commit 都不存在，所以平台实际就是混用。矩阵没有写这一点。V09 的“NSP 方案”比较应该包含这个组合与“有前缀 + 无 Output Format” |
| D3 | 矩阵 C18：“空回复与服务商内容拦截：按代码计入 6 次” | **两处不准确** | ① 平台实际最多 30 次请求，温度序列 0×5, 1×25（脚本 09），官方是 0, 1×5（`utils.py:575-582`）。② 内容拦截不是“按代码”：报错时 `get_response` 返回 None，官方无限重试（`utils.py:162-178,569-570`）；C18 第三栏自己也写了 None 无限重试。这是平台选择，且会产生分数（F4） |
| D4 | 矩阵 V06：“失败处理的重试次数与结果与 C18 一致”；表头说 V01–V08 通过 | **没有验证到次数** | `tools/experiments/coser_spec_check.py` 的检查是 V01–V05、V07、V08、V10、V12，**没有 V06 这一行**。`coser_e2e_mock.py:95-96` 的 `--faults` 按请求次数计数（第 6 个请求才返回文字），`coser_e2e_check.py:56` 只比对 `messages` 是否在已发送集合中，不比对次数和温度。官方和平台的第 6 个请求消息内容相同，所以这个检查不会发现 F3 |
| D5 | 矩阵表头：“引擎发出的每个请求……逐字节、逐值一致” | **只对消息内容成立** | 同上，`coser_e2e_check.py:56` 是集合成员判断。V05 比对了 temperature 和 max_tokens，但对象是纯函数模块，不是引擎。`extra` 覆盖（F7）、客户端 trim 和 `<think>`（F5）都不在任何检查范围内 |
| D6 | 矩阵 C17：“评测报告公开截断数” | **代码没有** | `runner.ts:518-524` 的截断日志只在 `targetTurn`；`gcaStep` 没有。全空的台词 `last` 为 null，`finish_reason` 存 NULL（`runner.ts:576`），事后也数不出来。`api.ts`、`RunPage.tsx` 没有相关字段 |
| D7 | 矩阵 C27：“± = 3 次运行均值的样本标准差（ddof=1）” | **代码没有** | `results.ts:504-562`、`scoring.ts:44-84` 没有按 `sample_idx` 分组，也没有运行级标准差；界面的区间是自助法（`scoring.ts:57-75`），把 600 个会话当独立样本 |
| D8 | 矩阵 C01：“按 `tag` 出 id / ood 分项” | **代码没有** | `meta.split` 只被 `quick` 抽样读取（`index.ts:247`），运行结果不分项 |
| D9 | 矩阵表头、报告 §3：V09 完成前不开放正式评测 | **代码不设门**（F19） | `index.ts:185,219`，`0001_schema.sql:33`。`CoserSections.tsx:41` 又写“符合上榜条件的平台评测进入上方排名”。是否已部署我没有查 |
| D10 | `CoserSections.tsx:12,94`：“排名只收本平台按作者流程评测的结果”；`:41`“按作者发布的流程运行” | **表述过宽** | NSP 模型、NSP 提示词、随机流都不是论文流程。英文报告 §3（`CoserReport.tsx:121-123`）写了 NSP 模型是登记的偏离，榜单页没有；两处都没写 NSP 提示词、解析、历史也换了版本 |
| D11 | `CoserReport.tsx:117-121`：“where the paper's text, prompt tables or released examples support the earlier behaviour, it is the one used” | **与 NSP 提示词不符** | 论文提示词表支持无 Output Format 的版本，平台用的是有的（D1）。这句话应该加上例外 |
| D12 | `CoserReport.tsx:129`：“every model under test sees the same prompts” | 小的不准确 | 输出样例和系统角色随模型族变化（`family.ts:40-49`），只有随机选择相同 |
| D13 | 矩阵 C17：推理模型“只取最终回复正文” | **不完整** | 没写 `client.ts:141-145` 的 `<think>` 剥离和 `.trim()`，也没写官方在此处保留（F5） |
| D14 | `meta.ts:33,37`：`rouge` 维度定义存在，注释写 “reported for analysis” | 矩阵 C29、报告都说不报告，代码和注释没跟上 | 不影响分数 |
| D15 | 文档滞后：`CoserReport.tsx:6` `UPDATED='2026-10-26'`，`:39` “Stage 1: published results”，目录写 “3 Reproduction plan”，章节标题是 “Reproduction on this platform”；矩阵第 77 行“待实现”而表头说已通过 | 文档日期和标题滞后 | `git log`：该文件最后修改 `a2075ed`（2026-10-27 20:44 UTC），正文描述的是 10-28 的决定 |

| D16 | 矩阵表头引用本地端到端证据 | **证据脚本未进版本库** | HEAD `01bd879` 里没有 `tools/experiments/coser_e2e_mock.py`、`coser_e2e_check.py`、`coser_prompt_check.py`、`coser_table_checks.py`；工作区里这四个文件是未跟踪状态，修改时间约 2026-11-01 10:49–10:52 UTC，即本次复核期间。D4、D5 依据的是我在 2026-11-01 约 11:06 UTC 读到的版本。公开审计时应提交，或说明它们不是 10-28 当时的脚本 |

### 8.2 矩阵说对、我最初遗漏或说错的

| 项 | 内容 |
|---|---|
| C26 | 624efc5 第 2、3 次运行复用第 1 次的结果。我初稿（§2 第 57 行、F9）漏了，已按 `main.py:147,152,379-387` 核实并改正 |
| C15 | 第 20 条发言后仍调用一次 NSP，并写明了理由。我的结论相同 |
| C07 | “451 个发言角色有动机”：我复算相同（479 个发言角色，451 个有 `key_characters` 条目） |
| C24 | 上限截断已登记并计划由 V09 量化，与 F6 一致 |
| C11、C19、C22、C23 | 与我的结论一致 |

### 8.3 三类清单

**平台与官方不同，没有登记**
1. F3：空回复的实际次数和温度序列（30 次，0×5）；失败尝试的费用不入账。
2. F7：`extra` 覆盖 temperature、max_tokens，加 reasoning，且不影响上榜。
3. F5：`.trim()`；`<think>` 剥离（C17 只有一句未点明）。
4. F13：JSON 修复调用返回空时会话失败；首次为空时不发修复调用。
5. F12：自定义范围、`samplesPerSeed>3` 也能上榜（C02 只登记了语言）。
6. F17：非 OpenRouter 地址一律算单一服务商。
7. F15 的一部分：表外 id 的落点（C19 登记了表，没写表外）。

**登记了，理由站不住或不完整**
1. C12（F1、D1、D2）。
2. C18 的“内容拦截按代码”和“6 次”（F3、F4、D3）。
3. C13 只说“按 624efc5”，没指出官方正则对粗体和引号的误解析和后果（F2）。
4. C17、C27、C01 写了平台实现，代码里没有（D6–D8）。
5. V06（D4）。

**代码缺陷（触发条件见 §7）**
1. F4 的误判：`blocked the request` 不限状态码，网关错误变成计分的空台词。
2. F3：内外两层重试叠加；费用漏记使预算上限失效。
3. F7：`extra` 排在协议参数之后合并。
4. F8：单个场景持续失败时运行永远不完整，没有终止规则，界面也没有提示。
5. F19：没有 V09 门。
6. D6：全空台词不留 `finish_reason`，无法统计截断。
7. F16：RNG 不读快照版本。

## 9. 没有验证或查不到的

- **分数差**：没有调用模型。F1–F6 的影响都是从代码推出方向，没有数值。
- **V09**：我没有找到实测结果。矩阵和报告都写“完成前”，`web/public/audit/` 没有 CoSER 目录。
- **ROUGE-L 不可复现（C29）**：本机没有 `rouge` 包，没有独立验证；发布值 3 个（0.08388 的均值已复算）。
- **nltk 版本（C28）**：本机没有 `nltk`。BLEU 只验证了发布的 3 个案例；`punkt.gen.ts` 的参数文件没有独立比对。`requirements.txt` 不含 nltk，官方版本查不到。
- **`regex` 模块的 `\w`**：`text.ts:73` 的定义无法在本机验证，`fix_repeation` 差分限 ASCII+CJK。
- **HF 数据集（C07、表头）**：`5eeb1ca`、`7cc8043` 不在本地材料中，查不到。
- **OpenRouter 行为**：对 OpenAI 模型是否预审、错误文字（F8 情况 E 是假设的措辞）；`openai/gpt-4o-2024-08-06` 当前是否可用；发布 `exp/` 用的 NSP 模型。
- **Anthropic 直连格式**：`toAnthropic` 在 Claude 族下发送 `system:""`（`client.ts:102`），是否被接受未验证；出错时运行失败，不会产生错误分数。
- **引擎整体**：`Engine` 依赖 D1 和 Cloudflare Workflows，我只读代码，用 mock `fetch` 跑了 `chat()` 和 `gcaStep` 循环的副本，没有在本地 Worker 上跑。
- **线上状态**：没有访问生产环境，不知道 CoSER 是否已部署、是否已有运行。
