☰
LLM-as-Judge实战:大模型互评与自我批评提升生成质量
2026/10/20 20:42:10 网站建设 项目流程

做AI应用开发这段时间,我越来越频繁地遇到一个尴尬场景:模型生成的内容没人帮忙把关。人工一条条看,慢且贵,尤其是在批量评估几百条结果的时候,整个人都会被拖垮;用BLEU、ROUGE这类传统指标,又完全评不出语义质量,经常出现“字面不重叠但意思完全正确”被判低分的情况。最后我干脆把思路换成“大语言模型对大语言模型进行的批评和自我批评”——让一个大语言模型去评判另一个模型的答案,甚至让同一个模型在完成输出后,再切换成挑剔的评审者角色,回头狠批自己刚才写的东西。

这个思路听起来有点绕,但落地之后效果相当直接。一套“生成-批评-修订-再评估”的流程跑下来,文本质量能明显提升,而且评估成本只有人工的零头。这篇文章我会把整体设计思路、关键Prompt怎么写、参数怎么调、流程怎么搭建,以及我踩过的坑全部梳理一遍,适合正在做LLM应用、需要批量质控内容,或者想把“模型互评”用在自己项目里的开发者参考。

1. 思路拆解:为什么要让大语言模型互评和自我批评

1.1 传统评估手段的瓶颈

在做大语言模型应用之前,我做过一段时间传统NLP,那时候评估生成质量基本靠三件套:BLEU、ROUGE、人工打分。BLEU看的是n-gram词面重叠度,模型把“今天天气很好”改写成了“今日天晴,气温舒适”,词面完全不重合,得分直接跳水,但语义上明明是对的。ROUGE稍微好一点,主要面向摘要场景,但本质上也逃不开“词面匹配”这条死路。

人工打分呢,质量确实可靠,但问题在速度和成本。我有一段时间每天要评估两百条模型回复,三个人轮流打,打了两周之后大家开始凭感觉给分,一致性越来越差。更关键的是,人工评估没法嵌进自动化Pipeline,如果一个流程里每天要跑几千条数据,不可能每条都请人看。这就逼着我找一个能批量、低成本、还能保持一定稳定性的评估方式。

这个时候“模型评模型”的思路就很自然冒出来了。大语言模型本身具备理解复杂指令、识别逻辑漏洞、判断语义相关性的能力,把它当裁判来用,正好补上传统指标和人工评估之间的空档。

1.2 批评机制的本质:LLM-as-Judge

所谓“大语言模型对大语言模型的批评”,本质上是目前实践中被反复验证的LLM-as-Judge模式,只是把“打分”这一步往前推得更深,不仅打分,还要求给出具体的批评意见、问题清单和修改方向。

它跟传统评估最大的区别在于:传统指标输出一个数值,模型评论输出一段结构化的、可执行的反馈。比如你问一个批评式LLM:“这段回答哪里有问题?”它不会只说“0.78分”,它会告诉你“第二段关于XX的说法缺少依据”“结尾没有回应用户真正想问的XX问题”“建议补充一个具体的操作步骤”。这种反馈密度,人工评审可以做到,但做不到一次调用才几厘钱。

我做项目时发现,一个完整的批评环节至少包含三个层次:

  • 第一层:发现问题,指出错误、遗漏、逻辑断裂。
  • 第二层:给出判断依据,说明为什么这是问题。
  • 第三层:提出修改方向,甚至直接给出改写建议。

三个层次缺一不可。如果批评者只说“这里不好”,那是废话;如果只给修改方向但不说哪里不好,被批评的模型也没法定位问题。所以在设计流程时,我始终把“结构化批评输出”当成核心,而不是让模型自由写一段评语。

1.3 交叉批评与自我批评的模式对比

在具体实践里,“批评”和“自我批评”其实是两套不同玩法,各有用武之地。

交叉批评,也就是用模型A评模型B的输出。这种模式适合两个能力侧重点不同的模型组合,比如用一个指令跟随能力强、对格式要求严格的模型去评审另一个知识面广但容易啰嗦的模型。好处是天然有“外部视角”,批评者不太会被原文带的节奏带偏。坏处是调用成本翻倍,而且如果批评模型本身能力不够,就会评出一堆似是而非的废话。

自我批评,也就是同一个模型生成完之后,切换心态重新审视自己。这种模式成本低,不需要再引一个额外模型,而且从实测看,很多模型切换到“评审者”角色之后,确实能发现自己刚才回复里的漏洞。坏处也很明显:模型有“自恋倾向”,对自己生成的内容容易网开一面,这个我后面在问题排查章节里会展开讲。

