news 2026/8/15 8:25:40

[基于AgentEvals的自动化评估-04]全面优化面向LangGraph的轨迹评估[下篇]

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
[基于AgentEvals的自动化评估-04]全面优化面向LangGraph的轨迹评估[下篇]

为了彻底解决AgentEvals针对LagnGraph轨迹评估无法解决同一Superstep内多个节点并发执行的问题,我通过定义一个全新的graph_trajectory_matchgraph_trajectory_match_async函数提供一种更加灵活的评估方案。上篇提供了针对这种方案的编程体验,本篇介绍这这两个函数涉及的轨迹评估究竟是如何实现的。

1. 针对Agent执行轨迹的表达

我们先来回顾一下定义在AgentEvals中的如下这个GraphTrajectory类型,它用来表示作为工作流的Agent的执行轨迹。由于它表示的并非针对Agent的某一次单一调用的执行轨迹,而是站在Thread的角度,将同于Thread中的多次调用轨迹合并在一起,所以它的输入、执行结果和执行步骤都是一个列表,而且每个列表的长度是相同的。问题就出在steps字段使用节点名称列表来表示每次Agent调用的执行轨迹,无法确定执行的节点于Superstep之间的关系。

classGraphTrajectory(TypedDict):inputs:Optional[list[dict]]results:list[dict]steps:list[list[str]]

比如[“foo”,“bar”,“baz”]具有如下几种执行场景:

  • 三个节点在三个不同的Superstep中执行;
  • “foo"和"bar”,"baz"在两个相邻Superstep中执行;
  • “foo”,"bar"和"baz"在两个相邻Superstep中执行。

为此我定义了如下这个类似的GraphRunTrajectory,它仅仅表示针对单一Agent调用的执行轨迹。所以输入和执行结果都是一个字典,steps字段则是一个两层列表,第一个对应Superstep,第二个对应具体某个Superstep中执行的节点。

classGraphRunTrajectory(TypedDict):inputs:dict|Noneresults:dict|Nonesteps:list[list[str]]

针对前面列举的三种执行场景,steps字段可以通过如下的两层列表来表示:

  • [["foo"],["bar"],["baz"]]
  • [["foo"],["bar","baz"]]
  • [["foo","bar"],["baz"]]

2. 将匹配模式纳入评估基准

由于同一个Superstep涉及多个节点的并发执行,由此引入了四种针对单步的节点匹配模式:

  • Exact: 虽然LangGraph无法控制并发节点的执行顺序,但是如果并发节点针对具有依赖关系,也可以通过编程的手段来实现。这种情况下,相当于采用AgentEvals默认的评估模式;
  • Unordered:这是默认的匹配模式,将节点列表视为无序集合(set),元素一样即可;
  • Subset: 将节点列表视为无序集合(set),但要求执行的节点在指定的节点内。如果某个节点具有m个下游节点,只要执行n个节点(n<=m)就可以了,可以采用此模式;
  • Superset:将节点列表视为无序集合(set),但要求执行的节点包含指定的节点。如果某个节点具有m个下游节点,严格要求执行指定的一个或者多个下游节点,可以采用此模式。

我们将匹配模式应用到作为评估基准的GraphRunReferenceTrajectory类型上。GraphRunReferenceTrajectory是针对GraphTrajectory的评估基准,它们之间的不同之处在于其steps字段的定义,这里的集合元素不仅仅可以是表示节点名称列表的list[str]对象,还可以是一个ReferenceStep对象。

classGraphRunTrajectory(TypedDict):inputs:dict|Noneresults:dict|Nonesteps:list[list[str]]classReferenceStep(NamedTuple):steps:list[str]match_mode:GraphTrajectoryMatchModel="unordered"classGraphRunReferenceTrajectory(TypedDict):inputs:dict|Noneresults:dict|Nonesteps:list[ReferenceStep|list[str]]

ReferenceStep表示针对一个Superstep的评估轨迹基准,steps字段表示节点列表,match_mode表示评估时采用的匹配规则。如果直接使用list[str]对象,意味着使用默认的Unordered匹配模式。

3. 同步轨迹评估

同步版本的轨迹评估实现在如下这个graph_trajectory_match函数中,它具有如下三个参数:

  • outputs: 待评估的执行轨迹,是一个GraphRunTrajectory对象的列表;
  • reference_outputs:执行轨迹评估基准,是一个GraphRunReferenceTrajectory对象的列表;
  • eval_results:是否在评估轨迹的时候验证执行结果是否相同,默认为False

针对单次Agent调用的轨迹评估实现在_scorer函数中,逻辑其实很简单:先验证表示轨迹的列表长度是否相同,在遍历针对每一步的节点列表,根据对应评估基准指定的匹配模式进行对比就可以了。如果开启了eval_results开关,则先比较执行结果是否一致。

