[基于OpenEvals的自动化评估-02]LLM-as-a-Judge:让LLM当裁判来评估Agent的输出

在大部分情况下,我们会借助LLM的能力来评估Agent的输出,我们将这种评估模式成为LLM-as-a-Judge。这是一种利用大型语言模型对生成式AI输出进行自动化评估的范式,其核心思想是让模型承担裁判角色,对候选答案进行打分、排序或选择,能够在开放式任务中判断质量、相关性、逻辑性、事实性与有用性等更细腻的属性。由于LLM是一个概率模型,所以评估的结果本质上是一个概率分布,所以代表评估结果的Score一般是一个[0-1]的分数,返回的二元布尔值也是基于指标的开率计算出来的(比如得分是否大于0.5)。

1. LLM-as-a-Judge评估器的创建

基于LLM-as-a-Judge的评估器通过如下所示的create_llm_as_judgecreate_async_llm_as_judge两个工厂函数创建而成,可以说这是整个OpenEvals最为核心的两个方法。这两个函数最终会创建代表同步和异步评估器的SimpleEvaluator和SimpleAsyncEvaluator,我们在基于OpenEvals的自动化评估-01:通过一个例子了解评估模型已经对这两个类型进行过详细介绍。

def create_llm_as_judge(
    *,
    prompt: Union[str, Runnable, Callable[..., list[ChatCompletionMessage]]],
    feedback_key: str = "score",
    judge: Optional[Union[ModelClient, BaseChatModel]] = None,
    model: Optional[str] = None,
    system: Optional[str] = None,
    continuous: bool = False,
    choices: Optional[list[float]] = None,
    use_reasoning: bool = True,
    few_shot_examples: Optional[list[FewShotExample]] = None,
    output_schema: Optional[Union[dict, type]] = None,
) -> Union[SimpleEvaluator, Callable[..., Any]]

def create_async_llm_as_judge(
    *,
    prompt: Union[str, Runnable, Callable[..., list[ChatCompletionMessage]]],
    feedback_key: str = "score",
    judge: Optional[Union[ModelClient, BaseChatModel]] = None,
    model: Optional[str] = None,
    system: Optional[str] = None,
    continuous: bool = False,
    choices: Optional[list[float]] = None,
    use_reasoning: bool = True,
    few_shot_examples: Optional[list[FewShotExample]] = None,
    output_schema: Optional[Union[dict, type]] = None,
) -> Union[SimpleAsyncEvaluator, Callable[..., Awaitable[Any]]]

class FewShotExample(TypedDict):
    inputs: Any
    outputs: Any
    score: Union[float, bool]
    reasoning: Optional[str]

这两个工厂方式具有相同过的参数,说明分别如下:

  • prompt:定义评估提示词模板,类型可以是字符串,也可以是类似于PromptTemplate这样的Runnable对象(可以采用LCEL表达式于LLM进行连接),或者是一个用于返回LLM输入的消息列表的函数。如果以字符串定义提示词,可以包含如下的占位符,它们对应着调用SimpleEvaluator/SimpleAsyncEvaluator指定的参数:
    • {inputs}
    • {outputs}
    • {reference_outputs}
    • {由kwargs指定的参数名称}
  • feedback_key:评估结果中存储的字段名,默认为score;
  • judge/model: 用于提供用来实施评估的LLM。如果使用model参数指定包含提供商的模型标准名称,方法内部会利用标准的URL创建代表LangChain LLM组件的BaseChatModel对象。如果需要连接自定义URL指向的LLM部署地址,或者需要对LLM组件对象进行定制,可以直接利用judge参数指定一个BaseChatModel对象。也可以直接执行一个ModelClient对象,ModelClient是OpenEvals针对LLM客户端组件的表达,但是此时依然需要利用model参数指定模型名称;
  • system: 系统提示词;
  • continuous:评估结果是否是连续值:
    • True: 输出为0~1的连续浮点数;
    • False: 输出为布尔值。
  • choices:如果希望评估结果采用指定的选项,可以利用此参数指定一个列表,比如choices=[0.0, 0.5, 1.0]意味着最终得分只有指定的三种选择;
  • use_reasoning:是否要求模型输出评分理由:
    • True:输出包含解释;
    • False:只输出评分。
  • few_shot_examples: 用于在提示词中加入few-shot示例,提升评估一致性;
  • output_schema:评估结果的结构化输出格式。

1.1 输入消息的生成

