# 任务级评测 — 设计与预注册

2026-08-04 · 在产出任何数据之前写完并提交。预测写在下面，跑完不改。

机制层的结果见 `research/2026-08-04-focus-hint-eval.md`：230 道单图问答，
五个条件，全部 p ≥ 0.227，零结果。那一层的结论**不外推到本层**，本层也不复用它的题库。

## 为什么换一层测

机制层测的是「同一张图、同一个问题下，换 prompt 能让描述好多少」。它有一个内建的
让步：**每道题都自带一个明确的问题**，问题本身就替代了 hint 和转写条款的一部分
作用——描述侧被告知要找什么之前，答题侧已经知道了。

真实使用不长这样。用户粘一张 CI 失败截图说「修一下」，没人说「请注意第 87 行的
Expected: 4500」。**agent 必须自己决定看什么、看几次、什么时候承认没看清。**
本层测的就是这个，且它能测到机制层测不到的三件事：

1. 描述漏掉的东西，agent 会不会**察觉**并回查（决策日志 #2）。
2. 逐字转写条款防的**静默遗漏**，在没有明确问题时是否真的发生（决策日志 #4）。
3. 任务成败本身。

## 五个条件

Codex 用 `codex exec` 非交互驱动，每个条件一个独立 `CODEX_HOME`
（`~/cdv-task-eval/homes/<condition>/`），**生产配置 `~/.codex/config.toml` 全程只读，
不修改**。除代理端点与 skill 外，配置逐字复制生产设置（模型、reasoning effort、
`disable_response_storage`），因为要测的是用户真实在用的系统。代理一律以默认
flag 启动（`--codex-header-compat` 与 `--inject-reasoning-summary` 保持关闭）。
密钥经 `env_key` 由环境传入，不落盘。

| 条件 | Codex 指向 | 隔离出的变量 |
|---|---|---|
| `blind` | 代理变体，图片一律替换为固定占位文字 | 视觉通道整体的价值（下限） |
| `naive` | 代理变体，`DEFAULT_PROMPT`（"describe this image in detail"） | 普通转接方案 |
| `hint` | 生产代理（`_ROLE_PROMPT` + hint + 转写条款） | **线上实际行为** |
| `hint_tools` | 生产代理 + 已发布的 `vision-tools` skill | 回查工具的价值 |
| `native` | Codex 直连原生多模态模型 | 文字中转的代价（上限） |

四个判断：

- `blind → naive`：**有效性检查**，不是结论。见下节。
- `naive → hint`：hint 在没有明确问题时值不值。
- `hint → hint_tools`：**决策日志 #2 第一次被检验**。
- `hint_tools → native`：还差多远。

`native` 需要视觉模型所在中转站支持 Codex 认识的 wire API。**若不支持，该条件记为
未跑，不用任何替代品冒充上限。**

代理变体不改生产代码：与机制层同样的做法，`codex-vision-proxy.py` 与
`vision_client.py` 从 `main` 现取副本再改常量，副本里对被改的原句加断言，
生产 prompt 措辞一变就报错退出，不会静默退化成另一个条件的副本。

### 一处必须写在前面的更正：回查通道在三个条件里都开着

读生产代码时发现的，写在这里是因为它改变第 4 条预测该怎么读。

代理在每次对话第一张图处注入一段 `_CHANNEL_NOTE`，大意是：图片是以文字形式到达你
这里的，你从来收不到视觉 token；**只要描述里缺你要的东西，就说出你在找什么并调用
`view_image`，下一份描述会照着这个问题写。**

也就是说，**「发现描述不够 → 回查」这条路径 `hint` 条件本来就有**，不是
`hint_tools` 才带来的。因此：

- `hint → hint_tools` 隔离出的是**那四个 CLI 工具**（`glance`/`ground`/`detect`/`trace`）
  的增量，**不是「能不能回查」本身**。预测 #4 说的「增益很小或为零」也只能读作
  「工具相对已有的回查通道增益小」，不能读成「回查没用」。要单独检验回查通道，
  得再加一个删掉 `_CHANNEL_NOTE` 的条件，本轮不加。
- `_CHANNEL_NOTE` 在 `naive` 里**原样保留**。`naive → hint` 要隔离的是视觉侧 prompt，
  两个条件之间只准差这一样东西；顺手把 channel note 也删掉，得到的差值就无法归因了。
- `blind` 是例外，它不在这条链上。该条件不发起任何视觉调用，就把 note 换成一句
  诚实的「本环境无视觉能力，再看也拿不到」——邀请模型去回查一个答不了的通道，
  只会白烧回合。有效性检查不需要是受控臂。

## 题目：为什么必须自己生成

