SpringBoot 集成 DeepSeek 实现 RAG 文档问答一、概述1.1 什么是 RAGRAGRetrieval-Augmented Generation检索增强生成是一种将「信息检索」与「大语言模型」相结合的技术方案。大模型本身存在两个痛点知识截止训练数据有截止日期无法回答最新信息幻觉问题对不熟悉的内容容易一本正经地胡说八道。RAG 的核心思路是先从外部知识库文档中检索出与用户问题最相关的片段再把这些片段连同问题一起喂给大模型让模型看着资料答题从而既准确又可控。1.2 整体流程一个完整的 RAG 流程分为两个阶段1.3 技术选型组件选型说明框架Spring Boot 3.5.16 Spring AI 1.0.1Spring 官方 AI 集成框架JDKJava 21大模型DeepSeekdeepseek-chat通过 Spring AI 的 DeepSeek Starter 接入Embedding 模型all-MiniLM-L6-v2本地 ONNX无需调用外部 API本地推理零成本向量存储SimpleVectorStore内存版轻量级适合 demo 与学习文档解析Apache Tikaspring-ai-tika-document-reader支持 PDF / Word / TXT 等多种格式为什么用本地 ONNX Embedding因为 DeepSeek 官方 API 目前没有开放的 Embedding 接口所以我们用 Spring AI 的TransformersEmbeddingModel加载本地 ONNX 模型来完成向量化完全不依赖第三方 API Key。二、pom 文件引入依赖核心依赖如下仅展示 AI 相关部分完整 pom 见项目源码propertiesjava.version21/java.version/propertiesdependencies!-- Spring Boot Web --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency!-- ① DeepSeek 大模型接入 --dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-starter-model-deepseek/artifactId/dependency!-- ② 本地 ONNX Embedding 模型无需 API Key --dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-starter-model-transformers/artifactId/dependency!-- ③ 向量存储包含 SimpleVectorStore 内存版 --dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-vector-store/artifactId/dependency!-- ④ 文档解析支持 PDF/Word/TXT 等 --dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-tika-document-reader/artifactId/dependency/dependencies!-- Spring AI 版本统一管理 --dependencyManagementdependenciesdependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-bom/artifactIdversion1.0.1/versiontypepom/typescopeimport/scope/dependency/dependencies/dependencyManagement四个依赖各司其职依赖作用spring-ai-starter-model-deepseek自动配置 DeepSeek 的ChatModel与ChatClient.Builderspring-ai-starter-model-transformers提供TransformersEmbeddingModel加载本地 ONNX 模型做向量化spring-ai-vector-store提供SimpleVectorStore内存向量库及SearchRequest检索 APIspring-ai-tika-document-reader提供TikaDocumentReader解析多种格式文档为Document对象三、配置文件3.1 application.ymlserver:port:8080spring:profiles:active:devapplication:name:demoservlet:context-path:/3.2 application-dev.ymlDeepSeek 的 API Key 与模型参数配置在这里spring:ai:deepseek:api-key:sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx# 替换为你的 DeepSeek API Keychat:options:model:deepseek-chat# 可选: deepseek-chat / deepseek-reasonertemperature:0.8获取 API Key前往 DeepSeek 开放平台 注册后在「API Keys」页面创建。3.3 本地 ONNX 模型文件Embedding 模型文件放在src/main/resources/onnx/all-MiniLM-L6-v2/目录下src/main/resources/ └── onnx/ └── all-MiniLM-L6-v2/ ├── model.onnx # 模型文件 └── tokenizer.json # 分词器文件all-MiniLM-L6-v2是一个轻量级句子向量模型384 维可从 HuggingFace 下载放到上述目录即可。四、实现代码4.1 AI 配置类RagConfig手动注册 Embedding 模型、向量库和 ChatClient 三个 Beanpackagecom.zhh.web_demo_ai.config;importcom.zhh.web_demo_ai.service.OrderAssistantService;importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.ai.embedding.EmbeddingModel;importorg.springframework.ai.transformers.TransformersEmbeddingModel;importorg.springframework.ai.vectorstore.SimpleVectorStore;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.core.io.ClassPathResource;/** * AI 配置类Embedding 模型、向量存储、ChatClient */ConfigurationpublicclassRagConfig{/** * 本地 ONNX Embedding 模型使用已下载的模型文件不走 HuggingFace 网络下载 */BeanpublicTransformersEmbeddingModelembeddingModel(){varmodelnewTransformersEmbeddingModel();model.setModelResource(newClassPathResource(onnx/all-MiniLM-L6-v2/model.onnx));model.setTokenizerResource(newClassPathResource(onnx/all-MiniLM-L6-v2/tokenizer.json));returnmodel;}/** * 内存版向量存储依赖上面的 EmbeddingModel */BeanpublicSimpleVectorStoresimpleVectorStore(EmbeddingModelembeddingModel){returnSimpleVectorStore.builder(embeddingModel).build();}/** * ChatClient */BeanpublicChatClientchatClient(ChatClient.Builderbuilder,OrderAssistantServiceorderAssistantService){returnbuilder//.defaultTools(orderAssistantService) // 如需 Function Calling 可放开.build();}}要点说明TransformersEmbeddingModel指定本地 ONNX 文件路径启动时加载模型之后向量化全在本地完成。SimpleVectorStore把向量存在内存里适合 demo生产环境可替换为RedisVectorStore、PgVectorStore等接口一致。ChatClient由 DeepSeek Starter 自动注入ChatClient.Builder直接build()即可使用。4.2 RAG 核心服务YourRagService这是整个 RAG 的核心包含入库和问答两个方法packagecom.zhh.web_demo_ai.service;importlombok.extern.slf4j.Slf4j;importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.ai.chat.messages.SystemMessage;importorg.springframework.ai.chat.messages.UserMessage;importorg.springframework.ai.chat.prompt.Prompt;importorg.springframework.ai.document.Document;importorg.springframework.ai.reader.tika.TikaDocumentReader;importorg.springframework.ai.transformer.splitter.TokenTextSplitter;importorg.springframework.ai.vectorstore.SearchRequest;importorg.springframework.ai.vectorstore.SimpleVectorStore;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.core.io.Resource;importorg.springframework.stereotype.Service;importjava.util.List;importjava.util.stream.Collectors;/** * RAG 文档问答服务 */Slf4jServicepublicclassYourRagService{AutowiredprivateSimpleVectorStorevectorStore;AutowiredprivateChatClientchatClient;/** * 文档入库读取 → 切块 → 向量化 → 存入向量库 */publicvoidingestDocument(ResourcedocumentResource){// 1. 读取文档Tika 自动识别 PDF/Word/TXT 等格式TikaDocumentReaderreadernewTikaDocumentReader(documentResource);ListDocumentdocumentsreader.read();log.info(读取到 {} 个原始文档段落,documents.size());// 2. 文本切块800 token/块TokenTextSplittersplitterTokenTextSplitter.builder().withChunkSize(800).withMinChunkSizeChars(50).build();ListDocumentchunkssplitter.apply(documents);log.info(切分后得到 {} 个文本块,chunks.size());// 3. 存入向量库add 时自动调用 EmbeddingModel 向量化vectorStore.add(chunks);log.info(已将所有文本块存入向量库);}/** * 文档问答检索相关块 → 拼成 Prompt → 发给 DeepSeek */publicStringquery(Stringquestion){// 1. 从向量库检索最相关的 4 个文本块ListDocumentrelevantChunksvectorStore.similaritySearch(SearchRequest.builder().query(question).topK(4).similarityThreshold(0.0).build());if(relevantChunks.isEmpty()){return未在文档中找到相关内容。;}log.info(检索到 {} 个相关文本块,relevantChunks.size());// 2. 将检索到的文本块拼成上下文StringcontextrelevantChunks.stream().map(Document::getText).collect(Collectors.joining(\n\n---\n\n));// 3. 构建 PromptSystem 约束 User 拼上下文varsystemMessagenewSystemMessage(你是一个文档助手。请严格根据下面提供的文档内容回答问题。如果文档中没有相关信息请直接说「文档中未找到相关信息」不要编造答案。);varuserMessagenewUserMessage(文档内容\n\ncontext\n\n问题question);// 4. 调用 DeepSeek 生成回答varpromptnewPrompt(List.of(systemMessage,userMessage));StringanswerchatClient.prompt(prompt).call().content();log.info(问题{},question);log.info(回答{},answer);returnanswer;}}核心逻辑拆解ingestDocument()— 入库三步走步骤组件作用读取TikaDocumentReader把 PDF/Word/TXT 等解析为Document列表切块TokenTextSplitter按 token 数800/块切分避免单块过长超出模型上下文存储vectorStore.add()存入时自动调用 Embedding 模型将文本转为向量query()— 问答四步走步骤说明检索similaritySearch用问题的向量去向量库里找最相似的 topK4 个文本块拼接把检索到的文本块用分隔符拼成一段上下文构造 PromptSystem Message 约束模型只根据文档回答、不许编造User Message 把上下文和问题一起给模型生成调用 DeepSeekChatClient生成最终回答防幻觉关键SystemMessage中的约束语是控制幻觉的核心——明确告诉模型找不到就说找不到不要编。配合similarityThreshold过滤低相关度结果双重保险。4.3 测试入口RagChatTestpackagecom.zhh.web_demo_ai.ai;importcom.zhh.web_demo_ai.service.YourRagService;importlombok.extern.slf4j.Slf4j;importorg.junit.jupiter.api.Test;importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.context.SpringBootTest;importorg.springframework.core.io.ClassPathResource;/** * 简单RAG 文档问答demo基于入库的文档回答问题 */Slf4jSpringBootTestpublicclassRagChatTest{AutowiredprivateChatClientchatClient;AutowiredprivateYourRagServiceyourRagService;/** * 简单问答冒烟测试验证 DeepSeek 连通性 */Testpublicvoidshow(){StringmsgchatClient.prompt().user(你好).call().content();System.out.println(msg);}/** * RAG 文档问答基于入库的文档回答问题 */TestpublicvoidtestRagQuery(){// 先入库varresourcenewClassPathResource(test-doc.txt);yourRagService.ingestDocument(resource);// 问一个文档中有明确答案的问题Stringquestion云上贵州的核心产品是什么公司有多少人?;StringansweryourRagService.query(question);System.out.println(问题question);System.out.println(回答answer);System.out.println(---);// 问一个文档中没有的问题验证不会胡编Stringquestion2云上贵州2025年的营收是多少;Stringanswer2yourRagService.query(question2);System.out.println(问题question2);System.out.println(回答answer2);}}测试用的文档test-doc.txt放在src/test/resources/下内容是一段关于云上贵州公司介绍的文本。测试里故意问了两个问题文档里有的核心产品是什么、多少人 → 验证能准确回答文档里没有的2025年营收 → 验证不会编造五、运行效果执行testRagQuery()测试预期输出类似第一个问题从文档中精准提取了答案第二个问题文档里没有模型遵守了 System Prompt 的约束没有编造。六、总结本文实现了一个基于Spring Boot Spring AI DeepSeek的 RAG 文档问答最小可用方案核心就两个方法ingestDocument()文档 → Tika 解析 → Token 切块 → Embedding 向量化 → 存入向量库query()问题 → 向量检索 topK → 拼接上下文 → 构造 Prompt → DeepSeek 生成回答整体方案的特点✅零外部 Embedding 成本用本地 ONNX 模型不依赖第三方 Embedding API✅防幻觉System Prompt 约束 相似度阈值双重控制✅多格式文档支持Tika 天然支持 PDF/Word/TXT 等✅代码简洁Spring AI 封装度高核心逻辑不到 100 行可扩展方向向量库替换为PgVector/Redis/Milvus等持久化方案加入文档管理接口支持动态上传入库接入 Function Calling让模型能调用外部工具增加多轮对话上下文记忆
SpringBoot 集成 DeepSeek 实现 RAG 文档问答
SpringBoot 集成 DeepSeek 实现 RAG 文档问答一、概述1.1 什么是 RAGRAGRetrieval-Augmented Generation检索增强生成是一种将「信息检索」与「大语言模型」相结合的技术方案。大模型本身存在两个痛点知识截止训练数据有截止日期无法回答最新信息幻觉问题对不熟悉的内容容易一本正经地胡说八道。RAG 的核心思路是先从外部知识库文档中检索出与用户问题最相关的片段再把这些片段连同问题一起喂给大模型让模型看着资料答题从而既准确又可控。1.2 整体流程一个完整的 RAG 流程分为两个阶段1.3 技术选型组件选型说明框架Spring Boot 3.5.16 Spring AI 1.0.1Spring 官方 AI 集成框架JDKJava 21大模型DeepSeekdeepseek-chat通过 Spring AI 的 DeepSeek Starter 接入Embedding 模型all-MiniLM-L6-v2本地 ONNX无需调用外部 API本地推理零成本向量存储SimpleVectorStore内存版轻量级适合 demo 与学习文档解析Apache Tikaspring-ai-tika-document-reader支持 PDF / Word / TXT 等多种格式为什么用本地 ONNX Embedding因为 DeepSeek 官方 API 目前没有开放的 Embedding 接口所以我们用 Spring AI 的TransformersEmbeddingModel加载本地 ONNX 模型来完成向量化完全不依赖第三方 API Key。二、pom 文件引入依赖核心依赖如下仅展示 AI 相关部分完整 pom 见项目源码propertiesjava.version21/java.version/propertiesdependencies!-- Spring Boot Web --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency!-- ① DeepSeek 大模型接入 --dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-starter-model-deepseek/artifactId/dependency!-- ② 本地 ONNX Embedding 模型无需 API Key --dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-starter-model-transformers/artifactId/dependency!-- ③ 向量存储包含 SimpleVectorStore 内存版 --dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-vector-store/artifactId/dependency!-- ④ 文档解析支持 PDF/Word/TXT 等 --dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-tika-document-reader/artifactId/dependency/dependencies!-- Spring AI 版本统一管理 --dependencyManagementdependenciesdependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-bom/artifactIdversion1.0.1/versiontypepom/typescopeimport/scope/dependency/dependencies/dependencyManagement四个依赖各司其职依赖作用spring-ai-starter-model-deepseek自动配置 DeepSeek 的ChatModel与ChatClient.Builderspring-ai-starter-model-transformers提供TransformersEmbeddingModel加载本地 ONNX 模型做向量化spring-ai-vector-store提供SimpleVectorStore内存向量库及SearchRequest检索 APIspring-ai-tika-document-reader提供TikaDocumentReader解析多种格式文档为Document对象三、配置文件3.1 application.ymlserver:port:8080spring:profiles:active:devapplication:name:demoservlet:context-path:/3.2 application-dev.ymlDeepSeek 的 API Key 与模型参数配置在这里spring:ai:deepseek:api-key:sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx# 替换为你的 DeepSeek API Keychat:options:model:deepseek-chat# 可选: deepseek-chat / deepseek-reasonertemperature:0.8获取 API Key前往 DeepSeek 开放平台 注册后在「API Keys」页面创建。3.3 本地 ONNX 模型文件Embedding 模型文件放在src/main/resources/onnx/all-MiniLM-L6-v2/目录下src/main/resources/ └── onnx/ └── all-MiniLM-L6-v2/ ├── model.onnx # 模型文件 └── tokenizer.json # 分词器文件all-MiniLM-L6-v2是一个轻量级句子向量模型384 维可从 HuggingFace 下载放到上述目录即可。四、实现代码4.1 AI 配置类RagConfig手动注册 Embedding 模型、向量库和 ChatClient 三个 Beanpackagecom.zhh.web_demo_ai.config;importcom.zhh.web_demo_ai.service.OrderAssistantService;importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.ai.embedding.EmbeddingModel;importorg.springframework.ai.transformers.TransformersEmbeddingModel;importorg.springframework.ai.vectorstore.SimpleVectorStore;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.core.io.ClassPathResource;/** * AI 配置类Embedding 模型、向量存储、ChatClient */ConfigurationpublicclassRagConfig{/** * 本地 ONNX Embedding 模型使用已下载的模型文件不走 HuggingFace 网络下载 */BeanpublicTransformersEmbeddingModelembeddingModel(){varmodelnewTransformersEmbeddingModel();model.setModelResource(newClassPathResource(onnx/all-MiniLM-L6-v2/model.onnx));model.setTokenizerResource(newClassPathResource(onnx/all-MiniLM-L6-v2/tokenizer.json));returnmodel;}/** * 内存版向量存储依赖上面的 EmbeddingModel */BeanpublicSimpleVectorStoresimpleVectorStore(EmbeddingModelembeddingModel){returnSimpleVectorStore.builder(embeddingModel).build();}/** * ChatClient */BeanpublicChatClientchatClient(ChatClient.Builderbuilder,OrderAssistantServiceorderAssistantService){returnbuilder//.defaultTools(orderAssistantService) // 如需 Function Calling 可放开.build();}}要点说明TransformersEmbeddingModel指定本地 ONNX 文件路径启动时加载模型之后向量化全在本地完成。SimpleVectorStore把向量存在内存里适合 demo生产环境可替换为RedisVectorStore、PgVectorStore等接口一致。ChatClient由 DeepSeek Starter 自动注入ChatClient.Builder直接build()即可使用。4.2 RAG 核心服务YourRagService这是整个 RAG 的核心包含入库和问答两个方法packagecom.zhh.web_demo_ai.service;importlombok.extern.slf4j.Slf4j;importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.ai.chat.messages.SystemMessage;importorg.springframework.ai.chat.messages.UserMessage;importorg.springframework.ai.chat.prompt.Prompt;importorg.springframework.ai.document.Document;importorg.springframework.ai.reader.tika.TikaDocumentReader;importorg.springframework.ai.transformer.splitter.TokenTextSplitter;importorg.springframework.ai.vectorstore.SearchRequest;importorg.springframework.ai.vectorstore.SimpleVectorStore;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.core.io.Resource;importorg.springframework.stereotype.Service;importjava.util.List;importjava.util.stream.Collectors;/** * RAG 文档问答服务 */Slf4jServicepublicclassYourRagService{AutowiredprivateSimpleVectorStorevectorStore;AutowiredprivateChatClientchatClient;/** * 文档入库读取 → 切块 → 向量化 → 存入向量库 */publicvoidingestDocument(ResourcedocumentResource){// 1. 读取文档Tika 自动识别 PDF/Word/TXT 等格式TikaDocumentReaderreadernewTikaDocumentReader(documentResource);ListDocumentdocumentsreader.read();log.info(读取到 {} 个原始文档段落,documents.size());// 2. 文本切块800 token/块TokenTextSplittersplitterTokenTextSplitter.builder().withChunkSize(800).withMinChunkSizeChars(50).build();ListDocumentchunkssplitter.apply(documents);log.info(切分后得到 {} 个文本块,chunks.size());// 3. 存入向量库add 时自动调用 EmbeddingModel 向量化vectorStore.add(chunks);log.info(已将所有文本块存入向量库);}/** * 文档问答检索相关块 → 拼成 Prompt → 发给 DeepSeek */publicStringquery(Stringquestion){// 1. 从向量库检索最相关的 4 个文本块ListDocumentrelevantChunksvectorStore.similaritySearch(SearchRequest.builder().query(question).topK(4).similarityThreshold(0.0).build());if(relevantChunks.isEmpty()){return未在文档中找到相关内容。;}log.info(检索到 {} 个相关文本块,relevantChunks.size());// 2. 将检索到的文本块拼成上下文StringcontextrelevantChunks.stream().map(Document::getText).collect(Collectors.joining(\n\n---\n\n));// 3. 构建 PromptSystem 约束 User 拼上下文varsystemMessagenewSystemMessage(你是一个文档助手。请严格根据下面提供的文档内容回答问题。如果文档中没有相关信息请直接说「文档中未找到相关信息」不要编造答案。);varuserMessagenewUserMessage(文档内容\n\ncontext\n\n问题question);// 4. 调用 DeepSeek 生成回答varpromptnewPrompt(List.of(systemMessage,userMessage));StringanswerchatClient.prompt(prompt).call().content();log.info(问题{},question);log.info(回答{},answer);returnanswer;}}核心逻辑拆解ingestDocument()— 入库三步走步骤组件作用读取TikaDocumentReader把 PDF/Word/TXT 等解析为Document列表切块TokenTextSplitter按 token 数800/块切分避免单块过长超出模型上下文存储vectorStore.add()存入时自动调用 Embedding 模型将文本转为向量query()— 问答四步走步骤说明检索similaritySearch用问题的向量去向量库里找最相似的 topK4 个文本块拼接把检索到的文本块用分隔符拼成一段上下文构造 PromptSystem Message 约束模型只根据文档回答、不许编造User Message 把上下文和问题一起给模型生成调用 DeepSeekChatClient生成最终回答防幻觉关键SystemMessage中的约束语是控制幻觉的核心——明确告诉模型找不到就说找不到不要编。配合similarityThreshold过滤低相关度结果双重保险。4.3 测试入口RagChatTestpackagecom.zhh.web_demo_ai.ai;importcom.zhh.web_demo_ai.service.YourRagService;importlombok.extern.slf4j.Slf4j;importorg.junit.jupiter.api.Test;importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.context.SpringBootTest;importorg.springframework.core.io.ClassPathResource;/** * 简单RAG 文档问答demo基于入库的文档回答问题 */Slf4jSpringBootTestpublicclassRagChatTest{AutowiredprivateChatClientchatClient;AutowiredprivateYourRagServiceyourRagService;/** * 简单问答冒烟测试验证 DeepSeek 连通性 */Testpublicvoidshow(){StringmsgchatClient.prompt().user(你好).call().content();System.out.println(msg);}/** * RAG 文档问答基于入库的文档回答问题 */TestpublicvoidtestRagQuery(){// 先入库varresourcenewClassPathResource(test-doc.txt);yourRagService.ingestDocument(resource);// 问一个文档中有明确答案的问题Stringquestion云上贵州的核心产品是什么公司有多少人?;StringansweryourRagService.query(question);System.out.println(问题question);System.out.println(回答answer);System.out.println(---);// 问一个文档中没有的问题验证不会胡编Stringquestion2云上贵州2025年的营收是多少;Stringanswer2yourRagService.query(question2);System.out.println(问题question2);System.out.println(回答answer2);}}测试用的文档test-doc.txt放在src/test/resources/下内容是一段关于云上贵州公司介绍的文本。测试里故意问了两个问题文档里有的核心产品是什么、多少人 → 验证能准确回答文档里没有的2025年营收 → 验证不会编造五、运行效果执行testRagQuery()测试预期输出类似第一个问题从文档中精准提取了答案第二个问题文档里没有模型遵守了 System Prompt 的约束没有编造。六、总结本文实现了一个基于Spring Boot Spring AI DeepSeek的 RAG 文档问答最小可用方案核心就两个方法ingestDocument()文档 → Tika 解析 → Token 切块 → Embedding 向量化 → 存入向量库query()问题 → 向量检索 topK → 拼接上下文 → 构造 Prompt → DeepSeek 生成回答整体方案的特点✅零外部 Embedding 成本用本地 ONNX 模型不依赖第三方 Embedding API✅防幻觉System Prompt 约束 相似度阈值双重控制✅多格式文档支持Tika 天然支持 PDF/Word/TXT 等✅代码简洁Spring AI 封装度高核心逻辑不到 100 行可扩展方向向量库替换为PgVector/Redis/Milvus等持久化方案加入文档管理接口支持动态上传入库接入 Function Calling让模型能调用外部工具增加多轮对话上下文记忆