游乐游手机版
首页/AI教程/文章详情

MCP+Function Calling实现模型自主驱动工具链多步推理

时间:2026-06-08 16:14
一、前两篇做了什么,这篇做什么先说个背景,之前写过两篇相关文章。第一篇介绍了 AgentExecutor,让模型通过 ReAct 格式的纯文本输出来驱动工具调用——说白了,就是让模型输出类似 Action: xxx Action Input: yyy 这样的文本,然后我们去解析它。这套方案的好处

一、前两篇做了什么,这篇做什么

先说个背景,之前写过两篇相关文章。第一篇介绍了 AgentExecutor,让模型通过 ReAct 格式的纯文本输出来驱动工具调用——说白了,就是让模型输出类似 Action: xxx / Action Input: yyy 这样的文本,然后我们去解析它。这套方案的好处是不挑模型,什么模型都能用,但本质上还是在“读模型写的文章”来判断意图。

MCP + Function Calling:让模型自主驱动工具链完成多步推理

第二篇讲的是 MCP 的基础接入方式:用 McpManager 注册 HTTP 工具,手动调用 run() 执行,然后把结果拼回 Prompt 里。这个方式倒是能跑通,也能验证工具是否可用,但问题是——整个过程中,“调哪个工具”、“传什么参数”,模型根本就没有参与决策。

这一篇要解决的就是这个核心问题:让模型和 MCP 真正配合起来,实现一个完整的 Agent 功能。具体做法是把 MCP 的工具清单直接注册给支持 Function Calling 的模型。这样模型就不再输出文本格式的 Action,而是输出结构化的 ToolCall——包含函数名和 JSON 参数。开发者只需要负责执行这个 ToolCall,把结果写回 Prompt,然后让模型继续推理就行了。

这三种方式的核心区别,一句话说清楚:

方式谁决定调哪个工具工具参数格式模型要求
ReAct 文本驱动模型(文本输出)字符串 / JSON 文本任意
Function Calling模型(结构化输出)标准 JSON Schema需支持 Function Calling

二、场景:三步连贯推理,全程无人工介入

这一篇要做的场景挺有意思:自动检测当前环境下的公网 IP、定位城市、然后查询天气。整个过程需要用到三个工具,按顺序调用:

  1. get_export_ip:获取本机公网出口 IP
  2. get_ip_location:根据 IP 查询城市、经纬度、ISP 信息
  3. get_weather_open_meteo:根据经纬度查询实时天气

这三个工具是串联的——上一步的返回值就是下一步的输入参数。用户只需要说一句话,Agent 就会自主完成全部推理和工具调用,最终给出一个包含“位置 + 天气”的总结。而且这三个工具都在 mcp.config.jsondefault 分组里声明好了,不需要改任何代码,框架就能自动加载。

三、核心代码逐步拆解

第一步:构建 Prompt 并注入工具清单

// Prompt 模板:系统指令明确调用顺序和输出格式
BaseRunnable prompt = ChatPromptTemplate.fromMessages(List.of(
    BaseMessage.fromMessage(MessageType.SYSTEM.getCode(),
        """
        你是一名能够调用 MCP HTTP 工具的智能体,需要按以下顺序完成任务:
        1) 调用 get_export_ip 获取公网 IP;
        2) 将该 IP 传给 get_ip_location,获取城市、经纬度以及网络信息;
        3) 使用经纬度调用 get_weather_open_meteo,并设置 current_weather=true;
        4) 总结位置与天气(只输出结论,不暴露工具名称)。
        工具只在必要时调用,每个工具最多执行一次。
        """
    ),
    BaseMessage.fromMessage(MessageType.HUMAN.getCode(), "用户问题:${input}")
));

// manifestForInput() 把 mcp.config.json 转成模型所需的 JSON Schema 格式
List tools = mcpManager.manifestForInput().get("default");

// 把工具清单注册给 LLM,模型推理时会自动决定何时调用哪个工具
ChatAliyun llm = ChatAliyun.builder()
    .model("qwen3.6-plus")
    .temperature(0f)
    .tools(tools)
    .build();

注意这里的 System Prompt 写得比较细致——明确告诉模型要按照什么顺序调用工具。这样做有两个好处:一是能减少模型漏掉关键步骤的概率,二是调试起来更容易判断是哪个环节出了问题。

第二步:循环条件——有 ToolCall 就继续

int maxIterations = 5;
Function shouldContinue = round -> {
    if (round >= maxIterations) {
        return false; // 防止死循环
    }
    if (round == 0) {
        return true; // 第一轮必须执行
    }
    // 检查上一轮 LLM 输出是否包含 ToolCall
    AIMessage lastAi = ContextBus.get().getResult(llm.getNodeId());
    return lastAi instanceof ToolMessage toolMessage &&
           CollectionUtils.isNotEmpty(toolMessage.getToolCalls());
};

