【SpringAIAlibaba新手村系列】(18)Agent 智能体与今日菜单应用

简介: 本章以 ReactAgent 为入口,将本地菜单工具与 MCP 外部工具合并注册,统一通过 /eatAgent 执行任务,展示 Agent 在多工具协同下的意图理解、工具调用与结果整合能力。

第十八章 Agent 智能体与今日菜单应用

版本标注

  • Spring AI: 1.1.2
  • Spring AI Alibaba: 1.1.2.0

章节定位

  • 本章在 Agent 入口上使用 ReactAgent
  • 同时把本地菜单工具与外部 MCP 工具合并注册,形成“本地能力 + 外部能力”的组合示例。

s01 > s02 > s03 > s04 > s05 > s06 > s07 > s08 > s09 > s10 > s11 > s12 > s13 > s14 > s15 > s16 > s17 > [ s18 ]


一、本章要做什么

这一章的目标不是再做一个“只有本地工具”的 Agent,而是把两类工具放进同一个 ReactAgent

  1. 本地工具:菜单推荐能力。
  2. 外部工具:通过 MCP Client 自动发现到的第三方工具。

这样可以直接验证:Agent 在一次对话里,可以同时调度本地 @Tool 和外部 MCP 工具。

二、和前面 ChatModel / ChatClient 的区别

前面章节里,ChatModel / ChatClient 更偏“发起一次对话调用”。

本章里的 ReactAgent 更偏“执行任务”:

  1. 理解用户意图。
  2. 判断要调用哪个工具。
  3. 调用工具并观察结果。
  4. 基于结果组织最终答案。

对于简单问题,这三者体感差异可能不大;但一旦涉及多工具组合,ReactAgent 的优势会更明显。


三、Saa18 当前可运行代码

3.1 配置类:合并本地工具与 MCP 工具

package cn.edu.nnu.opengms.config;

import cn.edu.nnu.opengms.service.MenuTools;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.support.ToolCallbacks;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Arrays;
import java.util.stream.Stream;

@Configuration
public class DashScopeConfig {
   
    @Bean
    public ReactAgent menuAgent(ChatModel chatModel, MenuTools menuTools, ToolCallbackProvider mcpTools)
    {
   
        ToolCallback[] localTools = ToolCallbacks.from(menuTools);
        ToolCallback[] externalTools = mcpTools.getToolCallbacks();

        ToolCallback[] allTools = Stream.concat(Arrays.stream(localTools), Arrays.stream(externalTools))
                .toArray(ToolCallback[]::new);

        return ReactAgent.builder()
                .name("menu-agent")
                .description("根据用户问题推荐菜单、解释菜品或查询天气")
                .model(chatModel)
                .tools(allTools)
                .build();
    }
}

这段代码是本章核心。

它做了三件事:

  1. ToolCallbacks.from(menuTools):加载本地菜单工具。
  2. mcpTools.getToolCallbacks():加载外部 MCP 工具。
  3. 合并后通过 .tools(allTools) 注册到同一个 Agent。

这就是“本地 + 外部”组合能力的关键实现。

3.2 本地工具类:菜单推荐

package cn.edu.nnu.opengms.service;

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.stereotype.Service;

@Service
public class MenuTools {
   
    @Tool(description = "根据口味推荐菜单")
    public String recommendMenu(String taste) {
   
        if (taste == null || taste.isBlank()) {
   
            return "请先告诉我口味偏好(甜/咸/辣)。";
        }

        if (taste.equalsIgnoreCase("甜")) {
   
            return "推荐菜单:蛋挞、奶茶、甜甜圈";
        } else if (taste.equalsIgnoreCase("咸")) {
   
            return "推荐菜单:炸鸡、薯条、汉堡";
        } else if (taste.equalsIgnoreCase("辣")) {
   
            return "推荐菜单:麻辣香锅、辣子鸡、火锅";
        } else {
   
            return "抱歉,暂时没有相关口味的菜单推荐。";
        }
    }

