[基于OpenEvals的自动化评估-05]基于JSON相似度的评估[LLM参与的语义匹配]

基于JSON内容的评估可以说是OpenEvals最重要的部分,基于OpenEvals的自动化评估-04:基于JSON相似度的评估对用于提供相关评估器的如下两个工厂函数从无LLM参与的角度进行了介绍。我们着重讨论了这两个函数的aggregatorlist_aggregatorlist_match_mode参数。其余的参数除了exclude_keys均与LLM-as-a-Judge有关。如果有了LLM加持,我们在评估的时候将不再仅限于严格的字符串比较,可以进行真正的基于自然语言的语义相似度比较,这样才能真正发挥AI的能力。

python 复制代码
def create_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",
) -> SimpleEvaluator

def create_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. 参数说明

create_json_match_evaluatorcreate_async_json_match_evaluatorLLM-as-a-Judge有关的参数由如下几个:

  • rubric:通过一个字段为JSON的每个字段定义评估规则;
  • judge :以ModelClient或者BaseChatModel的方式指定连接LLM的客户端;
  • model:以标准的形式指定作为评估模型的名称;
  • use_reasoning :是否开启推理,并使用推理文本来填充EvaluatorResultcommment字段。

除了用来定义评估规则的rubric字段,其余几个参数的作用和使用方式都与create_llm_as_judge/create_async_llm_as_judge的同名参数相同,具体可以参考前面的文章基于OpenEvals的自动化评估-02:LLM-as-a-Judge:让LLM当裁判来评估Agent的输出

2. 根据自定义规则进行基于语义的评估

接下来我们通过如下几个例子来演示如何利用LLM根据我们自定义的规则对JSON的字段实施评估。我们依然沿用上一篇提供的演示程序,为了能够给出评估的理由,我们修改了辅助函数pretty_printEvaluatorResultcomment字段进行了输出。

python 复制代码
import asyncio
from openevals.json import create_async_json_match_evaluator
from openevals.types import EvaluatorResult
from langchain_openai import ChatOpenAI
from dotenv import load_dotenv

load_dotenv()

def create(name:str, age:int, **kwargs)->dict:
    person =  {"name":name, "age":age}
    return {**person, **kwargs}

def pretty_print(results: EvaluatorResult|list[EvaluatorResult]):
    if(isinstance(results, list)):
        for result in results:
            print(f"""\
{result['key']} = {result['score']}
{result['comment']}
""")
    else:
       print(f"""\
{results['key']} = {results['score']}
{results['comment']}
""")
    print()

async def eval(
    *,
    outputs: dict|list[dict],
    references: dict|list[dict],
    **kwargs):

    evaluator = create_async_json_match_evaluator(**kwargs)  
    results = await evaluator(outputs=outputs, reference_outputs=references)
    pretty_print(results)
    
async def main():
    rubric:dict[str, str] = {
        "name":"conduct strictly string comparison.",        
        "aget":"conduct strictly numeric comparison."
    }
    llm = ChatOpenAI(model="gpt-5.4-mini")
    alice1 = create("Alice",20)
    alice2 = create("alice",20)
    await eval(outputs=alice1, references=alice2, rubric=rubric, judge=llm)