退出条件其实很直观:模型如果不再输出 ToolCall,说明它已经拿到了足够的信息,准备给出最终回答了。

第三步:核心处理器——执行 ToolCall 并写回 Observation

这是整个链路最核心的一段代码:

TranslateHandler executeMcpTool = new TranslateHandler<>(msg -> {
    ChatPromptValue promptValue = ContextBus.get().getResult(prompt.getNodeId());

    // 不是 ToolCall,说明模型已在生成最终回答,直接透传
    if (!(msg instanceof ToolMessage toolMessage)) {
        return msg;
    }
    if (CollectionUtils.isEmpty(toolMessage.getToolCalls())) {
        return toolMessage;
    }

    // 1. 把模型的 ToolCall 请求记入对话历史
    promptValue.getMessages().add(toolMessage);

    // 2. 解析 ToolCall:拿到工具名和参数
    AiChatOutput.ToolCall call = toolMessage.getToolCalls().get(0);
    Map args = parseArgs(call.getFunction().getArguments());
    String toolName = call.getFunction().getName();

    // 3. 用 McpManager 执行真实 HTTP 请求
    System.out.println("[ToolCall] " + toolName + " params -> " + JsonUtil.toJson(args));
    Object result = mcpManager.runForInput("default", toolName, args);
    String observation = result != null ? result.toString() : "工具无返回内容";
    System.out.println("[Observation] " + observation);

    // 4. 把执行结果以 ToolMessage 形式追加到对话历史,供模型下一轮参考
    appendToolMessage(prompt, call, observation);
    return ContextBus.get().getResult(prompt.getNodeId());
});

这里 appendToolMessage 的作用是把工具的返回结果以标准的 ToolMessage 格式写回 Prompt。这样模型在下一轮推理时就能看到完整的上下文——它知道自己调了什么工具、得到了什么结果。

第四步:组装完整链路

FlowInstance chain = chainActor.builder()
    .next(prompt)
    .loop(shouldContinue,
        llm,
        chainActor.builder()
            .next(
                Info.c(needsToolExecution, executeMcpTool),
                Info.c(input -> ContextBus.get().getResult(llm.getNodeId()))
            )
            .build()
    )
    .next(new StrOutputParser())
    .build();

ChatGeneration finalAnswer = chainActor.invoke(chain, Map.of(
    "input", "不要询问额外信息,自动检测我的公网 IP,推断所在城市并告知当前天气后统一回复。"
));
System.out.println(finalAnswer.getText());

整个链路的结构其实非常清晰:Prompt → 循环( LLM → [有ToolCall? 执行MCP : 透传] ) → 输出最终回答

四、完整推理过程

运行之后控制台会打印完整的推理轨迹:

> MCP Function-Calling ReAct 链开始执行...
[ToolCall] get_export_ip params -> {}
[Observation] 123.117.177.40
[ToolCall] get_ip_location params -> {"ip": "123.117.177.40"}
[Observation] {"country_name":"China","region_name":"Beijing Shi","city":"Dongcheng Qu", "latitude":39.9117,"longitude":116.4097,"org":"AS4134 Chinanet"}
[ToolCall] get_weather_open_meteo params -> {"latitude":39.9117,"longitude":116.4097,"current_weather":"true"}
[Observation] {"current_weather":{"temperature":18.3,"windspeed":6.1,"weathercode":1}}
> 链执行完成。
=== 最终回答 ===
检测到你的公网 IP 位于中国北京市东城区,当前温度约 18°C,天气晴朗,风速 6.1 km/h。

可以看到,模型精确地执行了三次 ToolCall,没有遗漏任何一步,也没有多余的调用。每次 Observation 被写回 Prompt 后,模型在下一轮推理中就能自动提取所需字段——从 IP 到经纬度再到天气,整个过程不需要开发者做任何字段映射。

五、和 ReAct 文本驱动的本质区别

很多开发者会问:AgentExecutor 也能完成多步工具调用,这两种方式到底有什么实质区别?

这么说吧,ReAct 文本驱动(AgentExecutor)的方式下,模型输出的是纯文本,比如:

Action: get_ip_location
Action Input: {"ip": "123.117.177.40"}

框架需要通过字符串解析来提取工具名和参数——本质上是“读模型写的文章”。

而 Function Calling(本篇采用的方式)下,模型输出的是结构化的 JSON:

{
    "toolCalls": [
        {
            "function": {
                "name": "get_ip_location",
                "arguments": "{\"ip\":\"123.117.177.40\"}"
            }
        }
    ]
}

框架直接解析 JSON,工具名和参数都是强类型字段,不存在格式歧义的问题。

这两种方式各有各的适用场景:

