当前位置: 首页 > news >正文

[基于OpenEvals的自动化评估-04]基于JSON相似度的评估[无LLM参与的字符匹配]

由于Agent与LLM交互的数据基本采用基于JSON的序列化方式,针对数据结构的描述也基本采用JSON Schema,所以很多情况下针对Agent输出的评估体现在基于JSON的相似度比较。OpenEvals专门为此定义了如下两个用于创建基于JSON匹配评估器的工厂函数,其中create_json_match_evaluator用于同步调用,create_async_json_match_evaluator用于异步调用。这是两个非常重要的函数,我们不仅可以用它来评估基于JSON的结构化输出,Agent最核心的轨迹评估就是就依赖于它。如代码所示,我们在调用这两个方法的时候,可以利用judge参数指定连接LLM的客户端,所以我们也可以利用LLM实现基于语义的相似度比较。但是这是一个可选的参数,如果没有指定,默认采用严格的字符匹配。这篇文章主要关注无LLM参数的评估,下一篇我们将重点介绍基于LLM-as-a-Judge的评估。

defcreate_json_match_evaluator(*,aggregator:Optional[Literal["average","all"]]=None,list_aggregator:Literal["average","all"]="all",rubric:Dict[str,str]={},exclude_keys:list[str]=[],judge:Optional[Union[ModelClient,BaseChatModel,]]=None,model:Optional[str]=None,use_reasoning:bool=True,list_match_mode:Literal["superset","subset","same_elements","ordered"]="same_elements",)->SimpleEvaluatordefcreate_async_json_match_evaluator(*,aggregator:Optional[Literal["average","all"]]=None,list_aggregator:Literal["average","all"]="all",rubric:Dict[str,str]={},exclude_keys:list[str]=[],judge:Optional[Union[ModelClient,BaseChatModel,]]=None,model:Optional[str]=None,use_reasoning:bool=True,list_match_mode:Literal["superset","subset","same_elements","ordered"]="same_elements",)->SimpleAsyncEvaluator

1. 针对每个JSON字段得分的聚合

接下来我们会通过实例演示的方式介绍create_json_match_evaluatorcreate_async_json_match_evaluator函数定义的每个参数对应怎样的评估行为。我们下来看看aggregator参数,从两个函数的定义可以看出,这个参数具有Noneallaverage三个选项。我们定义了如下的演示程序,辅助方法会根据指定的关键字参数调用create_async_json_match_evaluator方法创建SimpleAsyncEvaluator对象,并调用此对象对传入的outputsreferences实施评估。作为评估结果的EvaluatorResult对象或者EvaluatorResult列表通过辅助函数pretty_printkeyscore这两个核心字段打印出来。

importasynciofromopenevals.jsonimportcreate_async_json_match_evaluatorfromopenevals.typesimportEvaluatorResultdefcreate(name:str,age:int,**kwargs)->dict:person={"name":name,"age":age}return{**person,**kwargs}defpretty_print(title:str,results:EvaluatorResult|list[EvaluatorResult]):print(f"{title}:")if(isinstance(results,list)):forresultinresults:print(f"{result['key']}={result['score']}")else:print(f"{results['key']}={results['score']}")print()asyncdefeval(title:str,*,outputs:dict|list[dict],references:dict|list[dict],**kwargs):evaluator=create_async_json_match_evaluator(**kwargs)results=awaitevaluator(outputs=outputs,reference_outputs=references)pretty_print(title,results)asyncdefmain():alice1=create("Alice",20,birth="1981-08-24")alice2=create("Alice",30,gender="female")awaiteval("aggregator=None",aggregator=None,outputs=alice1,references=alice2)awaiteval("aggregator=all",aggregator="all",outputs=alice1,references=alice2)awaiteval("aggregator=average",aggregator="average",outputs=alice1,references=alice2)asyncio.run(main())

从数据结构的角度来讲,JSON本质上也就是一个字典,所以我定义了辅助方法create根据指定的nameage以及其他任务额外字段创建一个字典来实施评估。说在main函数中,我们使用{"name":"Alice", "age":20, "birth" ="1981-08-24"}{"name":"Alice", "age":20, "gender" ="female"}这字典作为待评估的outputs和作为评估基准的reference_outputs。然后分别采用三种不同的aggregator选项调用eval函数,并得到如下的输出结果:

输出:

aggregator=None: json_match:age = 0 json_match:birthDate = 0 json_match:gender = 0 json_match:name = 1 aggregator=all: json_match:all = 0 aggregator=average: json_match:average = 0.25

从输出结果可以看出,如果采用默认的aggregatorNone),评估器会为JSON的每个字段生成一个EvaluatorResult对象,对应的Key为json_match:{fieldName}。对于本例来说,outputsreference_outputs的指定的字典一共涉及nameagebirthgender四个Key,所以默认会生成四个EvaluatorResult对象。对于这四个Key,只有name是相同的,所以只有它对应的score1,其余三个均为0

