Chapter 07

Evaluations:LLM-as-Judge 与自定义打分

Trace 和 dataset 是原料,评估才是把原料变成"决策依据"的那一步。本章把三种评分通道一次串起来:LLM-as-Judge、代码 evaluator、人工标注。

Langfuse 里的分数:Score

Langfuse 把所有评分统一成 Score 对象,挂在 trace 或 observation(span/generation)上:

{
  "name":      "answer_relevancy",
  "value":     0.83,            // 数值(默认), 也可以是 enum 或 boolean
  "data_type": "NUMERIC",       // NUMERIC | CATEGORICAL | BOOLEAN
  "comment":   "答案相关但遗漏了关键时间信息",
  "source":    "API" | "ANNOTATION" | "EVAL",
  "author":    "gpt-4o-judge" | "qa-zhang" | "rule:length-ok",
  "trace_id":  "trc_abc",
  "observation_id": null | "obs_xxx"
}

三种 data_type 适应不同场景:

NUMERIC
0-1 分数、BLEU、延迟 ms。趋势图、均值、分位都能直接画。最常用。
CATEGORICAL
good / mediocre / badsafe / risky。避免强行把类别塞成数字。UI 按类别分布展示。
BOOLEAN
pass / fail 类的检查。用"通过率"曲线看趋势,特别适合 guardrail。

三种评分通道

通道谁打分典型指标时机
① LLM-as-Judge裁判模型relevancy / faithfulness / tone离线 / 在线采样
② 代码 evaluator脚本长度、关键词、JSON schema、引用率离线 + 在线 100%
③ 人工标注QA / 用户真实相关性、品牌感、踩赞离线抽样 + 产品内反馈

真实项目一般三种都用。代码 evaluator 兜底便宜,LLM-as-Judge 量产可用,人工标注校准前两者。

① LLM-as-Judge

UI 内配置(最简单)

  1. Settings → LLM Connections 添加裁判模型(OpenAI/Azure/Anthropic/自建 vLLM 都行)
  2. Evaluators → New Evaluator 选模板或空白
  3. 写 prompt,用 {{input}} {{output}} {{expected_output}} 做占位
  4. 定义返回 schema:score (0-1) + reasoning
  5. 配触发范围:trace 的 tag 匹配某模式 / 某个 dataset / 线上采样率

一个 answer relevancy 的 prompt 范本:

你是一位严格的评估员。给定:

User query:
{{input}}

Model answer:
{{output}}

请从 0 到 1 评分 answer 与 query 的相关程度:
- 1.0 完全相关且回答了问题
- 0.5 相关但遗漏关键信息, 或部分跑题
- 0.0 完全无关或拒答

严格按 JSON 返回:
{"score": 0.0-1.0, "reasoning": "一句话说明理由"}

SDK 打分(自己发)

UI evaluator 是 Worker 侧跑的,方便但有延迟。要在业务流里实时出评分,直接 SDK:

from langfuse import Langfuse
import json, openai

lf = Langfuse()

def judge(trace_id: str, query: str, answer: str):
    rsp = openai.chat.completions.create(
        model="gpt-4o-mini",  # 裁判模型可以比主模型便宜
        response_format={"type": "json_object"},
        messages=[
            {"role": "system", "content": judge_prompt},
            {"role": "user", "content": json.dumps({"query": query, "answer": answer})},
        ],
    )
    result = json.loads(rsp.choices[0].message.content)
    lf.score(
        trace_id=trace_id,
        name="answer_relevancy",
        value=float(result["score"]),
        comment=result["reasoning"],
    )
裁判不是越大越好
gpt-4o-mini / claude-haiku / qwen-72b 做裁判通常够用。用 o1 / opus 当裁判会让评估成本反超被评估的生成成本。选择裁判模型的标准是和人工标注的一致性,先拿 100 条人工样本 calibrate,再决定。

Pairwise 对比(A/B 场景专用)

比较两个版本 A / B 的输出时,直接让 LLM 选"哪个更好"比单独打分稳得多——绝对分数偏差大,但相对排序相对一致:

下面两个回答哪个更好地回应了用户提问?
User: {{query}}
Answer A: {{output_a}}
Answer B: {{output_b}}

返回 JSON: {"winner": "A" | "B" | "tie", "reasoning": "..."}

注意: 评判时不要被回答长度 / 语气 / 格式干扰,只看是否正确、是否切题。

② 代码 evaluator

一大类指标根本不需要 LLM——写两行代码搞定,又快又便宜:

def evaluate_trace(trace, output: str, expected: str):
    lf.score(trace_id=trace.id, name="length_ok",
             value=1.0 if 10 <= len(output) <= 500 else 0.0,
             data_type="BOOLEAN")

    lf.score(trace_id=trace.id, name="has_citation",
             value=1.0 if "[1]" in output else 0.0,
             data_type="BOOLEAN")

    # JSON schema 合规
    try:
        schema.validate(json.loads(output))
        valid = 1.0
    except:
        valid = 0.0
    lf.score(trace_id=trace.id, name="json_valid", value=valid,
             data_type="BOOLEAN")

    # BLEU / ROUGE / 编辑距离等
    if expected:
        lf.score(trace_id=trace.id, name="rouge_l",
                 value=rouge_l(output, expected))

代码 evaluator 的优势:100% 覆盖(不用采样)、成本接近 0、结果确定。所有"客观可判定"的指标全放这里,LLM-as-Judge 只做"主观+语义"的部分。

③ 人工标注(Annotation Queue)

有两个人工标注场景:

QA 队列
专人定期审 50-200 条 trace,给真实相关性打分。用于 calibrate 裁判模型、补充 dataset expected_output。
最终用户反馈
产品界面的👍/👎按钮,点击直接发 Score 给 Langfuse。量大但噪声也大,适合长期趋势。

配置 Annotation Queue

  1. UI 进 Annotation Queues → New Queue,起名 qa-daily-review
  2. 定义 score config:比如 overall_quality: CATEGORICAL (good/mediocre/bad) + faithfulness: NUMERIC 0-1
  3. 配入队规则:每天从符合 tag 的 trace 随机抽 50 条,或挑 LLM-judge score < 0.5 的
  4. 给 QA 组配权限,他们进 UI 就能看到待标注列表,点 trace → 评分 → 下一条

用户反馈 API

# 前端拿到 trace_id, 用户点👍/👎 时:
@app.post("/feedback")
def feedback(trace_id: str, thumbs: str, comment: str | None):
    lf.score(
        trace_id=trace_id,
        name="user_feedback",
        value=1.0 if thumbs == "up" else 0.0,
        data_type="BOOLEAN",
        comment=comment,
        source="ANNOTATION",
    )

在线 vs 离线:两种运行模式

在线(生产采样)

  • Evaluator 配 "Run on production traces, sample 10%"
  • 新 trace 进来 Worker 异步打分
  • 跟趋势、告警用
  • 代码 evaluator 可以 100% 覆盖,LLM evaluator 10-20% 足够

离线(dataset 回归)

  • Evaluator 配 "Run on dataset runs"
  • CI 跑 dataset run,evaluator 对所有 run 里的 trace 打分
  • prompt/模型升级决策用
  • 100% 覆盖,没必要采样

成本控制:最容易翻车的点

LLM-as-Judge 如果 100% 打线上流量,评估成本能轻松超过生成成本。几个必备手段:

给评估模型单独设预算
Langfuse Settings 里 LLM Connection 可以配月度预算上限。超过直接停,不会炸成天文数字。生产上一定要配。

把分数转换成告警

Langfuse 支持基于 score 的通知(UI 里 Alerts 面板)。常见的几个:

本章小结