# 插件/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的含义和格式，只要奖励代码中的解析逻辑与之对应即可。
  
 