有时我们并不需要进行如此细粒度的评估,这段JSON生成一个评估结果就可以了,这样的结果可以对基于每个JSON字段得分进行聚合得到。aggregator参数的allaverage选项就表示两种聚合方式:

  • all:如果outputsreference_outputs拥有相同的Key,且对应的Value均相同,得1分,否则得0分。EvaluatorResult中对应的Key为json_match:all。所以它的值为0
  • average:对每个Key对应的得分取平均值。EvaluatorResult中对应的Key为json_match:average。所以它的值为0.25

2. 在基于列表评估中针对列表元素得分的聚合

作为outputs参数表示的待评估的JSON不仅限于一个字典,还可以是一个字典的列表。在就行基于列表的评估时,就需要使用到list_aggregator参数。如果说aggregator是站在字典的层次对所有的Key对应score进行聚合,那么list_aggregator就是在站在列表的层次对字典对应的score进行聚合。整个待评估数据层次结构可以划分为:list->dict->keylist_aggregator只有如下两个选项:

  • all:如果列表中所有字典都能匹配,得1分,否则得0分
  • average:对列表中所有字典对应的得分取平均值。

我们修改了上面演示程序的main方法,使用相同的outputsreference_outputs,采用list_aggregatoraggregator6种组合调用eval方法,并将得到的评估结果打印出来。

asyncdefmain():alice=create("Alice",20)bob1=create("Bob",20)bob2=create("Bob",30)outputs=[alice,bob1]rerferences=[alice,bob2]awaiteval("list_aggregator=all; aggregator= none",list_aggregator="all",aggregator=None,outputs=outputs,references=rerferences)awaiteval("list_aggregator=all; aggregator= all",list_aggregator="all",aggregator="all",outputs=outputs,references=rerferences)awaiteval("list_aggregator=all; aggregator= average",list_aggregator="all",aggregator="average",outputs=outputs,references=rerferences)awaiteval("list_aggregator=average; aggregator= none",list_aggregator="average",aggregator=None,outputs=outputs,references=rerferences)awaiteval("list_aggregator=average; aggregator= all",list_aggregator="average",aggregator="all",outputs=outputs,references=rerferences)awaiteval("list_aggregator=average; aggregator= average",list_aggregator="average",aggregator="average",outputs=outputs,references=rerferences)

输出:

list_aggregator=all; aggregator= none: json_match:age = 0 json_match:name = 1 list_aggregator=all; aggregator= all: json_match:all = 0 list_aggregator=all; aggregator= average: json_match:average = 0 list_aggregator=average; aggregator= none: json_match:age = 0.5 json_match:name = 1.0 list_aggregator=average; aggregator= all: json_match:all = 0.5 list_aggregator=average; aggregator= average: json_match:average = 0.75

3. 列表的四种匹配模式

list_match_mode决定评估器在处理列表结构的outputsreference_outputs时,如何进行元素对齐与评分展开。它不是评分规则,而是结构对齐算法。它具有如下四个选项:orderedsupersetsubsetsame_elements

3.1 严格按照顺序逐个匹配

强制outputsreference_outputs严格按照索引提取列表元素进行匹配。如果两个列表长度不一,在list_aggregatorall时得0分,为average时这些多出部分得元素得0分,进而拉低整体平均分。下面演示程序执行了三项评估:第一次由于outputsreference_outputs列表的两个元素顺序颠倒,即使list_aggregatoraggregator都设置为average,也是0分。第二次在outputs列表额外添加了一个元素,导致只有2/3匹配得分0.6666666666666666,在将list_aggregator设置为all时得0分

asyncdefmain():alice=create("Alice",20)bob1=create("Bob",30)bob2=create("Bob",40)references=[alice,bob1]awaiteval("list_aggregator=average; aggregator= average;[bob1,alice]/[alice, bob1]",list_aggregator="average",aggregator="average",outputs=[bob1,alice],references=references,list_match_mode="ordered")awaiteval("list_aggregator=average; aggregator= average;[alice, bob1, bob2]/[alice, bob1]",list_aggregator="average",aggregator="average",outputs=[alice,bob1,bob2],references=references,list_match_mode="ordered")awaiteval("list_aggregator=all; aggregator= average;[alice, bob1, bob2]/[alice, bob1]",list_aggregator="all",aggregator="average",outputs=[alice,bob1,bob2],references=references,list_match_mode="ordered")

输出:

list_aggregator=average;aggregator=average;[bob1,alice]/[alice,bob1]:json_match:average=0.0list_aggregator=average;aggregator=average;[alice,bob1,bob2]/[alice,bob1]:json_match:average=0.6666666666666666list_aggregator=all;aggregator=average;[alice,bob1,bob2]/[alice,bob1]:json_match:average=0

3.2 遍历outputs(reference_outputs)匹配reference_outputs(outputs)

superset模式由reference_outputs列表主导匹配,具体匹配匹配流程为:

  • 遍历reference_outputs列表中得每个元素;
  • outputs列表中找最匹配且未绑定得元素;

如果list_aggregatorall,在reference_outputs列表中的每个元素均与绑定的outputs元素完全匹配情况下得1分,否则得0分;如果list_aggregatoraverage,对每个绑定的得分求平均。如下的演示程序很好地演示了这种模式的积分规则。

