Skip to content

大模型评测体系搭建

从 MMLU/HumanEval 到业务评测——自动化评测管线、评测指标设计、评测驱动开发,用数据而非直觉选模型、调 Prompt。


1. 为什么需要系统化评测

1.1 "Vibes-based Eval"的陷阱

大部分团队评模型的方式:

  产品经理: "我试了几个问题,GPT-4o 回答得更好"
  工程师:   "我感觉 Claude 更快"
  老板:     "用最便宜的那个"

  这就是 Vibes-based Eval(凭感觉评测)

  问题:
  1. 样本偏差  → 试了 5 个问题就下结论
  2. 不可复现  → 下次换个人试结果不同
  3. 维度缺失  → 只看"对不对",不看成本/延迟/安全
  4. 无法回归  → 改了 Prompt 不知道整体是变好还是变差

1.2 评测的三个层次:基准 → 任务 → 业务

评测金字塔:

                    ┌──────────┐
                    │ 业务评测  │  你的真实场景
                    │ (最重要)  │  "客服回答准确率"
                    ├──────────┤
                    │ 任务评测  │  通用任务能力
                    │          │  "摘要/翻译/QA"
                    ├──────────┤
                    │ 基准评测  │  标准化榜单
                    │          │  "MMLU/HumanEval"
                    └──────────┘

  基准评测:筛选候选模型(排除明显不行的)
  任务评测:对比候选模型在通用任务上的表现
  业务评测:用你的真实数据做最终决策 ← 最关键

1.3 评测驱动开发(Eval-Driven Development)

传统开发:写代码 → 手动测试 → 上线
评测驱动:写评测集 → 跑基线 → 改 Prompt/模型 → 跑评测 → 对比 → 上线

  Step 1: 定义评测集(100+ 真实样本)
  Step 2: 定义指标(准确率、忠实度、延迟、成本)
  Step 3: 跑基线(当前版本的分数)
  Step 4: 修改 Prompt / 换模型
  Step 5: 跑评测,对比基线
  Step 6: 只有指标提升才上线

  就像 TDD,但 E 代表 Eval:
  红(基线分低)→ 绿(优化后分高)→ 重构(降成本)

2. 通用基准解读:MMLU / HumanEval / MATH

2.1 知识类:MMLU / MMLU-Pro / GPQA

MMLU(Massive Multitask Language Understanding):
  - 57 个学科,14,042 道选择题
  - 高中到专业级别
  - 测试:广泛知识覆盖

  MMLU-Pro:
  - MMLU 的加强版,10 个选项(原版 4 个)
  - 更少的噪音题,更考验推理
  - 当前顶级模型: 70-80%

  GPQA(Graduate-Level Google-Proof QA):
  - 博士级科学问题
  - 即使 Google 搜索也难以回答
  - 测试:深层推理能力
基准题量类型难度当前顶级
MMLU14K选择题高中-本科~90%
MMLU-Pro12K选择题本科-研究生~75%
GPQA448选择题博士级~65%

2.2 代码类:HumanEval / MBPP / SWE-bench

HumanEval:
  - 164 道 Python 编程题
  - 给函数签名 + 描述 → 生成函数体
  - pass@1: 一次生成就通过的比例

  SWE-bench:
  - 真实 GitHub Issue 修复任务
  - 给 Issue 描述 → 生成 PR(修改多个文件)
  - 最难的代码评测,当前最强模型 ~50%
基准题量任务当前顶级
HumanEval164函数生成~95% pass@1
MBPP974简单函数~90%
SWE-bench Verified500真实 Bug 修复~50%

2.3 推理类:MATH / GSM8K / ARC

MATH:
  - 12,500 道数学竞赛题
  - 涵盖代数、几何、数论等
  - 需要多步推理

  GSM8K:
  - 8,500 道小学数学应用题
  - 测试基础数学推理
  - 当前模型基本满分

  ARC(AI2 Reasoning Challenge):
  - 科学推理选择题
  - 需要常识 + 逻辑

2.4 榜单的局限性与正确用法

榜单的三大陷阱:

  1. 数据污染(Contamination)
     模型训练数据可能包含测试题
     → 高分不代表真正理解

  2. 格式过拟合
     模型针对选择题格式优化
     → MMLU 高分但开放问答差

  3. 与业务脱节
     MMLU 考知识,你的业务可能考"能否正确提取发票金额"
     → 榜单第一不等于你的场景第一
