LLM 系统上线生产前如何做好评估
一个语言模型可以在干净的基准测试上表现出色,却依然在生产环境中真正重要的场景上栽跟头。
在基于 LLM 的系统做原型的阶段,基准测试和精选数据集是有用的。它们帮助团队比较模型、测试初始 prompt、判断一个想法在技术上是否站得住脚。
但随着系统一步步逼近生产,评估问题的性质就变了。
真实输入往往含糊不清。标注可能前后不一。关键上下文可能缺失或被截断。评估集未必反映生产环境的真实分布。基准中罕见出现的边缘案例,到了生产中可能成为常见的失败来源。即使离线指标改善,这些结果也未必能顺利转化为生产行为。
我们在评估一个旨在降低 GitHub secret scanning 误报的 LLM 系统时,就遇到了这些挑战。
Secret scanning 用于识别可能被提交进仓库的令牌、密钥等凭据。由于有些候选字符串形似密钥但并非真实凭据,开发者可能要花时间去排查那些根本无需处置的告警。
我们要弄清的不是 LLM 能否正确给字符串分类,而是这个系统能否在保住足够召回率、对安全工作流依然安全的前提下,减少嘈杂的告警。
在这篇文章中,我们分享帮助我们从「原型结果喜人」走向生产的实践。这些经验广泛适用于代码分析、开发者工具、安全、数据分析以及其他生产场景中的 LLM 驱动系统。