如上面的代码所示,我们在调用eval函数的时候,指定了如下两个参数:

  • rubric :为评估JSON的两个字段nameage定义了评估规则,要求它们进行严格的字符串和数值比较;
  • judge :指定了一个ChatOpenAI对象用来调用评估模型(gpt-5.4-mini

如下所示的输出满足我们的预期:指定的name由于大小写不一致被判定为不匹配,并给出了理由;age数值相同,视为匹配。

复制代码
json_match:age = 1
None

json_match:name = False
The output value is 'Alice' while the expected value is 'alice'. Since the criterion is strict string comparison, the case mismatch means they do not match.

现在我们对main函数做了如下的修改,在提供的rubric中对字典name的评估规则进行了修改,让LLM采用大小写不敏感的方式进行字符串比较。执行之后可以发现两个字段都是匹配的。

python 复制代码
async def main():
    rubric:dict[str, str] = {
        "name":"conduct string comparison with case-insensitive mode.",        
        "aget":"conduct strictly numeric comparison."
    }
    llm = ChatOpenAI(model="gpt-5.4-mini")
    alice1 = create("Alice",20)
    alice2 = create("alice",20)
    await eval(outputs=alice1, references=alice2, rubric=rubric, judge=llm)

输出:

复制代码
json_match:age = 1
None

json_match:name = True
The output value 'Alice' matches the expected value 'alice' under case-insensitive string comparison.

现在我们进一步,为两个字典定义如下的评估规则:

  • name:作为人名,可能提供姓(Last Name)、名(First Name)和姓名(Full Name),我们认为只要任意部分匹配就可以了;
  • age:只要年龄差距不超过5岁也被视为匹配。

在如下的演示程序中,我将上述的评估规则应用到rubric参数上。在调用eval函数进行评估时传入的两个对象,其nameage都不相同,但是满足上述的规则,所以LLM将它们都判定为匹配:

python 复制代码
async def main():
    rubric:dict[str, str] = {
        "name":"Considered matched if the same first name or last name is mentioned in both output value and referenced value",        
        "age":"Considered matched if the absolute difference between the output value and referenced value is less than 5."
    }
    llm = ChatOpenAI(model="gpt-5.4-mini")
    alice1 = create("Alice",20)
    alice2 = create("alice gates",23)
    await eval( outputs=alice1, references=alice2, rubric=rubric, judge=llm)

输出:

复制代码
json_match:age = True
The output age is 20 and the expected age is 23. The absolute difference is 3, which is less than 5, so it matches the criterion.

json_match:name = True
The output value 'Alice' shares the same first name as the expected value 'alice gates' (case-insensitive match), so it satisfies the criterion.

3. 忽略指定的字段

上一篇和这篇文章基本围绕着create_json_match_evaluatorcreate_async_json_match_evaluator这两个创建JSON评估器的工厂函数展开介绍,我们基本上完整介绍了两个函数的所有参数,除了exclude_keys。这个参数的语义很明确,就是忽略评估JSON中携带的某些字段。

如下面的演示程序所示,我在rubric参数中添加一个针对生日字段(birth)的评估规则,并在作为outputs的对象上同时添加了birthgender两个字段,但是利用exclude_keys参数加将后者忽略。所以输出只包含nameagebirth三个字段的评估结果:

python 复制代码
async def main():
    rubric:dict[str, str] = {
        "name":"Considered matched if the same first name or last name is mentioned in both output value and referenced value",        
        "age":"Considered matched if the absolute difference between the output value and referenced value is less than 5.",
        "birth":"Conduct date (without time) based comparison."
    }
    llm = ChatOpenAI(model="gpt-5.4-mini")
    alice1 = create("Alice",20, birth="1981-08-24", gender="female")
    alice2 = create("alice gates",23, birth="24 AUG 1981")
    await eval(outputs=alice1, references=alice2, rubric=rubric, judge=llm, exclude_keys=["gender"])

输出:

复制代码
json_match:age = True
The output age is 20 and the expected age is 23. The absolute difference is 3, which is less than 5, so it matches the criteria.

json_match:birth = True
The output date 1981-08-24 matches the expected date 24 AUG 1981 when compared by date only, ignoring format and time.

json_match:name = True
The output value 'Alice' shares the same first name as the expected value 'alice gates' (case-insensitive match on Alice/alice), so it matches the criterion.
相关推荐
破烂pan40 分钟前
AI-Agent-Book第一章思考题
人工智能·agent
赵大仁2 小时前
Agent 安全:沙箱、权限、Prompt 注入与审计
ai·大模型·agent·ai安全·合规
Loveyourself3 小时前
Claude Code Memory 总体系核心代码逐行解析
面试·agent
demo007x3 小时前
CoT(Chain-of-Thought)
程序员·llm·agent
FlyWIHTSKY3 小时前
使用langchain框架开发智能问答系统,需要有mcp服务和工具调用
langchain
AI分享猿3 小时前
从大纲到成稿:百智云PPT如何用“一段文字“驱动整套演示文稿
ai·powerpoint·ppt
京东云开发者3 小时前
【全栈实践】第一个 AI Agent 项目:从零搭建 AI 音频创作助手(入门篇)
typescript·agent·vuex
阿里云云原生3 小时前
智能体构建与进化——Agent 开源开发者沙龙·广州站精彩回顾 & PPT 下载
云原生·agent
Joy T4 小时前
Agent 开源项目全景解析(下):LlamaIndex、Dify、FastGPT 与真实工程选型
langchain·开源·框架·agent·springai·langgraph·mcp
DigitalOcean5 小时前
Qwen 3.8 已上线 DigitalOcean 推理云平台
agent