---
type: schema
node_id: schema:adversarial-verdicts
title: "表结构: adversarial_verdicts（Phase B agent 门判定）"
date: 2026-07-14
---

# 表结构: adversarial_verdicts（Phase B agent 门判定）

出题管线 Phase B 后置过滤层的持久化底座。记录 agent 对每题每 stage 的一次试答判定，支持
按 `(question_id, question_hash, stage)` 续跑、按 `agent_config` 变化作废、聚合作弊门正确率，
以及在当前 hash+config 下重建最终题库。与 Phase A 的 `question_gen_items` 表正交（互不影响）。

## 列定义

| 列名 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `question_id` | TEXT | PK(1/3) NOT NULL | 题目唯一标识 |
| `question_hash` | TEXT | PK(2/3) NOT NULL | 当前题面指纹；hash 不匹配的旧行视为未完成，需重跑试答 |
| `stage` | TEXT | PK(3/3) NOT NULL | cheat / flip_original / flip_mirror |
| `round` | INTEGER | NOT NULL | 过滤轮次 |
| `agent_prediction` | TEXT | | agent 预测答案字母（可 NULL） |
| `agent_correct` | INTEGER | | 作弊门是否答对（1/0；翻转门 stage 可为 NULL） |
| `verdict` | TEXT | NOT NULL | passed / filtered_too_easy / filtered_no_flip / flip_skipped |
| `pair_id` | TEXT | | 关联原题与镜像题（可 NULL） |
| `agent_config` | TEXT | NOT NULL | agent 配置指纹（skill_mode/max_steps/model） |
| `created_at` | TEXT | NOT NULL | ISO8601（写入/覆盖时刷新） |

主键 `(question_id, question_hash, stage)`：同三元组重复写入即 upsert 覆盖（幂等）。

### `verdict` 四枚举值

| 值 | 语义 |
|----|------|
| `passed` | 通过本 stage（作弊门：agent 答错=不太简单；翻转门：镜像题翻转成立） |
| `filtered_too_easy` | 作弊门剔除：agent 无视频即答对，题目太简单 |
| `filtered_no_flip` | 翻转门剔除：镜像题答案未按预期翻转 |
| `flip_skipped` | 翻转门跳过（未构造镜像题等） |

## DDL

```sql
CREATE TABLE IF NOT EXISTS adversarial_verdicts (
    question_id      TEXT NOT NULL,
    question_hash    TEXT NOT NULL,
    stage            TEXT NOT NULL,
    round            INTEGER NOT NULL,
    agent_prediction TEXT,
    agent_correct    INTEGER,
    verdict          TEXT NOT NULL,
    pair_id          TEXT,
    agent_config     TEXT NOT NULL,
    created_at       TEXT NOT NULL DEFAULT (datetime('now')),
    PRIMARY KEY (question_id, question_hash, stage)
);

CREATE INDEX IF NOT EXISTS idx_av_qid ON adversarial_verdicts(question_id);
CREATE INDEX IF NOT EXISTS idx_av_verdict ON adversarial_verdicts(verdict);
CREATE INDEX IF NOT EXISTS idx_av_round ON adversarial_verdicts(round);
```

## 非功能性行为

| 维度 | 策略 |
|------|------|
| 持久化 | 每次 `record_verdict` 立即 commit，崩溃最多丢失当前未提交的一条 |
| 幂等性 | 主键 upsert；`CREATE TABLE IF NOT EXISTS` 天然幂等，重复 `_init_schema` 安全 |
| 断点续跑 | `completed_stages(qid, hash, config)` 返回已完成 stage，重启只补跑未完成的 |
| config 作废 | `invalidate_stale_config(qid, config)` 真正 DELETE 非当前 config 的旧行 |

## Store 方法

| 方法 | 用途 |
|------|------|
| `record_verdict(...)` | upsert 一条 agent 门判定（Phase B Task 6 记 verdict） |
| `completed_stages(qid, hash, config)` | 续跑：当前 hash+config 下已完成的 stage 集合 |
| `invalidate_stale_config(qid, config)` | agent_config 变化时删除该题所有旧 config 行 |
| `cheat_agent_accuracy(round_no)` | 某轮 stage='cheat' 的 agent_correct 平均值（无数据返 0.0） |
| `final_passed_question_ids(hash_by_qid, config)` | 终判 passed 集合，final JSON 全量重建用 |

### 终判规则（防 stale 泄漏）

`final_passed_question_ids` 仅当该题在 **当前 question_hash + 当前 agent_config** 下同时满足：
存在 `stage='cheat'` 且 `verdict='passed'`，且不存在任何 stage 的 `verdict='filtered_no_flip'`，
才计入 final-passed。stale hash / stale config 的旧行因不匹配传入的 `(qid, hash, config)`
天然被排除，绝不泄漏进最终题库。
