章节12 / 14
  1. 01先把 AI 应用当成系统,而不是聊天框
  2. 02打通第一个多 Provider 模型请求
  3. 03把 Prompt 写成任务协议
  4. 04结构化输出不是格式化,而是业务门禁
  5. 05把等待时间拆成事件:流式响应实战
  6. 06Tool Calling:模型提出动作,应用执行动作
  7. 07有副作用的工具要先设计刹车
  8. 08RAG 的第一性问题:答案从哪里来
  9. 09让 RAG 回答经得起追问
  10. 10长上下文、会话状态与记忆压缩
  11. 11Agent 工作流要像状态机一样可恢复
  12. 12评测工程:用样例集防止应用退化
  13. 13可观测性与安全:线上问题要能被看见
  14. 14上线不是结束:路由、灰度、回滚和治理复盘
本文目录12
  1. 为什么需要把评测融入开发流
  2. 步骤一:构建高质量的黄金数据集 (Golden Dataset)
  3. 步骤二:定义多指标评审准则 (Evaluation Rubrics)
  4. 步骤三:编写基于 LangSmith 的自动化评测脚本
  5. 步骤四:建立 CI/CD 评测回归门禁
  6. 典型失败诊断指南
  7. 步骤五:配置线上观测与负反馈闭环
  8. 评估与练习:设计你自己的回归评测
  9. 交付物要求
  10. 验收标准
  11. 来源与复核说明
  12. 评测工程:用样例集防止应用退化:把判断写成可复查证据
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 编写的本地黄金数据集结构,用于评估一个客服机器人的分类与回复表现:

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 分数),往往会因为模型换了同义词而给出极低的评分,这显然不符合自然语言的特性。我们必须采用混合评审准则:

  1. 确定性评审(Regex / JSON Validation):检查输出中是否包含禁用词、是否成功提取了必要实体、格式是否为合法的 JSON。
  2. 语义相似度评审(Embedding Similarity):评估模型输出与参考答案的向量距离。
  3. 大模型裁判(LLM-as-a-judge):使用一个更强大的模型(如 Claude 3 Opus 或 GPT-4o)根据明确的 Rubric 细则给输出打分。

下面是一个针对 LLM-as-a-judge 的打分提示词模版。如果系统的实际输出包含违禁词或语调不符合要求,大模型裁判需要给出扣分理由及分值:

markdown
# 角色
你是一名资深的质量保证专家,负责评估智能客服系统的回答质量。

# 评估任务
请根据以下提供的 [用户输入]、[系统实际输出] 以及 [期望参考答案],对系统输出进行 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 函数来执行批量测试。

以下是一个完整的自动化评测脚本示例:

typescript
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 配置文件示例:

yaml
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 状态码)时,你应该按以下步骤进行排查:

  1. 定位失败 case:登录 LangSmith Console,进入对应的 Project,筛选出评分(Score)为 0 的 Runs。
  2. 检查差分 (Diff):观察 actual_outputreference 之间的具体偏离。如果是提示词变动导致语气太温和而丢失了关键术语,应当微调 Prompt 结构,强化对关键词的要求。
  3. 更新数据集(取舍判断):如果在评测中发现生成的 JSON 格式不合法,应该在数据集的输出期望(Ground Truth)中严格定义 JSON Schema,并使用解析器作为确定性评审器,因为只有规则评审器才能对格式提供 100% 的准确判定。

步骤五:配置线上观测与负反馈闭环

本节操作锚点:围绕“步骤五:配置线上观测与负反馈闭环”记录步骤、样例、诊断、风险、检查清单和验收结果。

没有任何一个评测集可以完美预测用户的真实行为。在线上环境中,用户的多样化输入源源不断,我们需要建立线上负反馈的捕获机制。

根据 LangSmith observability 指南,我们可以对线上运行的每个 Trace 进行打标,并将用户点击“踩(Thumbs Down)”或触发纠错行为的真实 Trace 一键导出为 Dataset 样例,实现“线上坏例 -> 评测用例 -> 修复提示词 -> 回归测试 -> 部署”的完整反馈闭环。

typescript
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 并打标签 -> 异步提取异常数据 -> 定期(如每晚)批量跑评测流


评估与练习:设计你自己的回归评测

本节操作锚点:围绕“评估与练习:设计你自己的回归评测”记录步骤、样例、诊断、风险、检查清单和验收结果。

现在,你需要独立完成一套回归评测的配置,防止应用在后续的迭代中出现悄无声息的退化。

交付物要求

请在你的项目根目录中创建并提交以下文件:

  1. tests/eval/dataset.ts: 一个包含至少 5 条代表性测试用例的黄金数据集。
  2. tests/eval/rubric.md: 编写一版针对你业务场景的 LLM-as-a-judge 打分细则文档。
  3. tests/eval/runner.ts: 使用 LangSmith SDK 实现的评测启动程序,执行完毕后输出当前版本的得分与未通过的用例列表。
  4. docs/regression-report-template.md: 每次 CI 阻断时需要填写的回归分析报告,模板结构如下:
markdown
# 评测回归分析报告

- **评测时间**: 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。