调用LLM实施评估需要生成消息列表作为输入,OpenEvals将用于LLM交互的消息定义成如下这个名为ChatCompletionMessage的TypedDict,对应的成员分别用来表示消息的ID、角色、内容和工具调用。

class ChatCompletionMessage(TypedDict):
    id: NotRequired[Optional[str]]
    content: Union[str, list[dict]]
    role: str
    tool_calls: NotRequired[Optional[list[dict]]]

create_llm_as_judgecreate_async_llm_as_judge这两个函数会才采用如下的逻辑生成作为LLM输入的消息列表:

  • 如果指定的prompt参数是一个Runnable对象:
    • 会将它视为一个类似于PromptTemplate的提示词模板,此时它会调用此对象生成完整的提示词,作为参数的字典会包含如下的占位符,分别对应调用评估器对应的参数:
      • {inputs}
      • {outputs}
      • {reference_outputs}
      • {由kwargs指定的参数名称}
    • 最后按照OpenAI消息风格将执行的结果转化成ChatCompletionMessage列表。
  • 如果指定的prompt参数是一个字符串:
    • 如果调用评估器利用kwargs指定了一个表示多媒体消息内容的attachments参数,会创建对应的Conent-Block(这里体现为一个dict对象,并非LangChain的ConentBlock类型)。此时会试图从提示词中定位对应的{attachments}占位符,并使用前面和后面(如果有)文本创建对象的文本类型的Content-Block,然后针对这些Content-Block作为内容创建角色为user的ChatCompletionMessage对象。
    • 否则根据提示词直接创建一个角色为user的ChatCompletionMessage对象;
    • 对于如上的两种情况,提示词文本提供的上述四种占位符会被对应的参数替换。
  • 如果prompt是一个Callable[..., list[ChatCompletionMessage]],直接调用此对象生成ChatCompletionMessage对列表。

上面生成ChatCompletionMessage列表,还会根据提供的参数添加如下的消息:

  • 如果指定了system参数,会据此创建一个角色为system的消息,置于消息列表的最前端;
  • 如果指定了few_shot_examples参数,会据此创建对应的消息列表,追加到消息列表的尾部。

1.2 结构化输出

由于评估结果具有固有的结构,所以在调用LLM的时候必须指定输出Schema以结构化输出的方式保证返回具有匹配结构的结果。输出Schema具有如下三种指定方式:

  • 调用create_llm_as_judgecreate_async_llm_as_judge这两个函数时指定output_schema参数;
  • 将prompt参数指定为StructuredPrompt对象,并由后者提供输出Schema;
  • 调用如下的函数使用默认的输出Schema作为兜底,三个参数对应两个工厂函数的同名参数。
def _construct_default_output_json_schema(
    *,
    continuous: bool = False,
    choices: Optional[list[float]] = None,
    use_reasoning: bool = True,
) -> tuple[dict, str]

在如下的演示程序中,我们以不同的参数调用了_construct_default_output_json_schema函数生成并输出相应的Schema:

from openevals.llm import _construct_default_output_json_schema
import json

schema  = _construct_default_output_json_schema()
print(json.dumps(schema,ensure_ascii=False, indent=2))

schema  = _construct_default_output_json_schema(
    continuous=True,
    choices=[0.0,0.5,1.0]
)
print(json.dumps(schema,ensure_ascii=False, indent=2))

输出:

[
  {
    "type": "object",
    "additionalProperties": false,
    "properties": {
      "reasoning": {
        "type": "string",
        "description": "A human-readable explanation of the score. You MUST end the reasoning with a sentence that says: Thus, the score should be: SCORE_YOU_ASSIGN."
      },
      "score": {
        "type": "boolean",
        "description": "A score that is true if criteria in the prompt are met, and false otherwise."
      }
    },
    "required": [
      "reasoning",
      "score"
    ]
  },
  "A score that is true if criteria in the prompt are met, and false otherwise."
]
[
  {
    "type": "object",
    "additionalProperties": false,
    "properties": {
      "reasoning": {
        "type": "string",
        "description": "A human-readable explanation of the score. You MUST end the reasoning with a sentence that says: Thus, the score should be: SCORE_YOU_ASSIGN."
      },
      "score": {
        "type": "number",
        "description": "A number that represents the degree to which the criteria in the prompt are met.",
        "enum": [
          0.0,
          0.5,
          1.0
        ]
      }
    },
    "required": [
      "reasoning",
      "score"
    ]
  },
  "A number that represents the degree to which the criteria in the prompt are met."
]

