Spring AI Alibaba Graph基于 ReAct Agent 的天气预报查询系统

1、在本示例中,我们仅为 Agent 绑定了一个天气查询服务,接收到用户的天气查询服务后,流程会在 AgentNode 和 ToolNode 之间循环执行,直到完成用户指令。示例中判断指令完成的条件(即 ReAct 结束条件)也很简单,模型 AssistantMessage 无 tool_call 指令则结束(采用默认行为)。

2、pom文件

<properties><gson.version>2.10.1</gson.version></properties><dependencies><dependency><groupId>net.sourceforge.plantuml</groupId><artifactId>plantuml-mit</artifactId><version>1.2024.4</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai-spring-boot-starter</artifactId></dependency><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>${gson.version}</version></dependency><!--		<dependency>-->
<!--			<groupId>com.alibaba.cloud.ai</groupId>-->
<!--			<artifactId>spring-ai-alibaba-starter</artifactId>-->
<!--			<version>${project.parent.version}</version>-->
<!--		</dependency>--><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-graph-core</artifactId><version>${project.parent.version}</version></dependency><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>spring-ai-alibaba-studio</artifactId><version>${project.parent.version}</version></dependency><dependency><groupId>com.alibaba.cloud.ai</groupId><artifactId>document-parser-tika</artifactId><version>1.0.0-M6.1</version></dependency><dependency><groupId>com.belerweb</groupId><artifactId>pinyin4j</artifactId><version>2.5.1</version><scope>compile</scope></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-extra</artifactId><version>5.8.20</version><scope>compile</scope></dependency><!-- HttpClient 核心库 --><dependency><groupId>org.apache.httpcomponents.client5</groupId><artifactId>httpclient5</artifactId><version>5.2.3</version></dependency><!-- 如果需要使用 HttpClient 连接池管理 --><dependency><groupId>org.apache.httpcomponents.core5</groupId><artifactId>httpcore5</artifactId><version>5.2.3</version></dependency><!-- This dependency automatically matches the appropriate version of Chrome Driver, --><!-- causing it to be slower on first boot --><dependency><groupId>io.github.bonigarcia</groupId><artifactId>webdrivermanager</artifactId><version>5.7.0</version></dependency><dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>4.25.0</version></dependency><dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-chrome-driver</artifactId><version>4.25.0</version></dependency><dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-api</artifactId><version>4.25.0</version></dependency><dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-remote-driver</artifactId><version>4.25.0</version></dependency><dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-manager</artifactId><version>4.25.0</version></dependency><dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-http</artifactId><version>4.25.0</version></dependency><dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.15</version></dependency></dependencies>

3、配置文件

#
# Copyright 2024-2025 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#server:port: 18080spring:application:name: spring-ai-alibaba-helloworldai:alibaba:toolcalling:weather:enabled: trueapi-key: aaaaaopenai:base-url: https://dashscope.aliyuncs.com/compatible-modeapi-key: sk-xxxxoooooyyyyyyxxxxooooochat:options:model: qwen-max-latest

4、天气接口 tool

