插件/MCP工具调用场景
在某些Agent场景中,最终答案是否正确只是结果的一部分,业务还要求模型必须按照预期调用插件、MCP工具,并正确传递参数、整合多步调用结果。这里的“多步工具调用”指的是Agent在处理一次用户输入时,可能与插件、MCP发生多次交互。
这类场景推荐使用自适应奖励对运行轨迹进行校验。
典型应用示例:员工信息查询助手
- 用户输入:帮我查一下张三本周请假情况,并给出直属主管邮箱。
- 期望行为:
先调用员工检索MCP,根据“张三”定位员工ID。
再调用请假查询插件,根据员工ID查询本周请假情况。
再调用组织架构插件,根据员工ID查询直属主管邮箱。
最终整合多次工具调用结果,返回给用户。
- 场景挑战:
模型可能跳过工具,直接根据经验编造结果。
模型可能选错插件或MCP。
模型可能在第一步获取到员工ID后,未将其正确传递给后续工具。
最终答案看似合理,但执行路径不符合业务要求。
数据集构造示例
此类场景中,reference_output可以根据您的奖励逻辑灵活设计,不一定必须是Agent最终回复的文本。如果主要检查工具参数传递是否正确,可将reference_output设计为关键参数的校验基准值,例如员工ID。
| 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的含义和格式,只要奖励代码中的解析逻辑与之对应即可。