CaptionQA 两种题面都失败了，失败方式值得记住：四选一饱和；去掉选项后，
**连直接看着图的 `native` 都答不出的题从 1 道涨到 10 道**——题面本来就是配合选项
才完整，抽掉选项不是变难，是变含糊。教训是一句话：

> **标准答案必须来自生成源，不能来自标注。**

因此每道任务题由 HTML 源码渲染成截图（headless Chrome），标准答案就是源码里的
那个字面量。判分零歧义、零 LLM judge。

每道题包含：

```
tasks/<name>/
  repo/          agent 的工作目录（一个能跑测试的小仓库）
  evidence.html  截图源码（含标准答案的字面量）
  evidence.png   渲染产物，放进 repo/
  prompt.txt     交给 agent 的话，措辞像用户，不像题目
  verify.sh      退出码 0=通过，非 0=失败；不调用任何模型
  gold.json      源码里的字面量，供 verify.sh 与人工核对
```

三条硬要求：

1. **图必须是承重的**：答案在仓库文本里任何地方都查不到。由 `blind` 条件实测保证。
2. **判分是脚本**：`verify.sh` 跑测试或解析产物做精确比较，绝不用 LLM 判定。
3. **prompt 不能剧透**：说「构建挂了，截图在 ci.png，修一下」，不说
   「请读出第 87 行的 Expected 值」。剧透就退回成机制层。

### 两类任务

**A 类·单点精确值**（例：CI 日志截图里 `Expected: 4500`，仓库里常量是 3000，
数字 4500 全仓不存在）。测的是精确值能不能穿过文字通道。

**B 类·双故障，其中一处不显眼**（例：截图里既有醒目的红色报错，也有一行不起眼的
弃用警告，两处都必须修，`verify.sh` 两项都查）。**这是本轮的重点**——它是唯一能测
到「静默遗漏」的形态：agent 不知道第二处存在，就不会回查，转写条款要么在这里
兑现，要么不兑现。A 类在机制层已经间接测过，B 类没有。

pilot 出 4 道题，**至少 2 道是 B 类**。

## 指标

- **主指标**：`verify.sh` 通过率。二元，脚本判定。
- **B 类拆开报**：显眼故障修没修 / 不显眼故障修没修。总通过率会掩盖后者，
  而后者才是被检验的主张。
- **过程指标**（从 `codex exec --json` 事件流数）：`view_image` 调用次数、回合数、
  墙钟耗时、token。`hint_tools` 额外记它**有没有真的调用** `glance`/`ground`——
  如果它从不调用，那么 `hint_tools ≈ hint` 说明的是工具没被触发，不是工具没用，
  两者结论完全不同，必须区分。
- **不设 LLM judge。** 一个都没有。

## 噪音与重复

agent 循环有随机性，单次运行不能归因。每个 (任务, 条件) 跑 **k=3** 次，
报 3 次里通过几次。配对分析以任务为单位。

温度、reasoning effort 与生产一致，不为降噪而调——降噪调出来的数字不是产品的数字。

## 有效性检查（写在看到数据之前）

- **`blind` 必须几乎全败。** 某道题 `blind` 通过了，说明答案能从仓库文本推出来，
  图不承重，**该题作废并单独列出**，不进配对集。这条规则的地位等同于机制层的剔除规则。
- **`native` 全败的题也作废**：连原生多模态直接看图都做不成的任务，测的是任务设计，
  不是描述通道。这是 CaptionQA 那 10 道歧义题教出来的规则，原样搬过来。
- 两条规则**剔除前后的数字都要报**。

### 第二条通道：本地 OCR（冒烟阶段发现，写在产出数据之前）

第一次冒烟跑就出事：`blind` 通过了 retry-budget——它在被告知「本环境无视觉能力」
之后，去 shell 里找了一圈，发现机器上装着 `tesseract`，直接把截图 OCR 了。

**这条路封不住。** agent 走的是 `/bin/zsh -lc` 登录 shell，PATH 由用户 zshrc 重建，
往前面插桩目录无效（实测）；就算封掉 tesseract，macOS 自带 `swift` 和 Vision
框架，几行代码就是一个 OCR。封堵是打不赢的仗，所以改成**如实记录**：
每次运行都记 `local_ocr` 字段，出现即留证。

因此 `blind` 通过要分成两种，只有一种作废题目：

| `blind` 通过的方式 | 含义 | 处置 |
|---|---|---|
| 没用 OCR | 答案能从仓库文本推出来，图不承重 | **该题作废** |
| 用了 OCR | agent 绕开了描述通道，与题目承重与否无关 | **题目不作废**，该次运行标记后单列 |