我的建议是,对质量要求高的核心场景,用交叉批评;对快速迭代、批量清洗的场景,用自我批评。两者也可以串联,先自我批评一轮,再由外部模型做终审,兼顾成本和稳定性。

2. 核心细节解析:批评类Prompt的关键设计

2.1 批评前先定标准,否则评了个寂寞

我见过很多人写批评Prompt,上来就是一句“请评价这段文本的质量”,结果模型回了一大段模棱两可的话,什么“内容较为完整,语言流畅,但在细节方面仍有提升空间”——这种评价等于没评价。

批评Prompt的前提是先定义“好”和“坏”的标准。这件事必须在Prompt里写得清清楚楚。我自己常用的一套维度是:准确性、完整性、可操作性、安全性。准确性负责查事实和逻辑,完整性负责查漏项,可操作性负责看建议能不能落地,安全性负责把关合规。

维度定义清楚之后,还要给评分量表。1到5分说清楚每个分数代表什么,比如“给1分意味着存在严重事实错误或完全偏离任务要求,给5分意味着无需修改可直接使用”。没有量表的批评者会自己发明一套标准,导致不同轮次之间分数不可比。

另外很关键的一点是:批评者必须知道“任务背景”。如果你让模型评一段“产品文案”,却不告诉它这个文案的目标用户是谁、投放渠道是什么,它只能按通用标准评,评出来的意见泛泛而谈。所以我的Prompt里会先复述一遍任务要求,再把待评内容放进去,最后才让批评者按维度打分。

2.2 评估型Prompt的完整设计框架

这里给出一个我反复调试后比较稳定的评估Prompt模板,适用于多数文本生成场景。它的结构是:角色定义 + 任务背景 + 待评内容 + 评估维度 + 输出格式。

我先贴一个精简版,你可以直接拿去改:

你是一名资深内容评审专家,擅长发现文本中的逻辑漏洞、事实缺失和表达冗余。 【任务背景】 用户原本的问题是:{user_query} 模型生成的目标用户:{target_audience} 任务要求:{task_requirements} 【待评内容】 {model_response} 【评估维度】 请从以下四个维度逐项评分,每个维度1~5分: 1. 准确性:是否存在事实错误、逻辑矛盾或未经证实的断言。 2. 完整性:是否覆盖用户问题的核心诉求,是否有明显遗漏。 3. 可操作性:给出的建议、步骤是否具体、可执行。 4. 安全性:内容是否合规,是否可能引起误导或风险。 【输出格式】 严格按以下Markdown格式输出,不允许额外客套话: | 维度 | 分数 | 理由 | |------|------|------| | 准确性 | ?/5 | 简要说明 | | 完整性 | ?/5 | 简要说明 | | 可操作性 | ?/5 | 简要说明 | | 安全性 | ?/5 | 简要说明 | 【问题清单】 按严重程度列出所有问题,每条包含: - 问题位置(引用原文片段) - 问题类型(事实错误/逻辑断裂/遗漏/冗余/风险) - 具体理由 【总体改进建议】 给出2~3条最关键的修订方向,必须具体到可以执行。

这个Prompt有四个设计细节:角色设定用“评审专家”而非“助手”,能激发更严格的审查姿态;任务背景放在待评内容之前,让模型先理解语境再判断;问题清单要求“引用原文片段”而不是泛泛描述,这能逼着批评者真正去看细节;最后要求“不许客套”,能有效压制模型默认的礼貌性夸奖。

我在实测中发现,加了“不允许额外客套话”这句话之后,批评质量和之前完全不是一个量级。不写这句,模型经常先夸一遍再挑毛病,而人看批评意见时最先看到的就是夸奖,很容易被带偏。

2.3 自我批评Prompt:让模型和自己“切割”

自我批评比交叉批评难设计,难在模型认识“自己”。同一个模型生成完内容后,你如果直接问“请评价你刚才的回答”,它会倾向于防御性地解释自己哪里做得好,批评起来很敷衍。

解决思路是“身份切割”。在Prompt里明确告诉它:刚才那段内容是另一个模型生成的,你现在要作为第三方评审者去挑刺。实操下来,只要能骗过模型的身份认知,它挑自己毛病的时候比挑别人还狠。

我常用的自我批评Prompt骨架如下:

