很多大模型项目会卡在一个“看起来不技术,但其实最工程”的问题:
改了 prompt / 换了模型 / 加了检索之后,到底是变好了,还是变差了?
如果你的回答是“感觉还行”,那你基本没法稳定迭代——因为每次上线都像抽卡。
这篇文章给一套可落地的评测体系:离线回归集(质量)+ 在线指标(体验/稳定/成本)+ 发布门禁(可回滚),并附一个最小脚手架,帮助你先把闭环跑起来。
0)先给结论(四句话)
- 没有评测就没有迭代:你无法回答“这次改动到底带来了什么变化”。
- 评测体系不是“搞一套大平台”,而是先把三件事做成工程:离线回归、线上观测、发布门禁。
- 离线评测先追求“可重复”:固定样本、固定口径、版本可追溯;自动化可以后置。
- 线上指标别只看“答案对不对”,还要看:延迟、token、错误率、拒答率、重试率、工具成功率。
1)把“评测”拆成三层(很多团队只做了 0.5 层)
1.1 离线回归集:回答“质量是否退化”
核心:给一组固定样本,每次改动都跑一遍,得到可比的结果。
1.2 在线指标:回答“体验/稳定/成本是否可控”
核心:在真实流量下记录关键字段,观察 P95 延迟、token、错误率、拒答率等。
1.3 发布门禁:回答“可不可以上线、出了事能不能回滚”
核心:上线前必须过阈值;上线后灰度观察;异常自动回滚或人工一键回滚。
工程视角:离线评测管“质量”,在线指标管“运行”,门禁管“发布”。
2)离线回归集怎么做(MVP 版:先跑起来)
你不需要一开始就 1 万条。建议分三档:
- MVP(50 条):覆盖 80% 核心场景 + 高频失败样本(先让团队能对齐口径)
- 可用(200–500 条):覆盖主要业务线 + 典型边界(上线前必跑)
- 稳定(1000+):覆盖更多长尾 + 对抗样本(持续回归)
2.1 评测集格式(jsonl)
先把数据结构定下来,后面才好扩展:
{"id":"q001","type":"chat","question":"...","gold_keypoints":["...","..."],"notes":"判分口径说明"}
{"id":"q101","type":"rag","question":"...","gold_evidence_ids":["docA#p3","docB#p1"],"gold_keypoints":["..."]}
{"id":"q201","type":"tool","question":"...","expected_tool_calls":["get_order_status"],"gold_keypoints":["..."]}
字段解释:
type:区分评测类型(chat / rag / tool),不同类型关注的指标不同gold_keypoints:答案必须覆盖的关键点(比“标准答案全文”更稳定)gold_evidence_ids:RAG 场景的证据锚点(文档ID/段落ID)notes:标注口径(避免同一条样本不同人判分不一致)
3)指标怎么选(别一上来就追求“万能分数”)
把质量拆成“可操作的指标”,你才能定位问题在哪。
3.1 通用质量指标(chat/rag/tool 都适用)
| 指标 | 含义 | 什么时候用 |
|---|---|---|
| keypoint 覆盖率 | 必要要点是否覆盖 | 适合多数业务问答 |
| 严重错误率 | 是否出现事实性错误/误导决策 | 上线门禁必备 |
| 拒答率 | 该拒答是否拒答、该回答是否误拒 | 有合规/安全要求时 |
3.2 RAG 专属指标(检索与引用)
| 指标 | 含义 | 典型用途 |
|---|---|---|
| hit@k | TopK 是否命中正确证据 | 先评检索再评生成 |
| cite_acc | 引用是否真的支撑结论 | 防止“引用看起来像” |
3.3 Tool/Agent 专属指标(工具调用)
| 指标 | 含义 |
|---|---|
| tool_success_rate | 工具是否成功执行(含超时/参数错误) |
| retry_rate | 重试次数/重试后成功率 |
| fallback_rate | 失败后是否走了降级策略 |
经验:先把“严重错误率”压下去,再谈平均分。
4)最小评测脚手架(先能跑回归,再逐步自动化)
下面用 OpenAI SDK 的兼容写法演示统一调用入口(你通常只需要改 base_url、api_key、model)。
示例参数(以支持 OpenAI 协议的推理服务为例,如 147AI 等平台):base_url=https://147ai.com/v1,
端点 POST /v1/chat/completions,
鉴权 Authorization: Bearer <KEY>(以控制台/文档为准)。
4.1 统一调用 + 记录 token/延迟
from openai import OpenAI
import json, time
def call_llm(base_url: str, api_key: str, model: str, messages: list[dict]) -> dict:
client = OpenAI(api_key=api_key, base_url=base_url)
t0 = time.time()
resp = client.chat.completions.create(model=model, messages=messages)
latency_ms = int((time.time() - t0) * 1000)
usage = getattr(resp, "usage", None)
return {
"text": resp.choices[0].message.content,
"latency_ms": latency_ms,
"usage": usage.model_dump() if usage else None,
}
4.2 LLM-as-judge(MVP:用 rubric 先做“可重复”的打分)
注意:自动评测不是银弹,但它能显著减少人工成本。
推荐做法:用 rubric(评分细则)约束判分,并保留人工抽检。
JUDGE_RUBRIC = """你是评测员。按以下规则输出 JSON:
1) keypoint_covered: 覆盖了多少 gold_keypoints(0~N)
2) critical_error: 是否存在严重事实错误(true/false)
3) rationale: 用一句话说明原因
只允许输出 JSON。"""
def judge_answer(base_url: str, api_key: str, model: str, question: str, answer: str, gold_keypoints: list[str]) -> dict:
messages = [
{"role": "system", "content": JUDGE_RUBRIC},
{"role": "user", "content": json.dumps({
"question": question,
"answer": answer,
"gold_keypoints": gold_keypoints,
}, ensure_ascii=False)}
]
out = call_llm(base_url, api_key, model, messages)
return {"judge_raw": out["text"], "latency_ms": out["latency_ms"], "usage": out["usage"]}
实战建议:judge 模型与被测模型尽量分开(避免同源偏差),并对 judge 输出做 JSON 校验与重试。
4.3 跑一次回归(eval.jsonl → results.jsonl)
def run_eval(eval_path: str, out_path: str, base_url: str, api_key: str, model_under_test: str, judge_model: str):
rows = [json.loads(l) for l in open(eval_path, "r", encoding="utf-8")]
outs = []
for r in rows:
q = r["question"]
messages = [
{"role": "system", "content": "你是严谨的技术助手。请按要点回答。"},
{"role": "user", "content": q},
]
ans = call_llm(base_url, api_key, model_under_test, messages)
judge = judge_answer(base_url, api_key, judge_model, q, ans["text"], r.get("gold_keypoints", []))
outs.append({
"id": r.get("id"),
"type": r.get("type"),
"answer": ans,
"judge": judge,
})
with open(out_path, "w", encoding="utf-8") as f:
for o in outs:
f.write(json.dumps(o, ensure_ascii=False) + "\n")
5)在线指标怎么做(先把日志字段打全)
线上最容易踩坑的是:出了问题你不知道是“检索坏了、模型变了、还是 prompt 变了”。
所以建议把这些字段作为最低标准(可按你系统实际调整):
| 字段 | 用途 |
|---|---|
| request_id / trace_id | 追踪一次请求全链路 |
| user_id(脱敏)/ tenant_id | 分群分析、回放 |
| model / model_version | 模型变更归因 |
| prompt_version | prompt 回滚/对比 |
| retrieval_k / hit@k(如有) | RAG 归因 |
| tool_calls / tool_success | 工具链路稳定性 |
| latency_ms(P50/P95) | 体验指标 |
| input_tokens / output_tokens / total_tokens | 成本与限流 |
| error_code / retry_count / fallback | 稳定性与降级 |
最小要求:任何线上变更都必须能通过版本字段回溯。
6)发布门禁怎么定(能上线、能回滚、可解释)
推荐一个可执行的门禁模板:
6.1 上线前(离线回归门禁)
- 严重错误率不升高(或低于阈值)
- keypoint 覆盖率不下降(或提升)
- 成本/延迟不超过预算(token 与 P95)
6.2 上线后(灰度 + 监控)
- 小流量灰度(例如 1%→5%→20%)
- 监控异常自动告警:P95 延迟、错误率、拒答率、成本突增
- 必须有回滚开关:
model/prompt_version/retrieval_onoff
7)常见失败模式(以及你应该先修哪一层)
- 离线很好,线上很差:线上分布不同、真实数据更脏 → 补回归样本 & 做分群评测
- 线上偶发“昨天还行今天不行”:缺版本字段/缺回滚 → 先补观测与发布门禁
- 平均分提升但用户更不满意:指标没对齐业务(只看“像不像”,没看“能不能用”)→ 重写 keypoints 与严重错误定义
8)你可以直接照抄的“第 1 周评测落地清单”
- 收集 50 条真实问题(线上日志/客服/销售)
- 每条写 3–5 个
gold_keypoints(不要写长答案) - 定义“严重错误”口径(上线门禁要用)
- 跑通评测脚手架(产出
results.jsonl) - 线上打齐版本字段(model/prompt/retrieval)
- 设置灰度与回滚开关
2774

被折叠的 条评论
为什么被折叠?



