LLM-as-a-Judge 评估:11 种类型与「上下文即真相」
30 秒导读: OpenLIT 用「一个 LLM 当判官(judge)去评审另一个 LLM 的回答」。判官引擎跑在服务端,Python SDK 只是个薄 HTTP 客户端——把 prompt/response 发过去,收回一组结构化评分。判官被提示词硬性约束:Contexts 区块永远是真相,哪怕它和现实矛盾;并且每发现一个
[X evaluation context]块,就产出恰好一个评估对象。内置 11 种评估类型(幻觉、偏见、毒性……),默认只开前三种。
本章讲清四件事:评估引擎在哪、三条运行入口怎么分、判官提示词为什么这么设计、人工打分怎么进来。规则如何把 trace 属性映射成 context,留给 06-rule-engine。
1. 这是什么(零基础也能懂)
一句话定义: LLM-as-a-Judge = 拿一个 LLM 当考官,给另一个 LLM 的输出打分,判断它有没有幻觉、偏见、毒性等问题。
解决什么问题: 你上线了一个聊天机器人,每天几万条回答。你没法人工一条条看「这条有没有胡编」。于是让一个模型自动读每条「问题 + 回答(+ 参考资料)」,输出「幻觉 0.8 分,判定:是」这样的结构化结论,再进面板统计。
它能做什么:
- 内置 11 种评估维度(幻觉 / 偏见 / 毒性 / 相关性 / 忠实度 / 安全 / 指令遵循 ……)。
- 三种运行方式:SDK 离线批量、Cron 定时自动、面板手动单条。
- 支持多家模型厂商当判官(OpenAI / Anthropic / Google / Mistral / Cohere / Groq ……)。
- 人工也能补一个「反馈分」(用户点了赞还是踩)。
用起来什么样——SDK 侧一行调用,评审逻辑全在服务端跑:
import openlit
# prompt/response 发给 OpenLIT 服务端的判官引擎,收回结构化评分
result = openlit.eval(
prompt="法国的首都是哪?",
response="法国的首都是柏林。",
contexts=["法国的首都是巴黎。"], # 这就是「真相」
)
# result.evaluations → [{type:"Hallucination", score:0.9, verdict:"yes", ...}]
一句话直觉: 把它当成「给 AI 输出配的自动质检员」。质检标准不是质检员脑子里的常识,而是你递给它的那份参考资料(Contexts)——资料说柏林是首都,它就得按柏林判。
本节不碰底层。记住一点:判官是个 LLM,标准是你喂的 Contexts。
2. 顶层全景(它大概怎么转)
怎么读这张图: 左边三种触发方式殊途同归,都汇进服务端的判官引擎;判官用 Vercel AI SDK 调真实厂商模型,吐 JSON,落 ClickHouse。SDK 自己不做任何评审。
触发方(客户端 / 定时 / UI) OpenLIT 服务端(判官引擎)
───────────────────────── ──────────────────────────────
openlit.eval() ───────┐
openlit.eval_batch() ─┼─HTTP─▶ /api/evaluation/offline ─┐
openlit.get_eval_types()┘ (runOfflineEvaluation) │
├─▶ runEvaluation()
Cron 定时 ────────────▶ /api/evaluation/auto ────────────┤ (Vercel AI SDK
(autoEvaluate → …ForTrace) │ generateText,
Dashboard 按钮 ───────▶ setEvaluationsForSpanId ──────────┘ temperature=0)
(…ForTrace) │
▼
LLM 厂商(OpenAI/Anthropic/…)
│ 返回 JSON 数组
▼
解析 → ClickHouse eval 表落库
部件一句话职责:
| 部件 | 干什么 | 在哪个文件 |
|---|---|---|
| SDK 离线客户端 | 组 payload、POST、解析、重试、终端摘要 | sdk/python/src/openlit/evals/offline.py:130 run_eval |
| SDK 结果模型 | Pydantic 定义 OfflineEvalResult 等类型 | sdk/python/src/openlit/evals/offline_types.py:43 |
| 离线路由 | 鉴权 + 取配置 + 调 runOfflineEvaluation | src/client/src/app/api/evaluation/offline/route.ts:12 |
| 判官引擎(核心) | 拼系统提示、调厂商模型、解析结果 | src/client/src/lib/platform/evaluation/run-evaluation.ts:139 runEvaluation |
| 评估编排 | 组装 context、跑评审、落库(离线/自动/手动共用) | src/client/src/lib/platform/evaluation/index.ts:732 runOfflineEvaluation |
| 类型定义 | 11 种内置类型 + 默认开关 | src/client/src/constants/evaluation-types.ts:10 EVALUATION_TYPES |
| 类型 context | 每种类型的默认判官指令 | src/client/src/constants/evaluation-type-contexts.ts:27 |
主线走一遍(高层): 请求带 prompt/response(可选 contexts)→ 服务端取该项目的评估配置(哪些类型开着、用哪个判官模型)→ 把「各类型的指令块 + 规则引擎/用户给的真相」拼成一大段 Contexts → 塞进系统提示 → 调判官模型 → 判官对每个类型块产出一个评分对象 → 落库 + 回传。
3. 三条运行入口(offline / auto / manual)
评审只有一个引擎,但有三种把活儿喂进去的方式。它们最终都走到 runEvaluation(判官那一下),区别在谁触发、同步还是批、结果打 什么 source 标记。
| 入口 | 触发方 | 服务端函数 | source | 同步? | 用的 spanId |
|---|---|---|---|---|---|
| offline | SDK 走 HTTP | runOfflineEvaluation | offline_sdk | 是,当场返回 | offline_<uuid>(合成) |
| auto | Cron 定时 | autoEvaluate → getEvaluationConfigForTrace | auto | 否,批处理 | 真实 trace 的 SpanId |
| manual | 面板按钮 | setEvaluationsForSpanId → getEvaluationConfigForTrace | manual | 是,单条 | 真实 trace 的 SpanId |
offline(SDK 主路径): run_eval 组 payload POST 到 /api/evaluation/offline,带一次 401/429/5xx 的简单重试(sdk/python/src/openlit/evals/offline.py:180 的 for attempt in range(2))。它不绑定任何已有 trace——服务端用合成的 offline_<uuid> 当 spanId 落库(src/client/src/lib/platform/evaluation/index.ts:870)。
auto(定时批): autoEvaluate 从 ClickHouse 捞出「操作类型在白名单内、且还没有 source='auto' 评估」的 trace,逐条评审。注意它用 LEFT JOIN 排除已评过的,但不因人工反馈而跳过(src/client/src/lib/platform/evaluation/index.ts:573-585 注释与 SQL)。
manual(面板单条): 用户在某条 trace 上点「评估」,setEvaluationsForSpanId 取该 span 的 prompt/response 现场评。auto 和 manual 共用 getEvaluationConfigForTrace(index.ts:404),只是 source 参数不同。
别混进来的第四个路由——/api/evaluation/llm/[evalType]: 名字像「跑评估」,其实是分析读接口。它调 getEvaluationDetectedByType(index.ts:668),对 ClickHouse 里 verdict='yes' 的记录按类型计数,给面板画「本周检出多少条幻觉」。它不触发任何判官调用。别被路径名骗了。
4. 判官引擎:一次评审怎么跑
这是整章工程含量最高的一支,全在 run-evaluation.ts。分三步:选模型 → 拼系统提示 → 调用并解析。
4.1 多厂商适配(getModel)
判官可以是任意一家的模型。getModel 是个 provider→构造器的 switch,把 Vercel AI SDK 的各家 client 统一成一个可调用的 model 实例(src/client/src/lib/platform/evaluation/run-evaluation.ts:68 getModel):
// run-evaluation.ts:70 —— 按 provider 分派;OpenAI 兼容的厂商复用 createOpenAI + baseURL
switch (mappedProvider) {
case "openai": return createOpenAI({ apiKey })(model);
case "anthropic": return createAnthropic({ apiKey })(model);
case "google": return createGoogleGenerativeAI({ apiKey })(model);
case "groq": return createOpenAI({ baseURL: "https://api.groq.com/openai/v1", apiKey })(model);
// … mistral / cohere / perplexity / deepseek / xai / together / fireworks
}
巧点:凡是 OpenAI 兼容协议的厂商(Groq/DeepSeek/xAI/Together/Fireworks/Perplexity),都靠 createOpenAI 换个 baseURL 搞定,不必各写一个 SDK。PROVIDER_MAP(run-evaluation.ts:13)只做一个别名:gemini → google。