假设你是一名与我刚才对话完全无关的第三方专家评审。下面这段内容是另一个系统生成的回复,你需要用最挑剔的眼光审查它: {model_response} 请忽略“它看起来态度友好”之类的表面印象,只关注实质问题。逐条列出: 1. 事实性错误或可疑论断(请引用原文位置并说明可疑点); 2. 表达含混、可被多种理解的地方; 3. 针对用户核心问题,遗漏或回避的部分; 4. 可以删掉的冗余句子或段落。 每条必须附带修改建议。不允许写“整体质量不错”这类评价,至少要挑出3个问题。

这里有个非常实用的心理技巧:把“整体不错”这种话直接禁止掉,模型就会把思维切换到“找茬模式”。我试过不加这句话的版本,它经常输出“整体上内容丰富,只有以下小瑕疵”,而且小瑕疵往往不重要;加了之后,它会主动去翻逻辑漏洞。

自我批评还有一个配合动作:让模型先“复述任务要求”。在进入批评阶段之前,先让模型解释一遍“用户真正想要什么”,再开始批评。这一步能激活模型对任务目标的理解,批评起来更有针对性,而不是漫无目的地挑语法毛病。

2.4 参数配置:评估场景和生成场景要分开

很多人在同一个项目里用同一套API参数跑生成和评估,这是大忌。生成要多样性,所以temperature通常设0.7到0.9;评估要稳定,所以temperature必须降到0或者0.1,否则连续评估两次同一段文本,分数可能差出1分以上。

我自己实测的一组对比:同一段待评文本,批评者temperature分别设0.7和0时,跑20次,0.7版本的评分标准差约为0.8,0版本的约为0.2,差异非常明显。评估任务追求的是“多次调用口径一致”,温度越低越可靠。

另外两个参数容易被忽略:

  • max_tokens:批评者要输出打分表、问题清单、修改建议,往往需要和原文差不多长度的输出空间。我踩过坑:某次生成了一篇800字的文案,批评者max_tokens只给300,结果它写到问题清单一半就截断了,评分理由全是半截话。
  • top_p:如果用了temperature=0,top_p基本可以不动。如果你需要一定程度降低随机性又不想完全贪婪解码,可以用temperature=0.3 + top_p=0.8的组合,但我个人更推荐直接temperature=0,省心。

还有一个细节:评估性调用建议统一system prompt,不要每轮换角色设定。批处理一大批数据时,如果系统提示词里角色称呼偶尔是从“专家”变成“顾问”,批评标准会跟着漂移,最终汇总的分值同一批数据都不可比。我在工程上把所有批评任务的system prompt抽成一个常量,每次调用固定引用,避免手动复制出错。

3. 实操过程与核心环节实现

3.1 完整流程搭建:“生成-批评-修订”闭环

真正让我觉得“批评和自我批评”值回票价,是在搭建完整闭环之后。单次批评意义有限,只有把批评意见作为反馈喂回生成环节,让模型重新修订,再进入下一轮批评,质量才会一轮比一轮高。

我项目里跑的标准流程分四步。

第一步,初始生成。用产品模型按正常参数生成回复。这个阶段生不生成候选集都行,简单场景一条就够,复杂场景可以先生成三条候选,后面用批评者打分挑一条。

第二步,批评评估。把生成的回复连同任务背景发给批评者模型,拿到结构化评分和问题清单。这里如果做交叉批评,就用另一个模型;如果做自我批评,就还是同一个模型,但用2.3节的身份切割Prompt。

第三步,修订合并。把问题清单按严重度排序,决定哪些意见要采纳。这条很关键,不是每条批评都要照做。我自己会在修订Prompt里把批评意见列进去,并明确要求“只修改被指出的问题,不改变原有结构和风格”。

第四步,再评估。修订后的内容再次送入批评者,重新打分,并对比上一轮分数。分数提升则保留修订版,如果分数没有变化甚至下降,说明修订方向错了,退回上一版或者调整采纳策略。

迭代轮次我一般控制在两到三轮回。第一轮批评通常能发现大的架构问题,第二轮发现细节问题,第三轮开始边际收益递减,再跑下去就是烧钱。

3.2 评分汇总与一致性校验

当你批处理数百条文本时,批评结果怎么汇总,直接决定你后续能不能信这批分数。

我的做法是:每一条待评内容,让批评者跑三次,得到三组维度分,然后取每个维度的均值。三次分数如果不一致,看标准差。标准差大于0.8,说明这条内容本身处于“质量模糊区”,评分不稳定,单独标记出来,后续人工抽检优先看这些。

举个例子,某条回答四个维度三次评分分别是(4,4,5)(3,5,4)(4,4,4),完整性维度均值约4.3,标准差约0.6,勉强稳定;如果出现(2,5,3)这种大幅摆动,说明批评者对这个维度的判断逻辑不一致,这条分数就不能直接采信。