    @Tool(description = "根据天气状况和温度推荐菜单,参数示例:weather=小雨, temperature=12")
    public String recommendByWeather(String weather, Integer temperature) {
   
        if (weather == null || weather.isBlank() || temperature == null) {
   
            return "请提供完整信息,例如:weather=小雨, temperature=12。";
        }

        String w = weather.toLowerCase();

        if (temperature <= 5) {
   
            return "当前" + weather + ",约" + temperature + "度,推荐暖身菜单:羊肉汤、番茄牛腩、砂锅米线。";
        }

        if (temperature <= 15) {
   
            if (w.contains("雨") || w.contains("雪")) {
   
                return "当前" + weather + ",约" + temperature + "度,推荐热汤类:酸辣汤面、菌菇鸡汤、馄饨。";
            }
            return "当前" + weather + ",约" + temperature + "度,推荐家常热菜:土豆炖牛肉、青椒肉丝、米饭。";
        }

        if (temperature <= 25) {
   
            if (w.contains("雨")) {
   
                return "当前" + weather + ",约" + temperature + "度,推荐温和口味:鲜虾粥、番茄鸡蛋面、清蒸鱼。";
            }
            if (w.contains("晴")) {
   
                return "当前" + weather + ",约" + temperature + "度,推荐轻食搭配:鸡胸沙拉、玉米浓汤、全麦三明治。";
            }
            return "当前" + weather + ",约" + temperature + "度,推荐均衡菜单:宫保鸡丁、时蔬、紫菜蛋花汤。";
        }

        if (w.contains("雨") || w.contains("闷")) {
   
            return "当前" + weather + ",约" + temperature + "度,推荐清爽低负担:绿豆粥、凉拌鸡丝、蒸南瓜。";
        }

        return "当前" + weather + ",约" + temperature + "度,推荐夏季清凉菜单:凉面、手撕鸡、冬瓜排骨汤。";
    }
}

这个版本比“甜咸辣”单一推荐更适合测试 Agent 的决策路径,尤其是当问题里同时出现天气和温度时。

3.3 控制器:统一入口

package cn.edu.nnu.opengms.controller;

import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MenuController {
   
    @Resource(name = "menuAgent")
    private ReactAgent menuAgent;

    @GetMapping(value = "/eatAgent")
    public String eatAgent(@RequestParam(name = "msg", defaultValue = "今天吃什么") String msg) throws GraphRunnerException {
   
        return menuAgent.call(msg).getText();
    }
}

这一层保持简单:把用户输入交给 Agent,后续工具决策由 Agent 运行期处理。


四、application.yml 与依赖

4.1 当前配置

server:
  port: 8001

spring:
  application:
    name: Saa09

  ai:
    dashscope:
      api-key: ${
   DASHSCOPE_API_KEY}

    mcp:
      client:
        request-timeout: 20s
        toolcallback:
          enabled: true
        stdio:
          servers-configuration: classpath:/mcp-server.json5

说明:

  1. 本章除了模型 API Key,还需要 MCP Client 配置。
  2. servers-configuration 指向外部 MCP 服务清单文件。

4.2 关键依赖

<dependency>
    <groupId>com.alibaba.cloud.ai</groupId>
    <artifactId>spring-ai-alibaba-agent-framework</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>

前者提供 Agent 运行能力,后者提供外部 MCP 工具接入能力。


五、测试建议

启动后可先测:

http://localhost:8001/eatAgent?msg=我想吃辣的
http://localhost:8001/eatAgent?msg=今天小雨12度,推荐晚饭
http://localhost:8001/eatAgent?msg=今天的南京天气适合吃什么?

观察点:

  1. 是否会触发菜单推荐工具。
  2. 当问题涉及天气时,是否能利用外部工具结果再给菜单建议。
  3. 返回是否明显比纯对话更“任务化”。

六、本章小结

当前 Saa18 的价值在于跑通了一个实用结构:

  1. ReactAgent 作为统一执行入口。
  2. 本地 MenuTools 提供业务逻辑。
  3. ToolCallbackProvider 注入外部 MCP 工具。
  4. 在一个 Agent 中完成“本地能力 + 外部能力”的协同调用。

这个结构是后续扩展多工具、多步骤 Agent 的基础。


本章重点

  1. 读懂本章不是单工具示例,而是工具合并示例。
  2. 理解 .tools(allTools) 里的本地与外部工具来源。
  3. 用更真实菜单逻辑验证 Agent 的任务执行能力。

📝 编辑者:Flittly
📅 更新时间:2026年4月
🔗 相关资源Spring AI 官方文档 | Spring AI Alibaba