def_scorer(*,outputs:GraphRunTrajectory,reference_outputs:GraphRunReferenceTrajectory,eval_results:bool)->bool:ifeval_resultsandoutputs["results"]!=reference_outputs["results"]:returnFalseforout_step,raw_ref_stepinzip(outputs["steps"],reference_outputs["steps"]):ref_step:ReferenceStep=raw_ref_stepifisinstance(raw_ref_step,ReferenceStep)elseReferenceStep(raw_ref_step,"exact")match_mode=ref_step.match_modeifmatch_mode=="exact"andout_step!=ref_step.steps:returnFalseout_step_set=set(out_step)ref_step_set=set(ref_step.steps)matchmatch_mode:case"unordered":ifout_step_set!=ref_step_set:returnFalsecase"subset":ifnotout_step_set.issubset(ref_step_set):returnFalsecase"superset":ifnotout_step_set.issuperset(ref_step_set):returnFalsereturnTruedefgraph_trajectory_match(*,outputs:list[GraphRunTrajectory],reference_outputs:list[GraphRunReferenceTrajectory],eval_results:bool=False)->EvaluatorResult:ifoutputsisNoneorreference_outputsisNoneorlen(outputs)!=len(reference_outputs):raiseValueError("Strict trajectory match requires both outputs and reference_outputs")matric_name="graph_trajectory_match"result:EvaluatorResultforoutput,referenceinzip(outputs,reference_outputs):result=cast(EvaluatorResult,_run_evaluator(run_name=matric_name,scorer=functools.partial(_scorer,eval_results=eval_results),feedback_key=matric_name,outputs=output,reference_outputs=reference,))ifnotcast(bool,result.get("score",False)):comment=f"""\ Trajectory not match. outputs:{output}reference_outputs:{reference}"""return{**result,"comment":comment}# type: ignorereturnresult# type: ignore

graph_trajectory_match函数会先验证outputsreference_outputs在长度上是否匹配,在从中依次提取出GraphRunTrajectoryGraphRunReferenceTrajectory,通过调用_run_evaluator函数借助_scorer函数实施评估。如果评估没有通过,直接返回生成的EvaluatorResult对象。为了利于Debug,我们会将待评估轨迹和基准轨迹放到comment字段中。

4. 异步轨迹评估

同步版本的轨迹评估实现在如下这个graph_trajectory_match_async函数中,其实现的本质就是利用async_wrapper函数将针对同步函数_scorer的调用转换成异步形式,然后在每次迭代中通过调用_arun_evaluator函数利用async_wrapper函数实施评估。

asyncdefgraph_trajectory_match_async(*,outputs:list[GraphRunTrajectory],reference_outputs:list[GraphRunReferenceTrajectory],eval_results:bool=False)->EvaluatorResult:asyncdefasync_wrapper(**kwargs:Any):return_scorer(eval_results=eval_results,**kwargs)ifoutputsisNoneorreference_outputsisNoneorlen(outputs)!=len(reference_outputs)orlen(outputs)==0:raiseValueError("Strict trajectory match requires both outputs and reference_outputs")matric_name=f"graph_trajectory_match"result:EvaluatorResultforoutput,referenceinzip(outputs,reference_outputs):result=cast(EvaluatorResult,await_arun_evaluator(run_name=matric_name,scorer=async_wrapper,feedback_key=matric_name,outputs=output,reference_outputs=reference,))ifnotcast(bool,result.get("score",False)):comment=f"""\ Trajectory not match. outputs:{output}reference_outputs:{reference}"""return{**result,"comment":comment}# type: ignorereturnresult# type: ignore
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/15 8:18:46

《暗淡的未来》的传播入口:不确定感如何形成试听理由

当加班后坐上回程的人走到下班后的车厢或出租屋门口&#xff0c;《暗淡的未来》往往会比空泛安慰更先开口——不是要你热闹起来&#xff0c;而是把说不清的那截情绪&#xff0c;轻轻按进旋律里。《暗淡的未来》适合被放进一个具体时刻里理解&#xff1a;城市傍晚&#xff0c;人…

作者头像 李华
网站建设 2026/8/15 8:16:56

MySQL查询优化实战:从基础语法到索引设计与性能调优

1. 从“查”开始&#xff1a;为什么你需要一份自己的MySQL语句手册 每次接手一个新项目&#xff0c;或者隔了几个月再回头维护老代码&#xff0c;面对数据库时&#xff0c;你是不是也经常有这种感觉&#xff1a;这个查询条件怎么写来着&#xff1f;那个统计函数的具体参数是啥&…

作者头像 李华
网站建设 2026/8/15 8:14:10

Git Rebase操作详解与SourceTree实战指南

1. SourceTree中Rebase操作的核心价值 作为一名长期使用Git进行版本控制的开发者&#xff0c;我深刻体会到代码提交历史整洁的重要性。SourceTree作为一款优秀的Git图形化工具&#xff0c;其Rebase功能能够帮助我们重构提交历史&#xff0c;让分支合并更加清晰有序。与传统的me…

作者头像 李华
网站建设 2026/8/15 8:09:48

Claude Code 高效使用方法

引言 Claude Code 的定位并非代码补全工具或问答机器人&#xff0c;而是一个拥有终端权限的编程智能体。这一本质差异决定了它的使用范式与传统 IDE 插件或聊天式 AI 存在根本不同。然而&#xff0c;许多开发者将其视为“能写代码的搜索引擎”&#xff0c;以零散、模糊的指令与…

作者头像 李华
网站建设 2026/8/15 8:06:25

LeetCode 430:多级双向链表扁平化算法详解与实现

1. 项目概述&#xff1a;当链表有了“孩子”——多级双向链表的扁平化挑战 如果你刷过一些链表题&#xff0c;可能会觉得单链表、双向链表都已经是老朋友了。但LeetCode 430这道“扁平化多级双向链表”的题目&#xff0c;第一次看到时&#xff0c;可能会让人有点懵。什么是“多…

作者头像 李华