news 2026/9/3 14:44:42

springai Alibaba(下)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
springai Alibaba(下)

十二.RAG检索增强生成

12.1 基础概念

那么我们可以通过一个案例来告诉大家什么是RAG。假设我们现在有一个需求,就是AI智能运维助手,通过提供的错误编码,给出异常解释来辅助运维人员更好的定位问题和维护系统。

比如我们现在提供的错误代码中,00000代表success,A0001代表网络故障,A0002代表系统限流。但是传统的LLM是有缺陷的,比如LLM的知识不是实时的,不具备知识更新;LLM可能不知道你私有的领域/业务知识;LLM有时会在回答中生成看似合理但实际上是错误的信息。

LLM的知识仅限于它所接受的训练数据。如果你想让一个LLM了解特定领域的知识或专有数据,降低大模型出现幻觉的概率,你可以使用RAG;

简单来说,RAG(检索增强生成)是一种从你的数据中查找相关信息,并在将提示发送给LLM之前将其注入到提示中的方法。这样一来,LLM就能获得(希望是)相关的信息,并基于这些信息进行回答,从而降低产生幻觉的概率

RAG技术就像给AI大模型装上了「实时百科大脑」,为了让大模型获取足够的上下文,以便获得更加广泛的信息源,通过先查资料后回答的机制,让AI摆脱传统模型的"知识遗忘和幻觉回复"困境。

类似考试时有不懂的,给你准备了小抄,对大模型知识盲区的一种补充

12.2 流程

RAG流程分为两个不同的阶段:索引(Index)检索(Retrieval)

索引(Index)

索引首先清理和提取各种格式的原始数据,如PDF,HTML等,然后将其转换为统一的纯文本格式。为了适应语言模型的上下文限制,文本被分割为更小的,可消化的块(chunk)。然后使用嵌入模型将块编码成向量表示,并存储在向量数据库中。这一步对于在随后的检索阶段实现高效的相似性搜索至关重要。知识库被分割成chunks,并将chunks向量化至向量库中。

检索(Retrieval)

检索阶段通常在线进行,此时用户提交的问题应使用检索文档来回答

在收到用户查询之后,RAG系统采用与索引阶段相同的编码模型将查询转化为向量表示,然后计算索引语料库中查询向量和块向量的相似性得分。该系统优先级和检索最高k(Top-K)块,显示最大的相似性查询。

12.3 实战案例

需求: AI智能运维助手,通过提供的错误代码,给出异常解释辅助运维人员更好的定位问题和维护系统(SpringAl+阿里百炼嵌入模型text-embedding-v3+向量数据库RedisStack+DeepSeek来实现RAG功能)

步骤:

1.和以往一样,还是建立Module;和上一节向量一样加pom;yml也类似,不过改了模型变成了deepseek;然后就是主启动;原本的多模型modelclient和model的config类不变

2.接下来就是需要重视的业务类的编写了:

  • 提供ops.txt脚本放到resources中,让他存入向量数据库RedisStack,形成文档知识库

版本1:

1. 新建一个InitVectorDatabaseConfig类

初始化向量数据库对应的数据。即将上述的ops.text中的数据以文本向量化的形式作为向量化数据存储入向量数据库中

