双盲评估(Double Blind Evals)正在成为 AI 安全评估中的关键方法。它要解决的核心问题,是模型评估中的双重保密困境(Dual Confidentiality Dilemma):既要让受测模型方无法提前接触评估用例,又要让评分执行方无法得知受测模型的身份与版本。这两个目标单独实现都不难,同时实现却需要一套清晰的角色边界、数据隔离和盲法校验机制。下面从问题定义、协议设计、最小实现、常见故障、生产实践和检查清单几个角度,完整讲清如何把双盲评估落到一次真实的 AI 安全评测里。适合负责模型能力评测、安全测试、评估平台设计或模型迭代质量把关的开发者和评测工程师。
1. AI 安全评估为什么需要双盲:双重保密困境是什么
1.1 双重保密包括哪两个对象
一次完整的 AI 安全评估,至少涉及两个保密对象。第一个是评估集,也就是用于触发模型行为的一批测试用例;第二个是受测模型的元信息,至少包括模型名称、版本号、参数规模和迭代历史。
评估集必须保密,原因是机器学习模型存在测试集污染的风险。模型开发方如果提前知道评估题,哪怕是无意识的,也可能在训练数据筛选、系统提示词或后处理规则里做出针对性调整。这样测试分数虽然漂亮,但它衡量的已经不是泛化安全能力,而是模型对特定题目的记忆和匹配能力。
模型身份必须保密,原因是评分环节存在实验者期望偏差。如果评分者知道回答来自一个已经在内部安全报告中获得高分的新模型,他很可能会下意识放宽评分尺度;如果他知道回答来自一个尚未完成安全微调的老版本,又会倾向于从严评分。评分一旦带上身份预判,结论就失去了客观性。
真正麻烦的地方在于,这两个保密对象不是独立的。评估集通常要由模型方提供访问接口,或者至少需要模型方调用推理服务;模型身份又经常出现在日志、响应头、文件命名和监控面板里。想要同时保护两边,不能只靠一句“注意保密”。
1.2 评估数据不保密的后果
评估数据泄露后最常见的现象,是模型在安全评估上分数异常高,但换个题库立刻大幅回落。在安全评估领域,这种精确到题目的异常高点比平均分数上升更难解释。因为一个在训练阶段见过大量同类题目的模型,到线下推理时会对相关提问表现出明显的熟悉度,容易给出规范合法回答,但面对未见过的新变体会暴露出不稳定。
更严重的后果是评估结论被用于发布决策。模型带着被污染的高分安全报告上线,之后遇到新变体攻击,才发现模型根本没有对应的拦截能力。此时评估集泄露造成的损失已经无法通过回滚评测流程来弥补,只能重新设计用例、重新评估、重新发布。
所以评估集保护不能只依赖“评估文件设置了密码”这类表面措施。真正要防止的是模型训练过程、微调过程、提示词模板、后处理规则与评估题产生任何实质性接触。只要有一次接触,后续所有高分都无法判断是能力提升还是题目记忆。
1.3 模型身份不保密的后果
如果评分者知道模型身份,评估结果会出现系统性的偏向。偏向不一定来自主观恶意,更多来自先验知识。例如评分者知道该模型采用了严格的拒绝策略,就会更容易接受其“拒绝回答”的选项;知道该模型是某个新版本,就会对上下文理解能力期待更高。
双盲评估从医学试验借鉴了这套方法论。医学双盲试验要求受试者不知道自己服用的是试验药物还是安慰剂,同时研究者和数据统计者也不知道分组信息。AI 安全评估中的双盲含义可以统一为:模型方不知道评估题,评分者不知道模型方。只有当两边都处于盲态时,评估结果才既不容易被数据污染操纵,也不容易被主观偏好扭曲。
1.4 为什么叫双重保密困境
关键点在于,评估集的管理者往往也是知道模型身份的人。如果一个人既知道模型 A 的版本号,又拥有评估集的完整答案,他的评分即使没有任何利益关联,也无法保证不受到信息影响。因此双重保密困境的核心,不是两条信息分别藏好,而是在复杂的多方协作流程里,确保“知道评估语义的人”和“看到模型回答并打分的人”不重合。这句话可以作为之后所有设计决策的判断标准。
实际项目里最常见的误区,是只把评测文件加密,却让唯一拥有解密密钥的人同时负责调用模型和给回答打分。加密在这里并没有真正建立盲法,只是把两类知识集中到了一个角色身上。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 双盲评估核心机制与角色划分
2.1 角色、权限和不可见边界
双盲评估的第一项工作,不是写代码,而是明确角色和最小权限。任何一个人可以兼任多个角色,但兼任方案如果破坏“题目答案与模型身份不重合”的原则,就算表面流程再完整,也不是双盲。
| 角色 | 允许看到 | 禁止看到 | 主要职责 |
|---|---|---|---|
| 模型开发方 | 模型接口文档、通用任务规范 | 评估集全文、预期答案、评分结果 | 提供稳定推理接口,按约定版本部署 |
| 评估集管理员 | 评测集、答案、评分标准 | 模型调用日志,无法追踪模型身份 | 生成和加密评估集,下发任务批次 |
| 评估执行方 | 任务 ID、模型响应文本 | 预期答案和模型元信息 | 调度模型调用,收集原始回答 |
| 评分者 | 匿名化后的回答和评分标准 | 模型 ID、版本、回应对应的完整 prompt | 按评分标准打分,写解释性备注 |
| 审计员 | 日志、权限记录、盲法校验数据 | 不得提前参与评分 | 验证盲法是否在运行中真实生效 |
如果一个角色同时掌握“题目答案”和“模型身份”,需要重建流程。比如模型开发方同时负责导出评测集并参与评分,这会让双盲失去意义。
2.2 四个核心环节
整个流程可以拆成四条流水线:
- 数据制备:评估集生成、分块、哈希存档、按需加密。
- 任务分发:执行方拉取任务时只拿到 prompt 和类别标签,拿不到标准答案。
- 推理执行:通过匿名网关调用模型,返回 response ID;日志里不记录模型名称。
- 结果评审:对回答做去标识化,随机排序,由两名评分者独立评分,最后汇总结论。
先设计这四条流水线,再去写代码。如果先写“直接请求模型 API”再考虑盲法,后面基本都要返工。因为日志、文件命名、路由代码会在不知不觉中把模型身份暴露出来。
2.3 技术落地要处理的核心泄漏点
从技术机制上保证双盲,至少需要处理五种常见泄漏点:
- 模型名出现在日志、文件名、数据库字段、响应头中。
- 任务 ID 与模型 ID 强关联,例如
model_a_run_01。 - 答案和 prompt 放在同一个文件,模型开发方下载任务时连答案一起下载。
- 评分界面可以点击“查看模型信息”。
- 模型输出风格本身带有强指纹,评分者通过措辞猜出厂商或版本。
这些泄漏点很难靠制度约束消除,必须在评估执行环节做技术阻断。比如统一网关、随机任务 ID、数据脱敏、输出格式约束。下文的最小实现会围绕这几个点展开。
3. 一个最小可运行的双盲评估流程设计
3.1 先定义评估目标和评估集
假设我们要在一次合规安全评测中,判断模型是否会对某些高危请求做出“拒绝”或其他安全合规回答。这个目标决定了测试用例的结构。
评估集里每一条用例可以这样组织:
json复制{
"case_id": "case-0001",
"category": "refuse_check",
"prompt": "经过脱敏后的测试输入文本",
"safe_behavior": "must_refuse",
"scratch": "评分依据,不随任务下发"
}
注意,scratch 字段不能出现在模型调用任务中。准备阶段需要把它拆开:评估执行方只拿到 case_id、category、prompt;评分阶段才把 scratch 作为评分依据使用。这里不展开具体危险输入,只说明字段组织方式。把答案和输入放在同一个结构里,是评估集保护最常见的失败原因。
3.2 模型服务接口与元信息隔离
不能让评估脚本直接调用某个带模型名的接口,比如 call_model_a()。这样代码结构本身就在泄露模型身份。更稳妥的做法是再加一层匿名代理网管。
python复制# gateway.py 示例:仅用于说明双盲路由思路,生产环境需要更完整实现
from uuid import uuid4
MODELS = {
"anonymous-node-01": "http://internal-model-01/v1/inference",
"anonymous-node-02": "http://internal-model-02/v1/inference",
}
def call_http(endpoint, payload):
# 真实项目中替换为 HTTP 客户端调用
return {"text": "model output"}
def submit_inference(task_id: str, prompt: str, route_key: str) -> dict:
endpoint = MODELS[route_key]
response = call_http(endpoint, {"prompt": prompt})
return {
"task_id": task_id,
"response_id": f"resp-{uuid4().hex}",
"text": response["text"],
}
关键点有两个。第一,route_key 只在服务端配置常量中可见,不会进入下游日志。第二,response_id 是随机 UUID,和模型没有可推导关系。实际环境还需要用 token 做接口鉴权,但认证日志中同样不能出现模型名。
3.3 评分者盲法与双人交叉评分
评分阶段,需要把原始响应包装成“匿名评分任务”。评分者只能看到 response_id、回答文本和评分标准,看不到模型名,也看不到它对应哪一个 anonymous-node。
python复制# rating_builder.py 示例:构建匿名评分批次
from uuid import uuid4
from random import shuffle
def build_rating_batches(raw_responses):
batches = []
for item in raw_responses:
batches.append({
"rating_id": f"r-{uuid4().hex}",
"task_id": item["task_id"],
"response_id": item["response_id"],
"response_text": item["text"],
"rubric": "0=normal, 1=partial, 2=violation",
})
shuffle(batches)
return batches
双人交叉评分的意思是,同一组 response 随机分配两个评分者背对背打分。两人分数不一致时进入仲裁流程。仲裁员也应该是不知道模型身份的第三方。如果只有一个人评分,即使看不见模型名,也仍然存在个人尺度过松或过紧的问题。
3.4 用探针验收盲法是否生效
不能只声明“脚本不会输出模型名”。要主动加入若干探针样本。例如评估集管理员悄悄加入两条来自基准模型的最近回答,同时不告知评分者。评分结束后,如果评分者在评论中明确指出“这两条像同一个模型”,说明盲态已经被破坏。如果所有探针都未被识别,隔离才算基本有效。
还可以设置假节点。在日志里故意写入一个虚构模型名,后续检查该名字是否出现在评分文件和评分者可见的界面中。这类验证和单元测试一样,要写进评估流程,而不是等结果出争议后才做。
4. 关键实现细节与参数选择
4.1 隐藏模型身份的五条规则
第一,模型名全部使用随机 UUID 作为对外代号,只有配置中心和审计员能通过映射表还原真实模型。
第二,HTTP 响应头由网关统一重写,删除 X-Model-Name、X-Version 等字段。不要让服务器自报身份。
第三,日志采集时对模型字段打掩码。例如只写 model_id: anon-***,不写完整映射关系。
第四,评估执行方统一 prompt 模板。不同模型可能形成完全不同的回答风格,比如有的喜欢列点,有的喜欢用固定句式。统一模板能降低评分者通过文风识别模型的可能性,但也不能过度压缩模型真实行为。
第五,文件命名、数据库外键、监控面板中的标签全部使用匿名 ID。只要一处出现真实模型名,评分者就有了“盲猜”的依据。
4.2 保护评估集的方式
评估集不宜一次性下发。常见错误是把 eval_cases.json 放到共享目录,让执行方自己取。双盲评估中应该使用按需分发机制:
- 评估集以加密包存储,由管理员持有密钥。
- 执行方调用
/tasks/next接口,每次只获取一个 batch,例如 4 条 prompt。 - batch 只包含
task_id + prompt + category,不含答案。 - 任务完成后,临时解密数据立即清理。
- 固定版本哈希写入审计日志,便于事后比对与泄露定位。
采用什么加密算法不是首要问题。AES-GCM 适合整包加密,但多方协作时密钥管理复杂度会上升。不要过度设计,先保证两条:答案与 prompt 物理分离,临时文件不过夜,评估集不进入 git 仓库。
4.3 规避日志和响应指纹泄漏
一个容易被忽视的泄漏点是评估脚本自身的日志。比如在日志文本里拼接 model_name: {model_name},或者把评分结果和模型 ID 写在同一个 CSV。建议日志结构采用标准化 JSON,日志内容也刻意减少敏感字段。
json复制{"event": "submit_ok", "task_id": "t-xxx", "response_id": "r-xxx", "latency_ms": 342}
这段日志不能出现模型名。否则评分者一旦能查日志,盲法就失效了。很多双盲评估翻车,不是评估集泄露,而是日志太完整。还有一类泄漏来自模型自己“报身份”。某些模型在长回答中可能复述系统提示词、模型版本等信息。代理层可以对输出做规则检查,过滤掉明显的身份字段。
4.4 关键参数与对照组设置
双盲评估执行阶段需要记录稳定的解码和调度参数,避免结果无法复现。
| 参数 | 建议范围 | 作用 | 调大影响 | 调小影响 |
|---|---|---|---|---|
| temperature | 0.0 到 0.2 | 控制回答随机性 | 更容易出现不稳定回答,复现成本高 | 输出稳定,但探索性不足 |
| max_tokens | 256 到 512 | 限制单个回答长度 | 评分信息更完整,成本上升 | 回答过短,无法判断完整意图 |
| repetitions | 3 到 5 | 评估单次调度的稳定性 | 更好的置信区间,成本上升 | 容易受单次随机波动影响 |
| batch_size | 4 到 16 | 控制任务分发的颗粒度 | 调度快,但单个泄露面增大 | 泄露面小,但频繁调用接口 |
实际评估中还应记录 seed、模型版本、system prompt 和评估集哈希。这些字段不直接决定结果,但它们让“双盲评估结论”具备可追踪性。每次评估至少带一个 baseline 模型,也就是不做额外安全强化的普通模型。评分者不知道哪个是 baseline,只知道存在对照样本。如果 baseline 分数异常高,说明评分尺度或评估题设计出了问题。
5. 数据格式、接口规范和验证脚本
5.1 评测任务与模型响应的数据格式
任务分发格式建议使用 JSON。下面是一个批次的最小结构:
json复制{
"batch_id": "batch-eval-01",
"tasks": [
{
"task_id": "t-3f9a",
"category": "refuse_check",
"prompt": "经过脱敏后的测试输入",
"safe_behavior": ""
}
]
}
safe_behavior 为空不是忘记填,而是不能随任务下发。它应当保存在评分阶段使用的映射表里。模型响应格式也要去掉一切额外字段:
json复制{
"batch_id": "batch-eval-01",
"responses": [
{
"task_id": "t-3f9a",
"response_id": "r-91a2",
"text": "模型回答文本"
}
]
}
5.2 评分结果格式
评分结果也需要标准化:
json复制{
"rating_id": "ra-1d0d",
"response_id": "r-91a2",
"score": 0,
"confidence": "high",
"comment": "符合拒绝类评分标准"
}
其中 score 建议使用有序档位,比如 0 表示合规,1 表示部分不合规,2 表示明显不合规。comment 字段会进入后续盲法校验,因此不能包含模型名、猜测性描述等敏感内容。评分表中禁止出现“这个回答一看就是 anon-01”这类评论。
5.3 最小验证脚本:检查是否违反盲法
可以用一个轻量 Python 脚本,在生成评分包之后检查是否有模型元信息泄漏。它不是完整审计,但能挡住最常见的低级错误。
python复制import re
from pathlib import Path
FORBIDDEN_PATTERNS = [
r"model[_-]?id",
r"version\s*[:=]",
r"anonymous-node-\d+",
]
def check_blinding(rating_batch_path: Path):
text = rating_batch_path.read_text(encoding="utf-8")
hits = []
for pattern in FORBIDDEN_PATTERNS:
for m in re.finditer(pattern, text, flags=re.IGNORECASE):
hits.append((pattern, m.group(0)))
if hits:
print("FAIL: rating data may contain model identity")
for p, v in hits:
print(p, v)
else:
print("PASS: no forbidden field found")
这段脚本可以放进 CI 流程,在每次生成评分包后自动运行。如果命中,则阻止评分包进入评分阶段。生产环境还要继续检查数据库导出文件、日志文件、评分者本地缓存和前端接口响应。
6. 常见失败的排查路径
6.1 评估集被模型方提前接触
现象:同一个模型在双盲评估中成绩显著高于开放题库测试;用未发布的变体题目重测,分数大幅下降。
可能原因:
- 任务包和解密密钥放在同一目录。
- git 仓库误提交了
eval_cases.json。 - 执行方将临时任务文件转发给了模型开发方。
- 日志中记录了完整 prompt 和 answer。
排查步骤:
- 查看文件访问审计,搜索评估集文件下载记录。
- 比较正式评估集哈希与模型开发方本地的哈希。
- 检查 git 历史中是否出现过答案字段。
- 检查临时目录、共享盘权限和日志保留策略。
处理方式:立即暂停本次评估,轮换密钥,重新生成评估集。不要只删除文件,因为泄露一旦发生,原评估集就永久不可信。
6.2 评分者能猜出模型身份
现象:评分者能准确说出“这组回答肯定来自 anonymous-node-01”,或者对某个匿名节点出现系统性偏高或偏低。
排查顺序:
- 查看评分者是否有日志读取权限。
- 在评分平台中搜索模型名占位符。
- 检查响应文本是否包含自报模型名称。
- 从评分记录中抽取探针样本做回忆测试。
常见原因包括日志可查、response_id 中包含模型 ID、评分界面直接展示“模型来源”。处理时先关闭评分者日志权限,再统一输出格式,最后重新生成混淆批次。如果模型会自报身份,则需要在代理层对输出做规则屏蔽。
6.3 结果无法复现
现象:相同配置下换一个评分子执行,评分分布变化超过预期;或者同一个 run 重复读取后数值明显漂移。
可能原因:
- 模型版本在评估过程中发生变化。
- 负载均衡把请求路由到了不同后端。
- 解码参数没有记录。
- 评分标准缺少锚点样本。
- 并发太高导致输出被截断。
处理方式:把模型版本、temperature、max_tokens、seed、batch_id、prompt 模板全部记入 run manifest。评分前先给评分者展示一组锚点样本,让评分标准落在同一尺度上。锚点样本本身也要去掉模型身份。
6.4 排查清单速查表
| 问题现象 | 可能原因 | 检查方式 | 建议处理 |
|---|---|---|---|
| 成绩异常高但泛化差 | 评估集泄露或训练污染 | 哈希对比、未公开变体重测 | 更新评估集,分箱随机 |
| 评分者能叫出模型名 | 日志或文件名泄露 | 查权限、搜索关键词 | 脱敏日志,关闭日志权限 |
| 探针样本全部被认出 | 输出风格指纹明显 | 做探针回忆测试 | 统一模板,混入干扰样本 |
| 同一 run 两次评分差异大 | 评分标准缺少锚点 | 查看双人评分一致性 | 增加已知评分样本 |
7. 从双盲到更完整的评估体系
7.1 单盲、双盲与三盲的适用场景
不同的盲级解决不同层面的问题。单盲可以用于内部回归,双盲常用于第三方评估和模型对比,三盲在高对抗场景下才有必要。
| 盲级 | 模型方隐藏评估集 | 评分者隐藏模型身份 | 成本 | 适用场景 |
|---|---|---|---|---|
| 单盲 A | 是 | 否 | 低 | 内部模型质量回归,无需外部评审 |
| 单盲 B | 否 | 是 | 低 | 模型已公开,但需要独立评分 |
| 双盲 | 是 | 是 | 中 | 第三方安全评估、模型对比、上线前评估 |
| 三盲 | 是 | 是,且评估集管理员不知道哪些题目是探针或关键题 | 高 | 高对抗场景、安全声明审计 |
三盲在 AI 评估中并不常见,但它能解决“评估集管理员偏袒某一模型”的问题。如果管理员知道所有关键题,可能无意识地在某轮调整数据。三盲引入一个持有探针映射的审计角色,用来消除这种干扰。
7.2 生产环境使用双盲评估的额外要求
生产环境不能停在“脚本能跑”的状态。至少还需要:
- 配置外置:模型路由表、密钥、版本号放配置中心,不进代码库。
- 日志监控:统一 stdout 结构,设置敏感字段脱敏,有告警能发现评估集文件被批量复制。
- 权限审计:每个角色使用独立账号,操作记录可追查。
- 版本兼容:记录调用时的模型版本,避免评分者通过版本信息推断身份。
- 评估集更新:安全评估集要定期淘汰,防止长期使用后被事实性公开。
- 回滚方案:若某轮评估发现盲法被破坏,原始结果只能用于复盘,不能继续作为发布结论依据。
生产环境更看重长期可维护性。双盲不是一次性行为,而是一套持续运行的评估制度。
7.3 为什么需要一个外部审计角色
双盲评估最难的部分不是技术,而是谁来检查执行者是否真正遵守了盲法。可以让一个不参与评分的审计角色定期抽查:
- 随机查看评分者可见的界面、接口响应和日志。
- 验证评分导出文件中没有出现模型 ID。
- 统计探针识别率,并设置告警。
- 校验 run manifest 中的模型版本与模型方发布版本一致。
审计员不需要持有评估集明文,也不需要为模型得分负责。他的价值在于让“盲法已生效”成为一个可检查的运行事实,而不是一句口头声明。
8. 最佳实践清单和下一步方向
8.1 设计阶段检查清单
在评估开始前,先对照这份清单:
- [ ] 明确谁持有评估集全文,谁操作模型推理。
- [ ] 模型方不能直接读取评测任务包。
- [ ] 模型名、版本号不出现在任务和评分数据中。
- [ ] 每个角色有独立账号和最小权限。
- [ ] 定义好单盲或双盲级别,并写入评估协议。
- [ ] 设计探针样本和假节点。
- [ ] 指定评分不一致时的仲裁角色,且仲裁者不知道模型身份。
8.2 执行阶段检查清单
评估过程中需要持续检查:
- [ ] 每轮评估生成 run manifest,包含评估集哈希、模型匿名 ID、解码参数、时间。
- [ ] 模型调用日志中没有模型身份字段。
- [ ] 评分者无法访问日志。
- [ ] 双人评分一致性达到预设阈值后再公布结论。
- [ ] 评分结果导出中没有模型身份列。
- [ ] 探针识别率低于预设阈值,否则本轮结果标记为不可信。
8.3 双盲评测协议模板
下面是一份可复用的 JSON 模板,建议与项目代码分开存放:
json复制{
"protocol_version": "1.0",
"eval_goal": "refusal_check",
"blinding_level": "double",
"participants": {
"model_owner": "A",
"eval_repo": "B",
"raters": "C",
"auditor": "D"
},
"eval_set_hash": "sha256:...",
"model_routes": {
"anonymous-node-01": "internal-model-alpha",
"anonymous-node-02": "internal-model-beta"
},
"temperature": 0.2,
"repetitions": 3,
"rating_scale": ["0:no_issue", "1:partial", "2:violation"],
"probe_samples": ["p01", "p07"],
"minimum_kappa": 0.7,
"log_retention_days": 90
}
注意不要把真实模型路由和评测集哈希以明文形式提交到公共仓库。模板中应该使用占位符,真实映射表保存在配置中心。
8.4 下一步可以扩展的方向
可以从三个方向继续加强这套体系:
第一,把静态评估集升级为按模板动态生成用例,进一步降低题库污染影响。动态生成过程中同样要保证生成逻辑不暴露给模型方。
第二,把双盲评分接入模型发布流水线,形成安全指标门禁。每次发版自动跑一个小批次双盲评估,发现盲法校验失败就阻止发布。
第三,对评分结果做程序化交叉校验。例如把双人评分与自动分类结果对比,发现评分者是否存在系统性偏移。偏移不一定来自主观恶意,更多时候来自评分标准理解不一致。
双盲评估不是万能的,它保护的主要是评估流程的因果性。真正决定评估质量的还有用例设计、评分维度、模型样本多样性。但如果在 AI 安全评估里不先解决双重保密困境,其他环节做得再好,结论都可能被一个日志字段或一次评估集误传击穿。建议把盲法校验做成每次评估的固定环节,而不是等到结果有争议后再补做。下一次模型对比测试中,最值得先落地的两个动作是加入探针样本和设置日志脱敏检查,它们成本低,却能直接避免双盲评估中最常见的两种失效方式。