1. 从产品决策出发,而不是从模型出发
当一个 LLM 系统表现不及预期时,第一反应往往是去调它的技术组件。
团队可能重写 prompt、补充上下文、增加一步推理、调整周边管线,或者干脆换模型。但在做这些改动之前,应该先明确评估要支撑的决策是什么。
在 secret scanning 这项工作中,我们问的是:
系统能否在保住足够召回率、对生产安全工作流足够安全的前提下降低误报?
要回答这个问题,团队必须决定哪些错误可以接受、哪些指标应当驱动产品决策、哪些护栏必须保持在既定阈值之内。
在 secret scanning 里,错误地压制一个真实凭据,后果可能比让开发者多排查一条告警严重得多。因此我们没有把 precision 和 recall 当作可以随意互换的指标。
我们的首要目标是减少误报、提升 precision;recall 则作为安全约束:只有当下降幅度保持在预设可接受范围内时,实验才能继续推进。这给了我们权衡取舍的清晰标尺。我们最终选择的配置,是在满足召回要求和运维护栏的前提下,实现最大误报削减的那一个。
我们把评估标准组织为三个层级:
首要结果
衡量的是我们想要改善的用户收益:
- 误报削减
- Precision(精确率)
安全约束
防止表观的改善引入不可接受的安全风险:
- Recall(召回率)
运维护栏
这些决定结果是否具备部署可行性:
- 延迟
- 成本
- 可靠性
- 生产兼容性
这一区分避免了我们把所有指标一视同仁。一个降低误报却显著拉低 recall 的改动,并不自动等于改进;一个提升了质量、却让系统变得太慢、太贵、难以集成的改动,同样不算。
设想两组假想的实验结果:
| 实验 | Precision | Recall | 延迟 | 决策 |
|---|---|---|---|---|
| 实验 A | 大幅提升 | 跌破安全护栏 | 可接受 | 不推进 |
| 实验 B | 中等提升 | 保持在护栏之内 | 可接受 | 继续测试 |
如果孤立地看 precision,实验 A 似乎更强。但实验 B 与产品目标更一致:它在改善开发者体验的同时没有触碰 recall 护栏。
在评估 LLM 系统之前,先想清楚对用户而言什么才算成功、系统必须遵守哪些护栏。我们要生成的是能够支撑产品决策的证据。
2. 把离线评估当作集成测试来对待
一个基于 LLM 的系统在首次评估成功之后还会持续变化,因此评估不应是一次性的动作。团队会修改 prompt、更换新模型、改变输入与上下文的构造方式,并打磨周边业务逻辑。
这些改动中的任何一项都可能带来提升、引入回归,或以意想不到的方式改变系统行为。
因此,我们把离线评估当作端到端的集成测试来对待:只要对 prompt、模型、输入构造或更广泛的系统逻辑做了有意义的改动,就重新跑一遍。
评估还需要足够的可重复性,让每个新结果都能与已知基线比较。每次运行,我们都会记录 prompt、模型、数据集版本和系统配置。
这让我们得以回答诸如:
- 新 prompt 是否在未降低 recall 的前提下提升了 precision?
- 模型升级是在整个数据集上都有帮助,还是只在某些类别有效?
- 对输入或上下文的修改,是否在修掉一种错误模式的同时引入了另一种?
- 周边逻辑的改动是一致地改善了结果,还是只是把错误挪了地方?
没有这种纪律,团队很容易拿不同条件下产生的结果作比较,把改进归因到错误的改动上。
一次只改一个主要变量
只有可重复性还不够。实验设计还必须让结果的成因清晰可辨。
我们一次只改一个主要变量,并把每次运行与已知基线比较。例如,先分别评估 prompt 修订和模型升级,再把两者放在一起测试。
这一点很重要:哪怕很小的 prompt 改动都可能改变模型行为,而模型升级可能影响质量、成本、延迟或输出一致性。如果两者在同一实验里同时变化,我们将无从判断提升或回归究竟由谁引起。
我们像对待代码一样对待 prompt 和评估配置:为它们做版本管理,记录每次变更,让历史配置可复现,并保证可以回滚。
| 运行 ID | Prompt 版本 | 模型版本 | Precision | Recall | 延迟 | 备注 |
|---|---|---|---|---|---|---|
| R-001 | v1 | 模型 A | 0.71 | 0.78 | 1.2s | 基线 |
| R-002 | v2 | 模型 A | 0.75 | 0.77 | 1.2s | 仅改 prompt |
| R-003 | v1 | 模型 B | 0.74 | 0.80 | 1.0s | 仅改模型 |
上表评估运行追踪记录中的数值均为假想数据,仅用于说明如何追踪和比较评估运行。
定期测试模型升级
当 LLM 系统表现不佳时,开发者常常以在 prompt 里追加更多指令来应对。有时有效,但并非总是如此。比如,prompt 里可能背负着本该由模型本身承担的复杂度。
更强的模型配合更简单的 prompt,可能胜过旧模型配合大量调教。更简单的 prompt 也更容易理解、测试和维护。
模型升级仍需仔细评估。新模型可能在一个类别上提升表现,却在别处引入回归;也可能影响成本、延迟、输出格式或与现有管线的兼容性。
评估流程应当足够廉价、足够可重复,让测试新模型成为例行公事。任何对 prompt、模型或管线的有意义改动,都应在抵达生产之前先过一遍离线评估。
3. 让离线评估贴近生产
离线评估只有在其贴近系统在生产中要执行的任务时,才是有用的。
在 secret scanning 工作流中,模型很少只面对一个干净、孤立的值来评估。它可能要在周边代码和其他相关、不完整或带干扰性的信息之中评估某个候选值。这些信息呈现方式的差异会实质性地影响结果。
因此,我们的离线评估需要保留生产任务的重要特征,包括:
- 被评估的候选值
- 模型可获得的周边上下文
- 相关的辅助信息
- 输入的格式与约束方式
- 模型外围更广泛的系统逻辑
哪怕很小的差异都会让结果失真。更干净的数据集可能剔除模糊案例、提供更完整的上下文,或移除可能干扰模型的邻近值。
看一个简化的示例:
example_token = "sample_value_for_documentation"
production_api_key = get_secret_from_environment()
candidate_value = "flagged_value"
假设 candidate_value 是系统要评估的值。模型却可能盯上 example_token,因为它的变量名看起来更有安全相关性,进而对错误的值给出一个貌似合理的解释。
当评估样本只包含一个明显的候选值时,这类失败很容易被漏掉。它之所以浮出水面,正是因为离线评估保留了真实 secret scanning 工作流中的部分模糊性与干扰。
离线管线离生产管线越近,评估就越有用。当两者不一致时,漂亮的离线分数可能只是反映了一个比部署时更容易的问题。
4. 把生产标注当信号,而不是不容置疑的真相
生产数据能让评估更有代表性,但它的标注记录的往往是工作流结果,而非可靠的真值。比如,一条被忽略(dismiss)或已解决的 secret scanning 告警,并不必然等于一个误报。
开发者解决一条告警,可能是因为:
- 凭据已经轮换
- 风险已被接受
- 需要清掉告警来解除工作流阻塞
- 告警本身分类错误
这些结果在产品数据里看起来可能一模一样,代表的真值状态却各不相同。
在使用生产标注之前,先问:
- 这个标注是怎么产生的?
- 它是否与评估要回答的问题相匹配?
- 是否把不同的工作流结果归进了同一个类别?
对重要或模糊的子集,可能需要补充人工审核。你不必消除每一个不完美的标注,但要确保评估数据足够准确,能够支撑正在作的决策。
5. 用合成数据与开放数据集填补覆盖缺口
有代表性的生产数据可能有限、敏感,或在开发早期根本拿不到。合成样本、学术基准和开放数据集可以帮助开发者从零搭建评估、扩大覆盖面,但它们应当作为生产级数据的补充,而不是替身。
带着这个前提,合成样本对填补稀有或难以采集案例的空缺大有帮助,比如模糊输入、缺失上下文、异常格式和欠代表的失败模式。例如,一份凭据字符串列表可以测试模型是否识别常见格式,却无法充分评估模型在真实代码中如何对候选值进行推理。
我们对外部样本做了适配,使其贴合自己的任务,并复核了与产品定义不符的标注。我们还根据真实失败模式构造了有针对性的合成案例,涉及邻近的类凭据值、测试代码、占位符、间接引用和缺失上下文。
6. 用错误分析找出聚合指标掩盖的东西
聚合指标告诉你系统整体是否变好了;错误分析告诉你下一步该改什么。
precision 分数再高,也不会告诉你剩余的错误究竟来自模糊输入、糟糕的 prompt 表述、缺失的上下文、嘈杂的标注,还是狭窄的数据集。
要弄清这些问题,就去检查失败案例。
我们抽查了假阳性和假阴性样本,并按最可能的来源分组:模型、prompt、输入、管线、数据集还是标注。反复出现的问题中有几个前文已经提到,比如对错误的候选值做推理、上下文缺失、标注与评估定义不符。
每个类别指向不同的应对。对错误值的推理指向 prompt 或输入表述的问题;证据缺失指向上下文构造;标注错误则需要清理数据。反复出现的领域特定模糊,可能意味着需要更清晰的产品策略,或设立一个专门的评估类别。
人工审阅几十上百个例子确实耗时,但往往带来更快的进展。一旦某个反复出现的失败模式被看清,团队就能做出针对性改动,并衡量问题是否真正解决。
对每条错误都值得问一句:这个失败来自模型、prompt、输入、管线、数据集,还是标注?
这个分类动作把一个模糊的质量问题,变成了一项具体的工程任务。
7. 用 LLM-as-judge 聚焦人工审核
人工审阅每一个评估样本难以规模化。LLM-as-judge 可以分担这一负担:给清晰的案例自动分类、找出可能标错的样本、把模糊案例排进人工审核的优先队列。由于评判模型自己也会犯错,或出于错误的原因附和另一个模型,它的输出应被视为又一个预测,而不是真值。
更稳妥的模式是用评判模型做分诊:
- 自动处理清晰、低风险的案例。
- 把低置信、相互冲突或高影响的案例路由给人工审核。
- 定期抽检高置信案例,排查系统性错误。
- 追踪评判模型、被评估系统与人工审核者之间的分歧。
- 像对待其他模型组件一样,对评判 prompt 做版本管理并加以评估。
以这种方式使用,评判模型能把人类的注意力集中到「审核最有可能改变结果」的那些案例上。

