上线前如何评估 LLM:GitHub 密钥扫描实战手册

·阅读约12分钟·Evergreen Tools Team
LLM evaluation before production

💡 工具推荐落地 LLM 评估手册时,试试 Evergreen Tools 的 AI代码审查工具, AI提示词模板工具, AI Token计数器工具

一个语言模型可以在干净的基准上表现良好,却在生产环境真正重要的场景中翻车。基准和精选数据集在原型阶段很有用:它们帮助团队比较模型、测试初始提示词、判断一个想法在技术上是否可行。但随着系统接近生产,评估问题就变了:真实输入往往有歧义,标签可能不一致,重要上下文可能缺失或被截断,评估集可能不反映生产分布。GitHub 团队在评估一个用于减少密钥扫描误报的 LLM 系统时遇到了这些挑战,并把从原型到生产的实践写成了一份可复用的手册。

1. 先定义决策,而不是先调组件

当 LLM 系统表现不如预期时,第一反应往往是调整技术组件:重写提示词、加上下文、引入推理步骤、调整流水线、换模型。但在做任何这些改动之前,应该先定义评估要支持的决策。在密钥扫描场景,问题是:系统能否在保持足够召回率以保证安全的前提下减少误报?为此必须决定哪些错误可以接受、哪些指标驱动产品决策、哪些护栏必须保持在阈值内。密钥扫描中,错误地压制一个真实凭证比让开发者多审查一条告警更严重,所以他们不把精确率和召回率当作可互换的指标。主要目标(primary objective)是减少误报、提升精确率;召回率是安全约束——任何实验只有在召回率下降仍在预定范围内时才能推进。

// Define the decision first. Before changing the prompt,
// adding context, or switching models, decide what the
// evaluation is meant to support. In GitHub's secret
// scanning case: "Can the system reduce false positives
// while preserving enough recall to be safe?"
const DECISION = {
  "question": "reduce false positives while preserving recall",
  "primary": "precision",          // the product goal
  "guardrail": "recall >= 0.95",   // the safety constraint
  "operational": ["latency < 1.5s", "cost per scan <= $0.002"],
};
// Incorrectly suppressing a real credential is worse than
// asking a developer to review an extra alert -- so they
// did NOT treat precision and recall as interchangeable.

2. 一次只改一个变量,配置像代码一样版本化

LLM 系统在第一次成功评估后还会持续变化:团队会修改提示词、采用新模型、改变输入和上下文的构造、完善周边业务逻辑。任何改动都可能改进系统、引入回归或意外改变行为。因此他们把离线评估当作端到端集成测试:每当对提示词、模型、输入构造或系统逻辑做了有意义的改动就重跑。评估还必须可重复,让每次新结果都能与已知基线比较:每次运行记录提示词、模型、数据集版本和系统配置。设计实验时一次只改一个主要变量:先单独评估提示词修订,再单独评估模型升级,然后才一起测试——因为即使很小的提示词改动都可能改变模型行为,而模型升级可能影响质量、成本、延迟或输出一致性。

Precision vs recall tradeoff
// Change one variable at a time, compare against a known
// baseline. Evaluate a prompt revision separately from a
// model upgrade before testing the two together.
const RUNS = [
  { id: "R-001", prompt: "v1", model: "Model A",
    precision: 0.71, recall: 0.78, latency: "1.2s",
    notes: "Baseline" },
  { id: "R-002", prompt: "v2", model: "Model A",
    precision: 0.75, recall: 0.77, latency: "1.2s",
    notes: "Prompt-only change" },
  { id: "R-003", prompt: "v1", model: "Model B",
    precision: 0.74, recall: 0.80, latency: "1.0s",
    notes: "Model-only change" },
];
// If both changed in the same experiment, you would not
// know which one caused the improvement or regression.

3. 离线评估要贴近生产任务

离线评估只有在贴近生产任务时才有用。密钥扫描工作流中,模型很少评估一个干净、孤立的值:它要在一个候选值旁边评估周围的代码和其他信息,这些信息可能相关、不完整、甚至具有干扰性。信息呈现方式的差异会实质性地影响结果。假设 candidate_value 是系统要评估的值,模型可能因为 example_token 的变量名看起来更像安全相关而关注它,对错误的值给出看似合理的解释——这种失败在评估样例只含一个明显候选时很容易漏掉。离线流水线越接近生产流水线,评估越有用;两者差异越大,离线高分可能只是反映了比部署环境更简单的问题。

// Treat prompts and evaluation configurations like code:
// version them, record what changed, keep previous configs
// reproducible, make rollback possible.
{
  "eval-config": {
    "version": "2026-08-25.2",
    "prompt": "[email protected]:acme/prompts.git#v14",
    "model": "gpt-5.6-terra@2026-08-20",
    "dataset": "secret-scan-eval@v3",
    "pipeline": "offline-eval@v7",
    "rollback": "git revert <commit>"
  }
}
// Rerun evaluation like an end-to-end integration test
// whenever you make a meaningful change to the prompt,
// model, input construction, or broader system logic.