正确用法错误用法
初筛:排除明显不行的模型只看榜单选模型
参考趋势:模型迭代方向MMLU 高 1% 就切换
补充:结合业务评测一起看忽略成本和延迟

3. 业务评测数据集构建

3.1 测试集来源:日志采样 vs 人工构造

数据集构建策略:

  ┌─────────────────────────────────────────────┐
  │ 来源 1:生产日志采样                          │
  │                                              │
  │  生产请求 → 随机采样 → 人工标注 → 测试集       │
  │  优点:反映真实分布                            │
  │  缺点:需要人工标注成本                        │
  ├─────────────────────────────────────────────┤
  │ 来源 2:人工构造                              │
  │                                              │
  │  业务专家编写 → 覆盖边界情况 → 测试集          │
  │  优点:覆盖极端场景                            │
  │  缺点:可能不反映真实分布                      │
  ├─────────────────────────────────────────────┤
  │ 来源 3:LLM 合成 + 人工审核                   │
  │                                              │
  │  LLM 生成变体 → 人工审核 → 测试集             │
  │  优点:快速扩充数据量                          │
  │  缺点:可能有偏差                              │
  └─────────────────────────────────────────────┘

  推荐:70% 生产采样 + 20% 人工构造 + 10% LLM 合成

3.2 Golden Set 标注规范

python
# Golden Set 数据格式
from pydantic import BaseModel
from typing import Optional

class TestCase(BaseModel):
    id: str                      # 唯一标识
    category: str                # 分类:qa / summary / extraction
    difficulty: str              # easy / medium / hard
    input: str                   # 用户输入
    context: Optional[str]       # RAG 场景的上下文
    expected_output: str         # 期望输出(标准答案)
    acceptable_outputs: list[str] = []  # 可接受的其他答案
    metadata: dict = {}          # 额外信息

# 标注规范
class AnnotationGuideline(BaseModel):
    """标注员规范"""
    # 1. 答案必须完整、准确
    # 2. 如果有多种正确答案,列出所有
    # 3. 标注 difficulty:
    #    easy   = 单步推理
    #    medium = 多步推理
    #    hard   = 需要专业知识
    # 4. 每条至少 2 人交叉标注
    pass
python
# 示例测试集
test_cases = [
    TestCase(
        id="qa-001",
        category="qa",
        difficulty="easy",
        input="Node.js 的事件循环有几个阶段?",
        context="Node.js 事件循环分为 6 个阶段:timers、pending callbacks、idle/prepare、poll、check、close callbacks。",
        expected_output="6 个阶段",
        acceptable_outputs=["六个阶段", "6个阶段", "6 phases"],
    ),
    TestCase(
        id="extract-001",
        category="extraction",
        difficulty="medium",
        input="请从以下文本提取所有金额:总价 ¥12,500,税额 ¥1,250,实付 ¥13,750",
        expected_output='{"total": 12500, "tax": 1250, "paid": 13750}',
    ),
]

3.3 测试用例分层设计

测试集分层:

  Level 1 - 冒烟测试(Smoke Test)
    10-20 条核心场景
    每次提交必跑,< 2 分钟
    确保基本功能不挂

  Level 2 - 回归测试(Regression Test)
    100-200 条覆盖各分类
    每日/PR 合并时跑,< 15 分钟
    防止质量回归

  Level 3 - 全量评测(Full Evaluation)
    500+ 条含边界情况
    每周/发版前跑,< 1 小时
    全面质量报告

3.4 数据集版本管理

python
# 用 Git + JSON/JSONL 管理数据集
# datasets/
#   v1.0/
#     qa.jsonl
#     extraction.jsonl
#     summary.jsonl
#     metadata.json
#   v1.1/
#     ...

# metadata.json
{
    "version": "1.1",
    "created_at": "2026-05-10",
    "total_cases": 250,
    "categories": {
        "qa": 120,
        "extraction": 80,
        "summary": 50
    },
    "changelog": "新增 30 条金融领域提取用例",
    "annotators": ["alice", "bob"],
    "inter_annotator_agreement": 0.92
}

