章节12 / 14
本文目录12 节
评测工程:用样例集防止应用退化
本指南介绍如何将大模型应用评测融入日常开发流。通过建立黄金数据集、设计清晰的评审规则、编写自动化评测脚本、配置 CI/CD 门禁以及配置线上负反馈捕获,防止提示词或模型版本迭代时应用发生静默退化。
- 完成前序单元的练习或理解对应概念
- 具备基础的 TypeScript 异步编程知识
- 了解基础的大模型 API 调用流程
- 能够编写并维护一套符合 LangSmith 规范的黄金数据集 (Golden Dataset)
- 能够设计并编写可量化的 LLM-as-a-judge 评审细则 (Rubric)
- 能够编写评测回归脚本,并将其集成到 GitHub Actions 作为 CI 门禁
- 能够利用生产环境观测 trace 数据流,过滤并导出 Bad Cases 到评测集
在大模型应用开发中,你可能会经常遇到这种尴尬的场景:为了修复用户反馈的 Bug A,你微调了系统提示词(Prompt),结果在毫不知情的情况下,导致原本运行正常的场景 B 彻底失效。这种现象称为应用退化(Regression)。
由于大模型的非确定性输出特征,传统的单元测试很难覆盖语义层面的漂移。我们需要像对待传统软件的回归测试一样,为大模型应用建立一套可持续、自动化的评测工程流程。
为什么需要把评测融入开发流
本节操作锚点:围绕“为什么需要把评测融入开发流”记录步骤、样例、诊断、风险、检查清单和验收结果。
大模型应用的评测不是上线前临时跑一遍的演示。如果把评测当作应付上线的阶段性任务,你将无法及时发现模型升级或提示词调整带来的微弱负面影响。
根据 OpenAI 的 evals 指南,评测不仅是用来衡量模型能力的,更是用来防止提示词改动和模型升级导致系统表现倒退的核心手段。每次代码修改、提示词更新或基础模型版本更迭(例如从 gpt-4o-2024-05-13 切换到新版本),都必须跑一遍自动化回归评测。只有这样,你才能拥有上线新版本的信心,而不是靠“人眼看十条结果”这种碰运气的方式来做决策。
步骤一:构建高质量的黄金数据集 (Golden Dataset)
本节操作锚点:围绕“步骤一:构建高质量的黄金数据集GoldenDat”记录步骤、样例、诊断、风险、检查清单和验收结果。
评测的第一步是建立样例集。根据 LangSmith datasets 概念,数据集可以分为 Key-Value(KV)对、Chat 历史等多种类型。通常,一个标准的数据集需要包含输入参数和期望的参考答案(Ground Truth)。
根据 Anthropic 关于定义成功标准和构建评估的建议,设计测试用例时应当从最常见、最核心的业务场景开始,逐渐覆盖边缘案例(Edge Cases),并配合具体的定量评分标准。不要试图一开始就收集上千条用例,质量远比数量重要。一个包含 20-50 个高质量核心场景的“黄金数据集”(Golden Dataset),就足以在开发早期帮你拦截 80% 的退化问题。
以下是一个 TypeScript 编写的本地黄金数据集结构,用于评估一个客服机器人的分类与回复表现:
export interface EvalExample {
id: string;
input: {
user_query: string;
user_tier: 'free' | 'premium';
context: string;
};
reference?: {
expected_intent: string;
must_contain_keywords: string[];
forbidden_keywords: string[];
suggested_reply_snippet: string;
};
}
export const goldenDataset: EvalExample[] = [
{
id: "ex-001",
input: {
user_query: "我的账单金额扣错了,我想申请退款",
user_tier: "premium",
context: "用户在上个月订阅了专业版年费服务,昨日被扣除续费账单。"
},
reference: {
expected_intent: "refund_request",
must_contain_keywords: ["人工客服", "专线", "退款流程"],
forbidden_keywords: ["无法退款", "不支持退换"],
suggested_reply_snippet: "已为您接入尊贵版专属客服,我们将竭诚为您处理退款申请。"
}
},
{
id: "ex-002",
input: {
user_query: "怎么修改密码?",
user_tier: "free",
context: "用户忘记登录密码。"
},
reference: {
expected_intent: "password_reset",
must_contain_keywords: ["设置", "安全", "重置链接"],
forbidden_keywords: ["退款", "人工"],
suggested_reply_snippet: "请前往‘个人设置’ -> ‘安全中心’点击‘重置密码’。"
}
}
];
步骤二:定义多指标评审准则 (Evaluation Rubrics)
本节操作锚点:围绕“步骤二:定义多指标评审准则EvaluationR”记录步骤、样例、诊断、风险、检查清单和验收结果。
数据集建好后,我们需要定义如何对模型的实际输出进行打分。如果仅采用传统的字面匹配(如 BLEU、ROUGE 分数),往往会因为模型换了同义词而给出极低的评分,这显然不符合自然语言的特性。我们必须采用混合评审准则:
- 确定性评审(Regex / JSON Validation):检查输出中是否包含禁用词、是否成功提取了必要实体、格式是否为合法的 JSON。
- 语义相似度评审(Embedding Similarity):评估模型输出与参考答案的向量距离。
- 大模型裁判(LLM-as-a-judge):使用一个更强大的模型(如 Claude 3 Opus 或 GPT-4o)根据明确的 Rubric 细则给输出打分。
下面是一个针对 LLM-as-a-judge 的打分提示词模版。如果系统的实际输出包含违禁词或语调不符合要求,大模型裁判需要给出扣分理由及分值:
# 角色
你是一名资深的质量保证专家,负责评估智能客服系统的回答质量。
# 评估任务
请根据以下提供的 [用户输入]、[系统实际输出] 以及 [期望参考答案],对系统输出进行 1 至 5 分的打分。
# 评分细则 (Rubrics)
- 5分(极佳):系统回答准确无误,完美覆盖了参考答案的要点,语气亲切专业,且未包含任何 [禁用词]。
- 4分(良好):系统回答基本准确,语气符合要求,但遗漏了参考答案中的某一个次要细节,或者表达略显累赘。
- 3分(及格):系统回答大致对路,但语气偏生硬,或者漏掉了关键实体,但没有给用户提供错误引导。
- 2分(不及格):系统回答偏离主题,没有解决用户根本问题,或者包含了 [禁用词]。
- 1分(完全不可用):回答存在幻觉、提供了错误的配置指引、或者出现严重的态度问题。
# 输入数据
- 用户输入: {user_query}
- 期望参考答案: {suggested_reply_snippet}
- 禁用词列表: {forbidden_keywords}
- 系统实际输出: {actual_output}
# 输出格式要求
请必须输出以下格式的 JSON,不要带有任何 Markdown 标记:
{
"score": <1-5的整数>,
"reason": "详细的扣分或给分理由,请务必客观具体"
}
步骤三:编写基于 LangSmith 的自动化评测脚本
本节操作锚点:围绕“步骤三:编写基于LangSmith的自动化评测脚”记录步骤、样例、诊断、风险、检查清单和验收结果。
有了数据集和评审逻辑后,我们需要写一段可重复运行的脚本。根据 LangSmith evaluation concepts 的规范,我们可以使用官方的 @langchain/smith 库(或在 Python 中使用 langsmith)来注册数据集,并创建一个 Evaluator 函数来执行批量测试。
以下是一个完整的自动化评测脚本示例:
import { Client } from "langsmith";
import { runOnDataset } from "@langchain/smith";
import { EvaluationResult } from "langsmith/evaluation";
import { ChatOpenAI } from "@langchain/openai";
// 1. 初始化 LangSmith 客户端
const client = new Client({
apiKey: process.env.LANGSMITH_API_KEY,
apiUrl: "https://api.smith.langchain.com"
});
// 2. 定义我们的被测 Target(即你的 AI Agent 或 RAG 管道入口)
async function myAppTarget(input: { user_query: string; user_tier: string; context: string }) {
const model = new ChatOpenAI({ modelName: "gpt-4o-mini", temperature: 0 });
const systemPrompt = `你是一个智能客服。当前用户级别为: ${input.user_tier}。背景上下文: ${input.context}`;
const response = await model.invoke([
{ role: "system", content: systemPrompt },
{ role: "user", content: input.user_query }
]);
return { output: response.content as string };
}
// 3. 编写一个自定义的规则评审器 (Regex Evaluator)
const keywordEvaluator = async ({ run, example }: { run: any; example: any }): Promise<EvaluationResult> => {
const actualOutput = run.outputs?.output || "";
const mustContain = example.outputs?.reference?.must_contain_keywords || [];
const forbidden = example.outputs?.reference?.forbidden_keywords || [];
let score = 1;
let feedback = "通过所有规则校验";
for (const word of mustContain) {
if (!actualOutput.includes(word)) {
score = 0;
feedback = `缺失必需关键词: ${word}`;
break;
}
}
for (const word of forbidden) {
if (actualOutput.includes(word)) {
score = 0;
feedback = `包含了禁用关键词: ${word}`;
break;
}
}
return {
key: "keyword_and_forbidden_rules",
score,
comment: feedback
};
};
// 4. 执行评测任务
async function executeSuite() {
const datasetName = "customer-service-golden-dataset";
console.log("开始在 LangSmith 上运行评测...");
const results = await runOnDataset(
myAppTarget,
datasetName,
{
evaluationConfig: {
customEvaluators: [keywordEvaluator]
},
client,
projectMetadata: {
commit: process.env.GITHUB_SHA || "local-dev",
branch: process.env.GITHUB_REF_NAME || "main"
}
}
);
console.log("评测完成。结果概要:", JSON.stringify(results, null, 2));
}
// 运行评测
// executeSuite().catch(console.error);
步骤四:建立 CI/CD 评测回归门禁
本节操作锚点:围绕“步骤四:建立CI/CD评测回归门禁”记录步骤、样例、诊断、风险、检查清单和验收结果。
只在本地运行脚本是不够的,人为的疏忽经常会导致评测被遗忘。如果系统提示词或模型版本发生变更,必须先在黄金数据集上运行全量评测,因为大模型的输出具有非确定性,微小的改动也可能导致原有正确场景的输出质量退化。因此,我们需要把评测做成 GitHub Actions 的回归门禁(Quality Gate)。
以下是 GitHub Workflows 配置文件示例:
name: LLM Regressions and Evaluation Gate
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
evaluate:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Run Regression Evaluations
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }}
LANGSMITH_PROJECT: "prod-customer-service-eval"
GITHUB_SHA: ${{ github.sha }}
GITHUB_REF_NAME: ${{ github.ref_name }}
run: |
# 运行评测脚本,若有评审项返回 score=0,脚本必须抛出非零错误码以阻断 CI
node dist/scripts/run-eval.js
典型失败诊断指南
当 CI 门禁失败(抛出非 0 状态码)时,你应该按以下步骤进行排查:
- 定位失败 case:登录 LangSmith Console,进入对应的 Project,筛选出评分(Score)为
0的 Runs。 - 检查差分 (Diff):观察
actual_output与reference之间的具体偏离。如果是提示词变动导致语气太温和而丢失了关键术语,应当微调 Prompt 结构,强化对关键词的要求。 - 更新数据集(取舍判断):如果在评测中发现生成的 JSON 格式不合法,应该在数据集的输出期望(Ground Truth)中严格定义 JSON Schema,并使用解析器作为确定性评审器,因为只有规则评审器才能对格式提供 100% 的准确判定。
步骤五:配置线上观测与负反馈闭环
本节操作锚点:围绕“步骤五:配置线上观测与负反馈闭环”记录步骤、样例、诊断、风险、检查清单和验收结果。
没有任何一个评测集可以完美预测用户的真实行为。在线上环境中,用户的多样化输入源源不断,我们需要建立线上负反馈的捕获机制。
根据 LangSmith observability 指南,我们可以对线上运行的每个 Trace 进行打标,并将用户点击“踩(Thumbs Down)”或触发纠错行为的真实 Trace 一键导出为 Dataset 样例,实现“线上坏例 -> 评测用例 -> 修复提示词 -> 回归测试 -> 部署”的完整反馈闭环。
import { Client } from "langsmith";
const client = new Client();
// 当线上用户点击“踩”或者客服人员人工标记纠错时调用此函数
async function captureNegativeFeedback(runId: string, userFeedbackComment: string) {
// 1. 在原 Trace 上记录用户反馈标签
await client.createFeedback(runId, "user_score", {
score: 0,
comment: userFeedbackComment
});
// 2. 将对应的 Run 导入到“线上回归待改数据集”中,以便开发人员修复
await client.createDatasetCompatibleWithRun("online-failed-cases", {
sourceRunId: runId
});
console.log(`已成功将异常 Run ${runId} 导入线上异常回归集 online-failed-cases。`);
}
除非业务对延迟极度敏感,否则不要在生产环境实时运行复杂的 LLM-as-a-judge 评审器,因为这会增加用户等待时间和额外的 API 账单成本。推荐的方法是:线上收集 Trace 并打标签 -> 异步提取异常数据 -> 定期(如每晚)批量跑评测流。
评估与练习:设计你自己的回归评测
本节操作锚点:围绕“评估与练习:设计你自己的回归评测”记录步骤、样例、诊断、风险、检查清单和验收结果。
现在,你需要独立完成一套回归评测的配置,防止应用在后续的迭代中出现悄无声息的退化。
交付物要求
请在你的项目根目录中创建并提交以下文件:
tests/eval/dataset.ts: 一个包含至少 5 条代表性测试用例的黄金数据集。tests/eval/rubric.md: 编写一版针对你业务场景的 LLM-as-a-judge 打分细则文档。tests/eval/runner.ts: 使用 LangSmith SDK 实现的评测启动程序,执行完毕后输出当前版本的得分与未通过的用例列表。docs/regression-report-template.md: 每次 CI 阻断时需要填写的回归分析报告,模板结构如下:
# 评测回归分析报告
- **评测时间**: 202X-XX-XX
- **提交 Hash**:
- **测试数据集**:
- **触发门禁的用例 ID**:
- **退化原因分析**:
- [ ] 提示词过于宽泛,导致生成格式跑飞
- [ ] 更换的模型版本在长文本理解上能力下降
- [ ] 新规则与旧逻辑发生语义冲突
- **修复方案**:
- **验证结论 (通过/不通过)**:
验收标准
- 运行
npm run eval时,脚本能够拉取本地数据集并把运行 trace 推送到 LangSmith。 - 手动修改提示词使其故意“漏掉关键信息”,验证你的自定义评审器(Evaluator)是否能敏感地拦截到该异常,使脚本异常退出(exit code 1),从而证明 CI 门禁在模拟故障时是生效的。
来源与复核说明
本节操作锚点:围绕“来源与复核说明”记录步骤、样例、诊断、风险、检查清单和验收结果。
- 主要参考源:
- LangSmith datasets concepts & evaluation concepts (访问于 2026-05-28):定义了数据集、自定义规则评审器、批处理评测在 Node.js 环境下的标准调用流。
- OpenAI Working with evals (访问于 2026-05-28) 与 Anthropic Success criteria & build evaluations (访问于 2026-05-28):规范了黄金数据集的场景挑选逻辑、评测基线建立和 LLM-as-a-judge 评审细则的设计理念。
- 时效性与复核触发条件:
- 如果
@langchain/smith库在后续版本中弃用了runOnDataset或底层接口定义发生重构,需重新对本教程中的 TypeScript 代码进行签名对齐。 - 建议每 90 天复核一次 LangSmith 的数据导出与 Feedback 记录方法,确保隐私和留存配置符合当地合规要求。
- 如果
评测工程:用样例集防止应用退化:把判断写成可复查证据
本节把前面的操作收束成一次小验收,用来发现哪里仍然不可控。本课交付物是 一套 eval dataset、评审 rubric、CI 门禁和回归报告模板,它要服务于 AI 应用 的下一步,而不是只证明你读过这一节。
如果你现在还没有真实输入,先用一个最小样例完成 评测工程:用样例集防止应用退化,因为没有输入就无法判断产物是否可用。 如果本课产物会影响后续交付,应该写明适用条件和不适用条件,否则下一课会把不确定性误当成基础。 如果检查结果只剩一句“看起来可以”,必须补一条反例或失败样例,只有能解释失败原因时,这个判断才站得住。
不要把偶然成功当作通过;至少要能在相近条件下重复一次。围绕 评测工程:用样例集防止应用退化 做检查时,至少保留步骤、样例、风险、修复和验收五项。
LangChain 的《LangSmith evaluation concepts》说明:支撑数据集、实验、评审器、回归评测和质量门禁。;这意味着 评测工程:用样例集防止应用退化 不能只写经验结论,要把来源变成检查动作。LangChain 的《LangSmith datasets concepts》提醒:支撑样例集设计、输入输出标注、数据集版本和评测数据治理。;因此本课方案必须写清边界。OpenAI 的《Working with evals》提供的证据是:支撑评测集、测试运行、模型输出质量回归和迭代门禁。;所以当前结论按 2026-05-28 的来源状态使用。
复核触发条件要写具体:如果官方 API/SDK、软件工具版本、版权政策、安全合规要求、行业规范、平台发布规则或关键来源页面发生变化,需要重新检查 评测工程:用样例集防止应用退化 的步骤、样例、风险和验收清单。
练习验收:把 评测工程:用样例集防止应用退化 应用到一个自己的任务,交付一份包含输入、步骤、样例、失败诊断、来源证据和复测结果的记录。缺少其中任何一项,都先标记为 needs_review。