McpAgentExecutor 混合挂载:HTTP 工具与 NPX 服务器同时接入同一 Agent

标签 :Java MCP Agent j-langchain McpAgentExecutor 混合工具 Function Calling
前置阅读 :McpAgentExecutor:用几行代码让模型自主调用 HTTP 工具多步推理 → McpAgentExecutor + McpClient:让 Agent 直接操作文件系统和数据库
适合人群:已分别接入 HTTP 工具和 NPX 服务器,希望在同一个 Agent 中串联使用的 Java 开发者


一、从单一来源到混合来源

前两篇文章分别介绍了两种工具来源:

  • 文章 12 :McpManager + mcp.config.json → HTTP API 工具(查 IP、查天气、调内部接口)
  • 文章 13 :McpClient + mcp.server.config.json → NPX MCP 服务器(文件系统、数据库、记忆存储)

但真实业务中,一个任务往往横跨两类工具。典型的场景是:先调用 HTTP API 获取数据,再把结果写入文件或存入数据库。如果为这类任务专门维护两个 Agent、手动拼接结果,不仅代码繁琐,还引入了不必要的协调层。

McpAgentExecutor 支持链式 .tools(...) 调用,可以在同一个 Agent 上同时挂载多个来源的工具。模型在推理时能自由选择 HTTP 工具或 NPX 工具,开发者不需要关心工具的来源。


二、核心用法:链式注册工具

java 复制代码
McpAgentExecutor agent = McpAgentExecutor.builder(chainActor)
    .llm(ChatAliyun.builder().model("qwen3.6-plus").temperature(0f).build())
    .tools(mcpManager, "default")         // 第一层:HTTP 工具(get_export_ip 等)
    .tools(mcpClient, "filesystem")       // 第二层:NPX filesystem 工具
    .systemPrompt("""
        你是一个智能助手,可以调用工具获取网络信息和操作文件系统。
        当你完成用户的所有要求后,直接给出最终答案,不要再调用任何工具。
        """)
    .maxIterations(8)
    .onToolCall(tc -> System.out.println(">> Tool: " + tc))
    .onObservation(obs -> System.out.println(">> Result: " + obs))
    .build();

两次 .tools(...) 调用的工具列表会在框架内部合并,统一转成 Function Calling Schema 交给模型。注册顺序不影响功能,但有一点需要注意:如果两个来源中存在同名工具,后注册的会覆盖前一个 ,建议在 mcp.config.json 和服务器命名时按业务域区分,避免冲突。


三、完整示例:查询公网 IP 并写入文件

java 复制代码
@Test
public void mcpMixedAgent() {

    McpAgentExecutor agent = McpAgentExecutor.builder(chainActor)
        .llm(ChatAliyun.builder().model("qwen3.6-plus").temperature(0f).build())
        .tools(mcpManager, "default")
        .tools(mcpClient, "filesystem")
        .systemPrompt("""
            你是一个智能助手,可以调用工具获取网络信息和操作文件系统。
            当你完成用户的所有要求后,直接给出最终答案,不要再调用任何工具。
            """)
        .maxIterations(8)
        .onToolCall(tc -> System.out.println(">> Tool: " + tc))
        .onObservation(obs -> System.out.println(">> Result: " + obs))
        .build();

    ChatGeneration result = agent.invoke(
        "帮我查一下公网 IP,然后把 IP 地址写入 /tmp/my_ip.txt 文件,读取文件内容确认写入成功"
    );

    System.out.println("\n=== 最终答案 ===");
    System.out.println(result.getText());
}

四、完整推理轨迹

任务横跨三个工具------一个来自 HTTP,两个来自 NPX 服务器------模型自主完成全部调用:

复制代码
>> Tool: get_export_ip -> {}
>> Result: 123.117.177.40

>> Tool: write_file -> {"path": "/tmp/my_ip.txt", "content": "123.117.177.40"}
>> Result: 写入成功

>> Tool: read_file -> {"path": "/tmp/my_ip.txt"}
>> Result: 123.117.177.40

=== 最终答案 ===
公网 IP 已成功写入 /tmp/my_ip.txt,读取确认内容为 123.117.177.40。

模型没有任何"这个工具来自 HTTP、那个工具来自 NPX"的概念,它只看到一份统一的工具列表,根据任务需要自行选择。这是混合挂载最核心的价值:工具的来源对模型透明,对开发者也不需要额外处理。


五、工具列表越大越好吗?

把所有工具都挂到一个 Agent 上听起来很方便,但实际使用中工具列表的大小对效果有明显影响。