本章小结

知识点要点
评测金字塔基准(筛选)→ 任务(对比)→ 业务(决策)
通用基准MMLU(知识)/ HumanEval(代码)/ MATH(推理)
榜单局限数据污染、格式过拟合、与业务脱节
业务数据集70% 生产采样 + 20% 人工 + 10% LLM 合成
分层测试冒烟(10条)→ 回归(200条)→ 全量(500+)

下一章:评测指标设计——BLEU/ROUGE/BERTScore、LLM-as-Judge、多维评分卡。


4. 评测指标设计

4.1 传统 NLP 指标:BLEU / ROUGE / BERTScore

python
# BLEU:翻译/生成质量(n-gram 匹配)
from nltk.translate.bleu_score import sentence_bleu

reference = "Node.js 事件循环分为六个阶段".split()
candidate = "Node.js 的事件循环有六个阶段".split()
score = sentence_bleu([reference], candidate)
# → 0.72(n-gram 重合度)

# ROUGE:摘要质量(召回率导向)
from rouge_score import rouge_scorer
scorer = rouge_scorer.RougeScorer(['rouge1', 'rouge2', 'rougeL'])
scores = scorer.score(
    "Node.js 事件循环分为六个阶段",
    "Node.js 的事件循环有六个阶段",
)
# → rouge1: 0.80, rougeL: 0.75

# BERTScore:语义相似度(基于 BERT 嵌入)
from bert_score import score as bert_score
P, R, F1 = bert_score(
    ["Node.js 的事件循环有六个阶段"],
    ["Node.js 事件循环分为六个阶段"],
    lang="zh",
)
# → F1: 0.95(语义接近,即使措辞不同)
指标原理优点缺点适合
BLEUn-gram 精确率快速、标准化对同义词不敏感翻译
ROUGEn-gram 召回率适合摘要对语序不敏感摘要
BERTScore语义嵌入理解同义需要 GPU通用 ✅
Exact Match完全匹配零歧义太严格提取

4.2 LLM-as-Judge 评估

python
import openai

client = openai.OpenAI()

async def llm_judge(
    question: str,
    answer: str,
    reference: str,
    criteria: str = "accuracy",
) -> dict:
    """用 LLM 做评审"""
    
    JUDGE_PROMPT = """你是一个严格的评审专家。
请根据以下标准评估 AI 的回答质量。

## 评估标准:{criteria}

## 问题
{question}

## 参考答案
{reference}

## AI 的回答
{answer}

请返回 JSON:
&#123;&#123;
  "score": 1-5 的整数评分,
  "reasoning": "评分理由",
  "issues": ["问题1", "问题2"]
&#125;&#125;

评分标准:
5 = 完美,与参考答案一致或更好
4 = 良好,有轻微瑕疵
3 = 合格,基本正确但有遗漏
2 = 较差,有明显错误
1 = 不可用,完全错误或有害"""
    
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{
            "role": "user",
            "content": JUDGE_PROMPT.format(
                criteria=criteria,
                question=question,
                reference=reference,
                answer=answer,
            ),
        }],
        response_format={"type": "json_object"},
        temperature=0,
    )
    
    return json.loads(response.choices[0].message.content)
LLM-as-Judge 的注意事项:

  1. 用比被评模型更强的模型做 Judge
     被评:gpt-4o-mini → Judge:gpt-4o
     被评:claude-3.5  → Judge:gpt-4o 或 claude-3.5-opus

  2. Position Bias(位置偏见)
     A/B 对比时,交换 A/B 顺序各评一次,取平均

  3. 校准
     先用人工标注的 50 条数据校准 Judge
     确保 Judge 和人工的一致率 > 85%

  4. 多 Judge 投票
     用 2-3 个 Judge 模型投票,减少偏差

4.3 业务自定义指标

python
# 格式合规率
def format_compliance(answer: str, expected_format: str) -> float:
    """检查输出是否符合预期格式"""
    if expected_format == "json":
        try:
            json.loads(answer)
            return 1.0
        except:
            return 0.0
    elif expected_format == "markdown":
        return 1.0 if answer.startswith("#") else 0.0
    return 1.0