**关键在于这是不是全局混淆。** 同批冒烟里 `hint` 全程没碰 OCR——它第一轮就从描述里
拿到了 4500，没有任何理由去找替代通道。也就是说，**伸手去 OCR 是「饿出来的」行为，
不是通用行为**。若正式跑里 `local_ocr` 只在 `blind` 出现，中间三个条件的比较不受影响，
且这一点会用数据说明而不是假定；**若它出现在 `naive`/`hint`/`hint_tools`，
那么本轮比较被第二条视觉通道污染，必须如实报出，不得当作干净结果**。

同理还记 `looked_outside`：命令里出现本次工作目录以外的评测路径就留证。
冒烟里 `hint` 确实翻了一眼隔壁目录（没拿到东西）。运行目录因此改成哈希命名、
verify.sh 与 gold.json 移出 agent 可浏览范围——**第一次冒烟时 `blind` 正是靠
`cat gold.json` 和 diff 隔壁已完成的 `native` 目录拿到答案的**。渲染用的 HTML 源码
（含标准答案字面量）也从 `repo/` 移走了，同样是那次冒烟发现的。

## 预注册的预测

| # | 预测 | 依据 |
|---|---|---|
| 1 | `blind` ≈ 0/12 | 有效性检查；不为 0 就是题设计错了 |
| 2 | `naive` 在 A 类上接近 `hint` | 机制层已显示单点值提取上两者打平 |
| 3 | **`naive` 在 B 类不显眼故障上明显落后 `hint`** | 本轮主假设：无 hint 时描述按图片自身结构组织，不显眼的东西被写没 |
| 4 | **`hint_tools` 相对 `hint` 增益很小或为零** | **对决策日志 #2 的反向预测**（见下） |
| 5 | `native` 高于全部条件，但差距小于机制层的 2–3 pp | 任务层错误来源更多，描述通道不是唯一瓶颈 |

**第 4 条是故意押的反向预测，且赌的是本项目自己的主张。** 决策日志 #2 说「首轮描述是
地图，深度由主模型重查」。但回查有一个前提：**agent 得先意识到自己缺东西。**
如果描述静默漏掉了一处故障，agent 手里没有任何触发回查的线索——它不会去问一个它
不知道存在的问题。这一点在本轮尤其干净，因为**回查通道在 `hint` 里已经开着**
（见上文「回查通道在三个条件里都开着」）：若 `hint_tools` 仍不优于 `hint`，
差的就不是工具或通道，而是**触发信号**。若 `hint_tools` 在 B 类不显眼故障上
并不优于 `hint`，那么决策日志 #2 的适用范围就要收窄成「只对 agent 已经知道自己
要什么的情况成立」，而这恰恰**加强**决策日志 #4（转写条款）的地位：
工具救不了的，只能靠首轮不漏。

反过来，若第 3 条不成立（`naive` 在 B 类上也不落后），那 hint 在任务层同样没有可测
价值，README 的相应措辞必须改。**两个方向都写在这里，不留事后选择的余地。**

## 规模与代价

pilot：4 题 × 5 条件 × 3 次 = **60 次 agent 运行**。

冒烟测试实测：一次琐碎任务 9.7k token。真实任务估 40–80k，60 次约 3–5M token，
走用户 DeepSeek 官方 API；视觉调用另计，走评测自己的 env（**不动生产 env**）。
按当前定价量级约合数美元。墙钟按 2 并发估 1.5–2.5 小时。

**pilot 的目的不是出结论，是验仪器**：4 题的配对检验没有统计功效，这一点现在就写明，
免得事后拿 4 道题的 p 值说事。pilot 的成功标准只有两条：

1. `blind` 全败、`native` 大多通过 → 题是承重且可做的。
2. 中间三个条件**至少在某些题上不同** → 题库有区分度。

两条都满足才扩到 15–20 题。**这一步是上一轮花了 230 道题才学到的：先验区分度，
再上规模。**

## 已知局限（写在前面）

- 一个 agent harness（Codex 0.146.0）、一个答题模型、一个视觉模型，均与生产一致。
- 题由我生成，存在出题人偏好风险。缓解：标准答案来自渲染源码不来自判断；
  `native` 作上限对照——**若某条件超过 `native`，几乎必然是题出歧了**，当作红旗。
- 合成截图比真实截图干净。这是真实局限，不在本轮解决。
- k=3 只能分辨「几乎总是」与「几乎从不」，分辨不了 50% vs 65%。

## 产物

代码与数据在 `~/cdv-task-eval/`（**不放 `/tmp`**，macOS 会清理，上一轮吃过一次）。
长跑挂 screen，不挂在 assistant 宿主进程下，中间结果勤 commit。