在上述默认Schema基础上,一般还会添加额外的必要的字段成员。

1.3 LLM的调用

作为输入的消息列表生成之后,我们来看看如何将它们作为输入调用对应的LLM来完成评估工作。create_llm_as_judge和create_async_llm_as_judge这两个函数与LLM相关的参数有如下两个:

  • model:评估模型的标准名称;
  • judge:评估模型对应的BaseChatModel或者ModelClient对象。

我的系列文章03.LangChain语言模型组件对BaseChatModel表示的LangChian语言模型组件进行了详细介绍,这里我们来看看ModelClient类型的定义。ModelClient是一个代表LLM 客户端的抽象协议,要求任何实现它的对象必须提定义一个chat属性来提供ChatCompletionsClient类型,并以此作为创建LLM组件。ChatCompletionsClient代表采用OpenAI风格的文本补齐模型的客户端。

@runtime_checkable
class ModelClient(Protocol):
    @property
    def chat(self) -> type[ChatCompletionsClient]: ...

@runtime_checkable
class ChatCompletionsClient(Protocol):
    def create(self, **kwargs) -> ChatCompletion: ...

class ChatCompletion(TypedDict):
    choices: list[dict]

create_llm_as_judge和create_async_llm_as_judge这两个函数最终会采用如下的方式来创建用来实施评估的LLM组件,并将上面解析出来的消息列表作为如下调用它们实施评估:

  • 如果judge是一个ModelClient对象,此时必须要求同时提供model参数,此时会调用judge.chat.completions.creat方法生成完成针对LLM的调用,作为参数传入的字典会包含如下三个成员:
    • messages: 消息列表;
    • model:模型名称;
    • response_format:输出Schema。
  • 如果只提供了model参数,直接调用LangChain的init_chat_model函数根据此参数创建对应的BaseChatModel对象,直接将消息列表作为如下调用此对象;
  • 如果指定的judge参数是一个BaseChatModel,直接将消息列表作为如下调用此对象。

2. 利用LLM-as-a-Judge评估器实施评估

接下来我们会使用一些简单的实例演示如何指定不同的参数调用create_async_llm_as_judge函数创建相应的评估器,并对手工指定的输出实施评估。如下面的演示程序所示,我们调用create_async_llm_as_judge函数时利用prompt参数指定了一段简短的提示词来完成正确性评估,judge参数则设置为一个采用gpt-5.4-mini模型的ChatOpenAI对象。我们针对同一个问题:说出战国四大名将的姓名,针对不同的两个答案实施评估。

from openevals import create_async_llm_as_judge
from langchain_openai  import ChatOpenAI
from dotenv import load_dotenv
import asyncio,json

load_dotenv()

prompt="""\
确认针对指定问题的提供的答案是否正确:

**问题**
{inputs}

**答案**
{outputs}
"""

async def eval(outputs:str):
    evaluator = create_async_llm_as_judge(
        prompt = prompt,
        judge= ChatOpenAI(model="gpt-5.4-mini"))
    result = await evaluator(
        inputs= "说出战国四大名将的姓名",
        outputs=outputs
    )
    print(json.dumps(result,ensure_ascii=False, indent=2))

async def main():
    await eval("吴起、廉颇、李牧和王翦")    
    await eval("白起、廉颇、李牧和王翦")

asyncio.run(main())

输出结果:

{
  "key": "score",
  "score": false,
  "comment": "题目问“战国四大名将”的姓名,通行说法通常指吴起、白起、廉颇、李牧。给出的答案是吴起、廉颇、李牧和王翦,其中“王翦”并非这一常见“四大名将”名单中的成员,而是将“白起”写错/替换了。因此该答案不正确。Thus, the score should be: false.",
  "metadata": null
}
{
  "key": "score",
  "score": true,
  "comment": "题目要求说出“战国四大名将”的姓名。通常公认的战国四大名将指白起、王翦、廉颇、李牧,答案中给出的四人完全一致,只是顺序不同。因此该答案正确。Thus, the score should be: true.",
  "metadata": null
}

从输出可看出,评估结果的score模式采用二元布尔值(continuous= False),并且默认开启了推理(use_reasoning = True)。顺便说一下,由于战国四大名将其实并没有一个权威的说法,所以第一次评估采用了吴起、白起、廉颇、李牧这种说法,第二种则采用了白起、王翦、廉颇、李牧这种说法。