# 安全性检测
async def safety_check(answer: str) -> float:
    """检测有害内容"""
    response = client.moderations.create(input=answer)
    result = response.results[0]
    return 0.0 if result.flagged else 1.0

# 延迟指标
def latency_score(latency_ms: float, threshold_ms: float = 2000) -> float:
    """延迟评分(越快越好)"""
    if latency_ms <= threshold_ms * 0.5:
        return 1.0
    elif latency_ms <= threshold_ms:
        return 0.5 + 0.5 * (1 - latency_ms / threshold_ms)
    else:
        return max(0, 1 - latency_ms / (threshold_ms * 2))

# 成本指标
def cost_score(cost_usd: float, budget_usd: float = 0.01) -> float:
    """成本评分"""
    return min(1.0, budget_usd / max(cost_usd, 0.0001))

4.4 多维评分卡设计

多维评分卡:

  ┌────────────────────────────────────────────┐
  │  模型评分卡: gpt-4o-mini + Prompt v2.1     │
  │                                            │
  │  准确率      ████████░░  82%               │
  │  忠实度      █████████░  93%               │
  │  格式合规    ██████████  100%              │
  │  安全性      ██████████  99%               │
  │  平均延迟    ████████░░  1.2s              │
  │  平均成本    █████████░  $0.003            │
  │                                            │
  │  综合得分: 87/100                          │
  │  基线对比: ▲ +5 (vs v2.0)                 │
  └────────────────────────────────────────────┘
python
from dataclasses import dataclass

@dataclass
class Scorecard:
    model: str
    prompt_version: str
    accuracy: float         # 准确率
    faithfulness: float     # 忠实度
    format_compliance: float # 格式合规
    safety: float           # 安全性
    avg_latency_ms: float   # 平均延迟
    avg_cost_usd: float     # 平均成本
    
    @property
    def composite_score(self) -> float:
        """加权综合分"""
        weights = {
            "accuracy": 0.30,
            "faithfulness": 0.25,
            "format_compliance": 0.15,
            "safety": 0.15,
            "latency": 0.10,
            "cost": 0.05,
        }
        latency_s = latency_score(self.avg_latency_ms)
        cost_s = cost_score(self.avg_cost_usd)
        
        return (
            self.accuracy * weights["accuracy"] +
            self.faithfulness * weights["faithfulness"] +
            self.format_compliance * weights["format_compliance"] +
            self.safety * weights["safety"] +
            latency_s * weights["latency"] +
            cost_s * weights["cost"]
        ) * 100

5. 自动化评测管线

5.1 评测管线架构

自动化评测管线:

  ┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐
  │ 数据集    │ →  │ 推理引擎  │ →  │ 评分引擎  │ →  │ 报告生成  │
  │ 加载      │    │ 批量调用  │    │ 多指标    │    │ 可视化   │
  └──────────┘    └──────────┘    └──────────┘    └──────────┘
       ↑                                               │
       │           ┌──────────┐                        ↓
       └───────────│ CI/CD    │←───────────── 回归检测
                   └──────────┘

5.2 Python 评测框架实现

python
import asyncio
import json
import time
from pathlib import Path
from dataclasses import dataclass, field

@dataclass
class EvalResult:
    test_case_id: str
    model: str
    prompt_version: str
    input: str
    expected: str
    actual: str
    scores: dict[str, float] = field(default_factory=dict)
    latency_ms: float = 0
    cost_usd: float = 0
    error: str | None = None

