☰
Spring AI Alibaba Graph框架实现RAG私有知识库问答
2026/10/10 2:57:34 网站建设 项目流程

目录

前言

一、先聊背景:为什么需要 RAG?

二、最终目标

三、环境准备

3.1 技术栈

3.2 安装与启动 Redis Stack

3.2.1 前置条件:启用 WSL 2

3.2.2 安装 Docker Desktop

3.2.3 启动 Redis Stack 容器

3.2.4 验证 Redis Stack 是否正常运行

3.2.5 常用管理命令

四、Maven 依赖

五、核心配置

六、RedisVectorStore 配置类

七、文件上传 + 文档入库

7.1 核心 Controller

7.2 这段代码做了什么

八、RAG 问答接口

九、最终完整结果测试


前言

这篇是 Spring AI 进阶系列的第三篇。在前两篇中,我们已经完成了 Spring Boot 3.3.4 集成 Spring AI Alibaba、使用 DashScope 大模型实现流式对话。但通用大模型有一个致命短板:它对我们的私有知识一无所知。本文将通过 RAG(检索增强生成)技术,配合 Redis Stack 向量数据库,手把手实现一个可上传文档、切割向量化、基于私有知识库回答的完整 RAG 系统。

一、先聊背景:为什么需要 RAG?

前面两篇我们成功把大模型接入了 Spring Boot,对话也跑通了。但很快就发现一个问题,你问它“我们公司最新的产品手册里有哪些功能”,它完全答不上来。这不是模型不行,而是它的训练数据里根本没有你的私有文档。大模型就像一位知识渊博但从未读过你公司资料的专家,知识广度够但深度不够。

解决这个问题有两条路:

  • 微调(Fine-tuning):拿私有数据重新训练模型。成本高、周期长、每次数据更新都要重新训练。

  • RAG(Retrieval-Augmented Generation):不改模型,而是在问答时先从私有知识库检索相关内容,拼接进 prompt 一起发给模型。

RAG 就像给大模型外挂了一个“知识U盘”。用户提问时,系统先去向量库里找最相关的文档片段,然后把这些片段和问题一起“喂”给大模型,让它基于参考资料回答。这回要做的就是把这个流程完整落地。

二、最终目标

完成后你将拥有:

  1. 一个文件上传接口,支持 PDF、DOCX、TXT 等格式

  2. 文档自动经过 Tika 解析 → Token 切割 → 向量化 → 存入 Redis Stack

  3. 一个问答接口,提问时自动检索私有知识库,由大模型基于文档内容回答

  4. 一条完整的 ETL 数据流:上传 → 解析 → 切割 → 向量化 → 存储 → 检索 → 增强回答

三、环境准备

3.1 技术栈

组件版本
JDK17
Spring Boot3.3.4
Spring AI1.0.3
Spring AI Alibaba1.0.0.4
RedisRedis Stack(Docker 运行)
对话模型deepseek-v4-flash(阿里云 MaaS)
嵌入模型text-embedding-v3

3.2 安装与启动 Redis Stack

RAG 需要向量检索能力,普通 Redis 不行,必须用 Redis Stack,它内置了 RediSearch 模块,支持向量索引和相似度搜索。在 Windows 上运行 Redis Stack,官方推荐的方式是通过 Docker 容器-。下面从零开始,分步完成环境搭建。

3.2.1 前置条件:启用 WSL 2

Docker Desktop 在 Windows 11 上需要 WSL 2 作为后端-。如果你的系统尚未安装 WSL 2,以管理员身份打开 PowerShell,执行:

wsl --install

安装完成后重启电脑。再次打开 PowerShell,验证 WSL 版本:

wsl --version

确保 WSL 版本不低于 2.1.5-。如果版本过低,执行wsl --update升级。

3.2.2 安装 Docker Desktop
  1. 访问 Docker 官网,下载Docker Desktop for Windows (AMD64)安装包-。

  2. 双击Docker Desktop Installer.exe运行安装程序。在安装向导中,务必勾选“Use WSL 2 instead of Hyper-V (recommended)”选项-。

  3. 按照提示完成安装,安装完成后重启电脑。

  4. 从开始菜单启动 Docker Desktop。首次启动会提示接受服务条款,点击Accept。等待任务栏托盘中的 Docker 图标变为稳定的绿色,表示 Docker 引擎已就绪。

  5. 打开 PowerShell,验证 Docker 是否安装成功:

docker --version

若正常输出版本号(如Docker version 27.x.x),说明安装成功。

3.2.3 启动 Redis Stack 容器

Docker Desktop 就绪后,在 PowerShell 中执行以下命令,一键拉取并启动 Redis Stack:

docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 -e REDIS_ARGS="--requirepass 123456" redis/redis-stack:latest