/** Copyright 2024-2025 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package com.alibaba.cloud.ai.example.graph.react.tool.weather.function;import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.util.StringUtils;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;/*** @author yingzi* @since 2025/3/27:11:07*/
public class FreeWeatherService implements Function<FreeWeatherService.Request, FreeWeatherService.Response> {private static final Logger logger = LoggerFactory.getLogger(FreeWeatherService.class);private static final String WEATHER_API_URL = "http://t.weather.sojson.com/api/weather/city/";private final WebClient webClient;private final ObjectMapper objectMapper = new ObjectMapper();private final static Map<String, String> CITY_NAME_CODE_MAP = Map.of("杭州", "101210101","上海", "101020100", "南京", "101190101", "合肥", "101220101");public FreeWeatherService() {this.webClient = WebClient.builder().defaultHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded").build();}public static Response fromJson(Map<String, Object> json) {Map<String, Object> location = (Map<String, Object>) json.get("cityInfo");Map<String, Object> data = (Map<String, Object>) json.get("data");List<Map<String, Object>> forecastDays = (List<Map<String, Object>>) data.get("forecast");Map<String, Object> current = forecastDays.get(0);String city = (String) location.get("city");return new Response(city, current, forecastDays.subList(1, forecastDays.size()));}@Overridepublic Response apply(Request request) {if (request == null || !StringUtils.hasText(request.city())) {logger.error("Invalid request: city is required.");return null;}try {return doGetWeatherMock(request);} catch (Exception e) {logger.error("Failed to fetch weather data: {}", e.getMessage());return null;}}@NotNullprivate Response doGetWeatherMock(Request request) throws JsonProcessingException {return doGetWeather(WEATHER_API_URL, request);}@NotNullprivate Response doGetWeather(String url, Request request) throws JsonProcessingException {String city = request.city();String cityCode = CITY_NAME_CODE_MAP.get(city);if (org.apache.commons.lang3.StringUtils.isBlank(cityCode)) {return null;}Mono<String> responseMono = webClient.get().uri(url + cityCode).retrieve().bodyToMono(String.class);String jsonResponse = responseMono.block();assert jsonResponse != null;Response response = fromJson(objectMapper.readValue(jsonResponse, new TypeReference<Map<String, Object>>() {}));logger.info("Weather data fetched successfully for city: {}", response.city());return response;}@JsonInclude(JsonInclude.Include.NON_NULL)@JsonClassDescription("Weather Service API request")public record Request(@JsonProperty(required = true, value = "city") @JsonPropertyDescription("city name") String city,@JsonProperty(required = true,value = "days") @JsonPropertyDescription("Number of days of weather forecast. Value ranges from 1 to 14") int days) {}@JsonClassDescription("Weather Service API response")public record Response(@JsonProperty(required = true, value = "city") @JsonPropertyDescription("city name") String city,@JsonProperty(required = true,value = "current") @JsonPropertyDescription("Current weather info") Map<String, Object> current,@JsonProperty(required = true,value = "forecastDays") @JsonPropertyDescription("Forecast weather info") List<Map<String, Object>> forecastDays) {}}
/** Copyright 2024-2025 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package com.alibaba.cloud.ai.example.graph.react.tool.weather.function;import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Description;@Configuration
@ConditionalOnClass(FreeWeatherService.class)
@ConditionalOnProperty(prefix = "spring.ai.alibaba.toolcalling.weather", name = "enabled", havingValue = "true")
public class FreeWeatherAutoConfiguration {@Bean(name = "getWeatherFunction")@ConditionalOnMissingBean@Description("Use api.weather to get weather information.")public FreeWeatherService getWeatherFunction() {return new FreeWeatherService();}}

5、核心代码

/** Licensed to the Apache Software Foundation (ASF) under one or more* contributor license agreements.  See the NOTICE file distributed with* this work for additional information regarding copyright ownership.* The ASF licenses this file to You under the Apache License, Version 2.0* (the "License"); you may not use this file except in compliance with* the License.  You may obtain a copy of the License at**     http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.alibaba.cloud.ai.example.graph.react;import java.util.concurrent.TimeUnit;import com.alibaba.cloud.ai.graph.CompiledGraph;
import com.alibaba.cloud.ai.graph.GraphRepresentation;
import com.alibaba.cloud.ai.graph.GraphStateException;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.util.Timeout;import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.tool.resolution.ToolCallbackResolver;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestClient;@Configuration
public class ReactAutoconfiguration {@Beanpublic ReactAgent normalReactAgent(ChatModel chatModel, ToolCallbackResolver resolver) throws GraphStateException {ChatClient chatClient = ChatClient.builder(chatModel).defaultTools("getWeatherFunction").defaultAdvisors(new SimpleLoggerAdvisor()).defaultOptions(OpenAiChatOptions.builder().internalToolExecutionEnabled(false).build()).build();return ReactAgent.builder().name("React Agent Demo").chatClient(chatClient).resolver(resolver).maxIterations(10).build();}@Beanpublic CompiledGraph reactAgentGraph(@Qualifier("normalReactAgent") ReactAgent reactAgent)throws GraphStateException {GraphRepresentation graphRepresentation = reactAgent.getStateGraph().getGraph(GraphRepresentation.Type.PLANTUML);System.out.println("\n\n");System.out.println(graphRepresentation.content());System.out.println("\n\n");return reactAgent.getAndCompileGraph();}@Beanpublic RestClient.Builder createRestClient() {// 2. 创建 RequestConfig 并设置超时RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(Timeout.of(10, TimeUnit.MINUTES)) // 设置连接超时.setResponseTimeout(Timeout.of(10, TimeUnit.MINUTES)).setConnectionRequestTimeout(Timeout.of(10, TimeUnit.MINUTES)).build();// 3. 创建 CloseableHttpClient 并应用配置HttpClient httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).build();// 4. 使用 HttpComponentsClientHttpRequestFactory 包装 HttpClientHttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);// 5. 创建 RestClient 并设置请求工厂return RestClient.builder().requestFactory(requestFactory);}}
/** Copyright 2024-2025 the original author or authors.** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      https://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package com.alibaba.cloud.ai.example.graph.react;import java.util.List;
import java.util.Map;
import java.util.Optional;import com.alibaba.cloud.ai.graph.CompiledGraph;
import com.alibaba.cloud.ai.graph.OverAllState;import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.messaging.Message;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/react")
public class ReactController {private final CompiledGraph compiledGraph;ReactController(@Qualifier("reactAgentGraph") CompiledGraph compiledGraph) {this.compiledGraph = compiledGraph;}@GetMapping("/chat")public String simpleChat(String query) {Optional<OverAllState> result = compiledGraph.invoke(Map.of("messages", new UserMessage(query)));List<Message> messages = (List<Message>) result.get().value("messages").get();AssistantMessage assistantMessage = (AssistantMessage) messages.get(messages.size() - 1);return assistantMessage.getText();}}

测试地址 

http://localhost:18080/react/chat?query=%E5%88%86%E5%88%AB%E5%B8%AE%E6%88%91%E6%9F%A5%E8%AF%A2%E6%9D%AD%E5%B7%9E%E3%80%81%E4%B8%8A%E6%B5%B7%E5%92%8C%E5%8D%97%E4%BA%AC%E7%9A%84%E5%A4%A9%E6%B0%94

测试结果如下

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/77891.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HCIP(综合实验2)

1.实验拓补图 2.实验要求 1.根据提供材料划分VLAN以及IP地址&#xff0c;PC1/PC2属于生产一部员工划分VLAN10,PC3属于生产二部划分VLAN20 2.HJ-1HJ-2交换机需要配置链路聚合以保证业务数据访问的高带宽需求 3.VLAN的放通遵循最小VLAN透传原则 4.配置MSTP生成树解决二层环路问题…

使用 rebase 轻松管理主干分支

前言 最近遇到一个技术团队的 dev 环境分支错乱&#xff0c;因为是多人合作大家各自提交信息&#xff0c;导致出现很多交叉合并记录&#xff0c;让对应 log 看起来非常混乱&#xff0c;难以阅读。 举例说明 假设我们有一个项目&#xff0c;最初develop分支有 3 个提交记录&a…

使用openssl为localhost创建自签名

文章目录 自签名生成命令安装安装证书浏览器证书管理器 自签名 生成命令 使用openssl生成私钥和证书。 openssl req -x509 -newkey rsa:4096 -nodes -days 365 -subj "/CNlocalhost" -addext "subjectAltNameDNS:localhost" -keyout cert.key -out cer…

AI编程助手Cline之快速介绍

Cline 是一款深度集成在 Visual Studio Code&#xff08;VSCode&#xff09; 中的开源 AI 编程助手插件&#xff0c;旨在通过结合大语言模型&#xff08;如 Claude 3.5 Sonnet、DeepSeek V3、Google Gemini 等&#xff09;和工具链&#xff0c;为开发者提供自动化任务执行、智能…

1.微服务拆分与通信模式

目录 一、微服务拆分原则与策略 业务驱动拆分方法论 • DDD&#xff08;领域驱动设计&#xff09;中的限界上下文划分 • 业务功能正交性评估&#xff08;高内聚、低耦合&#xff09; 技术架构拆分策略 • 数据层拆分&#xff08;垂直分库 vs 水平分表&#xff09; • 服务粒…

Element Plus表格组件深度解析:构建高性能企业级数据视图

一、架构设计与核心能力 Element Plus的表格组件&#xff08;el-table&#xff09;基于Vue 3的响应式系统构建&#xff0c;通过声明式配置实现复杂数据渲染。其核心设计理念体现在三个层级&#xff1a; 数据驱动&#xff1a;通过data属性绑定数据源&#xff0c;支持动态更新与…

07前端项目----面包屑

面包屑 效果实现代码全局事件总线-$bus 效果 实现代码 上节searchParams中参数categoryName是表示一二三级分类所点击的列表名 <!--bread面包屑--> <div class"bread"><ul class"fl sui-breadcrumb"><li><a href"#"…

kafka jdbc connector适配kadb数据实时同步

测试结论 源端增量获取方式包括&#xff1a;bulk、incrementing、timestamp、incrementingtimestamp&#xff08;混合&#xff09;&#xff0c;各种方式说明如下&#xff1a; bulk: 一次同步整个表的数据 incrementing: 使用严格的自增列标识增量数据。不支持对旧数据的更新…

基于Hadoop的音乐推荐系统(源码+lw+部署文档+讲解),源码可白嫖!

摘要 本毕业生数据分析与可视化系统采用B/S架构&#xff0c;数据库是MySQL&#xff0c;网站的搭建与开发采用了先进的Java语言、爬虫技术进行编写&#xff0c;使用了Spring Boot框架。该系统从两个对象&#xff1a;由管理员和用户来对系统进行设计构建。主要功能包括&#xff…

CentOS的安装以及网络配置

CentOS的下载 在学习docker之前&#xff0c;我们需要知道的就是docker是运行在Linux内核之上的&#xff0c;所以我们需要Linux环境的操作系统&#xff0c;当然了你也可以选择安装ubuntu等操作系统&#xff0c;如果你不想在本机安装的话还可以考虑买阿里或者华为的云服务器&…

【条形码识别改名工具】如何批量识别图片条形码,并以条码内容批量重命名,基于WPF和Zxing的开发总结

批量图片条形码识别与重命名系统 (WPF + ZXing)开发总结 项目适用场景 ​​电商商品管理​​:批量处理商品图片,根据条形码自动分类归档​​图书馆系统​​:扫描图书条形码快速建立电子档案​​医疗档案管理​​:通过药品条形码整理医疗图片资料​​仓储管理​​:自动化识…

RAGFlow安装+本地知识库+踩坑记录

RAGFlow是一种融合了数据检索与生成式模型的新型系统架构&#xff0c;其核心思想在于将大规模检索系统与先进的生成式模型&#xff08;如Transformer、GPT系列&#xff09;相结合&#xff0c;从而在回答查询时既能利用海量数据的知识库&#xff0c;又能生成符合上下文语义的自然…

android liveData observeForever 与 observe对比

LiveData 是一个非常有用的组件,用于在数据变化时通知观察者。LiveData 提供了两种主要的观察方法:observe 和 observeForever。这两种方法在使用场景、生命周期感知以及内存管理等方面有所不同。 一、observe 方法​​ ​​1. 基本介绍​​ ​​生命周期感知​​:observe…

web-ssrfme

一、题目源码 <?php highlight_file(__file__); function curl($url){ $ch curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_HEADER, 0);echo curl_exec($ch);curl_close($ch); }if(isset($_GET[url])){$url $_GET[url];if(preg_match(/file…

企业AI应用模式解析:从本地部署到混合架构

在人工智能快速发展的今天&#xff0c;企业如何选择合适的大模型应用方式成为了一个关键问题。本文将详细介绍六种主流的企业AI应用模式&#xff0c;帮助您根据自身需求做出最优选择。 1. 本地部署&#xff08;On-Premise Deployment&#xff09; 特点&#xff1a;将模型下载…

OpenCV 图形API(49)颜色空间转换-----将 NV12 格式的图像数据转换为 BGR 颜色空间函数NV12toBGR()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 将图像从NV12&#xff08;YUV420p&#xff09;颜色空间转换为BGR。 该函数将输入图像从NV12颜色空间转换为RGB。Y、U和V通道值的常规范围是0到25…

【java实现+4种变体完整例子】排序算法中【桶排序】的详细解析,包含基础实现、常见变体的完整代码示例,以及各变体的对比表格

以下是桶排序的详细解析&#xff0c;包含基础实现、常见变体的完整代码示例&#xff0c;以及各变体的对比表格&#xff1a; 一、桶排序基础实现 原理 将数据分到有限数量的桶中&#xff0c;每个桶内部使用其他排序算法&#xff08;如插入排序或快速排序&#xff09;&#xf…

Linux[基本指令]

Linux[基本指令] pwd 查看当前所处的工作目录 斜杠在Linux中作为路径分割符 路径存在的价值为了确定文件的唯一性 cd指令 更改路径 cd 你要去的路径(直接进入) cd . 当前目录 cd . . 上级目录(路径回退) 最后的’/为根目录(根节点) Linux还是window的目录结构都是树状…

git -- 对远程仓库的操作 -- 查看,添加(与clone对比),抓取和拉取,推送(注意点,抓取更新+合并的三种方法,解决冲突,对比),移除

目录 对远程仓库的操作 介绍 查看 (git remote) 介绍 查看详细信息 添加(git remote add) 介绍 与 git clone对比 从远程仓库中抓取与拉取 抓取(git fetch) 拉取(git pull) 推送(git push) 介绍 注意 抓取更新合并的方法 git fetch git merge 解决冲突 git …

vue3 excel文件导入

文章目录 前言使用在vue文件中的使用 前言 最近写小组官网涉及到了excel文件导入的功能 场景是导入小组成员年级 班级 邮箱 组别 姓名等基本信息的excel表格用于展示各组信息 使用 先下载js库 npm install xlsx为了提高代码的复用性 我将它写成了一个通用的函数 import ap…