目录
相关文章
|
2月前
|
人工智能 Java API
【SpringAIAlibaba新手村系列】(17)百炼 RAG 知识库应用
本章基于 Spring AI Alibaba 落地百炼 RAG,完成 DashScopeApi、ChatModel、ChatClient 配置,并通过检索器与 DocumentRetrievalAdvisor 组装检索增强问答链路,实现可运行的知识库问答接口。
633 1
|
2月前
|
人工智能 Java 定位技术
【SpringAIAlibaba新手村系列】(14)MCP 本地服务与工具集成
本章从 MCP Server 视角出发,说明如何将本地天气查询能力整理并暴露为标准化工具服务。内容涵盖 @Tool、ToolCallbackProvider、MethodToolCallbackProvider 的作用,以及 Streamable-HTTP 协议下服务端的能力注册与对外提供逻辑。
578 13
|
3月前
|
人工智能 前端开发 Java
【SpringAIAlibaba新手村系列】(4)流式输出与响应式编程
本文围绕 Spring AI 中的流式输出与响应式编程展开,重点解释了传统一次性响应与流式返回的差异,以及 Flux 在异步数据流中的核心作用。文章结合 ChatModel.stream() 与 ChatClient 的多种代码示例,说明如何实现 AI 内容的边生成边返回,并帮助读者理解流式调用在用户体验、性能和长文本场景中的实际价值。
1165 4
【SpringAIAlibaba新手村系列】(4)流式输出与响应式编程
|
2月前
|
人工智能 Java 定位技术
【SpringAIAlibaba新手村系列】(16)调用百度 MCP 服务
本章展示如何在客户端接入第三方百度 MCP 服务。通过 spring-ai-starter-mcp-client、application.yml 与 mcp-server.json5 完成 stdio 方式连接,自动发现并注册远端工具到 ChatClient,实现天气、IP 归属地、路线规划等能力调用。
542 9
|
2月前
|
人工智能 JSON 编解码
【SpringAIAlibaba新手村系列】(15)MCP Client 调用本地服务
本章从 MCP Client 视角说明如何连接上一章提供的本地服务,并把远端工具接入 ChatClient。重点讲解 Streamable-HTTP 配置、ToolCallbackProvider 的注入方式,以及模型如何通过 JSON-RPC 消息完成工具调用与结果回传。
546 21
|
存储 人工智能 Java
【SpringAIAlibaba新手村系列】(3)ChatModel 与 ChatClient 的深度对比
本章深度解析 Spring AI 中 `ChatModel`(底层接口)与 `ChatClient`(高级封装)的本质区别:前者如“手动挡”,精准控制但需写大量样板代码;后者似“智能点餐机”,链式调用、支持系统提示、模板、工具调用等,开发高效。初学者推荐优先使用 `ChatClient`。
834 0
【SpringAIAlibaba新手村系列】(3)ChatModel 与 ChatClient 的深度对比
|
2月前
|
人工智能 JSON Java
【SpringAIAlibaba新手村系列】(6)PromptTemplate 提示词模板与变量替换
本章详解Spring AI的PromptTemplate提示词模板机制,涵盖变量替换、系统消息模板(SystemPromptTemplate)、外部文件加载等核心功能,助力实现提示词参数化、复用与动态组装,提升RAG、Agent及结构化输出场景下的开发效率与可维护性。
447 6
|
2月前
|
NoSQL Java 数据库
【SpringAIAlibaba新手村系列】(11)Embedding 向量化与向量数据库
本文围绕 Embedding 与向量数据库展开,讲解了文本向量化、相似度检索和 VectorStore 的基本用法,并结合 SimpleVectorStore 示例说明了 Spring 中自动装配与手动注册 Bean 的区别,为后续学习 RAG 打下基础。
845 5
【SpringAIAlibaba新手村系列】(11)Embedding 向量化与向量数据库
|
2月前
|
人工智能 Java API
【SpringAIAlibaba新手村系列】(13)Tool Calling 函数工具调用技术
本文详细解析 Spring AI 的 Tool Calling 技术,阐明其如何弥补大模型“会说不会做”的局限。通过 @Tool 注解,开发者可轻松将 Java 方法暴露为 AI 工具。文中深入讲解了 ToolCallbacks.from() 注册工具的原理,以及工具方法在当前 Spring Boot 进程内通过反射动态执行的底层逻辑,强调了模型决策与框架执行的协同过程,为理解 AI 赋能实际操作奠定基础。
928 0
|
人工智能 JavaScript Java
【SpringAIAlibaba新手村系列】(1)初识 Spring AI Alibaba 框架
本文介绍了SpringAIAlibaba框架的基本概念和使用方法。作为Spring官方AI框架的阿里云实现版本,它简化了Java开发者调用AI模型的过程。文章详细讲解了核心概念如ChatModel、ChatClient,以及阿里云百炼平台的功能。通过HelloWorld项目示例,展示了如何配置APIKey、编写控制层代码,实现普通调用和流式输出两种AI交互方式。重点阐述了SpringAI与SpringAIAlibaba的关系,以及自动配置机制的工作原理,帮助开发者快速上手这一框架。
5296 5