package ai.study.config; import jakarta.annotation.PostConstruct; import org.springframework.ai.document.Document; import org.springframework.ai.reader.TextReader; import org.springframework.ai.transformer.splitter.TokenTextSplitter; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.Resource; import java.nio.charset.Charset; import java.util.List; @Configuration public class InitVectorDatabaseConfig { @Autowired private VectorStore vectorStore; @Value("classpath:ops.txt") private Resource sqlFile;// 读取的文件 @PostConstruct public void init() { // 1.读取文件 TextReader textReader = new TextReader(sqlFile); textReader.setCharset(Charset.defaultCharset());//文档最好要设置一个编码,默认是utf-8,防止乱码 // 2.文件转换成向量(开启分词) List<Document> list = new TokenTextSplitter().transform(textReader.read()); // 3.写入向量数据库(Redis),无法去重复版 vectorStore.add(list); }}

2.此时建立一个controller

package ai.study.controller; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.rag.advisor.RetrievalAugmentationAdvisor; import org.springframework.ai.rag.retrieval.search.VectorStoreDocumentRetriever; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; @RestController public class RagController { @Resource(name = "qwenChatClient") private ChatClient chatClient; @Resource private VectorStore vectorStore; /** * http://localhost:8012/rag4aiops?msg=00000 * http://localhost:8012/rag4aiops?msg=C2222 * @param msg * @return */ @GetMapping("/rag4aiops") public Flux<String> rag(String msg) { String systemInfo = """ 你是一个运维工程师,按照给出的编码给出对应故障解释,否则回复找不到信息。 """; //顾问增强器(属于chatmemory中的内容) RetrievalAugmentationAdvisor advisor = RetrievalAugmentationAdvisor.builder() .documentRetriever(//检索 VectorStoreDocumentRetriever.builder() .vectorStore(vectorStore) .build() ) .build(); return chatClient.prompt() .system(systemInfo)//ai的身份 .user(msg)//用户输入 .advisors(advisor) // RAG功能,向量数据库查询 .stream() .content(); } }

但是上述版本会出现一个问题,就是每次我重启一次微服务,就会重新加载一次,然后redisstack中就会新增一次数据;因此需要重复数据过滤清洗的操作

版本二:---->向量数据库去重问题解决

使用redis setnx去重

1.先加入RedisConfig

package ai.study.config; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration @Slf4j public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactor) { RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactor); //设置key序列化方式string redisTemplate.setKeySerializer(new StringRedisSerializer()); //设置value的序列化方式json,使用GenericJackson2JsonRedisSerializer替换默认序列化 redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.afterPropertiesSet(); return redisTemplate; } }

2. 修改InitVectorDatabaseConfig:

@Configuration public class InitVectorDatabaseConfig { @Autowired private RedisTemplate<String,String> redisTemplate; ...... // 3.写入向量数据库(Redis),无法去重复版 //vectorStore.add(list); //4 去重复版本 String sourceMetadata = (String)textReader.getCustomMetadata().get("source"); String textHash = SecureUtil.md5(sourceMetadata); String redisKey = "vector-xxx:" + textHash; // 判断是否存入过,redisKey如果可以成功插入表示以前没有过,可以假如向量数据 Boolean retFlag = redisTemplate.opsForValue().setIfAbsent(redisKey, "1"); System.out.println("****retFlag : "+retFlag); if(Boolean.TRUE.equals(retFlag)) { //键不存在,首次插入,可以保存进向量数据库 vectorStore.add(list); }else { //键已存在,跳过或者报错 //throw new RuntimeException("---重复操作"); System.out.println("------向量初始化数据已经加载过,请不要重复操作"); }}

十三.ToolCalling

一句话概率,toolcalling就是LLM的外部utils工具类。ToolCalling(也称为FunctionCalling)它允许大模型与一组API或工具进行交互,将LLM的智能与外部工具或API无缝连接,从而增强大模型其功能。

但是注意,LLM本身并不执行函数,它只是指示应该调用哪个函数以及如何调用。如果不使用toolCalling,则会出现:

所以我们知道tool calling可以实现:

  1. 访问实时数据
  2. 执行某种工具类/辅助类操作

工作流程可以大致参考下述:

接下来就是开发步骤:

1. 建子模块Module,改pom(dashscope就行),写yml,主启动和以往一样

接下来是业务类

2.新建Tool工具类,类似于Utils工具类

package ai.study.utils; import org.springframework.ai.tool.annotation.Tool; import java.time.LocalDateTime; public class DateTimeTools { /** * 1.定义 function call(tool call) * 2. returnDirect * true = tool直接返回不走大模型,直接给客户 * false = 拿到tool返回的结果,给大模型,最后由大模型回复 */ //必须加描述description,LLM 靠这个判断调用哪个 @Tool(description = "获取当前时间", returnDirect = false) public String getCurrentTime() { return LocalDateTime.now().toString(); } }
  • 描述(Description)就是“咒语”: LLM 决定调不调用你的函数,完全取决于你写的@Description。如果描述写得模糊(比如只写“获取信息”),模型可能就不知道什么时候该用它。

3.controller类

import ai.study.utils.DateTimeTools; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.model.tool.ToolCallingChatOptions; import org.springframework.ai.support.ToolCallbacks; import org.springframework.ai.tool.ToolCallback; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ToolCallingController { @Resource private ChatModel chatModel; @GetMapping("/toolcall/chat") public String chat(@RequestParam(name = "msg",defaultValue = "你是谁,现在几点了") String msg) { // 1.工具注册到工具集合里 //这是把你的 Java 对象“翻译”成大模型能理解的 JSON Schema 格式 ToolCallback[] tools = ToolCallbacks.from(new DateTimeTools()); // 2.将工具集配置进ChatOptions对象 ChatOptions options = ToolCallingChatOptions.builder().toolCallbacks(tools).build(); // 3.构建提示词 Prompt prompt = new Prompt(msg, options); // 4.调用大模型 return chatModel.call(prompt).getResult().getOutput().getText(); } }

结果如下:

十四.MCP

之前的ToolCalling中每个大模型(如DeepSeek、ChatGPT)需要为每个工具单独开发接口(FunctionCalling),导致重复劳动。

而MCP就可以解决上述问题,它提供了一种标准化的方式来连接LLMs需要的上下文,MCP就类似于一个Agent时代的Type-C协议,希望能将不同来源的数据、工具、服务统一起来供大模型调用。更多概念详情,可查看:MCP简单介绍

  • Tool Calling:是“技能点”。适合简单的、项目内部的、特定的功能扩展。

  • MCP:是“生态圈”。它试图统一所有 AI 工具的接入标准。

14.1 本地MCP开发步骤

1. MCP-Server服务端的实现

仍然是建Module,然后就是改写pom

<dependencies> <!--注意事项(重要) spring-ai-starter-mcp-server-webflux不能和<artifactId>spring-boot-starter-web</artifactId>依赖并存, 否则会使用tomcat启动,而不是netty启动,从而导致mcpserver启动失败,但程序运行是正常的,mcp客户端连接不上。 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!--mcp-server-webflux--> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-server-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>

写yml

# spring.ai.dashscope.api-key=${aliQwen-api} # ====mcp-server Config============= spring.ai.mcp.server.type=async spring.ai.mcp.server.name=customer-define-mcp-server spring.ai.mcp.server.version=1.0.0

然后就是主启动,接下来就是业务类的编写

a. 天气预报WeatherService服务类

package ai.study.service; import org.springframework.ai.tool.annotation.Tool; import org.springframework.stereotype.Service; import java.util.Map; @Service public class WeatherService { @Tool(description = "根据城市名称获取天气预报") public String getWeatherByCity(String city) { Map<String, String> map = Map.of( "北京", "11111降雨频繁,其中今天和后天雨势较强,部分地区有暴雨并伴强对流天气,需注意", "上海", "22222多云,15℃~27℃,南风3级,当前温度27℃。", "深圳", "333333多云40天,阴16天,雨30天,晴3天" ); return map.getOrDefault(city, "抱歉:未查询到对应城市!"); } }

b.ToolCallbackProvider接口配置类

package ai.study.config; import ai.study.service.WeatherService; import org.springframework.ai.tool.ToolCallbackProvider; import org.springframework.ai.tool.method.MethodToolCallbackProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class McpServerConfig { /** * 将工具方法暴露给外部 mcp client 调用 * @param weatherService * @return */ @Bean public ToolCallbackProvider weatherTools(WeatherService weatherService) { return MethodToolCallbackProvider.builder() .toolObjects(weatherService) .build(); } }

在之前的 Tool Calling 里,我们要手动注册工具;而这里的MethodToolCallbackProvider像是一个“雷达探测器”,它会自动扫描WeatherService中所有带@Tool注解的方法,并打包发布。外部 Client 只要一连接,就能立刻看到这些“技能清单”。

2. MCP-Client客户端的实现

客户端的职责是:发现服务端 --------握手连接--------托管给 LLM。

接下来还是新建子模块Module,然后就是改pom

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--spring-ai-alibaba dashscope--> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter-dashscope</artifactId> </dependency> <!-- 2.mcp-clent 依赖 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-client</artifactId> </dependency>

然后就是写yml: 客户端需要知道 Server 在哪里

# ====SpringAIAlibaba Config============= spring.ai.dashscope.api-key=${aliQwen-api} # ====mcp-client Config============= spring.ai.mcp.client.type=async spring.ai.mcp.client.request-timeout=60s spring.ai.mcp.client.toolcallback.enabled=true # 指向你的 MCP Server 地址 spring.ai.mcp.client.sse.connections.mcp-server1.url=http://localhost:8014

mcp-server1是给这个连接起的别名。你可以配置多个 URL,连接来自全世界的MCP Server

主启动编写完,就是业务类的编写:

1. LLMConfig并添加tool调用

package ai.study.config; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.tool.ToolCallbackProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class SaaLLMConfig { @Bean public ChatClient chatClient(ChatModel chatModel, ToolCallbackProvider tools) { return ChatClient.builder(chatModel) .defaultToolCallbacks(tools.getToolCallbacks()) //mcp协议,配置见yml文件 .build(); } }
  • ToolCallbackProvider tools是从哪来的?这就是 MCP Starter 的“魔法”。当你开启了 MCP 客户端,Spring 会自动创建一个名为tools的 Bean,里面包含了从远程8014服务器抓取到的所有工具。

  • defaultToolCallbacks: 你把这些抓到的工具直接塞进了ChatClient的默认配置里。这意味着,只要你用这个chatClient说话,它就天生自带了远程服务器上的所有技能。

2.controller

package ai.study.controller; import jakarta.annotation.Resource; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatModel; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Flux; @RestController public class McpClientController { @Resource private ChatClient chatClient;//使用mcp支持 @Resource private ChatModel chatModel;//没有纳入tool支持,普通调用 // http://localhost:8015/mcpclient/chat?msg=上海 @GetMapping("/mcpclient/chat") public Flux<String> chat(@RequestParam(name = "msg",defaultValue = "北京") String msg) { System.out.println("使用了mcp"); return chatClient.prompt(msg).stream().content(); } @RequestMapping("/mcpclient/chat2") public Flux<String> chat2(@RequestParam(name = "msg",defaultValue = "北京") String msg) { System.out.println("未使用mcp"); return chatModel.stream(msg); } }

此时访问即为:

14.2 远程MCP增强案例-对接互联网通用MCP服务(百度地图)

如果我们想要本地当MCP Client,然后远程的百度地图来MCP Server,那么此时我们先要能到可以调用上万个通用MCP的地方:https://mcp.so/zh

原理说明:

那么在此,我们环境的配置选择的是NodeJS,接着我们应该注册百度地图账号+申请API-key

https://lbsyun.baidu.com/

接下来,我们加入编码开发:

  • 建完module之后,修改pom
<!-- 1.大模型依赖 --> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter-dashscope</artifactId> </dependency> <!-- 2.mcp-clent 依赖 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-mcp-client</artifactId> </dependency>
  • 写yml
server.port=8016 # 设置全局编码格式 server.servlet.encoding.enabled=true server.servlet.encoding.force=true server.servlet.encoding.charset=UTF-8 spring.application.name=springAI-16chat-mcpclient-call-baidumcp # ====LLM Config============= spring.ai.dashscope.api-key=${aliQwen-api} # ====mcp-client Config============= spring.ai.mcp.client.request-timeout=20s spring.ai.mcp.client.toolcallback.enabled=true # spring.ai.mcp.client.stdio.servers-configuration=classpath:/mcp-server.json5
  • 写mcp-server.json5,放在resources下:

{ "mcpServers": { "baidu-map": { "command": "cmd", "args": ["/c", "npx", "-y", "@baidumap/mcp-server-baidu-map"], "env": {"BAIDU_MAP_API_KEY": "你猜"} } } }

注意此时用json5而不是json的一大原因在于json不能写注释

  • 写主启动启动,此时可以看到后台mcp配置,然后写业务类

  • 业务类中SaaLLMConfig,controller和上节一摸一样,此处就不再演示

此时查询:http://localhost:8016/mcpclient/chat?msg=查询北京39.9042归到位置

十五.SAA生态篇,后续不再更新

具体看上方资源

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

音乐现场视频制作:从分轨录音到音画同步实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/3 14:40:52

GstarBIM介绍

浩辰 BIM&#xff08;官网&#xff09;是一款拥有自主内核的 BIM 平台软件&#xff0c;融合了 CAD 的精确性与快速可视化设计能力&#xff0c;提供从平面图绘制到参数化 3D 建模、技术文档自动生成以及渲染输出的完整工作流程&#xff0c;不仅直观易用且具备团队协作能力&#…

作者头像 李华
网站建设 2026/9/3 14:39:54

Claude Code用量限制解析:5小时窗口与每周限额的区别

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/3 14:37:41

Fluent UDF进阶:自定义蒸发冷凝模型原理、实现与调试全解析

简介&#xff1a;本资源是一套面向ANSYS Fluent用户的专业级蒸发-冷凝相变UDF开发包&#xff0c;专为具备C语言基础与Fluent UDF编程经验的CFD工程师设计&#xff0c;用于精准模拟空调系统、热交换器、燃烧室等场景中受压力变化驱动的动态相变过程。压缩包共50个文件&#xff0…

作者头像 李华
网站建设 2026/9/3 14:36:48

幻兽帕鲁1.0正式版存档继承与MOD适配完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/3 14:33:29

嵌入式开发避坑指南:从理论到实战,告别“嘴炮王者”

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华