news 2026/5/8 15:33:52

Spring AI Alibaba ReactAgent 调用Tool 实现多轮对话

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring AI Alibaba ReactAgent 调用Tool 实现多轮对话

官方文档:https://java2ai.com/docs/overview

前期准备及环境

apiKey: 阿里百炼大模型apiKey, 百度千帆apiKey

OS:Win11

idea:2025.1

JDK:17

SpringBoot: 3.2.12

Maven: 3.8.5

代码结构:

详细代码

1.pom.xml

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.12</version> <relativePath/> </parent> <groupId>com.david</groupId> <artifactId>spring-alibaba-react-agent</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-alibaba-react-agent</name> <properties> <java.version>17</java.version> <spring-ai.version>1.0.0-M4</spring-ai.version> <jackson.version>2.17.0</jackson.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring AI Alibaba Agent Framework --> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-agent-framework</artifactId> <version>1.1.2.0</version> </dependency> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter-dashscope</artifactId> <version>1.1.2.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>com.fasterxml.jackson</groupId> <artifactId>jackson-bom</artifactId> <version>${jackson.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

2.application.yml

spring: ai: dashscope: api-key: sk-xxxxxx #这里是阿里百炼api-key

3.工具类接口,Tool.java

package com.david.springalibabareactagentdemo.tools; import org.springframework.ai.chat.model.ToolContext; import org.springframework.ai.tool.ToolCallback; import java.util.function.BiFunction; public interface Tool<I, O> extends BiFunction<I, ToolContext, O> { ToolCallback toolCallback(); }

4.,搜索天气工具, WeatherTool.java

package com.david.springalibabareactagentdemo.tools; import com.fasterxml.jackson.annotation.JsonClassDescription; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyDescription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ai.chat.model.ToolContext; import org.springframework.ai.tool.ToolCallback; import org.springframework.ai.tool.function.FunctionToolCallback; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 天气工具 */ public class WeatherTool implements Tool<WeatherTool.Request, String> { private static final Logger log = LoggerFactory.getLogger(WeatherTool.class); private final RestTemplate restTemplate = new RestTemplate(); private static final String QIANFAN_API_URL = "https://qianfan.baidubce.com/v2/ai_search/web_search"; private static final String API_KEY = "百度千帆api-key"; @Override public ToolCallback toolCallback() { return FunctionToolCallback.builder("weather_tool", this) .description("联网实时查询城市天气信息") .inputType(Request.class) .build(); } @Override public String apply(Request request, ToolContext context) { log.info("weather request city: {}", request.city()); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setBearerAuth(API_KEY); Map<String, Object> requestBody = new HashMap<>(); Map<String, String> message = new HashMap<>(); message.put("content", request.city() + "天气"); message.put("role", "user"); requestBody.put("messages", List.of(message)); requestBody.put("search_source", "baidu_search_v2"); Map<String, Object> resourceFilter = new HashMap<>(); resourceFilter.put("type", "web"); resourceFilter.put("top_k", 3); requestBody.put("resource_type_filter", List.of(resourceFilter)); Map<String, Object> siteMatch = new HashMap<>(); siteMatch.put("site", List.of("www.weather.com.cn")); Map<String, Object> searchFilter = new HashMap<>(); searchFilter.put("match", siteMatch); requestBody.put("search_filter", searchFilter); // requestBody.put("search_recency_filter", "year"); HttpEntity<Map<String, Object>> r = new HttpEntity<>(requestBody, headers); try { ResponseEntity<String> response = restTemplate.postForEntity( QIANFAN_API_URL, r, String.class ); String responseBody = response.getBody(); log.info("weather response: {}", responseBody); return "搜索结果: " + responseBody; } catch (Exception e) { return "搜索请求失败: " + e.getMessage(); } } @JsonClassDescription("天气查询请求") public record Request( @JsonProperty(value = "城市", required = true) @JsonPropertyDescription("城市名称,例如:北京") String city ) {} }

5.web搜索工具, SearchTool.java

package com.david.springalibabareactagentdemo.tools; import com.fasterxml.jackson.annotation.JsonClassDescription; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyDescription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ai.chat.model.ToolContext; import org.springframework.ai.tool.ToolCallback; import org.springframework.ai.tool.function.FunctionToolCallback; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 搜索工具 */ public class SearchTool implements Tool<SearchTool.Request, String> { private static final Logger log = LoggerFactory.getLogger(SearchTool.class); private final RestTemplate restTemplate = new RestTemplate(); private static final String QIANFAN_API_URL = "https://qianfan.baidubce.com/v2/ai_search/web_search"; private static final String API_KEY = "百度千帆api-key"; @Override public ToolCallback toolCallback() { return FunctionToolCallback.builder("search_tool", this) .description("联网搜索全网实时信息") .inputType(Request.class) .build(); } @Override public String apply(Request request, ToolContext context) { log.info("web search request query: {}", request.query()); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setBearerAuth(API_KEY); Map<String, Object> requestBody = new HashMap<>(); Map<String, String> message = new HashMap<>(); message.put("content", request.query()); message.put("role", "user"); requestBody.put("messages", List.of(message)); requestBody.put("search_source", "baidu_search_v2"); Map<String, Object> resourceFilter = new HashMap<>(); resourceFilter.put("type", "web"); resourceFilter.put("top_k", 5); requestBody.put("resource_type_filter", List.of(resourceFilter)); HttpEntity<Map<String, Object>> r = new HttpEntity<>(requestBody, headers); try { ResponseEntity<String> response = restTemplate.postForEntity( QIANFAN_API_URL, r, String.class ); String responseBody = response.getBody(); log.info("search response: {}", responseBody); return "搜索结果: " + responseBody; } catch (Exception e) { return "搜索请求失败: " + e.getMessage(); } } @JsonClassDescription("搜索请求") public record Request( @JsonProperty(value = "需要搜索的问题", required = true) @JsonPropertyDescription("需要搜索的问题") String query ) {} }

6.Agent配置类, AgentConfig.java

package com.david.springalibabareactagentdemo.config; import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel; import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions; import com.alibaba.cloud.ai.graph.agent.ReactAgent; import com.alibaba.cloud.ai.graph.checkpoint.savers.MemorySaver; import com.david.springalibabareactagentdemo.tools.SearchTool; import com.david.springalibabareactagentdemo.tools.WeatherTool; import okhttp3.OkHttpClient; import org.springframework.ai.chat.model.ChatModel; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.TimeUnit; @Configuration public class AgentConfig { @Value("${spring.ai.dashscope.api-key}") private String apiKey; @Bean public ReactAgent reactAgent() { // 1. 初始化 DashScope API DashScopeApi dashScopeApi = DashScopeApi.builder() .apiKey(apiKey) .build(); // 2. 创建 ChatModel ChatModel chatModel = DashScopeChatModel.builder() .dashScopeApi(dashScopeApi) .build(); // 3. 构建React Agent return ReactAgent.builder() .name("ai_agent") .model(chatModel) .tools(new WeatherTool().toolCallback(), new SearchTool().toolCallback()) .systemPrompt(""" 你是一个博学的智能聊天助手,必须调用工具获取信息,不能编造答案。 调用工具后,根据结果回答用户。 """) // 添加对话记忆,可以选择数据库和redis,这里使用内存记忆 .saver(new MemorySaver()) .build(); } }

这里也可以改为deepSeek的模型

7.web对话接口, AiChatController.java

package com.david.springalibabareactagentdemo.controller; import com.alibaba.cloud.ai.graph.RunnableConfig; import com.alibaba.cloud.ai.graph.agent.ReactAgent; import com.alibaba.cloud.ai.graph.exception.GraphRunnerException; import org.springframework.ai.chat.messages.AssistantMessage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/ai") public class AiChatController { @Autowired ReactAgent reactAgent; @RequestMapping("/test") public String test() { return "test"; } @RequestMapping("/chat") public String chat(@RequestParam(value = "sessionId") String sessionId, @RequestParam(value = "message") String message) throws GraphRunnerException { RunnableConfig config = RunnableConfig.builder() // 设置会话标识 .threadId(sessionId) .build(); AssistantMessage response = reactAgent.call(message, config); // System.out.println(response.getText()); return response.getText(); } }

启动服务进行测试

1.入参sessionId是作为记忆对话唯一标识,先问下"杭州5月10日天气怎么样"

2.在问"当天文旅有什么活动吗?", 这里可以看到输出"当天"是5月10号

3.在接着问"我有1000元的预算,帮我安排下5月10日杭州的文旅活动行程吗",输出的时间也是5月10号

遇到的问题:

1.使用自动注入ChatModel会出现超时问题,在application.yml里面配置了read-timeout也无法生效,所以上面代码改为了自己创建一个ChatModel.

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/8 15:33:09

在Mac上原生运行iOS游戏:PlayCover终极指南与性能优化技巧

在Mac上原生运行iOS游戏&#xff1a;PlayCover终极指南与性能优化技巧 【免费下载链接】PlayCover Community fork of PlayCover 项目地址: https://gitcode.com/gh_mirrors/pl/PlayCover 想象一下&#xff0c;在Mac的大屏幕上流畅运行《原神》《崩坏&#xff1a;星穹铁…

作者头像 李华
网站建设 2026/5/8 15:32:45

如何利用Chatbox高效管理你的AI对话?5大核心功能解密

如何利用Chatbox高效管理你的AI对话&#xff1f;5大核心功能解密 【免费下载链接】chatbox Powerful AI Client 项目地址: https://gitcode.com/GitHub_Trending/ch/chatbox 在AI助手日益普及的今天&#xff0c;你是否曾为对话记录丢失而烦恼&#xff1f;精心构思的技术…

作者头像 李华
网站建设 2026/5/8 15:32:15

黑龙江省考笔试机构怎么选?行测申论课程 + 服务完整评测指南

一、为什么一定要重视黑龙江省考机构选型近些年黑龙江省考笔试竞争逐年白热化&#xff0c;行测、申论命题风格、本地素材权重和全国联考差异极大。很多考生选机构容易踩三大坑&#xff1a;课程不贴合黑龙江本地考情&#xff0c;学的通用内容用不上&#xff1b;宣传服务天花乱坠…

作者头像 李华