另外要注意,大多数LLM批评者存在“长文本偏好”,也就是待评内容越长,它越容易给高分。校准方法:如果你发现某个模型批评者面对1500字回复的平均分显著高于面对200字回复的平均分,而这两批内容实际质量差不多,那就要给每个文本单独标注长度因子,做归一化。我在某次评估一批产品介绍文案时,就发现批评者给长篇文案平均高出0.8分,非常离谱。

3.3 最小可跑通的代码骨架

理论讲再多,不如跑一段代码看清楚。下面是一个极简的Python骨架,用伪API封装的形式展示完整循环,你可以照着替换成具体模型SDK。

def generate(user_query, cfg): """用生成模型产出一段回复""" messages = [ {"role": "system", "content": cfg["system_prompt"]}, {"role": "user", "content": user_query}, ] resp = call_model( messages, temperature=cfg["generate_temperature"], # 0.7 max_tokens=cfg["max_output_tokens"], # 1024 ) return resp def critique(content, background, critic_cfg): """调用批评者模型,返回结构化评分与问题清单""" prompt = CRITIC_PROMPT_TEMPLATE.format( user_query=background["user_query"], target_audience=background["target_audience"], task_requirements=background["task_requirements"], model_response=content, ) messages = [ {"role": "system", "content": critic_cfg["system_prompt"], "temperature": 0.0}, {"role": "user", "content": prompt}, ] return call_model(messages, temperature=0.0, max_tokens=critic_cfg["max_tokens"]) def refine(content, critique_result, cfg): """根据批评意见修订原文""" messages = [ {"role": "system", "content": cfg["refine_system_prompt"]}, {"role": "user", "content": f"{content}\n\n批评意见:\n{critique_result}"}, ] return call_model(messages, temperature=cfg["refine_temperature"], max_tokens=cfg["max_output_tokens"]) for query in batch_queries: reply = generate(query, gen_config) for round_idx in range(MAX_ROUNDS): # 一般2轮 crit = critique(reply, query_meta, critic_config) scores = extract_scores(crit) # 解析Markdown表格 if all_score_above(4.5, scores): break reply = refine(reply, crit, gen_config) crit2 = critique(reply, query_meta, critic_config) # 如果分数低于上一轮,保留上一版 if avg_score(crit2) < avg_score(scores): reply = previous_reply break

这套骨架的细节在于:每轮修订后都做了一个“防回退”判断,如果新版本评分低于旧版本,会自动保留上一版,这可避免修改过头导致质量下降。实际项目中这个逻辑极大减少了无效迭代。

3.4 实例拆解:文案质量的多轮批评优化

拿我最近做过的一个“智能台灯产品推广文案”场景举例,能更直观看到批评意见的价值。

第一轮生成模型给出的文案开头是:

“我们的智能台灯采用高品质LED光源,显色指数高达Ra95以上,支持无级调光。”

批评者给出的问题清单里,有一条很扎眼:“‘无级调光’是一个行业术语,目标用户如果是普通家庭消费者,这个说法缺乏温度;同时开头没有点出‘为什么需要这盏灯’,缺少场景切入。”

这个意见我原本没意识到。于是修订版改成了:

“晚上在书桌前工作,灯光太亮刺眼、太暗伤眼。这款智能台灯把显色指数做到Ra95以上,接近自然光,还能无级调节亮度,让眼睛在长时间用光时更舒服。”

第二轮批评者的打分从第一轮的平均3.4分提升到了4.3分,而它又提出一个新问题:“建议把‘无级调光’具体化,比如说明调节范围是多少,读者才好想象。”这句话让我意识到,模型批评者会逼着内容走向“具体可感知”,而不是停留在“参数罗列”。

三轮跑完,一篇文案的评分从3.2到4.5,批评者每轮都能抓到至少一个人类评审大概率会提、但常规检查会漏掉的问题。这是我决定把这套流程固化下来的直接原因。

4. 常见问题与排查技巧实录

4.1 批评结果漂移,上次和这次打分差很多

我在一次批量评估中遇到特别奇怪的现象:同一批20条测试文本,上午跑完平均分4.2,下午重跑同一批,平均分变成了3.6。代码没动过,Prompt没改过,唯一的变量是时间。

排查之后发现,问题出在我用的批评模型接口端有版本更新,但当时接口没有明确暴露版本号。模型底层换过之后,评分标准自然变化。这是LLM-as-Judge最隐蔽的坑,你以为是你的流程波动,其实是底层模型已经变了。

应对办法有两层。第一层:对同一个批评模型,固定用带版本标签的接口;如果采购的是第三方服务,就锁定版本并做例行回归测试。第二层:在每次跑批量评估前,先放入3到5条“标定样本”,这些样本的分数是你人工事先定好的,如果批评者给出的分数跟基准值偏差超过0.5,整批结果都判定为不可信,需要排查。

4.2 “水货”批评的五个识别信号

不是所有大模型都会认真批评。我收集了上百条“水货”批评输出,发现五个典型信号,出现任意一个,这条批评结果就应该作废:

第一,通篇夸奖之后只跟了一条“瑕不掩瑜”式的小建议,这种基本等于没评。第二,问题清单里没有任何原文引用,全都是“内容略显不足”“建议加强细节”这种万金油话术。第三,四个维度分数全是4分或全是5分,没有区分度,说明批评者对内容没有逐项思考。第四,修改建议脱离任务背景,比如让一篇产品文案“加入更多社科理论支撑”,明显跑偏。第五,批评者复述了原文大量内容,但没有任何判断,输出像是摘要而不是评审。

我现在的处理方式是,对批评结果跑一遍规则过滤:输出长度少于100字视为无效;不包含“|”表格式评分视为无效;问题清单少于2条视为无效。这三条规则能过滤掉大概三分之一的垃圾批评输出,非常值得写进Pipeline。

4.3 被批评后越改越差,怎么设防

批评意见不是越多越好。我遇到过最尴尬的情况:一篇原本还算通顺的回复,被批评者指出七个问题,全部照做之后,内容变得冗长、刻意、失去原本的自然感,第二次评估分数反而下降了0.6分。

原因很好理解:过度采纳是修改过头。批评者为了显得自己负责,会尽可能多地找问题,其中有一部分是“伪问题”,也就是它自己也可能犯的风格偏好,并不影响真实质量。

所以我给修订环节加了三道约束。第一,修订Prompt里写明“只在被指出且同意的问题上做修改,保留原文风格和语气”;第二,从第二轮开始只处理严重级别标为“高”的问题,低级别问题先忽略;第三,修订完成后立刻做分数对比,如果比上一轮低,直接回退到上一版并终止迭代。这套机制救了很多次濒临报废的修改结果。

4.4 同一个模型自我批评为何会“失效”

同一模型自我批评最常出现的问题是:批评结果跟没批一样。明明用了身份切割Prompt,它还是净挑一些无关痛痒的语法问题,对核心逻辑漏洞视而不见。

我自己试验了几个破解方向,实测有效的是“反向验证法”。具体做法:在自我批评之前,先让模型对“一份故意包含三个明显错误的回复”进行批评,如果它连这三个明显错误都没找全,就不要指望它手术刀式地批评自己的真实回复。这个小测试能快速判断当前模型状态适不适合做自我批评。

另外,自我批评容易流于形式,往往不是能力问题,而是心态问题。给它一个“外部压力”多有帮助:在Prompt里设定“这份评审结果将被用于决定是否采纳该系统输出,如果评审不到位,将影响系统的整体使用”,虽然是假的,但模型在这种表述下确实会更认真。

4.5 工具选型小结与个人经验

最后说选模型这件事。批评者的能力要求和生成者很不一样。做生成,可以选择知识内容丰富、表达生动的模型;做批评,我更看重指令跟随能力和格式稳定性。很多知识型大模型生成能力强,但让它严格按Markdown表格输出评分时经常加戏;反过来,一些指令优化做得好的开源模型,做批评者反而更稳定。

成本方面也要算清账。一次“生成-批评-修订”循环,模型调用次数通常是2到3次,相比单次生成成本翻了近两倍。如果每天批量处理几万条内容,这个成本必须提前评估。我一般把批评调用放在低峰时段,用异步队列非实时跑,能把单位成本压下来不少。

回看整条路线,我最深的体会是:批评和自我批评这套玩法的上限,并不取决于模型的“智商”,而是取决于你有多认真地设计批评流程。能稳定输出高质量批评意见的前提,是把维度、角色、输出格式、一致性校验这些决定评审质量的关键点打磨好——Prompt把“怎么评”说清楚了,模型自然会还你一份有分量的评语。

如果你也在做内容质量的批量管控,建议先拿一批旧数据试跑“自我批评”模式,把上面提到的问题识别信号当过滤器用,再把交叉批评作为终审。等这两条链路都跑顺了,你大概就能看到几毛钱成本换一条高质量内容的甜头了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询