8. Secret scanning 教会我们的事
我们的目标是在一个安全敏感的工作流中降低误报、保住召回。离线评估为我们提供了一种受控的方式,在开始线上实验之前比较 prompt、模型、输入和管线的改动。
经过反复评估和有针对性的错误分析,我们在所评估的离线数据集上实现了 95% 的误报削减,同时把 recall 保持在既定护栏之内。更重要的是,我们清楚这个结果是如何产生的:评估更贴近生产任务,每次改动都对照可复现的基线来度量,剩余的失败模式也都记录在案。
离线评估并不能证明系统在每个生产场景中会如何表现。它提供的是足够结构化的证据,让我们能带着清晰已知的各种风险和护栏进入线上实验。
清单:把 LLM 系统推向生产之前
用这份清单检验你的评估是否提供了足够的证据来推进系统。逐节确认目标、数据、实验和剩余的生产风险都已清晰在握。
产品目标
- 产品决策与首要成功指标是否清晰?
- 安全与运维护栏是否已经定义?
数据与标注
- 评估数据是否贴近生产工作流并包含困难案例?
- 我们是否了解标注是如何产生的、哪里需要人工审核?
评估的严谨性
- prompt、模型、数据集和管线版本是否都有记录?
- 主要改动是否被隔离、并与已知基线作过比较?
错误分析与生产就绪
- 假阳性和假阴性是否按类别审阅过?
- 我们能否重跑评估,并解释离线结果可能与生产存在差异之处?
先评估,再信任
随着基于 LLM 的系统进入生产,评估应当成为常规工程流程的一部分。一次扎实的离线评估能说明:在代表性条件下产品目标是否达成、不确定性还剩多少、系统是否已为受控的生产上线做好准备。
生产中的不确定性不可避免。评估让它可见、可测、可控。