跳到主要内容

Spec-to-Eval:将产品需求转化为可证伪的 LLM 评估标准

· 阅读需 10 分钟
Tian Pan
Software Engineer

大多数 AI 功能用自然语言描述需求,也用自然语言进行评估。产品经理写下"助手应当给出有帮助的回复,并避免有害内容"。工程师上线了一个 Prompt,演示时的输出看起来符合要求。团队在站会上达成共识,却在上线时产生分歧——边缘案例浮现,不同工程师对同一输出的评判各异,而"有帮助"这个词,不同评审者的理解竟有七种之多。

这不是工具问题,而是翻译问题。规格说明一直停留在抽象层面,评估标准从未被具体化。Spec-to-Eval 是一门在编写第一个 Prompt 之前,将英文需求转化为可证伪标准的方法论——提前做这件事,将从根本上改变迭代速度。

为什么散文式规格说明与 LLM 开发不兼容

在传统软件开发中,需求歧义令人痛苦,但尚可应对。如果规格说明写着"优雅地处理无效输入",开发者可以寻求澄清、参考类似功能,并做出有据可查的决定。函数要么通过、要么失败单元测试——最终总有人会在代码中明确"优雅"的含义。

LLM 瓦解了这个反馈循环。输出空间巨大,行为具有概率性,"正确性"依赖于上下文,而这种依赖方式难以用代码捕捉。两位工程师读同一份规格说明,可能对成功的样子形成截然不同的心智模型——不是因为其中一人粗心,而是规格说明留下了多种合理解读的空间。

后果是,团队最终在脑子里做评估。上线一个改动,看几个输出,凭直觉判断是变好了还是变坏了。这种直觉往往是错的。它受近因偏差影响(你会过度关注最后看到的东西),受覆盖盲点影响(你只抽样了感觉有代表性但实际并非如此的案例),还受锚定效应影响(当前输出与之前的糟糕输出相比看起来不错,但你已经忘记了"好"究竟是什么样的)。

解决方案不是培养更好的直觉,而是在开始之前明确标准。

规格说明歧义的五大类型

当你尝试将散文式规格说明转化为评估标准时,五类歧义会持续浮现。每一类都迫使你做出规格说明未曾解决的决定。

1. 成功定义的歧义。 "回复应当准确"无法告诉你这里的准确意味着什么。是基于事实?与用户上下文一致?不出现关于特定实体的幻觉?可证伪的标准需要具体条件:"回复不与源文档中的信息相矛盾",或"回复不引用检索上下文中不存在的人物、日期或数字"。原始规格说明暗示了这两点,却没有说明究竟是哪一点。

2. 边缘案例处理的歧义。 规格说明描述的是正常路径,很少说明当用户查询不够明确、上下文缺失,或任务技术上可行但可能不是用户真实意图时该如何处理。Eval 强迫你枚举这些场景。助手是应该寻求澄清还是做出合理假设?是表达不确定性还是自信地给出答案?规格说明没有说。

3. 客观与主观的混淆。 有些标准有可以用代码检查的正确答案:回复不超过 200 字、JSON 格式有效、日期格式正确。另一些则需要判断:语气恰当、摘要抓住了要点、建议结合用户情况是合理的。混淆这两类会导致评估过于死板(因技术细节拒绝有效输出)或过于模糊(接受表面上符合标准的糟糕输出)。在正确评估之前,你需要将标准归入正确的类别。

4. 上下文依赖的歧义。 "用用户的语言回复"看起来无歧义,直到你考虑:如果用户用西班牙语写作但上传的是英文文档怎么办?如果用户在对话中途切换语言怎么办?如果语言无法可靠识别怎么办?每个分支都需要明确的决定。Eval 会暴露这些问题,因为你必须构建测试案例,而测试案例要求你选定具体场景。

5. 评分者刚性的歧义。 当团队编写评估时,他们往往会不经意地对路径而非结果进行编码。他们检查模型是否使用了特定工具、遵循了特定推理链,或以特定格式生成输出——而他们真正关心的只是用户的问题是否得到了解决。对方法进行评分的 Eval 会惩罚有效的替代解决方案,并在 Prompt 优化中制造扭曲的激励。

翻译方法论

将散文式规格说明转化为可证伪标准是一个具体的过程,而不是头脑风暴会议。

从手动检查开始。 在构建任何 Eval 框架之前,你其实已经在做评估了——只是没有记录下来。每次你测试一个 Prompt 并想"这看起来没问题",你就在应用一个隐式标准。从外化这些标准开始:在上线 Prompt 改动前,你会检查什么?哪些行为会让你说"这坏了"?将这些写成英文断言,然后使其可证伪。"它应该回答问题"变成"回复直接针对用户最后一条消息中提出的具体问题,而不是转向一个不同但相关的话题"。