参数含义:

参数说明
-d后台运行容器
--name redis-stack容器命名为 redis-stack,便于后续管理
-p 6379:6379将宿主机 6379 端口映射到容器,Spring Boot 通过此端口连接 Redis-
-p 8001:8001映射 RedisInsight 可视化界面端口,浏览器访问http://localhost:8001即可使用-
-e REDIS_ARGS="--requirepass 123456"设置 Redis 密码为 123456
redis/redis-stack:latest使用官方 Redis Stack 镜像,包含 RediSearch 等模块-

首次执行时 Docker 会从 Docker Hub 拉取镜像(约 500MB),需要等待几分钟。命令返回一长串容器 ID 表示启动成功。

3.2.4 验证 Redis Stack 是否正常运行

依次执行以下命令,确认服务一切正常:

# 1. 查看容器状态 docker ps

应看到redis-stack容器状态为Up,且PORTS列有0.0.0.0:6379->6379/tcp的箭头映射。

# 2. 测试 Redis 是否响应 docker exec -it redis-stack redis-cli -a 123456 ping

返回PONG即正常。

# 3. 验证 RediSearch 模块已加载 docker exec -it redis-stack redis-cli -a 123456 MODULE LIST

输出中应包含search模块。

# 4. 测试向量索引命令 docker exec -it redis-stack redis-cli -a 123456 FT._LIST

返回空列表(不报错)即正常。如果报ERR unknown command 'FT._LIST',说明连接到了普通 Redis,需要检查 6379 端口是否被本地 Redis 服务占用-。

3.2.5 常用管理命令
操作命令
停止容器docker stop redis-stack
启动已存在的容器docker start redis-stack
查看日志docker logs redis-stack
删除容器(数据丢失)docker rm -f redis-stack
进入容器内 Redis CLIdocker exec -it redis-stack redis-cli -a 123456

⚠️踩坑提醒:如果你之前安装过普通 Redis 服务(比如通过 MSI 安装包),它可能也在占用 6379 端口。Spring Boot 会连接到那个普通 Redis 上,导致FT._LIST报unknown command。解决方法:停掉本地 Redis 服务(在“服务”管理器中停止 Redis 服务),或把 Docker 映射到其他端口(如-p 6380:6379),并同步修改application.properties中的spring.data.redis.port。

四、Maven 依赖

现有 pom.xml 基础上,需要补充以下依赖:

<!-- Spring AI Redis 向量存储自动配置 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-vector-store-redis</artifactId> <version>${spring-ai.version}</version> </dependency> <!-- Spring AI Tika 文档读取器 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-tika-document-reader</artifactId> <version>${spring-ai.version}</version> </dependency> <!-- Spring AI RAG Advisor(QuestionAnswerAdvisor) --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-advisors-vector-store</artifactId> <version>${spring-ai.version}</version> </dependency>

五、核心配置

在application.properties中追加:

# ===================== Redis 连接 ===================== spring.data.redis.host=127.0.0.1 spring.data.redis.port=6379 spring.data.redis.password=123456 spring.data.redis.database=0 # ===================== Redis 向量库 ===================== # 首次启动自动创建索引 spring.ai.vectorstore.redis.initialize-schema=true # 索引名 spring.ai.vectorstore.redis.index-name=rag-idx # 向量数据键前缀 spring.ai.vectorstore.redis.prefix=rag: # ===================== 对话模型(你现有的配置) ===================== spring.ai.openai.api-key=sk-你的Key spring.ai.openai.base-url=https://llm-o4uz6dvcl1e8uyxv.cn-beijing.maas.aliyuncs.com/compatible-mode spring.ai.openai.chat.options.model=deepseek-v4-flash # ===================== 嵌入模型(RAG 必需) ===================== # ★ 关键:embedding 单独指定 base-url,因为你的 MaaS 端点可能不支持 /v1/embeddings spring.ai.openai.embedding.base-url=https://dashscope.aliyuncs.com/compatible-mode spring.ai.openai.embedding.options.model=text-embedding-v3

这里有一个非常容易踩的坑:如果你的 chat 和 embedding 共用同一个 base-url,而那个端点恰好只支持/chat/completions不支持/embeddings,调用vectorStore.add()时会直接报 404。解决办法就是单独为 embedding 指定一个可用的 base-url-。

六、RedisVectorStore 配置类

Spring AI 的RedisVectorStoreAutoConfiguration有一个硬性条件:容器中必须存在JedisConnectionFactoryBean。Spring Boot 默认使用 Lettuce 客户端,不会自动创建 Jedis 工厂,所以需要手动补一个-。