2.2 输出连续值

我们看看在调用create_async_llm_as_judge是将continuous参数成True,让评估结果体现为一个具体的分值:

evaluator = create_async_llm_as_judge(
        prompt = prompt,
        continuous= True,
        judge= ChatOpenAI(model="gpt-5.4-mini"))

输出:

{
  "key": "score",
  "score": 0.75,
  "comment": "题目问“战国四大名将”的姓名。通行说法中,战国四大名将通常指白起、王翦、廉颇、李牧。给出的答案里“廉颇、李牧和王翦”正确,但“吴起”不属于这一通常名单,应为“白起”。因此答案只部分正确。Thus, the score should be: 0.75.",
  "metadata": null
}
{
  "key": "score",
  "score": 1.0,
  "comment": "题目要求说出“战国四大名将”的姓名。通常公认的战国四大名将是白起、王翦、廉颇、李牧;给出的答案包含这四人,且顺序不同不影响正确性。因此答案正确。Thus, the score should be: 1.0.",
  "metadata": null
}

从输出可以看出,第一种答案答对了三个,得分0.75挺合理,第二个答案完全正确,得了满分。

2.3 限制分值和指定少样本示例

我们可以进一步利用choices参数将分支限制在[0.0,0.25,0.50,0.75,1.0]五个选项之间,并提供少样本实例知道LLM严格按照说对的比例打分。

from openevals import create_async_llm_as_judge
from openevals.types import FewShotExample
from langchain_openai  import ChatOpenAI
from dotenv import load_dotenv
import asyncio,json

load_dotenv()

prompt="""\
确认针对指定问题的提供的答案是否正确:

**问题**
{inputs}

**答案**
{outputs}
"""
inputs= "说出战国四大名将的姓名",
few_shot_examples:list[FewShotExample] = [
     {"inputs":inputs , "outputs":"吴起、司马错、乐毅、蒙恬", "score":0.0, "reasoning":"四个都错"},
     {"inputs":inputs , "outputs":"白起、司马错、乐毅、蒙恬", "score":0.25, "reasoning":"四个错其三"},
     {"inputs":inputs , "outputs":"白起、李牧、乐毅、蒙恬", "score":0.50, "reasoning":"四个错其二"},
     {"inputs":inputs , "outputs":"白起、李牧、王翦、蒙恬", "score":0.75, "reasoning":"四个错其一"},
     {"inputs":inputs , "outputs":"白起、李牧、王翦、廉颇", "score":1.0, "reasoning":"四个都对"},
]

async def eval(outputs:str):
    evaluator = create_async_llm_as_judge(
        prompt = prompt,
        continuous= True,
        choices=[0.0,0.25,0.50,0.75,1.0],        
        judge= ChatOpenAI(model="gpt-5.4-mini"),
        few_shot_examples=few_shot_examples)
    result = await evaluator(
        inputs= inputs,
        outputs=outputs
    )
    print(json.dumps(result,ensure_ascii=False, indent=2))

async def main():
    await eval("孙膑、李牧、王翦、廉颇")    
    await eval("孙膑、‌庞涓‌、李牧和司马错‌")