class EvalPipeline:
    def __init__(self, model: str, prompt_version: str):
        self.model = model
        self.prompt_version = prompt_version
        self.results: list[EvalResult] = []
    
    async def run(self, test_cases: list[TestCase]) -> list[EvalResult]:
        """执行评测"""
        tasks = [self._eval_one(tc) for tc in test_cases]
        self.results = await asyncio.gather(*tasks)
        return self.results
    
    async def _eval_one(self, tc: TestCase) -> EvalResult:
        """评测单条"""
        start = time.time()
        try:
            # 1. 推理
            answer, usage = await self._inference(tc.input, tc.context)
            latency = (time.time() - start) * 1000
            
            # 2. 评分
            scores = {}
            scores["exact_match"] = 1.0 if answer.strip() == tc.expected_output.strip() else 0.0
            scores["llm_judge"] = (await llm_judge(tc.input, answer, tc.expected_output))["score"] / 5
            scores["format"] = format_compliance(answer, tc.metadata.get("format", "text"))
            scores["safety"] = await safety_check(answer)
            
            cost = calculate_cost(self.model, usage["input"], usage["output"])
            
            return EvalResult(
                test_case_id=tc.id,
                model=self.model,
                prompt_version=self.prompt_version,
                input=tc.input,
                expected=tc.expected_output,
                actual=answer,
                scores=scores,
                latency_ms=latency,
                cost_usd=cost,
            )
        except Exception as e:
            return EvalResult(
                test_case_id=tc.id, model=self.model,
                prompt_version=self.prompt_version,
                input=tc.input, expected=tc.expected_output,
                actual="", error=str(e),
            )
    
    def generate_report(self) -> dict:
        """生成评测报告"""
        valid = [r for r in self.results if r.error is None]
        
        metrics = {}
        for key in valid[0].scores.keys():
            values = [r.scores[key] for r in valid]
            metrics[key] = {
                "mean": sum(values) / len(values),
                "min": min(values),
                "max": max(values),
            }
        
        return {
            "model": self.model,
            "prompt_version": self.prompt_version,
            "total_cases": len(self.results),
            "errors": len([r for r in self.results if r.error]),
            "metrics": metrics,
            "avg_latency_ms": sum(r.latency_ms for r in valid) / len(valid),
            "total_cost_usd": sum(r.cost_usd for r in valid),
            "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
        }

# 使用
pipeline = EvalPipeline(model="gpt-4o-mini", prompt_version="v2.1")
results = await pipeline.run(test_cases)
report = pipeline.generate_report()
print(json.dumps(report, indent=2, ensure_ascii=False))

5.3 CI/CD 集成:每次提交自动评测

yaml
# .github/workflows/eval.yml
name: LLM Evaluation

on:
  pull_request:
    paths:
      - 'prompts/**'
      - 'src/llm/**'

jobs:
  smoke-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      
      - name: Install dependencies
        run: pip install -r requirements-eval.txt
      
      - name: Run smoke test (Level 1)
        env:
          OPENAI_API_KEY: $&#123;&#123; secrets.OPENAI_API_KEY &#125;&#125;
        run: python eval/run.py --level smoke --output eval-report.json
      
      - name: Check regression
        run: python eval/check_regression.py --report eval-report.json --baseline eval/baseline.json --threshold 0.05
      
      - name: Comment PR with results
        uses: actions/github-script@v7
        with:
          script: |
            const report = require('./eval-report.json');
            const body = `## 📊 评测报告
            | 指标 | 分数 | 基线 | 变化 |
            |---|---|---|---|
            | 准确率 | ${report.metrics.accuracy.mean.toFixed(3)} | ... | ... |
            | 忠实度 | ${report.metrics.faithfulness.mean.toFixed(3)} | ... | ... |
            `;
            github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              body,
            });

5.4 可视化评测报告

python
# 生成 Markdown 评测报告
def generate_markdown_report(report: dict, baseline: dict | None = None) -> str:
    lines = [
        f"# 评测报告: {report['model']} / {report['prompt_version']}",
        f"时间: {report['timestamp']}",
        f"总用例: {report['total_cases']} | 错误: {report['errors']}",
        "",
        "## 指标汇总",
        "| 指标 | 平均 | 最低 | 最高 | 基线 | 变化 |",
        "|---|---|---|---|---|---|",
    ]
    
    for metric, values in report["metrics"].items():
        baseline_val = baseline["metrics"].get(metric, {}).get("mean", 0) if baseline else 0
        delta = values["mean"] - baseline_val
        emoji = "✅" if delta >= 0 else "❌"
        lines.append(
            f"| {metric} | {values['mean']:.3f} | {values['min']:.3f} | "
            f"{values['max']:.3f} | {baseline_val:.3f} | {emoji} {delta:+.3f} |"
        )
    
    lines.extend([
        "",
        f"**平均延迟**: {report['avg_latency_ms']:.0f}ms",
        f"**总成本**: ${report['total_cost_usd']:.4f}",
    ])
    
    return "\n".join(lines)

本章小结

