AI 评分功能实现方法详解(AI 大模型评测平台的 LLM-as-Judge 架构)

给模型答案打一个靠谱的分数,比想象中难得多——规则匹配太死,reward model 又养不起。评测平台的价值就压在这一环上:分打得不准,整个平台的结论都站不住。我最初试过两套方案,一是纯规则匹配,只能判断”有没有提到关键词”这类硬指标;二是自己训练 reward model,成本和维护负担都太重。反复权衡之后,我改用 LLM-as-Judge(用大模型当裁判)做 AI 评分,并引入多评委交叉验证来对抗单一评分员偏差。这套架构跑通后,评测结果的可信度和稳定性都上了一个台阶,下面把完整实现拆开讲。

一、为什么用 LLM 当裁判

先回到最基础的选型问题。评测平台要打分,传统做法无非两种,但都有硬伤:

  1. 人工评分:慢、贵、不可复制。1000 个答案要 5 个评估员 1 周。
  2. 规则评分:只能评”是否包含关键词”这类硬指标,评不了”代码质量””创意程度”。

规则评分看着省钱,实际写规则的成本一点都不低,评测维度一变,规则就得跟着重写。人工评分质量高,但规模一上来就扛不住,速度、成本、一致性全都不可控。

LLM-as-Judge 用大模型读答案+参考答案,按评分标准给分:

  • 速度:5 秒一条;
  • 成本:约 0.001-0.01 元/次;
  • 一致:相同输入给相同输出(同温度下);
  • 可解释:能输出”扣分原因”。

这四条正好补齐了前两种方案的所有短板,所以成为评测系统的默认选择。当然,LLM-as-Judge 也不是没有代价,它引入了一层不确定性——评委本身也是模型,也会犯错。所以后面的架构里必须带上多评委交叉和人工兜底,这套组合拳才是它能在生产环境立足的根本。

二、整体架构

方案定了,接下来是架构。单个评委模型打分有一个隐患:模型的偏好会影响分数,所以我设计了”多评委 + 统计聚合”的架构,整体链路如下:

用户提交答案 → 评分服务 → 选 3 个评委模型
                          ↓
                    评委 A 独立打分 ─┐
                    评委 B 独立打分 ─┼→ 取中位数 + 算方差
                    评委 C 独立打分 ─┘         ↓
                                       方差大 → 触发人工复核
                                       方差小 → 入库最终分

评委选 3 个来自不同家族的模型,避免”自我偏好”。这里的关键点是”不同家族”——同一个厂商的模型偏好趋同,选它们当评委等于白选。

三、评分 Prompt 设计

评委模型本身没有内置的”评分标准”,分数全靠 prompt 喂进去。Prompt 设计直接影响打分质量,平台用统一的”角色+标准+格式”四段式,把规则说清楚:

# 角色
你是一名资深的 AI 答案质量评审员,擅长从准确性、完整性、可用性评估答案质量。

# 评分标准
- 9-10:与参考答案一致,覆盖所有关键点
- 7-8:整体正确,遗漏次要细节
- 5-6:方向正确但有明显错误
- 3-4:偏离主题
- 0-2:与问题无关

# 输出格式(严格 JSON)
{
  "score": 8,
  "dimensions": {"accuracy": 9, "completeness": 7, "usability": 8},
  "issues": ["未提及 X 的边界条件"],
  "summary": "答案可用但 X 场景不完整"
}

# 待评估内容
问题:{question}
参考答案:{reference}
待评答案:{candidate}

“输出格式(严格 JSON)”这一条特别重要。一开始我们只要求”输出 JSON”,结果模型经常在 JSON 前后加解释文字,解析层只能做容错处理,后来才改成现在这样强调严格格式,解析失败率降了下来。

另一个容易被忽略的点是评分标准的颗粒度。标准分得太粗,评委给出的分数会集中在少数几个档位,区分度不够;分得太细,评委又容易因为理解偏差互相矛盾。我们现在用的 0-10 档是在几轮试验后定下来的,既保住了区分度,又没让评委模型频繁摇摆。

四、评分服务核心实现

核心服务的实现思路是”并行打分 + 中位数聚合 + 方差判定”,代码骨架如下:

@Service
public class JudgeService {
    @Autowired private List<JudgeModel> judges;  // 注入 3 个评委
    
    public JudgeResult score(ScoringRequest req) {
        // 1) 并行调用 3 个评委
        List<JudgeResult> results = Flux.fromIterable(judges)
            .flatMap(j -> j.scoreAsync(req).subscribeOn(Schedulers.boundedElastic()))
            .collectList()
            .block();
        
        // 2) 取中位数
        List<Integer> scores = results.stream()
            .map(JudgeResult::getScore).sorted().toList();
        int median = scores.size() % 2 == 1
            ? scores.get(scores.size() / 2)
            : (scores.get(scores.size()/2 - 1) + scores.get(scores.size()/2)) / 2;
        
        // 3) 算方差
        double variance = variance(scores);
        if (variance > VARIANCE_THRESHOLD) {
            log.warn("评分分歧大 触发人工复核: {}", scores);
            reviewQueueService.push(req);
        }
        
        // 4) 汇总多维分
        Map<String, Double> dimAvg = averageDimensions(results);
        return new JudgeResult(median, dimAvg, results, variance);
    }
}

三个评委是并行调用的,串行的话 3×5 秒,一条评分要等 15 秒,并行后压到 5 秒左右。judges 用 List 注入,意味着加评委、换评委都不需要动这段代码,扩展成本很低。

五、为什么用多评委交叉

