更新时间:2026-09-17 GMT+08:00
分享

插件/MCP工具调用场景

在某些Agent场景中,最终答案是否正确只是结果的一部分,业务还要求模型必须按照预期调用插件、MCP工具,并正确传递参数、整合多步调用结果。这里的“多步工具调用”指的是Agent在处理一次用户输入时,可能与插件、MCP发生多次交互。

这类场景推荐使用自适应奖励对运行轨迹进行校验。

典型应用示例:员工信息查询助手

  • 用户输入:帮我查一下张三本周请假情况,并给出直属主管邮箱。
  • 期望行为:

    先调用员工检索MCP,根据“张三”定位员工ID。

    再调用请假查询插件,根据员工ID查询本周请假情况。

    再调用组织架构插件,根据员工ID查询直属主管邮箱。

    最终整合多次工具调用结果,返回给用户。

  • 场景挑战:

    模型可能跳过工具,直接根据经验编造结果。

    模型可能选错插件或MCP。

    模型可能在第一步获取到员工ID后,未将其正确传递给后续工具。

    最终答案看似合理,但执行路径不符合业务要求。

数据集构造示例

此类场景中,reference_output可以根据您的奖励逻辑灵活设计,不一定必须是Agent最终回复的文本。如果主要检查工具参数传递是否正确,可将reference_output设计为关键参数的校验基准值,例如员工ID。

表1 数据集设计

input(用户输入)

reference_output(校验基准)

帮我查一下张三本周请假情况,并给出直属主管邮箱

1001

帮我查一下李四本月出差记录,并给出直属主管邮箱

2058

奖罚机制配置思路

推荐使用自适应奖励。本示例采用分步累加计分方式:模型每正确完成一步工具调用,即可获得对应的部分分数,不需要等到所有步骤全部完成才给分。

与“全对得1分、错一步得0分”相比,分步累加计分能为强化学习提供更密集的正向反馈,有效引导模型逐步掌握复杂的多步工具调用链路。

import json

def parse_tool_input(raw_input):
    """安全解析工具入参,兼容字符串和字典两种格式"""
    if isinstance(raw_input, dict):
        return raw_input
    if isinstance(raw_input, str):
        try:
            return json.loads(raw_input)
        except Exception:
            return {}
    return {}

def cal_reward(payload):
    try:
        data = json.loads(payload)

        # 统一转为字符串,避免 JSON 中整型与 reference_output 字符串类型不匹配
        expected_emp_id = str(data.get("reference_output", ""))
        steps = data.get("trajectory", {}).get("steps", [])

        employee_search_idx = -1
        leave_query_idx = -1
        org_query_idx = -1

        for idx, step in enumerate(steps):
            # 只处理工具调用步骤
            if step.get("type") != "tool":
                continue
            # 只处理执行成功的步骤
            if step.get("status") != "success":
                continue

            tool_name = step.get("name", "")
            tool_params = parse_tool_input(step.get("input"))

            # 第一步:员工检索 MCP,无需校验参数,只要调用即可
            if tool_name == "mcp_employee_search" and employee_search_idx == -1:
                employee_search_idx = idx

            # 第二步:请假查询插件,需校验 emp_id 参数,假设插件中使用emp_id表示员工ID
            elif tool_name == "plugin_leave_query" and leave_query_idx == -1:
                if str(tool_params.get("emp_id", "")) == expected_emp_id:
                    leave_query_idx = idx

            # 第三步:组织架构插件,需校验 emp_id 参数,假设插件中使用emp_id表示员工ID
            elif tool_name == "plugin_org_query" and org_query_idx == -1:
                if str(tool_params.get("emp_id", "")) == expected_emp_id:
                    org_query_idx = idx

        # 分步累加计分
        score = 0.0
        if employee_search_idx != -1:
            score += 0.3
        if leave_query_idx != -1:
            score += 0.35
        if org_query_idx != -1:
            score += 0.35

        # 顺序校验:如果三步都完成,但调用顺序不符合预期,则扣回后两步的分数
        # 期望顺序:员工检索 → 请假查询 → 组织架构查询
        if (
            employee_search_idx != -1
            and leave_query_idx != -1
            and org_query_idx != -1
            and not (employee_search_idx < leave_query_idx < org_query_idx)
        ):
            score = 0.3

        return score

    except Exception:
        return 0.0

print(cal_reward({{payload}}))

代码说明

  • parse_tool_input函数

    step.input可能是JSON字符串,也可能已经是字典对象。直接读取字段前需要先统一解析,避免因格式不一致导致参数校验失败。

  • status校验

    只对status == "success"的步骤进行计分。工具被调用了但执行失败的情况不计入得分,避免对错误路径给予正向奖励。

  • reference_output类型统一

    data.get("reference_output")取到的值可能是字符串,而工具参数解析后可能是整型(例如1001)。统一转为字符串后再比较,避免因类型不同导致误判。

  • 分步累加计分

    三个步骤分别对应0.3、0.35、0.35分,模型每完成一步即可获得对应奖励。这种设计能为强化学习提供更密集的正向反馈,相比全部完成才给满分的方式,更有利于引导模型逐步掌握长链路任务。

  • 顺序校验

    如果三步工具都被调用成功,但顺序不符合预期(例如先查询了请假信息,再去检索员工),则扣回后两步的分数,只保留第一步的0.3分。这种设计的目的是在鼓励模型大胆尝试工具调用的同时,对错误的执行链路给予一定的纠正信号,而不是完全扣零分。

  • reference_output的灵活用法

    在本示例中,reference_output不再表示Agent应当回复给用户的文本,而是作为工具参数校验的基准值。这种用法适合奖励逻辑以过程校验为主的场景。您可以根据实际业务,灵活设计reference_output的含义和格式,只要奖励代码中的解析逻辑与之对应即可。

相关文档