应用双专家测试。 一个可用的 Eval 标准是这样的:两位领域专家在给定相同输入和输出的情况下,能够独立得出相同的通过/失败判断。如果他们会有分歧,说明标准还不够具体。这个测试能捕捉到那些看似已经解决但实际上没有的歧义。"语气专业"无法通过这个测试。"回复避免使用俚语、非正式缩写和第一人称复数('我们认为'),除非角色人设明确使用这些表达"可以通过。

将客观与主观分开,然后分别处理。 客观标准变成基于代码的断言:正则检查、字数限制、Schema 验证、与已知真实情况的实体匹配。主观标准变成以二元通过/失败评分标准进行的 LLM 即评判评估——而不是 1 到 5 的评分,那会引入注释者间分歧,而不增加信号量。对于主观评判,评分标准本身必须通过双专家测试。

平衡正向和负向案例。 只包含"应该成功"案例的测试套件无法捕捉到总是自信作答的模型。对于你希望看到的每种行为,都应包含该行为不应出现的案例。如果你的助手应该搜索网络获取最新信息,就包含搜索是多余或不恰当的案例。如果它应该在查询不明确时寻求澄清,就包含清晰的查询——在这种情况下,寻求澄清会令人恼火且是错误的。

让失败模式指导标准制定。 你不会提前枚举所有正确标准。运行早期版本,观察失败,将其视为规格说明的空白。当你看到一个明显糟糕但没有触发任何现有标准失败的输出时,说明缺少一个标准。将其视为错误分析,而不仅仅是调试:对失败类型进行聚类,命名它们,将每个集群转化为一个标准。这个反馈循环会产生一个反映系统实际失败方式的测试套件,而不是你想象它可能失败的方式。

提前做这件事改变了什么

Spec-to-Eval 的商业理由在于迭代速度。没有明确标准,每次 Prompt 改动都需要有背景知识的人进行主观审查。两位工程师看同一个输出差异可能会对是否有所改善产生分歧。你再进行一轮审查来解决分歧,而到那时,原始上下文已经陈旧。

有了明确标准,一次 Prompt 改动会产生一个指标:测试套件通过率从 74% 提升到 81%,具体原因是"未能寻求澄清"类别的案例从 52% 改善到 76%。这是可证伪的、可讨论的、可重现的。你可以把它交给一个没有参与原始审查的团队成员,得到相同的评估。

迭代压缩来自于消除主观审查步骤。你仍然需要人类判断——用于编写和校准标准、验证自动化评判者是否跟踪了你真正关心的内容、捕捉你的 Eval 套件未覆盖的案例。但你是将人类判断花在标准上,一次,而不是每次都花在每个单独的输出上。

建立这种方法论的团队报告迭代周期缩短 3-5 倍,不是因为工具更快,而是因为那些原本需要开会才能做出的决定,现在看一个数字就能做出。Spec-to-Eval 翻译是你提前进行深度思考的环节。

常见失败模式

编写过于通用的标准。 "忠实度"和"有帮助"看起来像标准,但实际上不是。它们是需要针对你的特定产品进行操作化的属性。法律文件助手的忠实度标准与客户支持机器人的不同。通用标准产生的 Eval 在明显崩溃之前会告诉你一切正常。

构建上帝评估器。 一个同时对多个维度评分的单一评估器难以校准,也难以改进。当它失败时,你无法判断是哪个维度导致了失败。每个标准建立一个评估器。它们可以并行运行,但应该分别诊断。

将标准视为已完成。 Spec-to-Eval 翻译应该在你编写 Prompt 之前发生,但标准是活的文档。新的失败模式会在生产中浮现。新功能会引入新的成功定义。随功能上线的测试套件应该是起点,而不是上限。

因为负向案例更难构建而跳过它们。 想象模型不应该做什么的案例,比想象它应该做什么的案例需要更多创造力。这就是为什么 Eval 套件往往是单方面的,以及为什么单方面的 Eval 会产生过度自信、过度活跃的模型。负向案例值得付出这番努力。

起点

你不需要 Eval 框架就能开始这个过程。你需要的是一份文档——一份以"给定[输入条件],输出[动词][特定属性]"形式呈现的标准列表。在编写第一个 Prompt 之前写下它。用双专家测试来检验它。将客观标准与主观标准分开,并决定每一类如何衡量。

这个翻译过程会暴露你原本不知道自己有的问题。这正是它的意义所在。Spec-to-Eval 过程迫使你回答的那些问题,正是你的团队本来会在上线时争论的问题——在你已经构建并上线了错误的东西之后。

先回答它们。

References:Let's stay in touch and Follow me for more thoughts and updates