行业资讯
📅 2026/8/17 3:15:33
Spring AI Alibaba 入门开发3-持久化记忆、向量数据库、RAG (备份)
目录一、Chat Memory连续对话保存和持久化1.1 对话记忆介绍1.2 API介绍1.3 安装redis1.4 开发二、向量化和向量数据库2.1 向量文本化2.2 向量数据库2.3 开发三、RAG检索增强生成3.1 介绍3.2 构建模块文档加载器和解析器文本分割器嵌入模型向量存储检索器RAG 架构3.3 开发建议大家先看前两章内容Spring AI Alibaba 入门开发1-环境搭建、ollama部署、Chat和ChatModel、流式输出-CSDN博客Spring AI Alibaba 入门开发2-提示词功能、提示词模板、格式化输出-CSDN博客一、Chat Memory连续对话保存和持久化1.1 对话记忆介绍”大模型的对话记忆”这一概念根植于人工智能与自然语言处理领域特别是针对具有深度学习能力的大型语言模型而言它指的是模型在与用户进行交互式对话过程中能够追踪、理解并利用先前对话上下文的能力。 此机制使得大模型不仅能够响应即时的输入请求还能基于之前的交流内容能够在对话中记住先前的对话内容并根据这些信息进行后续的响应。这种记忆机制使得模型能够在对话中持续跟踪和理解用户的意图和上下文从而实现更自然和连贯的对话。1.2 API介绍RedisChatMemoryRepository实现了Spring AI 规定的ChatMemoryRepository接口支持redis持久化到本地。public class RedisChatMemoryRepository implements ChatMemoryRepository, AutoCloseable { private static final Logger logger LoggerFactory.getLogger(RedisChatMemoryRepository.class); private static final String DEFAULT_KEY_PREFIX spring_ai_alibaba_chat_memory:; private final JedisPool jedisPool; private final ObjectMapper objectMapper; private RedisChatMemoryRepository(JedisPool jedisPool) { Assert.notNull(jedisPool, jedisPool cannot be null); this.jedisPool jedisPool; this.objectMapper new ObjectMapper(); SimpleModule module new SimpleModule(); module.addDeserializer(Message.class, new MessageDeserializer()); this.objectMapper.registerModule(module); }Advisor API这是一个拦截器链设计模式允许你通过注入检索数据Retrieval Context和对话历史Chat Memory来修改传入的 Prompt。1.3 安装redis请提前安装redisWindows用户推荐使用dockerredis。1.4 开发新建子模块、修改pom.xml、创建启动类、新增配置文件。server.port8888 #解决中文乱码 server.servlet.encoding.charsetUTF-8 server.servlet.encoding.enabledtrue server.servlet.encoding.forcetrue spring.application.namessa-07-StrucOutput #Sprig AI Alibaba 相关配置 spring.ai.dashscope.api-key${qianwen-api} spring.ai.dashscope.base-urlhttps://ws-6tq01jo6mtthlhoq.cn-beijing.maas.aliyuncs.com/compatible-mode/v1 #spring.ai.dashscope.chat.options.modelqwen-max # redis config spring.data.redis.hostlocalhost spring.data.redis.port6379 spring.data.redis.database0 spring.data.redis.connect-timeout3 spring.data.redis.timeout5000ms spring.data.redis.passworddependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !--spring-ai-alibaba dashscope-- dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-starter-dashscope/artifactId /dependency !--spring-ai-alibaba memory-redis-- dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-starter-memory-redis/artifactId /dependency !--jedis-- dependency groupIdredis.clients/groupId artifactIdjedis/artifactId /dependency !--lombok-- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId version1.18.38/version /dependency !--hutool-- dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.8.22/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.11.0/version configuration compilerArgs arg-parameters/arg /compilerArgs source21/source target21/target /configuration /plugin /plugins /build repositories repository idspring-milestones/id nameSpring Milestones/name urlhttps://repo.spring.io/milestone/url snapshots enabledfalse/enabled /snapshots /repository /repositoriesSpringBootApplication public class SSA_08_PersistentApplication { public static void main(String[] args) { SpringApplication.run(SSA_08_PersistentApplication.class, args); } }2、新建Redis配置类Configuration public class RedisMemoryConfig { Value(${spring.data.redis.host}) private String host; Value(${spring.data.redis.port}) private int port; Value(${spring.data.redis.password}) private String password; Bean public RedisChatMemoryRepository redisChatMemoryRepository() { return RedisChatMemoryRepository.builder() .host(host) .port(port) .password(password) .build(); } }创建ChatClient配置类注意第二个参数Configuration public class LLMConfig { //定义各个模型名称 private final String DEEPSEEK_MODEL deepseek-v4-pro; private final String QWEN_MODEL qwen-max; //定义配置API-KEY的环境变量 private final String API_KEY qianwen-api; //创建deepseekChatModel Bean(name deepseekChatModel) public ChatModel deepseekChatModel() { return DashScopeChatModel.builder() .dashScopeApi(DashScopeApi.builder().apiKey(System.getenv(API_KEY)).build()) .defaultOptions(DashScopeChatOptions.builder().withModel(DEEPSEEK_MODEL).build()) .build(); } //创建qwenChatModel Bean(name qwenChatModel) public ChatModel qwenChatModel() { return DashScopeChatModel.builder() .dashScopeApi(DashScopeApi.builder().apiKey(System.getenv(API_KEY)).build()) .defaultOptions(DashScopeChatOptions.builder().withModel(QWEN_MODEL).build()) .build(); } //创建deepseekChatClient Bean(name deepseekChatClient) public ChatClient deepseekChatClient(Qualifier(deepseekChatModel)ChatModel chatModel, RedisChatMemoryRepository redisChatMemoryRepository) { MessageWindowChatMemory windowChatMemory MessageWindowChatMemory.builder() .chatMemoryRepository(redisChatMemoryRepository) .maxMessages(15)//最大聊天记录 .build(); return ChatClient.builder(chatModel) .defaultOptions(ChatOptions.builder().model(DEEPSEEK_MODEL).build()) .defaultAdvisors(MessageChatMemoryAdvisor.builder(windowChatMemory).build()) .build(); } //创建qwenChatClient Bean(name qwenChatClient) public ChatClient qwenChatClient(Qualifier(qwenChatModel) ChatModel chatModel, RedisChatMemoryRepository redisChatMemoryRepository) { MessageWindowChatMemory windowChatMemory MessageWindowChatMemory.builder() .chatMemoryRepository(redisChatMemoryRepository) .maxMessages(15) .build(); return ChatClient.builder(chatModel) .defaultOptions(ChatOptions.builder().model(QWEN_MODEL).build()) .defaultAdvisors(MessageChatMemoryAdvisor.builder(windowChatMemory).build()) .build(); } }二、向量化和向量数据库2.1 向量文本化嵌入(Embedding)的工作原理是将文本、图像和视频转换为称为向量Vectors的浮点数数组。这些向量旨在捕捉文本、图像和视频的含义。嵌入数组的长度称为向量的维度Dimensionality。嵌入模型EmbeddingModel是嵌入过程中采用的模型。当前EmbeddingModel的接口主要用于将文本转换为数值向量接口的设计主要围绕这两个目标展开可移植性该接口确保在各种嵌入模型之间的轻松适配。它允许开发者在不同的嵌入技术或模型之间切换所需的代码更改最小化。这一设计与 Spring 模块化和互换性的理念一致。简单性嵌入模型简化了文本转换为嵌入的过程。通过提供如embed(String text)和embed(Document document)这样简单的方法它去除了处理原始文本数据和嵌入算法的复杂性。这个设计选择使开发者尤其是那些初次接触 AI 的开发者更容易在他们的应用程序中使用嵌入而无需深入了解其底层机制。案例一四个维度对比运输工具如何确认相似性案例二图片对比总结嵌入模型是一种机器学习模型旨在在连续的低维向量空间中表示数据例如文本、图像或其他形式的信息。这些嵌入可以捕获数据之间的语义或上下文相似性是机器能否更有效地执行比较、聚类或分类等任务。假设要描述不同的水果不用长篇大论而是用数字来描述大小、颜色、甜度等特征。例如苹果可能是[8,5,4]香蕉是[12,8,7]。这些数字使比较或对相似的水果进行分组变更更容易。2.2 向量数据库向量存储VectorStore是一种用于存储和检索高维向量数据的数据库或存储解决方案它特别适用于处理那些经过嵌入模型转化后的数据。在 VectorStore 中查询与传统关系数据库不同。它们执行相似性搜索而不是精确匹配。当给定一个向量作为查询时VectorStore 返回与查询向量“相似”的向量。VectorStore 用于将您的数据与 AI 模型集成。在使用它们时的第一步是将您的数据加载到矢量数据库中。然后当要将用户查询发送到 AI 模型时首先检索一组相似文档。然后这些文档作为用户问题的上下文并与用户的查询一起发送到 AI 模型。这种技术被称为检索增强生成Retrieval Augmented GenerationRAG。VectorStore API提供了简单易用的接口供开发者对 VectorStore 进行操作。其核心功能是通过高效的索引结构和相似性计算算法支持大规模向量数据的快速查询与分析向量数据库维度越高查询精准度也越高查询效果也越好。向量数据库 将文本、图像和视频转换为称为向量Vectors的浮点数数组在 VectorStore中查询与传统关系数据库不同。它们执行相似性搜索而不是精确匹配。当给定一个向量作为查询时VectorStore 返回与查询向量“相似”的向量。指征特点捕捉复杂的词汇关系如语义相似性、同义词、多义词。向量嵌入为检索增强生成 (RAG) 应用程序提供支持。总结将文本映射到高维空间中的点使语义相似的文本在这个空间中距离较近.。2.3 开发使用redis8以上版本。新增子模块修改pom.xml、新增启动类、新增controllerdependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !--spring-ai-alibaba dashscope-- dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-starter-dashscope/artifactId /dependency !-- 添加 Redis 向量数据库依赖 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-vector-store-redis/artifactId /dependency !--lombok-- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId version1.18.38/version /dependency !--hutool-- dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.8.22/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.11.0/version configuration compilerArgs arg-parameters/arg /compilerArgs source21/source target21/target /configuration /plugin /plugins /build repositories repository idspring-milestones/id nameSpring Milestones/name urlhttps://repo.spring.io/milestone/url snapshots enabledfalse/enabled /snapshots /repository /repositoriesserver.port8888 #解决中文乱码 server.servlet.encoding.charsetUTF-8 server.servlet.encoding.enabledtrue server.servlet.encoding.forcetrue spring.application.namessa-11-EmbedToVertor #Sprig AI Alibaba 相关配置 spring.ai.dashscope.api-key${qianwen-api} spring.ai.dashscope.base-url spring.ai.dashscope.chat.options.modelqwen-max spring.ai.dashscope.embedding.options.modeltext-embedding-v3 # redis config spring.data.redis.hostlocalhost spring.data.redis.port6379 spring.data.redis.password spring.ai.vectorstore.redis.initialize-schematrue spring.ai.vectorstore.redis.index-namecustom-index spring.ai.vectorstore.redis.prefixcustom-prefixSpringBootApplication public class SSA_11_EmbedToVectorApplication { public static void main(String[] args) { SpringApplication.run(SSA_11_EmbedToVectorApplication.class, args); } }RestController Slf4j public class EmbedToVVectorController { Resource private EmbeddingModel embeddingModel; Resource private VectorStore vectorStore; //文本向量化 GetMapping(/v1/chat) public EmbeddingResponse char1(String msg) { EmbeddingResponse response embeddingModel.call(new EmbeddingRequest(List.of(msg), DashScopeEmbeddingOptions.builder().withModel(text-embedding-v3).build())); System.out.println(Arrays.toString(response.getResult().getOutput())); return response; } //保存到redis GetMapping(/v2/add) public void add() { ListDocument documents List.of( new Document(i study LLM), new Document(i love java) ); vectorStore.add(documents); } //从redis查询 GetMapping(/v3/get) public List getAll(RequestParam(name msg) String msg) { SearchRequest searchRequest SearchRequest.builder() .query(msg) .topK(2) .build(); ListDocument list vectorStore.similaritySearch(searchRequest); System.out.println(list); return list; } }三、RAG检索增强生成3.1 介绍大型语言模型LLM虽然强大但有两个关键限制有限的上下文——它们无法一次性摄取整个语料库静态知识——它们的训练数据在某个时间点被冻结检索通过在查询时获取相关的外部知识来解决这些问题。这是**检索增强生成RAG**的基础使用特定上下文的信息来增强 LLM 的回答。构建知识库检索允许 LLM 在运行时访问相关上下文。但大多数实际应用更进一步它们将检索与生成集成以产生基于事实的、上下文感知的答案。这是**检索增强生成RAG**的核心思想。检索管道成为结合搜索和生成的更广泛系统的基础。3.2 构建模块在 Spring AI Alibaba 中你可以使用以下组件构建 RAG 系统文档加载器和解析器从外部源文件、数据库、云存储、在线平台等摄取数据返回标准化的文档对象。Spring AI Alibaba 提供了丰富的Document Reader和Document Parser实现支持 PDF、Word、Markdown、GitHub、Notion、语雀等多种数据源和格式。文本分割器将大型文档分解为更小的块这些块可以单独检索并适合模型的上下文窗口。文本分割是 ETL 管道中的关键步骤详见ETL Pipeline。嵌入模型嵌入模型将文本转换为数字向量使得具有相似含义的文本在向量空间中靠近在一起。Spring AI Alibaba 支持多种Embedding Model实现包括 DashScope、OpenAI、Ollama 等。向量存储用于存储和搜索嵌入的专用数据库。Spring AI Alibaba 支持多种向量数据库包括Milvus、Pinecone、Redis、Elasticsearch等。更多实现请查看向量数据库文档。检索器检索器是一个接口给定非结构化查询返回文档。Spring AI 提供了模块化的 RAG 架构支持查询转换、查询扩展、文档后处理等高级功能。RAG 架构RAG 可以以多种方式实现具体取决于你的系统需求。我们在下面的部分概述每种类型。延迟延迟在两步 RAG中通常更可预测因为 LLM 调用的最大次数是已知且有上限的。这种可预测性假设 LLM 推理时间是主要因素。但是实际延迟也可能受检索步骤性能的影响——例如 API 响应时间、网络延迟或数据库查询——这些可能因使用的工具和基础设施而异。总结RAG技术就像给AI大模型装上了「实时百科大脑」为了让大模型获取足够的上下文以便获得更加广泛的信息源通过先查资料后回答的机制让AI摆脱传统模型的”知识遗忘和幻觉回复”困境。通过引入外部知识源来增强LLM的输出能力传统的LLM通常基于其训练数据生成响应但这些数据可能过时或不够全面。RAG允许模型在生成答案之前从特定的知识库中检索相关信息从而提供更准确和上下文相关的回答。RAG 流程分为两个不同的阶段索引和检索索引Indexing索引首先清理和提取各种格式的原始数据如PDF、HTML、Word和Markdown然后将其转换为统一的纯文本格式。为了适应语言模型的上下文限制文本被分割成更小的、可消化的块chunk。然后使用嵌入模型将编码成向量表示并存储在向量数据库中。这一步对于在随后的检索阶段实现高效是相似性搜索至关重要。知识库分割成chunks并将chunks向量化至向量库中。3.3 开发需求SpringAI阿里百炼嵌入模型text-embedding-v3向量数据库RedisStackDeepSeek来实现RAG功能。新建子模块项目结构如下修改pom.xml、新增启动类、配置文件、配置类、controller。dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !--spring-ai-alibaba dashscope-- dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-starter-dashscope/artifactId /dependency !-- 添加 Redis 向量数据库依赖 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-vector-store-redis/artifactId /dependency !--lombok-- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId version1.18.38/version /dependency !--hutool-- dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.8.22/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.11.0/version configuration compilerArgs arg-parameters/arg /compilerArgs source21/source target21/target /configuration /plugin /plugins /build repositories repository idspring-milestones/id nameSpring Milestones/name urlhttps://repo.spring.io/milestone/url snapshots enabledfalse/enabled /snapshots /repository /repositoriesops.txt00000 系统OK正确执行后的返回 A0001 用户端错误一级宏观错误码 A0100 用户注册错误二级宏观错误码 B1111 支付接口超时 C2222 Kafka消息解压严重server.port8888 #解决中文乱码 server.servlet.encoding.charsetUTF-8 server.servlet.encoding.enabledtrue server.servlet.encoding.forcetrue spring.application.namessa-11-EmbedToVertor #Sprig AI Alibaba 相关配置 spring.ai.dashscope.api-key${qianwen-api} #spring.ai.dashscope.base-urlxxxx spring.ai.dashscope.chat.options.modeldeepseek-r1 spring.ai.dashscope.embedding.options.modeltext-embedding-v3 # redis config spring.data.redis.hostlocalhost spring.data.redis.port6379 spring.data.redis.password spring.ai.vectorstore.redis.initialize-schematrue spring.ai.vectorstore.redis.index-namecustom-index spring.ai.vectorstore.redis.prefixcustom-prefixSpringBootApplication public class SSA_12_RAGApplication { public static void main(String[] args) { SpringApplication.run(SSA_12_RAGApplication.class, args); } }Configuration public class InitVectorDatabaseConfig { Autowired private VectorStore vectorStore; Autowired private RedisTemplateString,String redisTemplate; Value(classpath:ops.txt) private Resource opsFile; //-----------------------------------------第一版写入向量数据库当前无法去重每次启动服务都会新增下个版本修复 // PostConstruct // public void init() { // //读取文件 // TextReader reader new TextReader(opsFile); // //设置文件编码 // reader.setCharset(Charset.defaultCharset()); // //文件转换成向量(分词) // ListDocument documents new TokenTextSplitter().transform(reader.read()); // //写入向量数据库当前无法去重每次启动服务都会新增下个版本修复 // vectorStore.add(documents); // } //------------------------------第二版 PostConstruct public void init() { //1 读取文件 TextReader textReader new TextReader(opsFile); textReader.setCharset(Charset.defaultCharset()); //2 文件转换为向量(开启分词) ListDocument list new TokenTextSplitter().transform(textReader.read()); //3 写入向量数据库RedisStack //vectorStore.add(list); // 解决上面第3步向量数据重复问题使用redis setnx命令处理 //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(------向量初始化数据已经加载过请不要重复操作); } } }Configuration public class LLMConfig { //定义各个模型名称 private final String DEEPSEEK_MODEL deepseek-v4-pro; private final String QWEN_MODEL qwen-max; //定义配置API-KEY的环境变量 private final String API_KEY qianwen-api; Bean(name deepseekChatModel) public ChatModel deepseekChatModel() { return DashScopeChatModel.builder() .dashScopeApi(DashScopeApi .builder() .apiKey(System.getenv(API_KEY)) .build()) .defaultOptions(DashScopeChatOptions. builder(). withModel(DEEPSEEK_MODEL) .build()) .build(); } Bean(name qwenChatModel) public ChatModel qwenChatModel() { return DashScopeChatModel.builder() .dashScopeApi(DashScopeApi .builder() .apiKey(API_KEY) .build()) .defaultOptions(DashScopeChatOptions .builder() .withModel(QWEN_MODEL) .build()) .build(); } Bean(name deepseekChatClient) public ChatClient deepseekChatClient(Qualifier(deepseekChatModel) ChatModel chatModel) { return ChatClient.builder(chatModel) .defaultOptions(ChatOptions .builder() .model(DEEPSEEK_MODEL) .build()) .build(); } Bean(name qwenChatClient) public ChatClient qwenChatClient(Qualifier(qwenChatModel) ChatModel chatModel) { return ChatClient .builder(chatModel) .defaultOptions(ChatOptions .builder() .model(QWEN_MODEL) .build()) .build(); } }Configuration public class RedisConfig { Bean public RedisTemplateString, Object redisTemplate(RedisConnectionFactory redisConnectionFactor) { RedisTemplateString,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; } }RestController public class RagController { Resource(name qwenChatClient) private ChatClient chatClient; Resource private VectorStore vectorStore; GetMapping(/v1/chat) public FluxString chat1(String msg){ String sysRoleInfo 你是一位运维工程师按照给出的编码给出对应故障解释否者回复找不到错误信息。 ; RetrievalAugmentationAdvisor advisor RetrievalAugmentationAdvisor .builder() .documentRetriever( VectorStoreDocumentRetriever .builder() .vectorStore(vectorStore) .build() ) .build(); return chatClient .prompt() .system(sysRoleInfo) .user(msg) .advisors(advisor) // RAG功能向量数据库查询 .stream().content(); } }