单评委一定会出问题,这是我们在灰度阶段用真实数据验证过的:

  • GPT-4 评 GPT-4 答案普遍偏高(自我偏好);
  • Claude 评中文答案比 GPT-4 严(语言偏见);
  • 国产模型评代码题时严于评创意题(领域偏见)。

三类偏见在单评委下无解,多评委交叉才能对冲:

评委 优势 劣势
GPT-4o 综合能力强 自我偏好、成本高
Claude-3.5 逻辑严谨 偶有 over-critique
DeepSeek 中文理解好 创新性评价保守

取中位数比均值更稳健(避免被极端值拉偏)。方差超阈值入人工复核队列。实际跑下来,三方分歧大的用例正好集中在”答案边界模糊”的场景,人工复核介入的价值很高。

还有一层考虑是评委的错位分布。三个评委如果能力高度同质,交叉验证就没有意义;刻意选不同厂商、不同规模的模型,本质上是在用模型的多样性对冲彼此的盲区。

六、方差阈值的设定

方差阈值定多少,直接决定人工复核的量。定太低,人工复核泛滥;定太高,分歧大的评分直接入库,谁也说不清准不准。平台用历史数据训练阈值:

// 历史数据:相同答案不同评委分数的方差分布
// 90% 的情况 variance < 1.5
// variance > 1.5 视为显著分歧
private static final double VARIANCE_THRESHOLD = 1.5;

1.5 这个值不是拍脑袋定的,是从上线后一个月的数据里统计出来的 P90。阈值还会动态调整:每月统计一遍平台整体方差分布,把 P90 当新阈值,随着评委模型升级迭代,阈值也跟着修正。

七、评分成本与降级

3 个评委 × 1 次评分 = 3 次 LLM 调用,成本是单评委的三倍。评测平台天天跑批量任务,这笔账必须算清楚,平台用以下策略控本:

策略 收益
短答案只调 1 个评委 -60% 成本
高置信度(3 评委方差 < 0.5)结果直接入库 加快进度
低优先级任务用小模型 -50% 成本
评委之间结果高度一致时跳过复核 -90% 人工成本

核心逻辑是”按需降级”:不是所有答案都值得花三倍成本去评。

// 短答案(< 200 字)单评委
if (req.getCandidate().length() < 200) {
    return singleJudge.score(req);
}

短答案直接走单评委,长答案、高价值用例才走完整链路。这套降级策略上线后,整体评分成本降了差不多一半,长答案场景的评分质量没有明显变化。

八、与 Side-by-Side 的衔接

评测平台还有 SxS(Side-by-Side)模式,多个模型针对同一个问题出答案,这时候正好复用评分服务:

public SxSResult runSideBySide(EvalSubmitRequest req) {
    // 1) 多模型并行出答案
    List<Answer> answers = parallelCall(req.getModels(), req.getQuestion());
    
    // 2) 用评委给每个答案打分
    List<ScoredAnswer> scored = answers.stream()
        .map(a -> new ScoredAnswer(a, judgeService.score(...).getScore()))
        .sorted(Comparator.comparingInt(ScoredAnswer::getScore).reversed())
        .toList();
    
    return new SxSResult(scored);
}

SxS 模式默认开 3 评委,Prompt Lab 模式默认 1 评委(用户自费)。同一个评分服务两处复用,不用为每种模式各写一套打分逻辑。

九、踩过的坑

上线过程中踩的坑比预想的多,挑五个影响大的写出来:

  • JSON 解析失败:模型偶尔输出多余解释文字,prompt 加”严格遵守 JSON 格式”还不够,要在解析层加”提取首个 JSON 块”降级。
  • 评委 token 限额:长答案(5000 字)单次评分要 1500+ token,三个评委就是 4500 token,要选支持 8k+ 上下文的模型。
  • 评分慢拖慢整批:SxS 跑 50 条用例 × 3 评委 × 5 秒 = 12.5 分钟。评委独立可并行,最终聚合即可。
  • 评分员泄题:让 GPT-4 评 GPT-4 答案且 prompt 包含原题,可能触发”基于训练数据的先验”而不是真”基于答案”。解决办法:选跨家族评委 + 提示词里强调”只看候选答案”。
  • 评分结果漂移:同一答案隔一天评分差 1-2 分是正常的,温度设 0(greedy)可降但不能消除。

十、可解释性增强

评分不能只给一个数字,用户要知道”为什么是这个分”。平台在评分结果里带上扣分原因,让用户能照着改进答案:

{
  "score": 7,
  "dimensions": {"accuracy": 9, "completeness": 5, "usability": 8},
  "issues": [
    "未提及异常处理方案",
    "未给出性能数据"
  ],
  "summary": "答案整体可用,但缺关键细节"
}

前端在评分卡里把 issues 渲染成红字列表,用户一眼知道”我的答案缺什么”。这个设计让评分从”裁判判罚”变成”教练指导”,用户粘性提升了不少。

到这里,LLM-as-Judge 从选型、架构、prompt、实现到成本控制、可解释性的完整链路就成型了。评分这件事没有绝对正确,但多评委加统计聚合加人工兜底这套组合,足以让平台的大多数评分站得住脚。

常见问题(FAQ)

Q1:为什么不用 reward model?

Reward model 训练成本高、更新慢、跨模型泛化差。LLM-as-Judge 零训练、跨模型可换,业界事实标准。

Q2:评分稳定吗?

设温度 0 + 多评委中位数,稳定度可达 90%+。需要 100% 稳定只能上人工。

Q3:评委能换吗?

能。JudgeModel 接口化,新增评委写一个实现,Spring 自动注入,配置即可生效。平台已支持 6 个评委可选。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 qiqicto@qq.com 举报,一经查实,本站将立刻删除。
赞 (0)
其AI的头像其AI普通用户

相关推荐

返回顶部