知识点要点
传统指标BLEU(翻译)/ ROUGE(摘要)/ BERTScore(语义)
LLM-as-Judge用强模型评审弱模型,注意 Position Bias
业务指标格式合规 + 安全性 + 延迟 + 成本
评分卡六维加权综合分,对比基线
自动化管线数据集→推理→评分→报告→CI/CD

下一章:模型选型与 Prompt 优化评测——多模型 A/B、Pareto 分析、评测驱动迭代。


6. 模型选型与 Prompt 优化评测

6.1 多模型对比评测

python
# 同一数据集跑多个模型
async def compare_models(
    test_cases: list[TestCase],
    models: list[str],
    prompt_version: str,
) -> dict[str, dict]:
    """多模型对比评测"""
    reports = {}
    
    for model in models:
        print(f"\n{'='*50}")
        print(f"评测模型: {model}")
        print(f"{'='*50}")
        
        pipeline = EvalPipeline(model=model, prompt_version=prompt_version)
        await pipeline.run(test_cases)
        reports[model] = pipeline.generate_report()
    
    return reports

# 使用
models = ["gpt-4o", "gpt-4o-mini", "gpt-4.1-mini", "gpt-4.1-nano"]
reports = await compare_models(test_cases, models, prompt_version="v2.1")

# 对比表
print_comparison_table(reports)
多模型对比结果:

  ┌───────────────┬────────┬────────┬──────────┬──────────┬──────────┐
  │ 模型           │ 准确率  │ 忠实度  │ 平均延迟  │ 单次成本  │ 综合分   │
  ├───────────────┼────────┼────────┼──────────┼──────────┼──────────┤
  │ gpt-4o        │ 0.92   │ 0.96   │ 2.1s    │ $0.015  │ 88      │
  │ gpt-4o-mini   │ 0.85   │ 0.93   │ 0.8s    │ $0.002  │ 85      │
  │ gpt-4.1-mini  │ 0.87   │ 0.94   │ 0.6s    │ $0.003  │ 87      │
  │ gpt-4.1-nano  │ 0.78   │ 0.88   │ 0.3s    │ $0.001  │ 79      │
  └───────────────┴────────┴────────┴──────────┴──────────┴──────────┘

  结论:
  - 质量优先 → gpt-4o(但成本高 7x)
  - 性价比   → gpt-4.1-mini ✅(质量接近 4o,成本低 5x)
  - 成本优先 → gpt-4.1-nano(质量下降明显)

6.2 Prompt 版本 A/B 测试

python
async def ab_test_prompts(
    test_cases: list[TestCase],
    model: str,
    prompt_a: str,
    prompt_b: str,
) -> dict:
    """Prompt A/B 测试"""
    
    pipeline_a = EvalPipeline(model=model, prompt_version=prompt_a)
    pipeline_b = EvalPipeline(model=model, prompt_version=prompt_b)
    
    await pipeline_a.run(test_cases)
    await pipeline_b.run(test_cases)
    
    report_a = pipeline_a.generate_report()
    report_b = pipeline_b.generate_report()
    
    # 逐条对比
    wins_a, wins_b, ties = 0, 0, 0
    for ra, rb in zip(pipeline_a.results, pipeline_b.results):
        score_a = ra.scores.get("llm_judge", 0)
        score_b = rb.scores.get("llm_judge", 0)
        if score_a > score_b:
            wins_a += 1
        elif score_b > score_a:
            wins_b += 1
        else:
            ties += 1
    
    return {
        "prompt_a": {"version": prompt_a, "report": report_a, "wins": wins_a},
        "prompt_b": {"version": prompt_b, "report": report_b, "wins": wins_b},
        "ties": ties,
        "winner": prompt_a if wins_a > wins_b else prompt_b,
    }

6.3 成本-质量 Pareto 分析

Pareto 前沿(成本 vs 质量):

  质量
  1.0 │                          ★ gpt-4o
      │                    ★ gpt-4.1-mini (Pareto 最优)
  0.9 │              ★ gpt-4o-mini

  0.8 │    ★ gpt-4.1-nano

  0.7 │
      └──────────────────────────────────→ 成本
       $0.001   $0.003   $0.005   $0.015

  Pareto 最优 = 在相同成本下质量最高,或相同质量下成本最低
  → gpt-4.1-mini 是性价比最优选择
