LLM 评测与运维:从质量指标到灰度控制
0. 元信息
- 主题路径:
docs/topics/llm-applications/subtopics/llm-eval-and-ops/ - 父主题:
llm-applications - 适合对象:已经做出 LLM 原型、希望稳定上线的开发者
- 建议周期:1~2 周
- 前置知识:无额外前置(继承父主题依赖)
- 最终目标:能用多种评测方法衡量质量,监控延迟/错误/成本,并安全执行灰度与回滚
1. 学习路线
评测集与标注规范
→ 人工评测
→ A/B 测试与 LLM-as-judge
→ 质量/延迟/错误监控
→ Token 成本控制
→ 灰度、门槛与回滚
2. 阶段周数分配(1~2 周,每天 1.5~2 小时)
| 阶段 | 1 周方案 | 2 周方案 | 备注 |
|---|---|---|---|
| 1. 评测设计 | 0.25 周 | 0.5 周 | 任务定义、样本、rubric |
| 2. 多方法评测 | 0.25 周 | 0.5 周 | 人工 / A/B / LLM-as-judge |
| 3. 监控 | 0.25 周 | 0.5 周 | 质量、延迟、错误指标 |
| 4. 成本与灰度 | 0.25 周 | 0.5 周 | 预算、canary、回滚 |
分阶段概述:
- 第 1 周(1 周方案):完成评测集 + 多方法评测 + 监控面板;用
claude-haiku-4-5+claude-sonnet-4-5对比一组 judge 结果。 - 第 1~2 周(2 周方案):补成本控制 + canary / 回滚方案;末尾做一次”超阈值自动停止”的失败注入。
1 周方案每天 2 小时;2 周方案每天 1.5 小时,多留 2 天做灰度方案与回滚演练。先建评测集再谈上线——没有评测集的灰度是盲飞。
3. 阶段表
| 阶段 | 核心知识 | 实践产出 | 可观察学会标准 |
|---|---|---|---|
| 1. 评测设计 | 任务定义、样本、标签、人工 rubric | 30 条评测集 | 每条样本有期望行为和判定理由 |
| 2. 多方法评测 | 人工、A/B、LLM-as-judge、偏差校准 | 评测脚本与报告 | 能解释三种方法的适用边界,人工抽样校准 judge |
| 3. 监控 | 质量、成功率、P50/P95、首 token、上下文、拒答 | 指标面板和告警 | 能从 trace 定位失败阶段 |
| 4. 成本与灰度 | token 预算、缓存、路由、采样、canary、回滚 | 成本表与灰度方案 | 超门槛自动停止,能复现回滚决策 |
4. 第一周(每天 1.5~2 小时)
环境约定:本子主题统一使用 Python 3.11+ 与
python -m venv .venv;评测集与 trace 用 JSONL 保存(datasets/eval.jsonl/traces/<date>.jsonl);指标聚合用纯 Python(参考 §9.7.1);面板用 Grafana / Prometheus 或本地 Streamlit。API key 通过export ANTHROPIC_API_KEY=...共享 .env 模板:父主题attachments/.env.example含 Anthropic / OpenAI / Voyage / Qdrant / Ollama / OTel / Langfuse 全套变量;本地cp ../../attachments/.env.example .env后填值,.env加入.gitignore,加载用python-dotenv。禁止把真实 key 提交到仓库或写入 trace / 日志。 注入。
| 日 | 任务 | 当天交付 | 自检 |
|---|---|---|---|
| Day 1 | 写 10 条评测样本(覆盖 happy / 拒答 / 长尾 / 越权 4 类),含 input / expected / rubric / tags 字段 | datasets/eval.jsonl(10 行) | 每条样本能用一句话说清”期望行为”与”判定理由” |
| Day 2 | 跑评测脚本:把线上 / 本地模型回答写入 runs/<run_id>/answers.jsonl,按 rubric 算成功率 | notes/eval/day2.md + eval/runner.py | 10 条样本跑完;成功率按任务分桶统计 |
| Day 3 | 引入 LLM-as-judge:用 claude-sonnet-4-5 做盲评(隐藏模型名),输出 factuality / completeness / reason | notes/eval/day3.md + judge prompt | judge 输出的 JSON 可解析;reason 不为空;与人工 rubric 对比差异 ≤ 2 条 |
| Day 4 | 加 A/B 测试:同一评测集跑两个 prompt / 两个模型,记录 run_id / arm / score;按用户分桶 | notes/eval/day4.md + 一组 A/B 结果 | 两个 arm 样本量均衡;分流固定可复现;指标差异可解释 |
| Day 5 | 建监控:记录 latency_ms / tokens / error_type 到 traces/<date>.jsonl;写 LatencyWatch(P50/P95/P99) | notes/ops/day5.md + metrics/latency.py | 200 条样本聚合后 P50/P95/P99 数字合理;长尾样本未被平均掩盖 |
| Day 6 | 加成本监控:CostGuard 按分钟 / 按请求记录 spend,超阈值熔断;写 notes/cost/<date>.md | notes/ops/day6.md + metrics/cost.py | 模拟流量把单位成本推到阈值之上时,CostGuard.charge() 返回 False |
| Day 7 | 评测 + 监控 + 成本综合跑:把前 6 天产物串成一条流水线,输出 report.md;做一次失败注入(模型 5xx / 延迟飙升 / 成本超限) | notes/ops/day7.md + report.md | 失败注入时监控告警可见;超成本时 CostGuard 拒收新请求;trace 可定位失败阶段 |
第一周复盘要求
每日把样本、prompt、judge 结果、监控指标写到 notes/eval/ 与 notes/ops/ 对应日期的 md;Week 1 结束前用 30 分钟复盘:哪些 rubric 容易和 judge 偏差?哪些指标其实没区分度?
5. 阶段通用验收
- 不看答案独立重写评测集 schema 与指标聚合脚本;
- 用自己的话解释人工 rubric、A/B、LLM-as-judge 的适用边界与偏差来源;
- 画出”评测 → 指标 → 灰度 → 回滚”四阶段的依赖图;
- 测试样本不足、模型 5xx、judge 输出非法、P95 飙升、成本超限 5 类边界;
- 准备至少 3 组 A/B 实验并贴出实际报告;
- 记录成功率、P50/P95/P99、首 token、token 用量、单位请求成本;
- 能修改已有评测(加 rubric / 加指标 / 改告警阈值)并复现失败注入。
交付存放:第 3 项的图、第 5 项的报告、第 6 项的指标统一存到
notes/eval//notes/ops/或 README 对应章节,便于复盘与综合项目引用。
6. 最终验收(学完 1~2 周后)
- 完成 ≥ 30 条评测样本,分 ≥ 3 个任务(QA / 摘要 / 工具调用),每条带
input/expected/rubric/tags/gold_source_ids(如适用); - 至少 3 种评测方法(人工 / A/B / LLM-as-judge)同时产出报告,并报告 judge 与人工的偏差率;
- 监控面板覆盖:成功率、P50 / P95 / P99、首 token、错误分类、单位请求成本;
- 成本控制:每分钟 / 每请求预算可配;超阈值自动拒绝新请求;
- 灰度与回滚方案:canary cohort 固定、超阈值自动回滚、回滚决策可复现;
- 完成 1 个综合项目(见 §7),含 README、失败注入测试与回滚演练;
- 能用 15 分钟讲清楚评测设计、指标、监控、灰度与回滚之间的数据流与权衡。
API key 配置与回退方案
- 首选:Anthropic API(
claude-haiku-4-5做被测 / judge 模型用claude-sonnet-4-5或同家族更大模型),配置方式:export ANTHROPIC_API_KEY=...。 - 回退 1(无 API key):本地
ollama run qwen2.5:7b+nomic-embed-text(如需 Embedding)做被测与 judge;judge 选一个比被测大 1~2 档的开源模型,避免 judge 与被测同档导致偏差难以解释。 - 回退 2(纯离线):评测只跑人工 + 规则评测(如引用命中率、关键词覆盖率),指标聚合脚本仍可用;这一路径对评测 schema、监控指标、成本控制同样有效,但不验证 LLM-as-judge 的偏差校准。
- 记录:每次实验保存
{model, version, prompt_hash, judge_model, rubric_version, threshold, run_id}到notes/runs/<date>.json,便于复现。
7. 综合项目
首选:LLM 应用质量门禁(必做:评测集 + 多方法评测 + 监控面板 + 成本限流 + canary / 自动回滚)。
备选:A/B 测试平台原型(必做:用户分桶 + 多 arm 路由 + 指标聚合 + 报告生成)。
任何综合项目都必须包含:
- 需求说明:要解决的运维问题、用户故事、输入输出约定(含超阈值自动回滚分支);
- 数据流与模型选择理由:为什么选这套评测 / 监控 / 灰度方案;记录权衡;
- 算法与策略说明:rubric 设计、A/B 分桶、canary 阈值、回滚触发条件;
- 模块化源码:
eval//metrics//cost//canary.py等职责单一; - 边界与安全测试:模型 5xx、judge 失败、P95 飙升、成本超限、阈值误判;
- 运行说明:
make run或清晰python -m ...命令,注明环境变量与依赖; - README:项目介绍、运行步骤、目录结构、复盘(踩过的坑、可改进点);
- 评测与复盘记录:
notes/retrospective.md(用时、难点、收获、下一步)。
notes/ 与 README 存放规范
所有”画图(依赖图 / 灰度流程)“和”贴报告”类交付物统一存放在项目根目录的 notes/ 子目录或 README 的对应章节;提交时一并带上,避免散落在聊天或临时文件里。综合项目的 notes/ 至少包含:
notes/design.md:评测 schema、指标聚合逻辑、canary 与回滚策略;notes/test.md:每组测试数据的输入、期望指标、实际指标与触发分支;notes/retrospective.md:复盘(用时、难点、收获、下一步)。
本主题贡献(Loop 6-D · llm-applications / llm-eval-and-ops)
本主题把”评测 rubric、A/B 与 canary、LLM-as-judge、成本与延迟 trace”四条独立工程线拧成一条验收闭环,输出可被父主题与下游子主题直接复用的评测管线。
3 项核心职责
- rubric + LLM-as-judge:用结构化 rubric(任务维度 + 评分等级 1~5)写评测集,judge 用更强模型(sonnet / opus)给被测模型(haiku / sonnet)打分;judge 必须给”打分理由 + 引文片段”,避免黑盒分。
- A/B + canary:用流量分桶(哈希到 bucket_id)做 A/B,每桶独立指标;canary 阈值(P95 latency / 错误率 / 成本超限)一旦触发自动回滚到上一个稳定版本。
- trace + 成本:每次推理记 trace(prompt / completion / latency / tokens / cost),按 user_id + session 聚合出 P50 / P95 / 成本曲线,异常点配告警。
4 项交付物
- rubric + 评测集源码:
eval/rubric.yaml(维度 × 等级 × 权重)+eval/dataset.jsonl(gold 输入 / 输出 / 期望维度分),用 LLM-as-judge + 人工抽检双轨打分。 - A/B + canary 控制器:
canary.py(按 bucket_id 分流 + 阈值告警 + 自动回滚),配metrics/(P50 / P95 / 错误率 / 成本)+cost/(按模型 + token 单价聚合)。 - trace + 监控配置:
tracing.py(opentelemetry 风格 trace)+ Prometheus / Langfuse 接入,每次推理写入 prompt / completion / latency / tokens / cost。 - 边界测试 + 必读交付:模型 5xx / judge 失败 / P95 飙升 / 成本超限 / 阈值误判 5 类各 1 例,配
notes/design.md(rubric + 聚合逻辑 + canary 策略)+notes/retrospective.md。
3 个验收指标
- 评测一致性:LLM-as-judge 与人工抽检一致性 ≥ 85%(Cohen’s Kappa),评测集 ≥ 100 条覆盖 5 个维度。
- canary 响应:阈值告警到自动回滚 P95 ≤ 60 秒,5 类边界(5xx / judge 失败 / P95 飙升 / 成本超限 / 阈值误判)必须命中 ≥ 3 类。
- trace 完整率 ≥ 99%,按 user_id 可聚合出 P50 / P95 / 成本曲线,异常点配告警阈值。
8. 推荐开源资料(按角色分工,避免堆链接)
| 阶段 | 角色 | 资料 | 链接 | 用法 |
|---|---|---|---|---|
| 1 | 评测设计 | Anthropic Building Evals | https://docs.anthropic.com/en/docs/build-with-claude/develop-tests | 评测集与 rubric 写法参考 |
| 1 | 评测设计 | OpenAI Evals 框架 | https://github.com/openai/evals | 看评测结构;不要直接当生产评测 |
| 2 | LLM-as-judge | Anthropic Constitutional AI | https://www.anthropic.com/research | judge prompt 与偏差校准背景 |
| 2 | A/B 测试 | 《Trustworthy Online Controlled Experiments》 | https://experimentguide.com/ | 实验设计、样本量、分桶原则 |
| 3 | 可观测性 | OpenTelemetry Python | https://opentelemetry.io/docs/languages/python/ | trace / span / metric 接入;评测系统的工程基础 |
| 3 | 监控面板 | Grafana 文档 | https://grafana.com/docs/grafana/latest/ | 仪表盘与告警配置 |
| 3 | 监控采集 | Prometheus 文档 | https://prometheus.io/docs/ | counter / histogram / summary 选型 |
| 4 | 成本与限流 | OpenAI Rate Limits | https://platform.openai.com/docs/guides/rate-limits | 限流策略参考;不绑定供应商 |
| 4 | 灰度发布 | 《Continuous Delivery》 | https://continuous.com/ | canary / blue-green / 回滚原则;书籍说明,引用即可 |
| 5 | 评测指标 | Ragas 文档 | https://docs.ragas.io/ | faithfulness / answer_relevancy 等指标;必须配合人工抽样 |
| 通识 | API 查阅 | Anthropic API 文档 | https://docs.anthropic.com/en/api/overview | 查参数、token、限制与价格 |
| 通识 | 模型查阅 | Hugging Face Hub | https://huggingface.co/docs | 查模型卡、tokenizer、推理参数 |
许可证提示:复制或参考 OpenAI Evals / Ragas 等开源仓库代码前,先打开 LICENSE 确认。OpenAI Evals 采用 MIT 许可证;Ragas 采用 Apache-2.0。默认做法是读思路后自己重写,而不是复制粘贴;GPL 类代码用于商业 / 闭源项目前请逐条阅读。
API key 安全:示例代码统一使用
os.environ["ANTHROPIC_API_KEY"]或等价方式;不要把 key 提交到仓库;本地用.env(加入.gitignore)+python-dotenv加载;CI 上用 secret 注入。trace 中的input/expected可能含敏感字段,记录前脱敏。
默认使用顺序:先用 Anthropic Building Evals / OpenAI Evals 看评测结构 → 自己写评测集 schema + 指标聚合 → 引入 LLM-as-judge 并人工校准 → 接入 OpenTelemetry + Prometheus + Grafana 做监控 → 加 CostGuard 与 canary / 回滚 → 引入 Ragas 做辅助评测 → 跑失败注入 + 复盘到 notes/retrospective.md。
9. 学习资料汇聚(v0.3 自包含)
9.1 背景与动机
LLM 输出具有概率性,传统“接口返回 200”无法代表业务成功。评测把质量变成可比较数据,运维把质量、延迟、错误和成本变成持续反馈,灰度则把上线风险控制在小范围内。
9.2 概念地图
flowchart LR
Dataset[评测集] --> Human[人工 rubric]
Dataset --> AB[A/B 测试]
Dataset --> Judge[LLM-as-judge]
Human --> Gate[质量门槛]
AB --> Gate
Judge --> Gate
App[应用 trace] --> Metrics[质量/延迟/错误/成本指标]
Metrics --> Canary[灰度]
Gate --> Canary
Canary --> Rollback[回滚]
9.3 基础知识讲解
主推 OpenTelemetry 文档(日志、指标、trace)和项目自身的标注 rubric;备查 Ragas 等 RAG 评测工具。LLM-as-judge 只能作为辅助信号,必须保留人工校准样本和评测版本。
9.4 经典问题与经典案例
| 问题 | 最简答案 |
|---|---|
| 评测集过于简单 | 增加真实失败、拒答和长上下文样本 |
| judge 偏爱长答案 | 盲化模型名,加入长度和引用控制项 |
| 平均值掩盖尾延迟 | 同时看 P50/P95/P99 和首 token |
| 成本突然升高 | 记录 input/output token、模型路由和预算 |
| A/B 混入不同流量 | 固定分流、用户分桶和实验窗口 |
| 灰度没有回滚 | 上线前定义质量、错误率和成本阈值 |
9.5 学习难点
- 概念难点:相关性、忠实性、任务成功率不是同一个指标;按任务拆指标。
- 思维难点:评测是实验设计,不是挑几个好例子截图。
- 工程难点:指标必须关联 trace、版本和输入条件,否则无法复盘。
9.6 技术标准与接口
Entity
Dataset/version、rubric、judge score、trace/span、counter、histogram、cost budget、canary cohort。
Scope
指标描述系统表现,不能代替安全审查、人工责任或业务验收;灰度控制发布暴露面,不修复根因。
Structure
掌握成功率、引用正确率、faithfulness、P50/P95、首 token、错误分类、token usage、单位请求成本和回滚阈值。
Ecosystem
OpenTelemetry 是观测基础;Prometheus/Grafana 等负责采集展示;Ragas 等负责部分评测计算。具体命名和采样策略需在项目中固定。
Depth Tiers
L0 知道指标存在;L1 看懂评测报告;L2 能建立评测集和面板;L3 能定位回归并执行灰度;L4 能设计跨版本质量门禁。本子主题要求 L3。
Source
OpenTelemetry、Prometheus、Grafana、Ragas 官方文档;版本快照日期:2026-07-28。
9.7 关键代码
9.7.1 评测集 + 指标聚合
# 9.7.1 评测集与 recall@k、MRR、引用命中率
from collections import defaultdict
from statistics import mean
def recall_at_k(retrieved: list[str], gold: list[str], k: int) -> float:
return 1.0 if any(d in gold for d in retrieved[:k]) else 0.0
def mrr(retrieved: list[str], gold: list[str]) -> float:
for i, d in enumerate(retrieved, start=1):
if d in gold:
return 1.0 / i
return 0.0
def citation_hit(citations: list[str], gold_source_ids: list[str]) -> float:
return 1.0 if any(c in gold_source_ids for c in citations) else 0.0
def aggregate(samples: list[dict]) -> dict:
bucket = defaultdict(list)
for s in samples:
bucket[s["task"]].append({
"recall@5": recall_at_k(s["retrieved"], s["gold"], 5),
"mrr": mrr(s["retrieved"], s["gold"]),
"cite": citation_hit(s["citations"], s["gold_source_ids"]),
})
return {t: {k: mean(v) for k, v in m.items()} for t, m in bucket.items()}
if __name__ == "__main__":
samples = [
{"task": "qa", "retrieved": ["c1", "c2"], "gold": ["c1"],
"citations": ["c1"], "gold_source_ids": ["c1"]},
{"task": "qa", "retrieved": ["c9", "c2"], "gold": ["c2"],
"citations": [], "gold_source_ids": ["c2"]},
]
print(aggregate(samples)) # {'qa': {'recall@5': 1.0, 'mrr': 1.0, 'cite': 0.5}}
9.7.2 P50/P95 延迟监控 + 成本限流
# 9.7.2 延迟分桶、成本预算与超限熔断
import time
from collections import deque
from threading import Lock
class LatencyWatch:
def __init__(self, window: int = 200) -> None:
self.samples: deque[float] = deque(maxlen=window)
self.lock = Lock()
def record(self, latency_ms: float) -> None:
with self.lock:
self.samples.append(latency_ms)
def percentile(self, p: float) -> float:
if not self.samples:
return 0.0
s = sorted(self.samples)
idx = int(round((p / 100.0) * (len(s) - 1)))
return s[idx]
class CostGuard:
def __init__(self, budget_usd_per_min: float, input_per_1k: float, output_per_1k: float) -> None:
self.budget = budget_usd_per_min
self.input_rate = input_per_1k
self.output_rate = output_per_1k
self.spend = 0.0
self.window_start = time.time()
def charge(self, in_tok: int, out_tok: int) -> bool:
now = time.time()
if now - self.window_start >= 60:
self.spend = 0.0
self.window_start = now
cost = in_tok / 1000 * self.input_rate + out_tok / 1000 * self.output_rate
self.spend += cost
return self.spend <= self.budget
if __name__ == "__main__":
w = LatencyWatch()
for x in [80, 90, 100, 200, 1200, 110]:
w.record(x)
print(f"P50={w.percentile(50):.0f}ms P95={w.percentile(95):.0f}ms")
9.7.3 真实可运行:Anthropic SDK 跑一个 LLM-as-judge 评分
# 9.7.3 用 Anthropic SDK 做一次 LLM-as-judge 盲评
# 运行:export ANTHROPIC_API_KEY=...; python judge_anthropic.py
import os
import json
import anthropic
client = anthropic.Anthropic()
ANSWER = "RAG 是检索增强生成。"
REFERENCE = "检索增强生成(Retrieval-Augmented Generation)"
prompt = (
"你是评分员,对候选答案相对于参考答案的事实性与完整性打分 0~5 整数。"
"若候选答案与参考答案矛盾,扣到 0;只输出 JSON:"
"{\"factuality\": 0..5, \"completeness\": 0..5, \"reason\": str}\n"
f"参考答案:{REFERENCE}\n候选答案:{ANSWER}"
)
resp = client.messages.create(
model="claude-haiku-4-5",
max_tokens=128,
messages=[{"role": "user", "content": prompt}],
)
score = json.loads(resp.content[0].text.strip())
print(score)
10. 常见误区
- 评测集只有 5 条好例子:模型在长尾上崩盘发现不了,至少 30 条 + 真实失败/拒答样本;
- LLM-as-judge 不盲化模型名:judge 偏爱自己家族的输出,盲化 + 抽人工校准是底线;
- 指标只看平均延迟:掩盖 P95 抖动,长尾用户被打挂;
- 监控只看板不看 trace:发现 P95 飙了但定位不到是 prefill 还是工具调用,先记 span;
- 成本只看日总账单:单请求成本上升发现不了,按请求记 token 与路由;
- 灰度没有”自动回滚”:阈值触发要写到部署脚本里,依赖人工盯就晚一小时;
- A/B 流量混入:按用户分桶 + 固定实验窗口,不按请求随机;
- 评测和线上指标分离:评测高分不等于线上高分,灰度期必须并跑;
- 用”成功率 99%“当质量门:99% 在百万请求里是 1 万次失败,要按业务定绝对值;
- 回滚策略写在 wiki:执行时翻文档耗时,应该沉淀在 IaC 与发布脚本里。
11. 所有知识点分类(统一规则)
- 编程语言
- 数据结构与算法
- 计算机基础
- 工程技术
- Web 与后端
- 前端与客户端
- 数据与人工智能
- 项目与职业能力
本计划归属:数据与人工智能 主 + 工程技术 辅。