老张在AI文本工厂当质检员已经三年了。传送带上每天流过成千上万条模型回复:客服话术、SQL代码、JSON数据、日报摘要。他面前摆着一本厚厚的《规格说明书》,任务很简单:拿起一件产品,对照说明书上的条目,逐个打勾或打叉。整篇回复写得再漂亮,只要规格不符,照样送回返工线。这种不看文采、不看语气、全靠规则列表下结论的检查方法,就是确定性评估(Deterministic Eval)。 在LLM评估体系里,确定性评估不是最聪明的评委,却是最可靠的保安。你说不清一段话“好不好”,但一定能说清它“符不符合要求”:函数参数有没有写对?JSON字段齐不齐全?是否有不该出现的内容?固定的输入,配上固定的规则,每次检查都返回同一个答案——这就是“确定性”三个字的含义。本文就带你走进老张的检测台,看这套方法如何落地为工程利器。
掌握确定性评估,你就能用最小成本为LLM应用建起一道“守门员”:在函数调用、结构化输出、回归测试等场景中,用可复现的规则清单替代拍脑袋判断,为上线质量筑起可追溯、防回退的标准化防线。
目录
可视化演示
本节配有交互动效(进入视口后自动加载,约 3KB)。
Prompt Test Case Output 确定性评估结果 确定性评估流程:精确匹配、包含匹配、正则验证和 JSON 校验四种策略逐条验证 LLM 输出,生成可复现的评估结果。
评估概述:老张的检测台
老张之所以能用一上午查完三百件产品,是因为他从来不在“好不好”上纠结。他的桌子只有三样东西:一张规格说明书、一个红色印章、一个绿色印章。遇到符合要求的,盖绿章放行;遇到不符的,盖红章退回。这条流水线的聪明之处在于:规则一旦定死,任何两个质检员来查,结果都一模一样。确定性评估正是照着这个思路设计的——它把“模型输出是否符合预期”变成了一道不需要情商、不需要灵感的判断题。
放到工程里,确定性评估指的是:用代码预先定义一套判定规则,把LLM的输出当作输入喂进去,规则自动给出通过或失败的结论。这些规则可以是字符串完全相等、子串包含、正则匹配、JSON结构校验,等等。它不依赖人类打分,也不依赖另一个大模型来“点评”,所以每次运行结果稳定可复现,这在发布流程和自动化测试中尤其宝贵。
在搭建评估套件时,我们通常会准备一批“输入+预期输出”的测试用例,跑一遍模型,再用规则逐个比对,最后统计通过率。通过率是衡量这套评估是否放行的核心数字,也是回归测试中最直观的报警器。
老张的检测台告诉我们一个道理:评估不一定非要“智能”,把规则写清楚,机器就能替你守住质量关卡。确定性评估是整套LLM评估体系里最朴素、也最扎实的一环,所有花哨的主观打分,最后都要回归到这一张张可复现的红绿章上。
- 定义分子
passed_cases表示测试集中符合规则、成功通过的用例数量。 - 定义分母
total_cases表示这一批测试集中全部用例的数量。 - 计算比率两者相除后乘 100%,得到本次评估的通过率。
测试设计:检查清单从哪来
老张刚上岗时,老师傅给了他一份手写清单:“开机查电源,屏幕查划痕,按键查回弹,包装查封条。”这份清单就是工厂几百次事故后总结出来的智慧。测试设计在确定性评估中的地位,和这份清单一模一样——它决定了你的评估覆盖了哪些风险,又会漏掉哪些风险。
设计一套好的确定性评估测试集,需要手工构造有代表性的输入,再为每个输入标注一个确定性的“预期输出”或“预期约束”。比如让模型抽取机票信息,你就要准备航班号、日期、城市名等样本,并预先写明答案。接下来要考虑覆盖策略:不仅要覆盖正常输入,还要覆盖边界情况(空字符串、超长文本、缺失字段),以及常见的“幻觉高发区”。把这些场景都写进用例,评估才不是走过场。
覆盖率和通过率是两回事:通过率衡量“做得对不对”,覆盖率衡量“查得全不全”。一个只包含三个简单用例的套件,哪怕通过率 100%,也不能说明系统可靠。
检查清单不是一天写成的。老张的清单会随流水线升级而增删,测试用例也一样:每发现一个新bug,就把它固化成一条新用例,让评估套件越来越厚,也越来越聪明。
- 分子统计
covered_scenarios表示测试用例已经覆盖到的业务场景或功能分支数量。 - 分母统计
all_scenarios表示根据需求文档梳理出的全部应测场景数量。 - 求覆盖率两数相除乘 100%,得到的比值帮助我们发现“遗漏的检查项”。
精确匹配:游标卡尺式核验
老张桌上有一把游标卡尺,专门用来量那些必须分毫不差的零件。比如螺丝直径,图纸写 5.0mm,实际量出 4.9mm,哪怕只差零点一毫米,也直接退回。精确匹配(Exact Match)就是评估世界里的游标卡尺:把模型输出和预期输出逐字对比,完全一致才算通过,多一个空格、多一个标点都不行。
这种严苛适合用在输出空间已经被锁死的场景。比如让模型从一组固定选项里选择分类结果,或者让模型返回一个约定好的枚举值、状态码、ID。在这些场景里,答案本来就没有第二种写法,使用精确匹配既简单又高效。它的优点是零歧义、实现成本极低,几行代码就能完成。
但它的局限也很明显:LLM天生爱自由发挥。同一个意思它可能表达成“晴”或“晴天”,这时精确匹配就会误杀合格产品。所以在开放式问答、摘要生成这类本身就不存在唯一答案的任务里,精确匹配并不合适。
游标卡尺量得准,但只能量“硬尺寸”。生产线上那些需要灵活解读的环节,老张就得换上另一件工具——比如下一节要讲的放大镜检查法。
o 和预期输出 g 用等号比一比,完全相等就是 1(通过),否则就是 0(失败)。- 定义比较对象
o代表模型实际输出,g代表测试用例里预先写好的标准答案。 - 相等判断判断模型的输出与标准答案是否完全一致。
- 返回二值结果相等则返回 1,表示通过;不相等则返回 0,表示失败。
包含匹配:放大镜检查关键零件
有的零件不需要量整条边,只需要确认“这里有没有那个关键凸起”。老张于是戴上一只放大镜,在成品表面寻找图纸上指定的特征:有,就过;没有,就退。包含匹配(Contain Match)做的就是这件事——它不要求模型输出和参考答案一模一样,只要求“预期的关键内容确实出现在输出里”。
具体做法是用子串检查判断期望值是否包含在模型输出中。例如预期答案是“北京”,模型输出“今天北京晴转多云”,两者虽然不等同,但关键地点已经出现,就可以判定通过。这种规则特别适合实体核验类任务:地点是否被提到、某个专有名词是否出现、必答点有没有覆盖。它比精确匹配宽容得多,也更容易在真实场景里获得较高通过率。
包含匹配的代价是它只问“有没有”,不问“对不对”。如果用户要求模型“不要提竞品”,你却用包含匹配去查“竞品名是否出现”,判断逻辑反倒是反的。所以使用时要设计好判定方向,并结合业务规则一起使用——规则匹配往往不是一句简单的 in 操作,而是多个关键词、多条条件的组合判定。
放大镜能看到“有没有”,但看不到“结构对不对”。当老张面对一个需要组装的产品时,光看表面还不够,他必须拆开外壳检查内部结构——这正是结构校验的用武之地。
g是否作为子串出现在o里:出现了就是 1(通过),没出现就是 0(失败)。- 确定包含关系关注期望内容
g是否能在模型输出o中找到。 - 成立则通过只要找到子串,判定通过。
- 不成立则失败找不到子串时,判定失败。
结构校验:按图纸核对内部构造
老张最怕的不是尺寸误差,而是产品内部“缺胳膊少腿”:外壳光亮,里面却少了关键零件。所以质检线上还有一道工序——结构校验。它不关心模型输出的措辞是否优美,只关心输出是不是一个合法的 JSON 对象、字段是否齐全、类型是否正确。
在LLM应用中,模型经常被要求输出结构化数据,比如 {"city": "北京", "temp": 25.0}。下游数据库、API 管道只认字段名和类型,一旦模型漏掉 temp 字段,或者把 temp 写成了字符串 "25.0",程序就会崩溃。结构校验通过预先定义一份 Schema(模式清单),逐字段检查:字段必须存在,类型必须匹配,从而把“格式错误”提前拦截在进入业务系统之前。
这种校验方法的工程价值在于它非常稳定,并且能精确给出失败原因:是缺了字段,还是类型错了,是列表长度不对,还是嵌套结构不完整。调试时一眼就能定位问题,配合回归测试使用效果尤佳。
结构校验像是给产品套上了模具骨架,骨头正了,外壳再丑也能用。但有些场景里,老张还需要一个更灵活的图案比对器——能把“车牌号必须是一大写字母加数字”这种模式,直接打印成一张镂空模板。
S 里的每个字段 k,检查它是否存在于输出 o 中,并且类型是否与规定一致。所有字段都合格,整体才有效。- 遍历 Schema 字段
S是预先定义好的字段及类型约束集合,逐一取出字段k。 - 双重条件检查要求字段
k既存在于输出对象o中,且o[k]的类型等于 Schema 规定的类型。 - 连乘判定所有字段的结果相乘;任何一项为 0,整体结果为 0,表示校验不通过。
正则验证:镂空模板盖章检查
老张有一块镂空模板,形状像“京A12345”这种固定模式。他不用逐字读车牌,只需要把模板往牌子上一盖,能看到完整形状露出来,就算对上。正则验证(Regex Match)就是这样的镂空模板:你写一个模式(Pattern),正则引擎去模型输出里扫描,看有没有子串能贴合这个模式。
正则验证擅长处理“格式类”约束。比如校验邮箱格式、手机号区域号、航班号规则、订单编号是否由 3 个大写字母和 6 位数字组成。常用的正则语法包括字符类 [A-Za-z0-9]、量词 {3}、锚点 ^ 和 $ 等。例如模式 [A-Z]{3}\d{6} 能匹配“ABC123456“,从而让”抽出订单号”这类任务拥有了一个精确的验货规矩。
使用正则时要警惕两个陷阱:一是忘记转义,点号 . 在正则里代表任意字符,想匹配真实的小数点必须写成 \.;二是锚点缺失,\d{6} 可以匹配 "abc123456xyz" 中的数字片段,如果要求整体格式,就必须用 ^ 和 $ 锁死首尾。边界处理不当,很容易产生“你以为查完了,实际只查了一半”的误判。
镂空模板再精巧,也只能查一种图案。真正让老张安心的,是那本越来越厚的“历史缺陷档案”——每次改完流水线,他都要把档案里的老问题重新验一遍。这就是回归护航。
P 相当于模板,o 是被检查的文本。只要文本中存在某个子串能匹配 P,判定结果就是 1,否则为 0。- 扫描子串在输出
o中寻找任意子串s。 - 执行模式匹配判断子串
s能否匹配给定的正则模式P。 - 分支返回存在匹配子串就返回 1,一个都找不到就返回 0。
回归护航:永不删档的缺陷档案
老张的抽屉里锁着一本泛黄的档案,记录着建厂以来所有出过事故的产品类型。每次流水线调整后,他都要把这本档案里的案例重新检查一遍,确保老毛病没有复发。回归测试(Regression Testing)在LLM工程里做的正是这件事:把过去积累的测试用例保存成回测套件,每次模型更新、提示词修改、系统升级后,都重新跑一遍。
这套做法的价值在于“防回退”。LLM 是概率模型,改动一个 Prompt 可能让新任务表现更好,却悄悄破坏了旧任务。比如你优化了摘要生成的指令,结果函数调用变得不再遵守 JSON 格式。如果没有回归套件,这类问题往往要等到线上用户投诉才发现。有了回归测试,发布前就能自动拦截。
工程落地上,通常把回归测试接入持续集成(CI)流水线:每次提交代码或更新模型权重,自动执行评估套件,失败则阻断发布并推送预警。测试用例还会随着线上事故不断补充,形成“发现一个问题,固化一条用例,永久守护一个场景”的正循环。
老张的档案越厚,工厂翻车的概率越小。确定性评估的终极形态,就是把这本档案变成一套自动化、持续运行的守护机制——让每一次模型迭代都有迹可循、有例可查、有章可守。
- 统计回归通过数
regression_passed是回归套件中再次运行通过的历史用例数量。 - 统计回归总数
regression_total是回归套件累积下来的全部用例数量。 - 计算稳定率两者相除乘 100%,比值不仅是质量指标,也是版本能否发布的参考线。
AI 应用场景
函数调用参数验证
阶段:模型输出验证与后处理
解决的问题:LLM在工具调用(Function Calling)场景中经常生成 JSON 参数,如果字段名、类型或必填项出错,下游工具直接崩溃。传统做法靠人工阅读模型返回、肉眼检查参数,既慢又容易漏。确定性评估通过 JSON Schema 校验,在参数进入工具前自动拦截异常,保证每次调用都带上完整、合法的参数结构。
具体案例:一个订票助手的工具函数需要 city(字符串)、date(字符串)和 seats(整数)三个参数。评估套件预置 50 条用户语句,让模型生成调用参数,再逐条校验 JSON 字段是否存在、类型是否正确。某次 Prompt 调整导致模型把 seats 输出成了字符串,回归测试在 CI 阶段立刻亮红灯,团队及时回滚,避免了线上事故。
结构化数据抽取校验
阶段:数据管道入库前检查
解决的问题:从非结构化文本中抽取实体、关系并写入数据库时,格式不合法会导致整批数据入库失败。确定性评估用规则匹配和结构校验,确保抽取结果以约定格式交到下游,减少脏数据干扰。
具体案例:法律合同审阅系统从条款中抽取 party_a、party_b、effective_date 等字段。评估用例覆盖缺失日期、金额带逗号、多合同方姓名顺序不一等情况,通过包含匹配和正则验证,持续检查抽取结果是否满足入库规范。字段有缺漏时,系统自动标记该条并提示人工复核。
LLM回归测试
阶段:版本发布前的自动化检查
解决的问题:模型迭代和 Prompt 调整会带来不可预知的行为漂移,手动回归成本高且覆盖不全。确定性评估将历史用例固化为回测套件,每次发布前自动执行,快速发现旧功能回退。
具体案例:一家电商公司每周更新客服机器人的 Prompt。团队为 200 个高频问答建立了精确匹配与包含匹配的混合评估用例;每周五下午由 CI 流水线自动运行,通过率低于 95% 即阻断发布。三个月中成功拦截了 4 次 Prompt 改动引入的旧功能回退,节省了大量线上客诉成本。
Python 演示
"""确定性评估演示: 为 LLM 输出做固定规则校验"""import jsonimport re class DeterministicEval: def __init__(self): self.results = [] self.total = 0 def register(self, name, gold, pred, mode="exact"): self.total += 1 if mode == "exact": ok = self._check_exact(gold, pred) elif mode == "contain": ok = self._check_contain(gold, pred) elif mode == "regex": ok = self._check_regex(gold, pred) else: ok = self._check_json_struct(gold, pred) self.results.append((name, ok)) def _check_exact(self, gold, pred): return gold.strip() == pred.strip() def _check_contain(self, gold, pred): return gold in pred def _check_regex(self, pattern, pred): return bool(re.search(pattern, pred)) def _check_json_struct(self, schema, pred): try: obj = json.loads(pred) except json.JSONDecodeError: return False return all(k in obj and isinstance(obj[k], t) for k, t in schema.items()) def report(self): passed = sum(1 for _, ok in self.results if ok) print("passed = %d/%d, rate = %.1f%%" % (passed, self.total, passed * 100.0 / self.total)) if __name__ == "__main__": ev = DeterministicEval() ev.register("exact-pass", "晴", "晴", mode="exact") ev.register("exact-fail", "多云", "晴天", mode="exact") ev.register("contain-pass", "北京", "今天北京晴", mode="contain") ev.register("regex-pass", r"[A-Z]{3}\d{3}", "航班SHA-123", mode="regex") ev.register("json-pass", {"city": str, "temp": float}, '{"city":"北京","temp":25.0}', mode="json") ev.register("json-fail", {"city": str, "temp": float}, '{"city":"北京"}', mode="json") ev.report()
def register(self, name, gold, pred, mode="exact")注册一条测试用例,记录名称、预期值、模型预测和校验模式。ok = self._check_exact(gold, pred)调用严格相等判定,不处理任何模糊匹配。return gold.strip() == pred.strip()先掐头去尾再去比较,避免首尾空格干扰判定。return gold in predPython 原生的子串检查,判断预期内容是否出现在输出中。return bool(re.search(pattern, pred))用正则引擎扫描文本,存在匹配则返回 True。json.loads(pred)把模型输出的 JSON 字符串解析成 Python 对象,为结构校验做准备。all(k in obj and isinstance(obj[k], t) for k, t in schema.items())逐字段核对存在性和类型,任一不符则整体失败。ev.register("json-fail", {"city": str, "temp": float}, '{"city":"北京"}', mode="json")注册一条缺少 temp 字段的用例,用于演示结构校验的拦截效果。
常见误区
误区我以为确定性评估就是“模型输出和参考答案逐字对比”,所以把所有任务都交给精确匹配,结果通过率低得离谱,团队开始怀疑这套方法没用。
正解精确匹配只是一把游标卡尺,只适合量“硬尺寸”。在开放式问答、摘要生成这类没有唯一正确答案的任务里,它会把语义相同但措辞不同的合格输出全部误杀。正确的做法是先判断任务有没有唯一答案:有,才用精确匹配;没有,就要换包含匹配、正则或结构校验等更合适的规则。
为何常见:因为精确匹配是确定性评估里最直观、最容易被想到的实现方式,初学者很容易把它当成唯一的“对错判断器”。
误区包含匹配比精确匹配宽松,我就把所有用例都改成包含匹配,认为只要关键词出现就万无一失,结果很多错误输出也混过了评估。
正解包含匹配只回答“关键词在不在”,不回答“整体对不对”。它适合实体核验、必答点检查这类“出现即通过”的场景;但如果要同时约束不能出现的内容、字段的数量、文本的边界,就必须叠加正则或结构校验。多把尺子一起用,才能量出真正的合格品。
为何常见:包含匹配实现简单、通过率高,容易让人误以为“通过率高”等于“效果好”,忽略了对判定方向的精细设计。
误区我把 200 条用例跑进 CI 就再也不管了,认为只要套件在跑,模型就一定安全,结果线上场景早已变化,套件形同虚设。
正解回归套件是一本活档案。业务需求变了、Prompt 改了、线上出了新问题,都要及时补充和修订用例。每次线上事故都应当固化成一条新用例,让套件永远紧跟真实风险;过时的用例该删就删,否则评估结果会失真。
为何常见:搭建套件那一刻的效果立竿见影,人们容易忽略维护成本,误以为评估工作是一次性的投入。
误区正则就是随便写个 `\d{6}` 查一下,反正能匹配到数字就行,没想到"abc123456xyz"这种输入也通过了校验。
正解正则的边界和转义是正确性的关键。. 会匹配任意字符,想匹配真实点号必须写 \.;要求整体格式时,要用 ^ 锚定开头、$ 锚定结尾。每个正则上线前,都应该设计正反例各若干条,验证它既不过度匹配,也不漏匹配。
为何常见:正则语法简洁但语义密度高,新手容易只关注“匹配成功”的路径,而忽略“不该匹配却匹配了”的边界情况。
误区我盯着通过率看,以为通过率越高代表评估套件质量越好,却没发现测试用例本身覆盖范围极窄。
正解通过率反映的是这套用例下的表现,覆盖率才反映用例是否触及真实风险。一个只测三个简单输入的套件,100% 通过率也无意义。评估质量 = 用例覆盖率 × 规则正确性 × 结果可复现性,通过率只是三者的最终输出,不能单独指导决策。
为何常见:通过率是一个容易拿到、容易汇报的指标,人们常常用单一数字代替整体评估设计能力的判断。
面试题
Q1. 确定性评估的本质是什么?
- A.判断输出是否优雅
- B.用固定规则判定对错← 正确答案
- C.按印象给模型打分
- D.完全依赖人审结果
答案:B。确定性评估的精髓就是“对错有标尺”,由预先定义的规则自动判定结果,不掺入主观印象。AB 和 C 都属于主观评估范畴,D 丢失了自动化特征。
Q2. 精确匹配适用于以下哪种场景?
- A.开放式问答考验文笔
- B.枚举值固定选项比对← 正确答案
- C.摘要生成自由发挥
- D.翻译质量深层剖析
答案:B。精确匹配要求输出与标准答案完全一致,只适用于输出空间已被锁死的固定枚举场景。开放式问答、摘要、翻译都没有唯一标准答案,使用精确匹配会大量误杀。
Q3. 包含匹配的正确用法有哪些?
- A.检查输出是否含关键词← 正确答案
- B.检查输出是否包含指定实体← 正确答案
- C.要求输出与参考答案完全一致
- D.判断复杂逻辑是否合理
答案:AB。包含匹配的灵魂是“子串在不在”,所以适合关键词和实体核验。C 是精确匹配的职责,D 属于主观语义理解,超出包含匹配的能力范围。
Q4. 结构化校验(JSON Schema)主要关注什么?
- A.字符是否美观流畅
- B.字段存在性与类型← 正确答案
- C.文风是否前后一致
- D.内容是否幽默有趣
答案:B。结构化校验对每个字段做存在性检查和类型检查,能提前拦截格式错误。ACD 都是主观内容性评价,不是结构层问题。
Q5. 正则验证的核心作用是什么?
- A.让模型输出更流畅
- B.用模式匹配文本← 正确答案
- C.生成新的测试用例
- D.自动优化模型参数
答案:B。正则验证通过模式表达式在输出文本中寻找匹配内容,适合格式类约束。ACD 都不是正则在确定性评估中承担的职责。
Q6. 确定性评估适合用在哪里?
- A.函数调用参数验证← 正确答案
- B.结构化数据抽取校验← 正确答案
- C.LLM回归测试套件← 正确答案
- D.评估回答有趣程度
答案:ABC。函数调用、结构化抽取、回归测试都要求“符合规则”的可复现判定,是确定性评估的主战场。D 是主观体验评估,需要人类评委而非规则引擎。
Q7. 通过率的正确计算方式是?
- A.失败数除以总数
- B.通过数除以总数← 正确答案
- C.通过数减去失败数
- D.失败数减通过数
答案:B。通过率 = 通过用例数 ÷ 总用例数 × 100%。A 算的是失败率,CD 计算的是差值不是比率。
Q8. 构建一套高质量评估套件需要做什么?
- A.配置明确的预期输出← 正确答案
- B.覆盖关键业务场景← 正确答案
- C.定期更新并维护用例← 正确答案
- D.用随机文本代替真实数据
答案:ABC。预期输出、场景覆盖、持续维护是评估套件的三根支柱。随机文本无法表达业务约束,会使评估结果完全失真,因此不能作为替代方案。
Q9. 精确匹配与包含匹配最主要的区别是什么?
- A.前者宽松后者严格
- B.前者严格后者宽松← 正确答案
- C.两者本质完全相同
- D.前者只查关键字段
答案:B。精确匹配要求整体完全相等,包含匹配只要求子串存在,所以精确严格、包含宽松。C 显然错误,D 混淆了结构校验的概念。
Q10. 回归测试接入持续集成(CI)后最重要的价值是什么?
- A.发布前自动拦截功能回退← 正确答案
- B.直接提升模型智能水平
- C.免除所有人工测试工作
- D.替代模型训练过程
答案:A。回归套件与 CI 结合的核心价值是在发布前快速发现旧功能被破坏。B 是训练层目标,C 过度绝对,D 完全无关。确定性评估是守门员,不是教练。