python
def pareto_analysis(reports: dict[str, dict]) -> list[str]:
    """找到 Pareto 最优模型"""
    models = []
    for name, report in reports.items():
        accuracy = report["metrics"]["llm_judge"]["mean"]
        cost = report["total_cost_usd"] / report["total_cases"]
        models.append({"name": name, "accuracy": accuracy, "cost": cost})
    
    # 找 Pareto 前沿
    pareto = []
    for m in models:
        dominated = False
        for other in models:
            if other["accuracy"] >= m["accuracy"] and other["cost"] <= m["cost"] and other != m:
                dominated = True
                break
        if not dominated:
            pareto.append(m["name"])
    
    return pareto

6.4 评测驱动的迭代流程

评测驱动迭代的标准流程:

  ┌─────────────────────────────────────────────────┐
  │  Round 1: 基线                                   │
  │  模型: gpt-4o-mini / Prompt v1                   │
  │  准确率: 0.75 / 忠实度: 0.82 / 成本: $0.003      │
  ├─────────────────────────────────────────────────┤
  │  Round 2: 优化 Prompt                            │
  │  模型: gpt-4o-mini / Prompt v2(加约束规则)      │
  │  准确率: 0.82 ▲7% / 忠实度: 0.90 ▲8%            │
  ├─────────────────────────────────────────────────┤
  │  Round 3: 换模型                                 │
  │  模型: gpt-4.1-mini / Prompt v2                  │
  │  准确率: 0.87 ▲5% / 成本: $0.003 持平            │
  ├─────────────────────────────────────────────────┤
  │  Round 4: 添加 Few-shot                          │
  │  模型: gpt-4.1-mini / Prompt v3(3-shot)        │
  │  准确率: 0.91 ▲4% / 成本: $0.005 ▲小幅          │
  ├─────────────────────────────────────────────────┤
  │  ✅ 发布决策: Round 4                             │
  │  准确率 0.75 → 0.91(+21%),成本 +66% 可接受     │
  └─────────────────────────────────────────────────┘

7. 生产评测与持续监控

7.1 Shadow Mode 在线评测

python
# Shadow Mode:新版本和旧版本同时推理,对比结果
class ShadowEvaluator:
    def __init__(self, primary_model: str, shadow_model: str):
        self.primary = primary_model  # 线上生产版本
        self.shadow = shadow_model    # 待上线新版本
    
    async def evaluate(self, question: str, context: str) -> dict:
        # 1. 生产版本正常返回给用户
        primary_result = await inference(self.primary, question, context)
        
        # 2. Shadow 版本异步推理(不影响用户)
        shadow_task = asyncio.create_task(
            inference(self.shadow, question, context)
        )
        
        # 返回生产结果给用户
        yield primary_result
        
        # 3. 等 Shadow 完成,记录对比
        shadow_result = await shadow_task
        
        # 4. LLM-as-Judge 对比
        comparison = await compare_answers(
            question, primary_result, shadow_result
        )
        
        # 5. 记录到评测数据库
        await log_shadow_result({
            "question": question,
            "primary": primary_result,
            "shadow": shadow_result,
            "comparison": comparison,
            "timestamp": datetime.now(),
        })
Shadow Mode 工作流:

  用户请求 → 生产模型(v1)→ 返回用户
              ↓(异步)
           影子模型(v2)→ 记录对比结果

           积累 1000+ 条 → 统计分析

           Shadow 更优? → 切换为生产版本

7.2 用户反馈闭环

python
# 用户反馈收集
class FeedbackCollector:
    async def record_feedback(
        self,
        trace_id: str,
        feedback: str,        # "positive" / "negative"
        comment: str = "",
    ):
        await db.insert(feedback_table).values({
            "trace_id": trace_id,
            "feedback": feedback,
            "comment": comment,
            "timestamp": datetime.now(),
        })
    
    async def generate_improvement_dataset(
        self,
        min_count: int = 50,
    ) -> list[TestCase]:
        """从负面反馈中生成改进数据集"""
        negative = await db.query(
            """SELECT t.input, t.output, f.comment
               FROM traces t
               JOIN feedback f ON t.id = f.trace_id
               WHERE f.feedback = 'negative'
               ORDER BY f.timestamp DESC
               LIMIT $1""",
            [min_count]
        )
        
        # 转换为测试用例
        test_cases = []
        for row in negative.rows:
            tc = TestCase(
                id=f"feedback-{row['trace_id']}",
                category="user-reported",
                difficulty="unknown",
                input=row["input"],
                expected_output="",  # 需要人工标注
                metadata={"user_comment": row["comment"]},
            )
            test_cases.append(tc)
        
        return test_cases