4. 生产标签是信号,不是绝对真相

生产数据能让评估更有代表性,但它的标签往往记录的是工作流结果,而不是可靠的 ground truth。例如,一条被关闭或标记为已解决的密钥扫描告警,不一定代表误报。这些结果在产品数据里看起来相似,却对应不同的 ground-truth 状态。使用生产数据前要问:它是否匹配评估要回答的问题?不同的工作流结果是否被归入了同一类别?对重要或模糊的子集,可能需要人工复核。合成示例、学术基准和开放数据集可以帮助启动评估并扩大覆盖,但应补充而不是替代生产数据——例如,一份凭证字符串列表可以测试模型是否识别常见格式,却无法完整评估模型在真实代码中如何推理一个候选值。

Track evaluation runs like code

5. 代码实战:决策、追踪、版本化、贴近生产、错误分析

本文的代码块把手册拆开:代码块一是决策定义——主要目标(精确率)、安全护栏(召回率)、运营约束(延迟、成本),以及为什么不能把指标当可互换;代码块二是运行追踪表——R-001 基线、R-002 仅改提示词、R-003 仅换模型,一次一个变量;代码块三是评估配置版本化——提示词、模型、数据集、流水线全部用版本号记录,支持回滚;代码块四是贴近生产的评估样例构造——保留真实任务的歧义和干扰;代码块五是错误分类法——按模型、提示词、输入、流水线、数据集、标签六个来源分组,指导下一步改什么。

// Keep offline evaluation close to the production task.
// In secret scanning the model rarely evaluates one clean,
// isolated value -- it sees a candidate alongside code and
// other context that may be relevant, incomplete, or
// distracting. Preserve that ambiguity.
function buildEvalExample(candidate, surroundingCode) {
  return {
    candidate_value: candidate,   // the value to assess
    context: surroundingCode,     // nearby code (may distract)
    expected: classify(candidate),
  };
}
// Even small differences skew results: a cleaner dataset
// may exclude ambiguous cases, provide more complete
// context, or remove nearby values that distract the
// model. The closer the offline pipeline is to the
// production pipeline, the more useful the evaluation.

6. 核心教训

GitHub 这篇实践的核心教训可以概括为五条:第一,先定义评估要支持的决策,明确哪个指标是目标、哪个是护栏;第二,一次只改一个变量,记录每次运行的提示词、模型、数据集版本,避免把改进归因到错误的改动;第三,离线评估要保留生产任务的特征——歧义、缺失上下文、干扰信息;第四,生产标签是信号不是真相,重要子集要人工复核,合成数据只能补充不能替代;第五,聚合指标告诉你是否改进,错误分析告诉你下一步改什么——手动审查几十上百个例子看起来耗时,但往往带来更快的进展。

// Aggregate metrics tell you whether a system improved;
// error analysis tells you what to change next. Review
// samples of false positives and false negatives and group
// them by likely source.
const ERROR_TAXONOMY = {
  "model": "reasoning about the wrong candidate",
  "prompt": "poor framing or missing instructions",
  "input": "missing context or truncated evidence",
  "pipeline": "wrong data passed to the model",
  "dataset": "labels that do not match the definition",
  "label": "workflow outcome mistaken for ground truth",
};
// A dismissed alert is NOT necessarily a false positive.
// Production labels often capture workflow outcomes rather
// than reliable ground truth -- treat them as signals,
// and for ambiguous subsets, do manual review.

📌 常见问题 FAQ

为什么基准表现好不代表生产表现好?

真实输入往往有歧义、标签可能不一致、重要上下文可能缺失或被截断、评估集可能不反映生产分布。GitHub 团队在密钥扫描 LLM 评估中反复遇到这些差异(来源:GitHub Blog 2026-08-25)。

为什么基准表现好不代表生产表现好?

真实输入往往有歧义、标签可能不一致、重要上下文可能缺失或被截断、评估集可能不反映生产分布。GitHub 团队在密钥扫描 LLM 评估中反复遇到这些差异(来源:GitHub Blog 2026-08-25)。

为什么基准表现好不代表生产表现好?

真实输入往往有歧义、标签可能不一致、重要上下文可能缺失或被截断、评估集可能不反映生产分布。GitHub 团队在密钥扫描 LLM 评估中反复遇到这些差异(来源:GitHub Blog 2026-08-25)。

为什么基准表现好不代表生产表现好?

真实输入往往有歧义、标签可能不一致、重要上下文可能缺失或被截断、评估集可能不反映生产分布。GitHub 团队在密钥扫描 LLM 评估中反复遇到这些差异(来源:GitHub Blog 2026-08-25)。

