大模型评测体系搭建
从 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 搜索也难以回答
- 测试:深层推理能力| 基准 | 题量 | 类型 | 难度 | 当前顶级 |
|---|---|---|---|---|
| MMLU | 14K | 选择题 | 高中-本科 | ~90% |
| MMLU-Pro | 12K | 选择题 | 本科-研究生 | ~75% |
| GPQA | 448 | 选择题 | 博士级 | ~65% |
2.2 代码类:HumanEval / MBPP / SWE-bench
HumanEval:
- 164 道 Python 编程题
- 给函数签名 + 描述 → 生成函数体
- pass@1: 一次生成就通过的比例
SWE-bench:
- 真实 GitHub Issue 修复任务
- 给 Issue 描述 → 生成 PR(修改多个文件)
- 最难的代码评测,当前最强模型 ~50%| 基准 | 题量 | 任务 | 当前顶级 |
|---|---|---|---|
| HumanEval | 164 | 函数生成 | ~95% pass@1 |
| MBPP | 974 | 简单函数 | ~90% |
| SWE-bench Verified | 500 | 真实 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 人交叉标注
passpython
# 示例测试集
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(语义接近,即使措辞不同)| 指标 | 原理 | 优点 | 缺点 | 适合 |
|---|---|---|---|---|
| BLEU | n-gram 精确率 | 快速、标准化 | 对同义词不敏感 | 翻译 |
| ROUGE | n-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:
{{
"score": 1-5 的整数评分,
"reasoning": "评分理由",
"issues": ["问题1", "问题2"]
}}
评分标准:
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"]
) * 1005. 自动化评测管线
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: ${{ secrets.OPENAI_API_KEY }}
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 pareto6.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,再逐步往上。| 成熟度 | 核心能力 | 投入 | 目标 |
|---|---|---|---|
| L0 | 无 | 0 | — |
| L1 | 测试集 + 手动跑 | 1 人 | 有基线 |
| L2 | CI/CD 自动评测 | 1 周 | 防回归 ✅ |
| L3 | 在线监控 + Shadow | 1 月 | 全链路 |
| 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 节,从"凭感觉"到"数据驱动"的评测体系升级。 │
└─────────────────────────────────────────────────────────────┘🎉 核心原则:不要凭感觉,用数据说话。评测不是上线前的一次检查,而是持续运行的质量引擎。