
一致性四象限:用多次采样评估 AI 编码与分析的可靠性
一致性四象限:用多次采样评估 AI 编码与分析的可靠性
当 AI 代理被用来写代码或写 SQL 时,我们往往没有标准答案可以对照。本文介绍一套可落地的评估框架:把同一个问题重复交给模型或编码代理多次,从「语法一致性」和「输出一致性」两个维度把结果分到四个象限,从而在缺少 ground truth 的情况下判断结果是否可信。文中给出完整的命令行实验流程、指标计算方式、四象限的判读方法,以及温度、采样、推理模型等影响因素。
用大语言模型写代码、写 SQL 查询、生成分析报告,已经是很常见的做法。但这类系统有一个绕不开的问题:同一个问题问十次,可能得到十份不同的代码。当问题有明确正确答案时,这种不一致会直接损害结果的可信度。麻烦的是,真实场景里我们通常没有标准答案可以对照,也就无法直接判断某一次输出到底对不对。
本文介绍的是一套评估思路:把同一个问题重复交给模型或编码代理多次,然后从两个维度去观察结果——语法一致性(每次生成的代码或文本本身有多像)和输出一致性(这些代码实际跑出来的结果有多像)。把这两个维度交叉,就得到一张「一致性四象限」图,用来在没有标准答案的情况下估计可靠性。整套流程可以用一个命令行工具来跑,支持通过 API 调用不同厂商的基础模型,也支持本地模型和编码代理。
准备工作
理解两个维度
在动手之前,先把两个概念分清楚,后面所有指标都建立在这上面。
- 语法一致性:只看模型产出的 token 序列本身,不关心它做了什么。在数据分析场景里就是生成的 SQL 或 Python 代码,在对话场景里就是生成的文本。衡量方式是某种非语义的相似度指标,比如序列相似度、编辑距离、精确匹配率。
- 输出一致性:只看生成结果实际做了什么。SQL 查询返回了哪些数值、代码执行后得到什么结果、文本的语义分类是什么。这一层关心的是行为,不是写法。
这两个维度是独立的。一段代码可以每次写法都不同,但跑出来结果完全一样;也可以每次写法几乎一样,但结果却不一样。前者通常比后者更值得信任。
环境与依赖
整套实验围绕一个命令行工具展开,它负责批量调用模型、收集多次运行的结果、计算一致性指标。准备工作中需要确认几件事:
- 一个可用的 Python 环境,用于安装工具并运行待测的 Python 题目。
- 至少一个模型提供方的 API 密钥。工具通过统一的模型调用层对接多家厂商,因此密钥的形式取决于你选用的提供方。
- 如果打算测试本地模型,需要预先装好并启动本地推理服务。
- 如果打算测试编码代理(coding harness)而不是裸模型,需要该代理本身已经配置好并可非交互式调用。
具体支持的模型清单、安装命令、密钥环境变量名称,以及各提供方的计费方式,都会随版本变化,请以工具的官方仓库和所用模型提供方的官网当前信息为准。
准备题目集
实验需要一组有明确输入输出的小问题。为了控制成本,建议从约束良好的小型编程题入手,例如受 Mostly Basic Python Problems 数据集启发的那类题目:给一个函数签名和一段自然语言描述,要求写出能通过若干断言测试的实现。
每道题最好包含三部分:
- 问题描述,即交给模型的提示词。
- 一组测试断言,用来判断生成的代码是否真的能跑通、结果是否正确。
- 一个题目标识,方便在结果表里按题聚合。
测试断言的存在让你可以在有标准答案的子集上校准指标,再把结论外推到没有标准答案的题目上。
操作步骤
第一步:确定实验矩阵
一次实验由三个要素决定:题目、被测对象、重复次数。把它们列成一张表,例如:
| 题目 | 被测对象 | 重复次数 |
|---|---|---|
| problem_001 | 模型 A | 10 |
| problem_001 | 编码代理 B | 10 |
| problem_002 | 模型 A | 10 |
重复次数决定了指标的稳定性。次数太少,一致性估计的噪声会很大;次数太多,成本线性上升。对于小型题目,十次左右是一个常见的起点,但具体取多少要结合预算和题目难度自行权衡。
第二步:批量运行并保存原始输出
对矩阵中的每一个单元格,把同一段提示词原样发送多次,并把每次的完整返回保存下来。关键点是不要只保存最终代码,还要保存:
- 本次运行的原始文本输出,用于计算语法一致性。
- 本次运行实际使用的模型标识与调用参数。
- 运行时间戳与耗时,便于排查异常。
- 如果被测对象是编码代理,还要记录它做了哪些工具调用、读了哪些文件,因为这些中间步骤本身也是不一致的来源。
保存格式建议用每行一条记录的 JSON,方便后续按题目和被测对象分组。下面是一个记录结构的示意:
{
"problem_id": "problem_001",
"subject": "model_a",
"run_index": 3,
"raw_output": "def solve(xs):\n return sum(xs) / len(xs)",
"params": {"temperature": null},
"elapsed_ms": 1840
}
注意 params 里温度可能为 null,因为推理模型和编码代理通常不向调用方暴露采样参数,这一点后面会展开。
第三步:计算语法一致性
把同一题目、同一被测对象的多次原始输出放在一起,两两比较,得到一个相似度矩阵,再聚合成一个 0 到 1 之间的分数。可选的度量方式包括:
- 精确匹配率:完全相同的输出占所有配对的比例。最严格,也最容易受空白字符和注释影响。
- 归一化编辑距离:先去掉空白和注释,再计算字符级或 token 级编辑距离,最后归一化。
- 序列相似度:在 token 序列上做最长公共子序列或类似度量。
选择哪种度量取决于你关心什么。如果关心的是「模型是否稳定地写出同一份代码」,用精确匹配率;如果关心的是「模型是否稳定地采用同一种解法」,用归一化编辑距离更合适。
第四步:计算输出一致性
这一步需要真正执行生成的代码。对每次运行:
- 把生成的代码写入临时文件或直接在受限沙箱中执行。
- 运行该题目的测试断言,记录是否全部通过。
- 记录代码实际产生的返回值或标准输出。
然后把同一题目、同一被测对象的所有执行结果放在一起比较。如果所有运行都产生了完全相同的数值结果,输出一致性为 1;如果结果分散,则按结果的分组比例计算,例如最常见结果占比,或使用熵的归一化形式。
执行生成的代码有安全风险,务必在隔离环境中进行,限制文件系统访问和网络访问,并设置超时。
第五步:计算正确率(如果有标准答案)
对于带测试断言的题目,可以直接统计通过率:通过测试的运行次数除以总运行次数。这个数字不是一致性指标,但它是校准一致性指标的关键参照。把正确率和两个一致性分数放在同一张表里,就能看出它们之间的关系。
第六步:把结果画进四象限
以语法一致性为横轴、输出一致性为纵轴,每个「题目 × 被测对象」组合就是图上的一个点。四个象限的含义如下。
| 象限 | 语法一致性 | 输出一致性 | 解读 |
|---|---|---|---|
| 第一象限 | 高 | 低 | 写法几乎一样,结果却不同。这是最需要警惕的情况:表面上稳定,实际上不可靠。 |
| 第二象限 | 高 | 高 | 写法稳定,结果也稳定。最理想的状态。 |
| 第三象限 | 低 | 低 | 写法多变,结果也多变。可靠性最差,需要换模型或改提示词。 |
| 第四象限 | 低 | 高 | 每次写法不同,但结果一致。通常仍然可信,因为行为是稳定的。 |
直觉上,第二象限和第四象限的组合比第一象限和第三象限更可靠。如果能在有标准答案的题目上验证这个直觉——比如观察到第四象限的组合正确率确实很高——那么在没有标准答案的新题目上,只要它落进第四象限,就可以给出较高的置信度。
一个完整示例
下面走一遍最小可用的流程。假设只有一道题、一个被测模型、重复五次。
题目:写一个函数,接收一个整数列表,返回它们的平均值;空列表返回 0。
测试断言:
assert solve([1, 2, 3]) == 2
assert solve([]) == 0
assert solve([5]) == 5
运行五次,把每次的原始输出记录下来。假设得到下面五种写法(为便于阅读做了简化):
run 1: def solve(xs):\n return sum(xs)/len(xs) if xs else 0
run 2: def solve(xs):\n if not xs:\n return 0\n return sum(xs) / len(xs)
run 3: def solve(xs):\n return sum(xs)/len(xs) if len(xs) > 0 else 0
run 4: def solve(xs):\n if len(xs) == 0:\n return 0\n return sum(xs)/len(xs)
run 5: def solve(xs):\n return 0 if not xs else sum(xs)/len(xs)
语法一致性:五次输出两两比较,没有一对是完全相同的,精确匹配率为 0。用归一化编辑距离计算,平均相似度大约在中等水平,因为结构相近但条件写法不同。结论是语法一致性偏低。
输出一致性:把五段代码分别执行,跑同一组断言。五次全部通过,且对同一输入返回的数值完全相同。输出一致性为 1。
落点:这个组合落在第四象限——低语法一致性、高输出一致性。也就是说,模型每次写法不同,但行为稳定。
外推:现在换一道没有标准答案的新题,同样跑五次。如果观察到同样的模式——写法各异但执行结果一致——那么对这个结果的信任度可以相应提高。反之,如果五次写法几乎一样但结果各不相同,就落进第一象限,应当视为危险信号,需要人工复核。
注意事项
温度为零不等于绝对一致
把温度设为 0,在功能上等价于贪心采样,也就是每一步都选概率最高的 token。但这并不能保证生成的序列完全一致。原因有两类:一是推理计算中的浮点舍入误差,二是批处理和并行执行带来的非确定性。也就是说,即使参数完全相同,两次调用也可能产生细微差别。
推理模型和编码代理通常不暴露采样参数
较新的推理模型和编码代理往往不向调用方提供温度或采样控制。原因是推理模型需要经过精心选择且非零的温度与采样设置才能发挥效果,而编码代理很可能会根据当前问题和生成内容动态调整这些设置。这意味着你无法通过调参来强制一致性,只能通过多次采样来测量它。这也是本文这套方法的价值所在。
非确定性本身不是缺陷
大语言模型本质上是逐 token 生成的概率系统。每一步都是在一个概率分布上采样,序列开头的一个微小差异会在后续步骤中被放大。这种非确定性正是模型创造力和推理能力的来源,也是它能适应不同问题的原因。目标不是消除它,而是理解它、测量它,并为具体任务选择合适的设置。
一致性不等于正确性
一个系统可以非常一致地给出错误答案。一致性只是可靠性的代理指标,不是正确性的证明。在有标准答案的场景下,应当同时报告正确率;在没有标准答案的场景下,一致性只能用来做相对比较,不能单独作为结论。
人类分析师与 AI 代理的差别在于可审计性
人类分析师也会犯错,也会不一致。但人类的工作过程通常会被记录下来,经过评审后成为团队的标准流程。即使这个流程本身有误,它至少是稳定的、可追溯的。而把同一个问题问 AI 代理十次,更像是问了十个相互独立的分析师。如果问题本身模糊、温度又非零,得到不同答案几乎是必然的。因此,在把 AI 用于有明确正确答案的分析任务时,需要额外的机制来保证结果的可复现性。
成本与规模
重复次数乘以题目数量乘以被测对象数量,就是总的调用次数。使用小型模型和约束良好的题目可以控制成本,但结论向更大模型、更复杂任务外推时需要谨慎。不同模型与不同问题类型的组合,表现出的不一致特征可能完全不同。
执行生成代码的安全边界
输出一致性必须通过实际执行来测量,这意味着你要运行模型生成的代码。务必在隔离环境中执行,限制资源与网络访问,并设置超时,避免生成代码对宿主环境造成影响。
易变信息以官网为准
模型版本、可用参数、计费方式、支持的提供方清单都会随时间变化。本文描述的是方法框架,具体的命令、参数名和默认值请以工具官方仓库和所用模型提供方的官网当前信息为准。