工具越多,模型选错的概率越高。 Function Calling 本质上是让模型从一组候选函数中选出最合适的,候选集越大,干扰越多。实际测试中,工具数量超过 15~20 个时,模型的选择准确率会有明显下降。

推荐做法是按任务拆分工具组,而不是把所有工具塞进一个 Agent:

json 复制代码
// mcp.config.json
{
  "network": ["get_export_ip", "get_ip_location", "get_weather_open_meteo"],
  "crm":     ["query_customer", "update_order", "send_notification"],
  "ops":     ["check_server_status", "get_disk_usage", "restart_service"]
}

不同 Agent 只加载自己需要的工具组:

java 复制代码
// 网络诊断 Agent:只挂 network 组 + filesystem
.tools(mcpManager, "network")
.tools(mcpClient, "filesystem")

// 客服 Agent:只挂 crm 组
.tools(mcpManager, "crm")

// 运维 Agent:只挂 ops 组 + memory
.tools(mcpManager, "ops")
.tools(mcpClient, "memory")

这样每个 Agent 的工具列表保持在 5~10 个,选择准确率更高,System Prompt 也更容易写得具体。


六、System Prompt 写法对混合工具的影响

挂载多来源工具后,System Prompt 的质量对 Agent 的表现影响更大。以下两种写法的效果差距明显:

模糊写法(容易出问题):

复制代码
你是一个智能助手,可以调用工具回答用户问题。

模型可能会在工具之间来回调用,或者在任务完成后继续调用不必要的工具。

具体写法(推荐):

复制代码
你是一个网络诊断助手。
任务流程:先用 HTTP 工具获取网络信息,再用文件系统工具保存结果,保存完成后直接给出结论,停止调用工具。
每个工具最多调用一次,不要重复查询相同信息。

明确的任务流程描述能显著减少模型的无效调用,也能让 maxIterations 的设置更加精准。


七、maxIterations 怎么设置

混合挂载场景下,一次任务可能横跨多个工具,maxIterations 的设置需要更仔细估算。

基本原则:预估最坏情况下的工具调用次数,再加 2~3 作为 buffer。

以本例为例:

  • get_export_ip(1次)
  • write_file(1次)
  • read_file(1次)
  • 最坏情况合计 3 次 Action

设置 maxIterations(8) 是足够的。如果任务更复杂(如查多个数据源、写多个文件),相应调大即可。

设得过大的副作用是:当模型陷入重复调用时,需要消耗更多轮次才能触发终止,浪费 Token。System Prompt 里加一句"完成后停止调用工具"能有效减少这种情况。


八、总结

工具来源 对应配置 注册方式
HTTP API mcp.config.json .tools(mcpManager, "分组名")
NPX MCP 服务器 mcp.server.config.json .tools(mcpClient, "服务器别名")
两者混合 两个配置文件各配各的 链式调用两次 .tools(...)

混合挂载没有引入任何新概念,只是把前两篇文章的两行 .tools(...) 拼在一起。框架负责合并工具列表、管理推理循环,开发者只需关注工具分组是否合理 和System Prompt 是否清晰这两件事。

工具分组拆得越细,每个 Agent 的职责越单一,模型的选择准确率也越高。


📎 相关资源

相关推荐
学长毕设2 小时前
基于SpringBoot的实验室资产管理系统
java·spring boot·后端
zzu123zsw4 小时前
从臃肿的模板调用到声明式契约:深入 UniHttp 动态代理与多渠道生命周期扩展
java·spring boot·microsoft·unihttp
源码集结号4 小时前
云诊所门诊系统源码,基于SpringBoot+Vue.js架构,支持多租户设计与Docker容器化部署
java·spring boot·源码·医院信息系统·门诊系统·诊所系统
论文复现现场5 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
啃瓜子的松鼠5 小时前
泛微OAe9-后端二开异步调用
java
xrlfreedom5 小时前
MCP 面试高频考点:桌面客户端 stdio 连接失败,如何用 Resources、结构化输出与 OpenTelemetry 排查?
resources·opentelemetry·结构化输出·mcp
秋天的怀念_David6 小时前
第二十一夜 · 数字黑洞与西西弗斯的惩罚
java·程序人生·算法
外收内放6 小时前
Python基础语法练习题(73)
java·前端·python
Wang's Blog6 小时前
Java 项目实战: 交友项目 - 环信后台发送消息测试
java·交友
用户3721574261356 小时前
Java 操作 Word 脚注和尾注:添加、读取与删除
java