爆米花烘焙
66.80M · 2026-04-14
s01 > s02 > s03 > s04 > s05 > s06 > s07 > s08 > s09 > s10 > s11 > s12 > s13 > s14 > s15 > s16 > s17 > [ s18 ]
这一章的目标不是再做一个“只有本地工具”的 Agent,而是把两类工具放进同一个 ReactAgent:
这样可以直接验证:Agent 在一次对话里,可以同时调度本地 @Tool 和外部 MCP 工具。
前面章节里,ChatModel / ChatClient 更偏“发起一次对话调用”。
本章里的 ReactAgent 更偏“执行任务”:
对于简单问题,这三者体感差异可能不大;但一旦涉及多工具组合,ReactAgent 的优势会更明显。
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.ch@t.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 ch@tModel, 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(ch@tModel)
.tools(allTools)
.build();
}
}
这段代码是本章核心。
它做了三件事:
ToolCallbacks.from(menuTools):加载本地菜单工具。mcpTools.getToolCallbacks():加载外部 MCP 工具。.tools(allTools) 注册到同一个 Agent。这就是“本地 + 外部”组合能力的关键实现。
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 的决策路径,尤其是当问题里同时出现天气和温度时。
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 运行期处理。
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
说明:
servers-configuration 指向外部 MCP 服务清单文件。<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 工具接入能力。
启动后可先测:
我想吃辣的
今天小雨12度,推荐晚饭
今天的n京天气适合吃什么?
观察点:
当前 Saa18 的价值在于跑通了一个实用结构:
ReactAgent 作为统一执行入口。MenuTools 提供业务逻辑。ToolCallbackProvider 注入外部 MCP 工具。这个结构是后续扩展多工具、多步骤 Agent 的基础。
本章重点:
.tools(allTools) 里的本地与外部工具来源。