【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

目录
相关文章
|
存储 缓存 文件存储
如何保证分布式文件系统的数据一致性
分布式文件系统需要向上层应用提供透明的客户端缓存,从而缓解网络延时现象,更好地支持客户端性能水平扩展,同时也降低对文件服务器的访问压力。当考虑客户端缓存的时候,由于在客户端上引入了多个本地数据副本(Replica),就相应地需要提供客户端对数据访问的全局数据一致性。
32696 78
如何保证分布式文件系统的数据一致性
|
前端开发 容器
HTML5+CSS3前端入门教程---从0开始通过一个商城实例手把手教你学习PC端和移动端页面开发第8章FlexBox布局(上)
HTML5+CSS3前端入门教程---从0开始通过一个商城实例手把手教你学习PC端和移动端页面开发第8章FlexBox布局
17748 20
|
设计模式 存储 监控
设计模式(C++版)
看懂UML类图和时序图30分钟学会UML类图设计原则单一职责原则定义:单一职责原则,所谓职责是指类变化的原因。如果一个类有多于一个的动机被改变,那么这个类就具有多于一个的职责。而单一职责原则就是指一个类或者模块应该有且只有一个改变的原因。bad case:IPhone类承担了协议管理(Dial、HangUp)、数据传送(Chat)。good case:里式替换原则定义:里氏代换原则(Liskov 
36678 19
设计模式(C++版)
|
存储 编译器 C语言
抽丝剥茧C语言(初阶 下)(下)
抽丝剥茧C语言(初阶 下)
|
机器学习/深度学习 人工智能 自然语言处理
带你简单了解Chatgpt背后的秘密:大语言模型所需要条件(数据算法算力)以及其当前阶段的缺点局限性
带你简单了解Chatgpt背后的秘密:大语言模型所需要条件(数据算法算力)以及其当前阶段的缺点局限性
24756 14
|
机器学习/深度学习 弹性计算 监控
重生之---我测阿里云U1实例(通用算力型)
阿里云产品全线降价的一力作,2023年4月阿里云推出新款通用算力型ECS云服务器Universal实例,该款服务器的真实表现如何?让我先测为敬!
36660 15
重生之---我测阿里云U1实例(通用算力型)
|
SQL 存储 弹性计算
Redis性能高30%,阿里云倚天ECS性能摸底和迁移实践
Redis在倚天ECS环境下与同规格的基于 x86 的 ECS 实例相比,Redis 部署在基于 Yitian 710 的 ECS 上可获得高达 30% 的吞吐量优势。成本方面基于倚天710的G8y实例售价比G7实例低23%,总性价比提高50%;按照相同算法,相对G8a,性价比为1.4倍左右。
|
存储 算法 Java
【分布式技术专题】「分布式技术架构」手把手教你如何开发一个属于自己的限流器RateLimiter功能服务
随着互联网的快速发展,越来越多的应用程序需要处理大量的请求。如果没有限制,这些请求可能会导致应用程序崩溃或变得不可用。因此,限流器是一种非常重要的技术,可以帮助应用程序控制请求的数量和速率,以保持稳定和可靠的运行。
29836 52

热门文章

最新文章

下一篇
开通oss服务