场景推荐方式原因
模型不支持 Function CallingReAct 文本驱动唯一可用选项
参数复杂(嵌套 JSON、数组)Function Calling结构化输出更可靠
需要精确控制推理文本ReAct 文本驱动Thought 内容完全可读
对接标准 MCP 工具生态Function Calling工具 Schema 天然匹配
模型稳定性要求高Function Calling减少格式解析失败

六、工具调用失败怎么处理

真正上线之后,你就会发现生产环境里什么意外都可能发生:网络超时、参数错误……HTTP 工具调用失败是家常便饭。框架的处理方式是这样的:把错误信息同样以 ToolMessage 的形式写回 Prompt,让模型感知到“这次工具调用失败了”,然后由模型自己决定是重试、跳过还是向用户说明原因:

try {
    Object result = mcpManager.runForInput("default", toolName, args);
    String observation = result != null ? result.toString() : "工具无返回内容";
    appendToolMessage(prompt, call, observation);
} catch (Exception e) {
    log.error("调用 MCP 工具 {} 失败: {}", toolName, e.getMessage(), e);
    // 把错误信息写回 Prompt,模型会在下一轮 Thought 中处理
    appendToolMessage(prompt, call, "调用失败:" + e.getMessage());
}

这种做法比直接抛出异常要友好得多——模型可以在最终回答中说“天气数据获取失败,已为您提供位置信息”,而不是让整个链路直接崩溃。

七、总结

这一篇展示的是一个完整的“MCP + Function Calling”多步推理链路。和之前手动调用的方式相比,这里的工具执行顺序和参数完全由模型决定;和 ReAct 文本驱动的方式相比,这里用的是模型原生的 ToolCall 输出,参数解析更可靠。

核心思路其实就一句话:把 MCP 工具清单交给模型,让模型决定调什么,开发者只负责执行和回写结果。

也许你觉得这段循环逻辑还是有点绕。没关系,下一篇文章会介绍 McpAgentExecutor——用一行代码搞掂整个流程。

来源:https://juejin.cn/post/7631938951263698995
上一篇AI网络技术演进重塑路由协议分析(下) 下一篇WPS AI一键生成PPT快速缩短准备时间
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Windows Docker Desktop RabbitMQ生产级部署完整指南
AI教程 · 2026-06-29

Windows Docker Desktop RabbitMQ生产级部署完整指南

前言 在 Windows 本地开发环境中,直接安装 RabbitMQ 确实颇为周折:需要单独配置 Erlang 运行环境、手动管理环境变量、服务启停全凭手工操作。更令人困扰的是,版本兼容冲突、端口占用、环境不一致等问题层出不穷。笔者见过不少开发者为搭建环境就得耗费整整半天时间。 相比之下,借助 Do

AI搜索重构制造业采购逻辑的阿里云企业级GEOCMS优化实践
AI教程 · 2026-06-29

AI搜索重构制造业采购逻辑的阿里云企业级GEOCMS优化实践

先分享一个切实感受。过去两年,我们与福建制造企业合作较为频繁,发现一个非常突出的现象:超过80%的企业官网,产品参数仍然存放在PDF或图片中。AI爬虫?根本无法抓取。这些企业技术实力不弱、资质证照齐全、应用案例也丰富,但在AI搜索这一全新战场上,它们几乎处于隐身状态。 一、一个正在发生的行业变化 A

阿里云Token Plan团队版功能价格与省钱购买指南
AI教程 · 2026-06-29

阿里云Token Plan团队版功能价格与省钱购买指南

阿里云百炼近期推出了名为“Token Plan 团队版”的全新服务,这一服务专为企业与开发者量身打造,定位为AI大模型订阅平台。通过引入Credits作为统一计量单位,将文本生成、图像生成等多模态AI能力纳入单一计费体系,同时无缝兼容主流AI编程工具及智能体(Agent)生态系统。其核心亮点包括:全

阿里云物联网.NET Core客户端位置信息上报
AI教程 · 2026-06-29

阿里云物联网.NET Core客户端位置信息上报

阿里云物联网平台的位置服务并非一个完全独立的功能模块。位置信息可包含二维坐标与三维坐标,而位置数据的来源本质上是借助设备属性进行上传。换言之,若要让设备上报位置,您需先将其视为一个普通属性进行处理。 1)添加二维位置数据 操作过程十分简洁。进入数据分析 → 空间数据可视化 → 二维数据,点击添加,将

年阿里云服务器选型配置与网站部署全攻略
AI教程 · 2026-06-29

年阿里云服务器选型配置与网站部署全攻略

2026年,阿里云服务器生态已高度成熟,形成了清晰的轻量应用服务器与ECS云服务器两大产品阵营。无论你是计划搭建个人博客、企业官网,还是运营电商平台、进行应用开发,基本都能找到理想的解决方案。本指南将从服务器选型、配置选择、部署流程到安全运维,系统梳理2026年最实用的操作要点,帮助你少走弯路,让网