asyncio.run(main())
{
  "key": "score",
  "score": 0.75,
  "comment": "题目问的是“战国四大名将”,通常标准答案是白起、王翦、李牧、廉颇。给出的答案是孙膑、李牧、王翦、廉颇,其中只有李牧、王翦、廉颇三人正确,孙膑不属于这一组。Thus, the score should be: 0.75.",
  "metadata": null
}
{
  "key": "score",
  "score": 0.25,
  "comment": "题目问的是“战国四大名将”。通行答案一般是白起、王翦、李牧、廉颇。给出的答案“孙膑、庞涓、李牧和司马错”中只有李牧属于常见正确答案,其余三位都不属于这一组。因此,四个错其三。Thus, the score should be: 0.25.",
  "metadata": null
}
内容概要:本文围绕“组稀疏信号去噪”展开,深入研究了基于非凸正则化与凸优化的信号处理方法,并提供了完整的Matlab代码实现。文章系统阐述了如何通过引入非凸正则项克服传统稀疏恢复方法的局限性,从而在语音增强等实际应用中实现更优的去噪性能。研究采用组稀疏建模范式,将信号按子带或时频块进行分组,以更好地保留信号的结构性特征。文中详细构建了相应的数学模型,将原始优化问题转化为可通过凸优化技术求解的形式,并设计了高效的求解算法。通过全面的仿真实验,验证了该方法在提升信噪比和改善语音主观质量方面的显著优势,尤其在强噪声环境下表现出更强的鲁棒性。; 适合人群:具备一定信号处理理论基础和Matlab编程能力的研究生、科研人员,以及从事语音增强、音频处理、通信工程等相关领域的技术研发人员。; 使用场景及目标:①应用于语音通信系统、智能助听设备、语音识别前端预处理等对语音清晰度要求高的场景,有效提升嘈杂环境下的语音可懂度;②为科研工作者提供一个将非凸正则化理论与凸优化算法相结合的完整研究范例,促进先进信号恢复算法在实际工程系统中的转化与应用。; 阅读建议:建议读者结合提供的Matlab代码,深入剖析算法实现的核心步骤,重点关注目标函数的设计理念、非凸正则项的选取依据以及优化求解器的具体实现;同时,鼓励通过调整算法参数或在不同类型的噪声环境中进行测试,以探究算法的性能边界和鲁棒性特征。
内容概要:本文档聚焦于三电平ANPC并网逆变器的先进控制策略研究,重点阐述了一种融合DPWMA(离散脉宽调制)调制、正负序电流分离控制与电网电压前馈补偿的复合控制方法,并基于Simulink平台完成了系统建模与仿真验证。该方法旨在提升逆变器在电网电压不平衡等复杂工况下的动态响应性能与运行稳定性,特别针对低电压穿越(LVRT)能力进行了优化设计。研究内容还涵盖了中点电位平衡控制、虚拟同步发电机(VSG)技术以及构网型逆变器的双闭环控制架构,体现了对高阶电力电子变换系统深层次的建模与控制探索。文档同时罗列了大量相关的MATLAB/Simulink仿真实例,覆盖电力电子、新能源并网、优化算法、机器学习、路径规划等多个前沿科研领域,强调了科研工作中“借力”现有成果与追求“创新”突破的双重重要性。; 适合人群:从事电力电子与电力传动、新能源发电系统、微电网与智能电网控制等相关领域的研究生、科研人员及工程技术人员,要求具备扎实的电力系统基础知识、现代控制理论背景以及熟练的MATLAB/Simulink仿真操作能力。; 使用场景及目标:①深入研究三电平ANPC逆变器在不平衡电网条件下的高性能控制策略;②实现DPWMA调制技术与正负序电流独立控制的协同仿真与性能评估;③设计并验证融合电网前馈补偿与中点电位平衡的复合控制算法;④作为高级电力电子系统仿真与构网型控制技术的学习范本与项目开发参考。; 阅读建议:建议读者结合文档指引,通过指定公众号或网盘获取完整的仿真模型资源,在Simulink环境中动手复现核心控制模块,重点关注正负序分离算法、DPWMA调制波生成逻辑与前馈控制环节的实现细节,同时参考文末列出的多样化研究方向,以拓宽技术视野并激发创新思维。
内容概要:本文针对间歇性光伏出力条件下48V直流母线电压的稳定控制问题,构建了一个包含光伏阵列、Boost升压电路、储能电池、双向DC-DC变换器及负载的独立直流微网系统,并基于Simulink平台建立了完整的系统仿真模型。研究核心在于设计一套分层协同控制策略,实现光伏最大功率点跟踪(MPPT)与储能系统双向充放电的协调调控,以应对光照强度波动引起的功率不平衡挑战,保障直流母线电压的稳定。通过引入闭环反馈控制机制,优化系统能量管理流程,有效提升了离网运行工况下系统的动态响应速度与运行可靠性,为解决可再生能源供电系统中的电压波动问题提供了可行的技术路径。; 适合人群:电气工程、新能源科学与工程、自动化等相关专业的研究生及科研人员,以及具备电力电子技术、自动控制理论基础和Simulink仿真操作经验的工程技术人员。; 使用场景及目标:①用于离网型光伏直流微网系统的建模仿真与稳定性分析;②开展储能系统参与功率调节的协同控制策略研究;③支撑科研项目中的电压稳定控制算法设计与验证; 阅读建议:建议结合文中提到的Simulink仿真模型进行实操演练,重点关注MPPT控制与储能双向DC-DC协同调控环节的设计逻辑,配合参数调试加深对系统动态特性的理解,同时可参考同类研究拓展多目标优化与鲁棒性提升方向。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值