用户反馈闭环:

  用户 👎 → 收集负面反馈 → 标注正确答案 → 加入测试集

  改进 Prompt / 模型 → 跑评测 → 确认改善 → 上线

  持续监控 → 用户 👍 率提升                  ← 闭环完成

7.3 质量回归告警

python
class RegressionDetector:
    def __init__(self, baseline_path: str):
        with open(baseline_path) as f:
            self.baseline = json.load(f)
    
    def check(self, current_report: dict, threshold: float = 0.05) -> dict:
        """检查质量回归"""
        regressions = []
        
        for metric, values in current_report["metrics"].items():
            baseline_val = self.baseline["metrics"].get(metric, {}).get("mean", 0)
            current_val = values["mean"]
            delta = current_val - baseline_val
            
            if delta < -threshold:
                regressions.append({
                    "metric": metric,
                    "baseline": baseline_val,
                    "current": current_val,
                    "delta": delta,
                    "severity": "critical" if delta < -0.10 else "warning",
                })
        
        passed = len(regressions) == 0
        
        return {
            "passed": passed,
            "regressions": regressions,
            "summary": "✅ 无回归" if passed else f"❌ {len(regressions)} 项指标回归",
        }

# CI/CD 中使用
detector = RegressionDetector("eval/baseline.json")
result = detector.check(current_report)

if not result["passed"]:
    for r in result["regressions"]:
        print(f"  ❌ {r['metric']}: {r['baseline']:.3f}{r['current']:.3f} ({r['delta']:+.3f})")
    sys.exit(1)  # CI 失败,阻止合并

7.4 评测成熟度模型

评测成熟度五级模型:

  Level 0 - 无评测
    "试了几个问题感觉还行就上了"
    风险:极高

  Level 1 - 手动评测
    有测试集,手动跑,手动看
    工具:Excel / Notion
    团队:1 人

  Level 2 - 自动化评测
    CI/CD 集成,PR 自动跑评测
    工具:Python 脚本 + GitHub Actions
    团队:2-3 人

  Level 3 - 全链路评测
    离线评测 + 在线监控 + Shadow Mode
    工具:Langfuse + 自建管线
    团队:3-5 人

  Level 4 - 评测驱动
    评测数据驱动产品决策
    A/B 测试 + 用户反馈闭环
    工具:完整评测平台
    团队:5+ 人

  大部分团队应该先到 Level 2,再逐步往上。
成熟度核心能力投入目标
L00
L1测试集 + 手动跑1 人有基线
L2CI/CD 自动评测1 周防回归 ✅
L3在线监控 + Shadow1 月全链路
L4数据驱动决策持续评测文化

全书总结

┌─────────────────────────────────────────────────────────────┐
│        大模型评测体系搭建 · 知识地图                           │
│                                                              │
│  Ch.1  为什么评测    Vibes 陷阱 / 三层金字塔 / EDD            │
│  Ch.2  通用基准      MMLU·HumanEval·MATH / 榜单局限          │
│  Ch.3  业务数据集    采样策略 / Golden Set / 分层 / 版本管理   │
│  Ch.4  指标设计      BLEU·ROUGE / LLM-as-Judge / 评分卡      │
│  Ch.5  自动化管线    Python 框架 / CI/CD / 可视化报告          │
│  Ch.6  模型选型      多模型对比 / A/B 测试 / Pareto 分析      │
│  Ch.7  生产评测      Shadow Mode / 反馈闭环 / 成熟度模型      │
│                                                              │
│  7 章 25 节,从"凭感觉"到"数据驱动"的评测体系升级。           │
└─────────────────────────────────────────────────────────────┘

🎉 核心原则:不要凭感觉,用数据说话。评测不是上线前的一次检查,而是持续运行的质量引擎。

坚持是一种品格