asyncdefmain():alice=create("Alice",20)bob1=create("Bob",30)bob2=create("Bob",40)references=[bob1]awaiteval("list_aggregator=average; aggregator= average;[bob1, alice]/[bob1]",list_aggregator="average",aggregator="average",outputs=[bob1,alice],references=references,list_match_mode="superset")awaiteval("list_aggregator=all; aggregator= average;[bob1, alice]/[bob1]",list_aggregator="all",aggregator="average",outputs=[bob1,alice],references=references,list_match_mode="superset")awaiteval("list_aggregator=average; aggregator= average;[bob2, alice]/[bob1]",list_aggregator="average",aggregator="average",outputs=[bob2,alice],references=references,list_match_mode="superset")awaiteval("list_aggregator=all; aggregator= average;[bob2, alice]/[bob1]",list_aggregator="all",aggregator="average",outputs=[bob2,alice],references=references,list_match_mode="superset")

输出:

list_aggregator=average; aggregator= average;[bob1, alice]/[bob1]: json_match:average = 1.0 list_aggregator=all; aggregator= average;[bob1, alice]/[bob1]: json_match:average = 1 list_aggregator=average; aggregator= average;[bob2, alice]/[bob1]: json_match:average = 0.5 list_aggregator=all; aggregator= average;[bob2, alice]/[bob1]: json_match:average = 0

从语义上将superset匹配模式相当于希望outputs列表是reference_outputs列表的超集(Superset),适合如下的场景:

  • reference_outputs列表是权威列表
  • outputs集合可能包含更多元素(superset)
  • 希望每个reference_outputs元素都能找到最接近的outputs元素

subset匹配模式则这样相反,它希望outputs列表是reference_outputs列表的子集(Subset),相当于将两个集合对调过来。对于前面介绍的匹配流程和计分方式,将两个集合对调一下就是subset模式的算法。

3.3 更加严格的subset模式

对于subset模式来说,只要不影响outputs列表元素是否属于reference_outputs列表的判定,往reference_outputs列表添加任意数量的元素都不会改变最终的评估结果。same_elements则可以看成是subset模式的加强版,它按照subset模式相同的方式遍历outputs列表并从reference_outputs列表提取匹配度最高的元素,但是会对reference_outputs列表中未匹配的元素施加惩罚。这种模式非常适合要求outputsreference_outputs列表必须一致的应用场景。这是默认的匹配模式,我们在第二节针对list_aggregator的演示即使针对这种模式。

http://www.cnnetsun.cn/news/3930362.html

相关文章:

  • 如何永久备份微信聊天记录:开源工具WeChatMsg的完整解决方案
  • 汽车商城网站建设如何从0到1打造高转化率的线上购车平台?资深开发者真诚分享避坑指南
  • 风电功率预测数据集解析与LSTM模型实战
  • 终极指南:5个简单步骤在macOS上运行Windows应用
  • Arcade-plus:免费开源的专业Arcaea谱面编辑器终极指南
  • Claude Code Hooks:基于事件驱动的AI编程自动化实战指南
  • AI图像生成项目本地部署与测试全流程指南
  • Arcade-plus:免费开源的专业级Arcaea谱面编辑器完整指南
  • 邱县网站建设: 本地企业如何跳出低价陷阱,打造真正能获客的官方网站
  • Unity游戏AI知识库实战:基于RAG技术构建NPC专属记忆
  • 2026年辽宁做城市生命线安全工程建设的公司有哪些?
  • 3步解锁音乐自由:ncmdump网易云NCM格式解密完全指南
  • UE5蓝图实现2.5D动态设置菜单:3D模型交互与动画反馈系统
  • MuseTalk实战指南:5分钟学会AI唇音同步,让虚拟人说话更自然
  • AI Agent开发实战:从工具调用到工作流编排的完整指南
  • 腾讯云Lighthouse部署OpenClaw AI助手:从零到一的完整实践指南
  • 佛山网站建设与设计公司哪家好?揭秘优质服务商背后的选择智慧与避坑指南
  • ESP32音频流媒体架构设计:从本地存储到网络音频的完整实现路径
  • Python疫情数据可视化系统开发实战
  • APaaS平台一键安装实战:从Docker部署到生产环境优化
  • AI论文降重工具实战:嘎嘎降AI高效使用指南
  • 智能体框架版本升级踩坑指南:从Hermes v0.19问题看自动化工具稳健实践
  • NAND堆叠技术深度解析:从100层到900层,垂直堆叠的工程极限在哪里?
  • C++多态与虚函数实现详解
  • Illustrator脚本大全:12个提升Adobe设计效率的终极工具指南
  • 第5讲:数据集建设完整指南——质量决定上限
  • 分布式电源与储能系统优化规划及MATLAB实现
  • 商业网站建设的方法详解:如何打造高转化率的线上商业站点
  • go2_ros2_sdk实战:深度解析MID-360激光雷达多传感器集成方案
  • 基于Gemini与Playwright构建智能浏览器代理:从自动化到认知决策