为什么基准表现好不代表生产表现好?

真实输入往往有歧义、标签可能不一致、重要上下文可能缺失或被截断、评估集可能不反映生产分布。GitHub 团队在密钥扫描 LLM 评估中反复遇到这些差异(来源:GitHub Blog 2026-08-25)。

精确率和召回率应该怎么权衡?

取决于产品目标。密钥扫描中错误压制真实凭证比多审查一条告警更严重,所以精确率是主要目标、召回率是安全护栏:实验只有在召回率下降仍在预定范围内时才能推进。

精确率和召回率应该怎么权衡?

取决于产品目标。密钥扫描中错误压制真实凭证比多审查一条告警更严重,所以精确率是主要目标、召回率是安全护栏:实验只有在召回率下降仍在预定范围内时才能推进。

精确率和召回率应该怎么权衡?

取决于产品目标。密钥扫描中错误压制真实凭证比多审查一条告警更严重,所以精确率是主要目标、召回率是安全护栏:实验只有在召回率下降仍在预定范围内时才能推进。

精确率和召回率应该怎么权衡?

取决于产品目标。密钥扫描中错误压制真实凭证比多审查一条告警更严重,所以精确率是主要目标、召回率是安全护栏:实验只有在召回率下降仍在预定范围内时才能推进。

精确率和召回率应该怎么权衡?

取决于产品目标。密钥扫描中错误压制真实凭证比多审查一条告警更严重,所以精确率是主要目标、召回率是安全护栏:实验只有在召回率下降仍在预定范围内时才能推进。

如何避免把改进归因到错误的改动?

一次只改一个主要变量,并对比已知基线。先单独评估提示词修订,再单独评估模型升级,然后才一起测试。记录每次运行的提示词、模型、数据集版本和系统配置。

如何避免把改进归因到错误的改动?

一次只改一个主要变量,并对比已知基线。先单独评估提示词修订,再单独评估模型升级,然后才一起测试。记录每次运行的提示词、模型、数据集版本和系统配置。

如何避免把改进归因到错误的改动?

一次只改一个主要变量,并对比已知基线。先单独评估提示词修订,再单独评估模型升级,然后才一起测试。记录每次运行的提示词、模型、数据集版本和系统配置。

如何避免把改进归因到错误的改动?

一次只改一个主要变量,并对比已知基线。先单独评估提示词修订,再单独评估模型升级,然后才一起测试。记录每次运行的提示词、模型、数据集版本和系统配置。

如何避免把改进归因到错误的改动?

一次只改一个主要变量,并对比已知基线。先单独评估提示词修订,再单独评估模型升级,然后才一起测试。记录每次运行的提示词、模型、数据集版本和系统配置。

生产标签可以直接当 ground truth 吗?

不能。生产标签往往记录工作流结果而非可靠真相——被关闭的告警不一定是误报。对重要或模糊的子集需要人工复核,合成数据只能补充不能替代生产数据。

生产标签可以直接当 ground truth 吗?

不能。生产标签往往记录工作流结果而非可靠真相——被关闭的告警不一定是误报。对重要或模糊的子集需要人工复核,合成数据只能补充不能替代生产数据。

生产标签可以直接当 ground truth 吗?

不能。生产标签往往记录工作流结果而非可靠真相——被关闭的告警不一定是误报。对重要或模糊的子集需要人工复核,合成数据只能补充不能替代生产数据。

生产标签可以直接当 ground truth 吗?

不能。生产标签往往记录工作流结果而非可靠真相——被关闭的告警不一定是误报。对重要或模糊的子集需要人工复核,合成数据只能补充不能替代生产数据。

生产标签可以直接当 ground truth 吗?

不能。生产标签往往记录工作流结果而非可靠真相——被关闭的告警不一定是误报。对重要或模糊的子集需要人工复核,合成数据只能补充不能替代生产数据。

评估应该多久跑一次?

把离线评估当作端到端集成测试:每当对提示词、模型、输入构造或系统逻辑做了有意义的改动就重跑,保证可重复并与基线可比。

评估应该多久跑一次?

把离线评估当作端到端集成测试:每当对提示词、模型、输入构造或系统逻辑做了有意义的改动就重跑,保证可重复并与基线可比。

评估应该多久跑一次?

把离线评估当作端到端集成测试:每当对提示词、模型、输入构造或系统逻辑做了有意义的改动就重跑,保证可重复并与基线可比。

评估应该多久跑一次?

把离线评估当作端到端集成测试:每当对提示词、模型、输入构造或系统逻辑做了有意义的改动就重跑,保证可重复并与基线可比。

评估应该多久跑一次?

把离线评估当作端到端集成测试:每当对提示词、模型、输入构造或系统逻辑做了有意义的改动就重跑,保证可重复并与基线可比。