@Configuration public class RedisVectorStoreConfig { @Value("${spring.data.redis.host:127.0.0.1}") private String host; @Value("${spring.data.redis.port:6379}") private int port; @Value("${spring.data.redis.password:}") private String password; @Value("${spring.data.redis.database:0}") private int database; @Bean public JedisConnectionFactory jedisConnectionFactory() { RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(); config.setHostName(host); config.setPort(port); config.setDatabase(database); if (password != null && !password.isEmpty()) { config.setPassword(password); } return new JedisConnectionFactory(config); } }

注意:不能简单写new JedisConnectionFactory(),那样不会读取application.properties里的密码配置,会导致NOAUTH Authentication required。

七、文件上传 + 文档入库

7.1 核心 Controller

@RestController @RequestMapping("/api/rag") public class RagUploadController { private static final Logger log = LogManager.getLogger(HelloContoller.class); private final VectorStore vectorStore; public RagUploadController(VectorStore vectorStore) { this.vectorStore = vectorStore; } /** * 上传文件 → Tika 提取文本 → TokenTextSplitter 切割 → 存入 Redis 向量库 */ @PostMapping("/upload") public ResponseEntity<String> uploadAndIndex(@RequestParam("file") MultipartFile file) { String originalName = file.getOriginalFilename(); try { log.info("【1】开始保存临时文件"); File tempFile = File.createTempFile("rag_", "_" + originalName); file.transferTo(tempFile); tempFile.deleteOnExit(); log.info("【1】临时文件保存完成: {}", tempFile.getAbsolutePath()); log.info("【2】Tika 开始读取"); Resource resource = new FileSystemResource(tempFile); TikaDocumentReader reader = new TikaDocumentReader(resource); List<Document> documents = reader.get(); documents.forEach(doc -> doc.getMetadata().put("source", originalName)); log.info("【2】Tika 读取完成,文档数: {}", documents.size()); log.info("【3】开始切割"); TokenTextSplitter splitter = new TokenTextSplitter(); List<Document> chunks = splitter.apply(documents); log.info("【3】切割完成,chunk 数: {}", chunks.size()); log.info("【4】开始写入向量库(最可能卡在这)"); vectorStore.add(chunks); log.info("【4】向量库写入完成"); return ResponseEntity.ok("处理完成,chunk 数: " + chunks.size() + ",已存入 Redis"); } catch (Exception e) { log.error("【X】上传处理失败", e); return ResponseEntity.status(500).body( "上传处理失败:" + e.getClass().getSimpleName() + " → " + e.getMessage() ); } } }

7.2 这段代码做了什么

整个流程遵循 Spring AI 的 ETL Pipeline 模式:DocumentReader → DocumentTransformer → VectorStore-。

  • TikaDocumentReader:底层使用 Apache Tika,能从 PDF、DOCX、PPT、HTML 等上千种格式中提取纯文本-。

  • TokenTextSplitter:默认按 800 tokens 为一个块切分,块与块之间有重叠,避免语义在边界处断裂-。

  • vectorStore.add():自动调用 EmbeddingModel 把每个 chunk 转成向量,然后写入 Redis。

八、RAG 问答接口

@RestController @RequestMapping("/api/chat") public class RagChatController { private final ChatClient chatClient; public RagChatController(ChatClient.Builder builder, VectorStore vectorStore) { QuestionAnswerAdvisor ragAdvisor = QuestionAnswerAdvisor.builder(vectorStore) .searchRequest(SearchRequest.builder() .similarityThreshold(0.5) // 相似度阈值 .topK(5) // 返回最相似的 5 个 chunk .build()) .build(); this.chatClient = builder .defaultAdvisors(ragAdvisor) .build(); } @GetMapping("/ask") public Map<String, String> ask(@RequestParam("message") String message) { String answer = chatClient.prompt() .user(message) .call() .content(); return Map.of("answer", answer); } }

注意:similarityThreshold和topK不能直接写在QuestionAnswerAdvisor.builder()上,它们属于SearchRequest,需要通过.searchRequest()传入。SearchRequest有默认值(topK 默认 4,similarityThreshold 默认 0.5),不传也能用。QuestionAnswerAdvisor的工作原理:每次 prompt 时自动从 VectorStore 检索相关文档片段,拼接进 prompt 上下文后再发给模型。

九、最终完整结果测试

1.启动Docker 中 Redis stack,可以命令或者手动点击Actions中的三角按钮启动。

2.调用上传文件接口,完成PDF切割,并存入Redis向量数据库

点击上传了一篇关于知识共享的文献,大体内容如图所示

通过Redis Desktop工具,查看存储的向量数据,可以看到存储成功了。

3.调用问答接口

给出的问题,“什么是知识共享?”,根据上传的PDF文献,给出了回答,说明整套流程成功。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询