基于Spring AI Alibaba + RediSearch 实现RAG检索增强

基于Spring AI Alibaba + RediSearch 实现RAG检索增强

基于Docker 安装RediSearch

  • docker镜像源(若拉取镜像较慢,可替换为该镜像源)

    dockerfile
    复制代码
    { "registry-mirrors": [ "https://docker.1ms.run", "https://docker.m.daocloud.io", "https://docker.1panel.live", "https://hub.rat.dev" ] }
  • 拉取redisearch镜像

dockerfile
复制代码
docker pull redislabs/redisearch
  • 创建容器并运行
dockerfile
复制代码
docker run -d -p 55531:6379 -e REDIS_PASSWORD=your password redislabs/redisearch:latest

引入依赖

  • 引入主要依赖包
  • 文档读取器、redis、jedis连接池
xml
复制代码
<!--Spring Ai MarkDown 文档读取器--> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-markdown-document-reader</artifactId> <version>${spring-ai-markdown-document-reader.version}</version> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-vector-store-redis</artifactId> <version>1.0.0-M7</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
  • 完整依赖参考
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.4.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.alibaba</groupId> <artifactId>gu-agent</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>gu-agent</name> <description>gu-agent</description> <properties> <java.version>21</java.version> <maven.compiler.source>21</maven.compiler.source> <maven.compiler.target>21</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring-boot.version>3.4.4</spring-boot.version> <lombok.version>1.18.36</lombok.version> <hutool-all.version>5.8.37</hutool-all.version> <knife4j.version>4.4.0</knife4j.version> <jsonschema-generator.version>4.38.0</jsonschema-generator.version> <spring-ai-alibaba-starter.version>1.0.0-M6.1</spring-ai-alibaba-starter.version> <spring-ai-markdown-document-reader.version>1.0.0-M6</spring-ai-markdown-document-reader.version> <mysql.version>8.0.29</mysql.version> <mybatis-plus.version>3.5.12</mybatis-plus.version> <commons-lang3.version>3.17.0</commons-lang3.version> <redisson.version>3.50.0</redisson.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <optional>true</optional> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>${hutool-all.version}</version> </dependency> <!-- knife4j 接口文档 --> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> <version>${knife4j.version}</version> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.10.1</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>${commons-lang3.version}</version> </dependency> <!--===========================================================================--> <!-- Spring AI Alibaba --> <!--引入以后集成了调用阿里云百炼平台的大模型能力以外,还有提供的其他能力。例如RAG增强、Function Calling等--> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter</artifactId> <version>${spring-ai-alibaba-starter.version}</version> </dependency> <!-- 支持结构化输出 --> <dependency> <groupId>com.github.victools</groupId> <artifactId>jsonschema-generator</artifactId> <version>${jsonschema-generator.version}</version> </dependency> <!-- redis memory包 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>${spring-boot.version}</version> </dependency> <!--Spring Ai MarkDown 文档读取器--> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-markdown-document-reader</artifactId> <version>${spring-ai-markdown-document-reader.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <version>${spring-boot.version}</version> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-vector-store-redis</artifactId> <version>1.0.0-M7</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-bom</artifactId> <version>1.0.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <!-- 需要引入仓库配置,才能下载到最新的 Spring AI 相关的依赖 --> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> <repository> <id>sonatype</id> <name>OSS Sonatype</name> <url>https://oss.sonatype.org/content/groups/public/</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> </repository> </repositories> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </path> </annotationProcessorPaths> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>

创建自定义 RAG 顾问

java
复制代码
import lombok.extern.slf4j.Slf4j; import org.springframework.ai.chat.client.advisor.api.*; import org.springframework.ai.document.Document; import org.springframework.ai.vectorstore.SearchRequest; import org.springframework.ai.vectorstore.VectorStore; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.List; @Slf4j public class RagAdvisor implements CallAroundAdvisor, StreamAroundAdvisor { private final VectorStore vectorStore; private final String knowledgeSourcePrefix; // 新增:知识库来源前缀 public RagAdvisor(VectorStore vectorStore) { this(vectorStore, "知识库来源"); } // 新增带知识库来源前缀的构造器 public RagAdvisor(VectorStore vectorStore, String knowledgeSourcePrefix) { this.vectorStore = vectorStore; this.knowledgeSourcePrefix = knowledgeSourcePrefix; } @Override public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) { String query = advisedRequest.userText(); SearchRequest searchRequest = SearchRequest.builder() .query(query) .topK(5) .build(); List<Document> relevantDocs = vectorStore.similaritySearch(searchRequest); // 增强:添加知识库来源信息 String context = buildContextWithSource(relevantDocs); String augmentedQuery = context + "\n\n用户问题: " + query; log.info("增强后的查询 =======================>: {}", augmentedQuery); AdvisedRequest augmentedRequest = AdvisedRequest.from(advisedRequest) .userText(augmentedQuery) .build(); return chain.nextAroundCall(augmentedRequest); } @Override public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) { return Mono.fromCallable(() -> { String query = (String) advisedRequest.userText(); SearchRequest searchRequest = SearchRequest.builder() .query(query) .topK(5) .build(); List<Document> relevantDocs = vectorStore.similaritySearch(searchRequest); return buildContextWithSource(relevantDocs); // 使用增强的方法 }) .flatMapMany(context -> { String augmentedQuery = context + "\n\n用户问题: " + advisedRequest.userText(); log.info("增强后的查询 =======================>: {}", augmentedQuery); AdvisedRequest augmentedRequest = AdvisedRequest.from(advisedRequest) .userText(augmentedQuery) .build(); return chain.nextAroundStream(augmentedRequest); }); } // 增强:添加知识库来源信息 private String buildContextWithSource(List<Document> documents) { StringBuilder sb = new StringBuilder("相关上下文信息:\n"); for (int i = 0; i < documents.size(); i++) { Document doc = documents.get(i); String source = doc.getMetadata().getOrDefault("filename", "未知来源").toString(); sb.append(i + 1).append(". ") .append(doc.getText()) .append("\n[") .append(knowledgeSourcePrefix).append(": ") // 添加来源前缀 .append(source) .append("]\n\n"); } return sb.toString(); } @Override public String getName() { return "自定义 RagAdvisor 顾问"; } @Override public int getOrder() { return -100; } }

创建向量数据库配置文件

java
复制代码
import com.alibaba.guagent.rag.docmentloader.LoveAppDocumentLoader; import jakarta.annotation.Resource; import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.vectorstore.redis.RedisVectorStore; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import redis.clients.jedis.JedisPooled; import java.util.List; @Configuration public class VectorStoreConfig { @Value("${spring.data.redis.host}") private String redisHost; @Value("${spring.data.redis.port}") private int redisPort; @Resource private LoveAppDocumentLoader loveAppDocumentLoader; @Bean public RedisVectorStore redisVectorStore(EmbeddingModel dashscopeEmbeddingModel, JedisPooled jedisPooled) { RedisVectorStore redisVectorStore = RedisVectorStore.builder(jedisPooled, dashscopeEmbeddingModel) .indexName("love-app-vector-index") .prefix("love-app") .metadataFields( RedisVectorStore.MetadataField.text("filename"), RedisVectorStore.MetadataField.numeric("page"), RedisVectorStore.MetadataField.tag("category") ) .initializeSchema(true) .build(); List<Document> documents = loveAppDocumentLoader.loadMarkdowns(); redisVectorStore.doAdd(documents); return redisVectorStore; } @Bean public JedisPooled jedisPooled() { return new JedisPooled(redisHost, redisPort); } }

创建基于MarkDown的加载器

加载的同时在循环中自动的将一个文档进行分割并解析成多个文档对象。

  • ResourcePatternResolver(资源模式解析器)的getResources获取到所有文档。
  • 将返回的所有资源(Resource)类,进行处理在每一轮的循环中进行解析并分割,一轮循环结束以后一并存入文档集合中。
java
复制代码
import lombok.extern.slf4j.Slf4j; import org.springframework.ai.document.Document; import org.springframework.ai.reader.markdown.MarkdownDocumentReader; import org.springframework.ai.reader.markdown.config.MarkdownDocumentReaderConfig; import org.springframework.core.io.Resource; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.ArrayList; import java.util.List; @Component @Slf4j public class LoveAppDocumentLoader { private final ResourcePatternResolver resourcePatternResolver; public LoveAppDocumentLoader(ResourcePatternResolver resourcePatternResolver) { this.resourcePatternResolver = resourcePatternResolver; } /** * 加载 Markdown 文档知识库。 * * @return */ public List<Document> loadMarkdowns() { List<Document> allDocuments = new ArrayList<>(); try { // Resource[] resources = resourcePatternResolver.getResources("classpath:document/*.md"); for (Resource resource : resources) { String fileName = resource.getFilename(); MarkdownDocumentReaderConfig config = MarkdownDocumentReaderConfig.builder() .withHorizontalRuleCreateDocument(true) // 水平线是否创建文档 .withIncludeCodeBlock(false) // 是否包含代码块 .withIncludeBlockquote(false) // 是否包含引用块 .withAdditionalMetadata("filename", fileName) .build(); MarkdownDocumentReader reader = new MarkdownDocumentReader(resource, config); List<Document> documents = reader.get(); allDocuments.addAll(documents); } } catch (IOException e) { log.error("Markdown 文档加载失败", e); } return allDocuments; } }

在Agent应用种添加RAG检索增强

java
复制代码
@Component @Slf4j public class LoveApp { private final ChatClient chatClient; private static final String SYSTEM_PROMPT = "扮演深耕恋爱心理领域的专家。开场向用户表明身份,告知用户可倾诉恋爱难题。" + "围绕单身、恋爱、已婚三种状态提问:单身状态询问社交圈拓展及追求心仪对象的困扰;" + "恋爱状态询问沟通、习惯差异引发的矛盾;已婚状态询问家庭责任与亲属关系处理的问题。" + "引导用户详述事情经过、对方反应及自身想法,以便给出专属解决方案。"; @Resource private VectorStore redisVectorStore; @Resource private Advisor loveAppRagCloudAdvisor; /** * 基于Redis的消息对话持久化 * * @param dashScopeChatModel * @param redisTemplate */ public LoveApp(ChatModel dashScopeChatModel, RedisTemplate<String, Object> redisTemplate) { ChatMemory chatRedisMemory = new RedisChatMemory(redisTemplate); this.chatClient = ChatClient.builder(dashScopeChatModel) .defaultSystem(SYSTEM_PROMPT) .defaultAdvisors( // 自定义日志 Advisor,可按需开启 new AgentAdvisor() , MessageChatMemoryAdvisor.builder(chatRedisMemory).build() // 自定义推理增强 Advisor,可按需开启 //,new ReReadingAdvisor() ) .build(); } public LoveReport doChatWithReportAndRag(String message, String chatId) { LoveReport loveReport = chatClient .prompt() .system(SYSTEM_PROMPT + "每次对话后都要生成恋爱结果,标题为{用户名}的恋爱报告,内容为建议列表") .user(message) .advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId) .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10)) // 应用 RAG 检索增强服务(基于本地知识库) .advisors(new RagAdvisor(redisVectorStore)) .call() .entity(LoveReport.class); log.info("loveReport: {}", loveReport); return loveReport; } }

验证

  • 从redis管理面板中可得知,成功将文档的向量数据存入redis中。

1753360825836.png

1753361084694.png

  • 控制台日志信息
xml
复制代码
2025-07-24T20:45:33.900+08:00 INFO 1828 --- [gu-ai-agent] [nio-8123-exec-7] o.a.c.c.C.[Tomcat].[localhost].[/api] : Initializing Spring DispatcherServlet 'dispatcherServlet' 2025-07-24T20:45:33.900+08:00 INFO 1828 --- [gu-ai-agent] [nio-8123-exec-7] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 2025-07-24T20:45:33.902+08:00 INFO 1828 --- [gu-ai-agent] [nio-8123-exec-7] o.s.web.servlet.DispatcherServlet : Completed initialization in 2 ms 2025-07-24T20:45:35.548+08:00 INFO 1828 --- [gu-ai-agent] [nio-8123-exec-7] com.alibaba.guagent.rag.redis.RagAdvisor : 增强后的查询 =======================>: 相关上下文信息: 1. 选择合适时机,如在轻松的周末午后,心平气和开启话题。先分享自己对未来的设想,包括事业发展、家庭规划、生活愿景等,如“我希望未来几年能在事业上取得晋升,同时能多去旅行看看世界”。认真倾听对方想法,尊重差异,不强行要求一致。共同探讨如何协调双方规划,制定共同目标与阶段性计划。比如小吴和男友通过深入沟通,制定了先一起攒钱买房,再旅行的计划,感情更加稳固。 推荐课程:《恋爱中未来规划沟通秘籍》,课程教授沟通技巧与规划协调方法,助你和伴侣明确未来方向,携手走向幸福。 [知识库来源: 恋爱常见问题和回答 - 恋爱篇.md] 2. 定期安排二人世界,如每周一次看电影或共进晚餐。保持身体亲密接触,日常拥抱、亲吻。分享日常喜怒哀乐,深入交流内心想法。一起回忆美好过往,如旅行经历、恋爱趣事。为对方制造小惊喜,如纪念日礼物。像老张夫妇坚持每周约会,分享生活点滴,结婚多年仍甜蜜如初。 推荐课程:《婚后亲密关系维护秘籍》,课程提供多种维护亲密关系的方法与技巧,让婚后爱情持续升温,家庭幸福美满。 [知识库来源: 恋爱常见问题和回答 - 已婚篇.md] 3. 了解对方喜好是关键,若对方喜欢阅读,可精心挑选一本限量版书籍,附上手写情书。策划一场特别约会,比如在对方喜欢的海边看日出。日常也能制造小惊喜,如在对方下班时送上一杯热咖啡。或者为对方准备一场主题派对,布置成对方喜欢的风格。小李为女友精心策划了一场星空露营约会,让女友感动不已,感情愈发深厚。 推荐课程:《恋爱浪漫惊喜制造宝典》,课程涵盖各类浪漫惊喜创意与实施方法,帮你为爱人打造难忘瞬间,让爱情时刻充满新鲜感。 [知识库来源: 恋爱常见问题和回答 - 恋爱篇.md] 4. 制定详细的日程表,合理分配工作与家庭时间,如工作日晚上预留两小时陪伴家人。与配偶共同协商家务分工,依据各自擅长领域安排,如一方擅长烹饪负责做饭,另一方擅长清洁负责打扫卫生。在工作中提高效率,减少不必要加班,重要家庭活动提前安排工作。例如老陈通过合理规划,既能在工作上取得成绩,又能照顾好家庭,家庭关系和睦。 推荐课程:《婚后工作家庭平衡之道》,课程从时间管理、责任分配等方面入手,助你轻松应对婚后工作与家庭的双重挑战,实现和谐生活。 [知识库来源: 恋爱常见问题和回答 - 已婚篇.md] 5. 设定个人成长目标,如学习新技能、考取证书。利用碎片化时间学习,如在通勤路上听有声书。与伴侣商量争取其支持,共同安排家庭事务,为个人成长留出时间。参加线上或线下学习小组,保持学习动力与热情。比如老孙婚后利用业余时间学习编程,获得伴侣支持,成功转行进入新领域,实现自我价值。 推荐课程:《婚后个人成长与自我实现》,课程指导你在婚后繁杂生活中,坚持自我成长,实现个人梦想,拥有精彩人生。 [知识库来源: 恋爱常见问题和回答 - 已婚篇.md]
0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
下载 APP