评测体系怎么做:离线回归集 + 在线指标 + 发布门禁(含最小评测脚手架)

2025博客之星年度评选已开启 10w+人浏览 3.5k人参与

很多大模型项目会卡在一个“看起来不技术,但其实最工程”的问题:

改了 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@kTopK 是否命中正确证据先评检索再评生成
cite_acc引用是否真的支撑结论防止“引用看起来像”

3.3 Tool/Agent 专属指标(工具调用)

指标含义
tool_success_rate工具是否成功执行(含超时/参数错误)
retry_rate重试次数/重试后成功率
fallback_rate失败后是否走了降级策略

经验:先把“严重错误率”压下去,再谈平均分。


4)最小评测脚手架(先能跑回归,再逐步自动化)

下面用 OpenAI SDK 的兼容写法演示统一调用入口(你通常只需要改 base_urlapi_keymodel)。
示例参数(以支持 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_versionprompt 回滚/对比
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)
  • 设置灰度与回滚开关
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值