ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Spring AI RAG实战:从知识库到智能问答的完整工程化指南

Spring AI RAG实战:从知识库到智能问答的完整工程化指南 先说一个很常见的场景团队里知识库搭得漂漂亮亮Obsidian、语雀、Confluence或者公司自建的Wiki里堆了好几千篇文档该有的都有了可真正用起来的时候大家还是习惯去群里喊一嗓子“这个报错有谁遇到过”或者开个浏览器狂翻半天。手册躺在知识库里吃灰这是个比“没有知识库”更尴尬的困境。如果你是个Java后端程序员前面那句描述大概率已经引起了你的共鸣。知识库本身只是第一步它只解决了“内容存在哪”的问题距离“内容随叫随到”还差一大截。而Spring AI的出现正好给了Java生态一个很顺手的解法——把RAG检索增强生成和大模型能力接进业务系统里让知识库从“有人工查询文档”变成“由程序自动问答”的服务接口。这篇文章我不打算教你搭知识库而是会从知识库已经建好的前提切入讲清楚后续要从零实现一个Spring AI RAG问答服务时真正要做的那些事。文章内容围绕完整的RAG链路展开文档解析、文本切块、向量化、存储、检索、以及和本地或云端大模型编排对话每一步都会给出选型思路、参数取舍和Java侧可落地的代码参考。无论你是刚接触Spring AI的小白还是已经踩过一些坑的实践者这篇文章的内容应该都能帮你把整条链路理得更顺。1. 先摆清楚知识库有了缺的是什么很多团队对知识库的理解停留在“把文档放上去”这一步。文档是搬进去了搜索框也有但问题在于传统关键词搜索本质是字面匹配你搜“数据库连接池爆满”大概率搜出来一堆附带“数据库”三个字的无关页面真正的根因分析可能被埋在第N页。更重要的是阅读搜索结果、对比多篇文档、再归纳出结论这一连串动作依然得靠人脑完成。RAG解决的就是这个“最后一段路”。它把知识库里的内容切成可检索的片段转成向量存起来用户提问时先通过语义检索找到最相关的几个片段再把这些片段连同问题一起丢给大模型让模型基于这些材料生成回答。整个过程中知识库里的私有知识不需要重新训练进模型参数里模型参数也不变但回答却能做到“有据可依”。Java程序员在这个环节里做的事本质上是从“写业务CRUD”切换到“构建一条AI数据流水线”但核心能力依然是后端那套处理文档、管理数据、调API、封装服务。Spring AI这个框架的价值就是把这些步骤抽象成了统一的Java API让你不用去写Python脚本也不用去裸调各家大模型的HTTP接口而是用熟悉的方式把RAG串起来。1.1 RAG全链路里的关键角色一条完整的RAG链路可以分为“预处理”和“在线问答”两个阶段。预处理阶段在知识库内容入库时执行一次或定时执行先把文档解析成纯文本然后把长文本切成小块接着把每个块通过Embedding模型转成向量最后写入向量数据库。在线问答阶段则是实时执行用户问题进来同样转成向量在向量库里做相似度检索取出TopK个相关片段再把这些片段拼进Prompt里交给大模型生成答案。一句话概括知识库只是RAG的“原材料仓库”真正决定问答质量好坏的是切块策略、向量模型选型、检索精度、Prompt拼接方式这几个环节。Java程序员的价值恰恰体现在能把这条链路工程化、稳定化、可运维化而不是写个Demo跑通就算完。1.2 RAG和MCP、Agent的区别别混淆近段时间MCP、Agent、RAG这几个词经常一起出现很多刚入门的同学会搞混。简单说RAG解决的是“模型如何获取私有知识”的问题答案来源于检索回来的资料MCP解决的是“模型如何调用外部工具”的问题比如让它去查天气、查数据库、操作某个系统Agent则是利用模型做任务规划自动决策调用哪些工具、按什么顺序执行。对知识库问答这个场景来说RAG是主角Agent只是锦上添花。比如我可以在RAG之外再给模型接一个MCP工具让它去查业务系统的实时状态与知识库内容互补。但基础没打牢之前不建议一上来就上Agent否则检索结果就是错的Agent越智能错得越离谱。2. 文档解析与切块策略——RAG质量的隐形天花板我见过不少团队向量库也选了Embedding也调通了模型也能回答问题了但答案质量就是差翻来覆去找原因最后定位到源头文档都没处理好就入库了。一篇PDF文档直接整篇转成一个向量那语义早就被稀释得不像样了。切块太大检索出来的是整章内容噪音多切块太小语义不完整模型拿到的上下文缺失。2.1 你知识库里的文档可能是什么格式先盘点一下常见情况。技术团队的内部知识库通常是Markdown、Word、PDF、HTML这些格式混着来。不同格式的解析方式差异很大这一步做不好后续全完蛋。Markdown是比较幸福的场景结构清晰标题、代码块、表格都有标记解析时可以直接按标题层级切块。Word文档用Apache POI或者用文本提取工具处理都行。PDF最麻烦常见的有两类文本型PDF可以直接提取文字扫描型PDF得先过OCR才能拿到内容否则抽出来的就是空白。Spring AI本身没有把文档解析能力内置得很重它更偏重统一入口底层还是得靠你自己选择合适的解析器。我个人的建议是在预处理阶段先把所有文档统一转成纯文本或结构化Markdown后面的切块、向量化才好操作。2.2 切块为什么这么纠结切块策略直接影响两个指标查得准不准和答案完不完整。如果块太大比如一个章节几百行不切那这个块的向量其实是一个“混合平均向量”你在语义检索时它可能跟任何问题的相似度都不高不低很难精准命中。反过来如果块太小比如一句话就切一刀那语义上下文被切断了检索可能命中一个残缺片段模型根本看不懂这句话在说什么。实践中最常用的策略是递归字符切分加上重叠窗口。大概思路是这样设一个目标块大小比如500个字符注意不是token字符和token的换算约2~4字符一个token具体看模型然后按分隔符从大到小逐级递归切优先在段落边界切其次句子边界尽量保持语义完整。同时在相邻块之间保留一个重叠窗口比如重叠50个字符让被切断的边界信息能传递到下一个块里。我自己实际测试下来面向中文技术文档按以下参数起步是比较稳的块大小500~800字符重叠窗口50~100字符切分优先级为段落标题 一级段落 句子。这个区间兼顾了语义完整和检索精度。当然这个参数不是拍脑袋定死的后续要根据自己的知识库内容调优。还有个很多人忽略的点Metadata元数据。切块后你最好给每个块打上来源文档名、章节标题、文档作者、入库时间这些标签。好处是检索时可以通过标签过滤候选范围比如用户问“数据库连接池排查”你可以只在上次筛选“运维手册”这个来源下检索而不是在整个知识库里大海捞针。后面讲Spring AI代码时你也会看到MetadataFilter的作用很大。2.3 脚本化预处理还是写Java服务这个问题在实操中经常被问到。我的建议是如果是初次跑通可以写一次性脚本先完成存量文档的解析入库用Python生态的解析库确实方便一些。但既然标题是“Spring AI RAG实战”最终还是要回归Java把入库流程做成一个可重复执行的服务或定时任务方便知识库更新时增量处理。我现在的习惯是用Java写一个DocIngestService外部触发时遍历指定文件目录根据文件扩展名分发到不同解析器得到纯文本和结构信息后走统一的切块、向量化、入库流程。这个流程里的每一步都记日志、可重跑这也是Java后端相对脚本更有优势的地方——可观测、可恢复、能接入你现有的监控体系。3. 向量化与向量存储——Embedding模型怎么选、向量库怎么定文档切好块之后下一步就是把文本块转成向量。这个动作叫做Embedding本质上是用一个模型把文本映射成一组浮点数数组让语义相近的文本在向量空间里距离更近。选哪个Embedding模型、选哪个向量存储是整个RAG链路的另一个分岔口。3.1 Embedding模型的选择思路Embedding模型的选择核心看三件事语种适配性、向量维度、部署方式。中文技术文档场景我优先推荐BGE系列比如BAAI/bge-m3或者通义千问的text-embedding-v3这类对中文友好的模型。bge-m3支持8192长度输入输出维度1024中文效果在开源模型里很能打。如果是走云端APIDashScope阿里云百炼的text-embedding-v3也是稳定选择返回的也是1024维向量不过要看你是否愿意把文档内容送到云端。向量维度这个参数很多人不注意但它直接影响存储成本和检索速度。1024维的向量存10万条记录大概要占用1GB~2GB的内存或磁盘空间看存储引擎和索引方式维度越高存储越大、计算越慢但也不是说维度低就一定差关键是模型本身质量。我的取舍原则很简单优先用bge-m3这类开源中文模型做本地Embedding好处是数据不出内网、免费、可控如果实在没有硬件条件或需要跨语言场景再考虑云端API。3.2 向量数据库选型PGVector还是Milvus、Redis、ES到了存向量这一步Java程序员肯定要纠结一阵子是单独上Milvus还是用已经有的MySQL、PostgreSQL、Redis、Elasticsearch我直接给结论式建议按团队现有技术栈来如果项目已经在用PostgreSQL直接用pgvector扩展是最省事的选择一张表带一个向量列搞定不用引入额外中间件。如果团队对Redis很熟可以用Redis的向量检索能力RediSearch适合轻量场景。如果在已有Elasticsearch或OpenSearch也可以直接用它们的KNN向量检索跟已有搜索体系打通。如果知识库规模到了百万级甚至更高并且检索性能要求很高那Milvus值得正经调研它对大规模向量索引和分片支持更成熟。Spring AI抽象了一个VectorStore接口你在代码里面对的是统一API实际存储引擎可以换。这算是Spring AI的一大好处——先跑通业务存储引擎后续再优化也行。3.3 入库阶段一定要盯的细节向量化入库时有一个常见的坑大文档一次性塞给Embedding模型很多模型默认输入长度有限超长直接报错。所以一定要在切块阶段就控制好每个块的字符数上限同时写代码时对超长块做截断或跳过并打日志。另外增量更新场景不要每次全量重建库。给每个文档内容算个哈希入库时对比一下这个块是否变化过没变就跳过变了再重新Embedding这样才能让知识库的更新成本可控。开发初期图省事全量重建没问题但上生产前这个增量逻辑必须补上。4. 检索这一步决定了问答质量的大头很多RAG效果差问题不出在模型不够聪明而是检索出来的参考资料本身就不相关。你把一堆不相关的内容塞给一个强大模型它也只会一本正经地胡说八道。所以检索这环值得花大力气打磨。4.1 相似度检索的几个关键参数向量检索最核心的参数是TopK和相似度阈值。TopK指返回最相似的几个块设得太小容易漏设得太大容易混入噪音。我常用的起点是TopK5到8之间具体看你的切块粒度切块越小需要的TopK越高切块越大TopK可以适当降低。相似度阈值是过滤掉不相关结果的保险丝。向量库算出来的相似度分数在Spring AI里通常用cosine距离或欧氏距离你需要设置一个最低门槛低于这个值的检索结果直接不要。比如我用cosine相似度时低于0.6或者0.7的结果基本不会采纳具体阈值用一批真实问题跑一遍后统计分布来确定不能拍脑袋。4.2 混合检索和Rerank效果优化的两板斧纯向量检索有一个经典缺陷它对专有名词、缩写、精确数字不敏感。比如用户问“502 Bad Gateway”向量检索可能找不到包含“HTTP状态码502”的文档因为语义模型未必学过这类精确匹配关系。解决办法是混合检索向量检索之外再叠加一个传统的关键词检索BM25或Elasticsearch的match query然后把两者结果做合并。Spring AI目前对多路召回的支持还有点原始但你可以自己写个组合Service分别拿到结果后做归一化、去重、再排序。如果还是对排序不满意可以上Rerank重排用一个专门的排序模型对检索回来的候选集做精排。bge-reranker-base是中文场景里性价比很高的选择。思路就是先把TopK放大到20~50条然后用Rerank模型对每条候选和用户问题的相关性打分最终只取前5条喂给大模型。这样第一轮粗召回宁可多召回第二轮精排再把无关的踢掉实测效果比直接靠向量相似度排名要好不少。4.3 知识库检索时的Metadata过滤回到前面提到过的Metadata。当你给每个块都打了来源标签后检索时可以组合条件过滤。比如用户提问时你先做意图识别或分类判断问题属于“运维手册”还是“研发规范”然后在检索时带上MetadataFilter只在这一个分类下去找噪音瞬间小很多。Spring AI的VectorStore接口提供了可传递过滤条件的search方法实现方式很直观后面代码部分会展示。5. Spring AI集成落地从零到一跑通RAG问答前面那些环节聊得再热闹最终要落到代码上。Spring AI这个项目从2024年开始在Java社区升温到2025年已经发布了1.0.0 GA版本API逐渐稳定官方提供的ChatClient、EmbeddingModel、VectorStore等抽象已经可以支撑起一套生产可用的RAG服务。下面按一个最小可运行项目来拆解。5.1 引入依赖和配置文件如果用的是Spring Boot 3.x需要在pom.xml里引入Spring AI BOM和相关模块。当前主要用的是spring-ai-starter-model-openai如果你接OpenAI兼容接口或者spring-ai-starter-model-ollama本地模型。这里我用通义千问DashScope和本地Ollama两个方向都举例因为它们是Java圈子里最常碰到的两种场景。dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version1.0.0/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement然后按需引入starter!-- OpenAPI兼容风格适合各种开放平台API -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId /dependency !-- 本地Ollama部署的模型 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-ollama/artifactId /dependency配置文件里用OpenAI兼容接口时大致是这样spring: ai: openai: base-url: https://你的模型服务地址 api-key: ${MODEL_API_KEY} chat: options: model: qwen-plus temperature: 0.3 embedding: options: model: text-embedding-v3如果走Ollama本地模型spring: ai: ollama: base-url: http://localhost:11434 chat: options: model: deepseek-r1:7b temperature: 0.3 embedding: options: model: bge-m3这两个配置的关键点在于Chat模型和Embedding模型是两套独立的模型配置千万不要混用。很多第一次上手的人以为同一个模型既能做Embedding又能做对话结果就踩坑了。5.2 向量库配置与自动装配Spring AI对VectorStore做了自动配置比如引入了pgvector相关的starter就会自动装配出一个PgVectorStore对象。以pgvector为例依赖如下dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-vector-store-pgvector/artifactId /dependency然后在application.yaml里配置数据源和向量表结构信息spring: datasource: url: jdbc:postgresql://localhost:5432/kb_db username: postgres password: postgres ai: vectorstore: pgvector: table-name: kb_vector_store schema-name: public initialize-schema: trueinitialize-schema设置为true时Spring AI会在启动时自动建表开发阶段很方便。生产环境我更建议你自己维护DDL脚本把表结构迁移纳入常规的数据库迁移流程避免应用权限过大。5.3 文档入库从文件到向量的完整代码核心思路是写一个Service方法接收文件流或文本内容走完切块、Embedding、存储三步Service public class KnowledgeIngestService { private final EmbeddingModel embeddingModel; private final VectorStore vectorStore; public KnowledgeIngestService(EmbeddingModel embeddingModel, VectorStore vectorStore) { this.embeddingModel embeddingModel; this.vectorStore vectorStore; } public void ingestText(String documentId, String title, String content) { // 1. 切块这里用一个简化的递归切分器正式环境可替换为更完善的实现 ListString chunks splitIntoChunks(content, 600, 80); // 2. 给每个块打Metadata ListDocument documents chunks.stream() .map(chunk - new Document(chunk, Map.of( documentId, documentId, title, title, chunkSize, String.valueOf(chunk.length()) ))) .toList(); // 3. 向量化并存储 vectorStore.add(documents); log.info(Document {} ingested, chunks: {}, documentId, documents.size()); } }这里的splitIntoChunks可以用Spring AI自带的TokenTextSplitter也可以自己实现。我建议初期先用TokenTextSplitter搞定它不是最完美的但胜在简单可靠。import org.springframework.ai.transformer.splitter.TokenTextSplitter; public ListString splitIntoChunks(String content, int chunkSize, int overlap) { TokenTextSplitter splitter new TokenTextSplitter(chunkSize, overlap, 1, 2000, true); return splitter.split(content).stream().map(Text::getText).toList(); }如果你的知识库以Markdown为主强烈建议升级为按标题层级切分。Spring AI在1.0版本中也引入了针对Markdown的DocumentSplitter能识别标题结构生成带层级信息的块。这个对保留上下文效果提升明显值得花时间研究。5.4 问答检索用QuestionAnswerAdvisor简化RAGSpring AI提供了一个现成的QuestionAnswerAdvisor它做的事就是检索知识库、把检索结果注入Prompt、再调用大模型生成回答。配合ChatClient使用代码量可以控制得很小。Service public class RagChatService { private final ChatClient chatClient; public RagChatService(ChatClient.Builder chatClientBuilder, VectorStore vectorStore) { // 创建Advisor检索时取TopK5 QuestionAnswerAdvisor advisor new QuestionAnswerAdvisor(vectorStore, SearchRequest.builder().topK(5).similarityThreshold(0.6).build()); this.chatClient chatClientBuilder .defaultAdvisors(advisor) .build(); } public String ask(String question) { return chatClient.prompt() .user(question) .call() .content(); } }这样几个关键点都串起来了question进来后QuestionAnswerAdvisor会先去向量库检索相关文档把结果构造成Prompt的一部分再交给ChatModel生成答案。默认情况下回答中还会带上参考文档的来源信息方便你做溯源和展示。如果你不想用默认实现也可以手动控制检索过程拼一个上下文再调用ChatModelpublic String askWithCustomPipeline(String question) { // 1. 向量化用户问题 ListDocument documents vectorStore.similaritySearch( SearchRequest.builder() .query(question) .topK(5) .similarityThreshold(0.6) .build()); // 2. 拼接上下文与问题 String context documents.stream().map(Document::getContent).collect(Collectors.joining(\n\n---\n\n)); String prompt 你是企业知识库助手。请基于以下参考资料回答用户问题。 如果参考资料不足以回答问题请如实说明不要编造。 参考资料 %s 用户问题 %s .formatted(context, question); // 3. 调用大模型生成回答 return chatClient.prompt().user(prompt).call().content(); }自定义管线的优势是你可以对检索结果做更灵活的处理比如过滤低分数块、补充额外上下文、或者做混合检索合并。我个人在生产项目中更偏向这种写法虽然代码多了点但每一步都可控可测。5.5 注入Agent能力RAG之外的扩展方向如果你的场景不满足于简单问答想让它更有“主动性”可以往Agent方向扩展。Spring AI的ChatClient本身就支持Tool Calling你可以在RAG之外给模型注册几个工具方法让它根据用户问题决定是否调用。比如一个“查询工单状态”的Tool配合知识库问答就能做成“先查知识库再查实时业务数据”的复合助手。不过还是那句话先把RAG基础链路跑稳再考虑Agent编排。RAG做不好Agent只是给错误答案加了高速马达。6. 本地大模型部署与对接Java开发者的另一种姿态热词里反复出现了“spring ai对接本地部署的deepseek”。这确实是很多团队的诉求数据不出内网、成本可控、不依赖云端API。Spring AI对本地模型的支持主要通过Ollama或通过兼容OpenAI协议的本地推理服务来实现两种方式对接细节略有不同。6.1 用Ollama托管本地模型Ollama是目前最流行的本地模型运行工具安装简单、对Java友好。安装好Ollama后先拉取模型ollama pull deepseek-r1:7b ollama pull bge-m3这里deepseek-r1:7b是对话模型bge-m3是Embedding模型两者都要拉取。如果机器配置一般deepseek-r1:32b会卡得没法用7b或14b在16GB内存的机器上还比较现实。注意Ollama默认监听11434端口Spring AI的ollama starter会自动连这个端口无需额外配置。启动本地服务后Spring Boot里直接用前面yaml里的ollama配置即可启动应用。面试题常问的“本地部署大模型卡不卡”在实操里会有体感7b模型在M系列芯片的MacBook上能跑但并发极低想支撑线上服务至少得32GB以上内存的服务器最好还有张消费级显卡。6.2 Ollama之外的本地模型接入方式Ollama虽然方便但有些企业会部署自己的推理框架比如vLLM、TensorRT-LLM。这类框架普遍暴露的是OpenAI兼容的HTTP接口那你就不需要ollama starter直接用spring-ai-starter-model-openai把base-url指到本地推理服地址api-key随便填一个占位符就行。这个方案兼容面更广生产环境里我反而用得更频繁。6.3 本地模型的上下文窗口与并发限制对接本地模型有两个硬指标要盯上下文窗口长度和并发能力。DeepSeek R1系列支持的上下文长度在32K到128K之间看起来很大但RAG会把检索回来的文档块拼进Prompt随着知识库块数的增多上下文消耗很快。所以Prompt里要加一句“仅根据参考资料回答”并控制检索块总长度防止模型把大量不相关信息一起消费掉。并发方面本地模型的并发能力不像API那么稳定。建议在应用层把对大模型的调用做信号量限流或者串行化部分请求。Spring AI本身对同步调用做了阻塞处理你要做的是在Service层加一个简单限流器别把本地模型打崩。7. 工程化视角从Demo到可上线的RAG服务代码跑通只是第一步最多算Demo。真要上线让同事用起来还有一大堆工程问题等着知识库变更如何同步、问答效果如何评估、日志怎么记录、权限怎么做。7.1 知识库的增量更新与定时同步知识库不是一次入库就完了。文档每改一版向量库里还留着旧内容问答结果就会过时。我见过最粗放的方式是让运维手动跑一遍全量入库任务内容多的时候一次要跑半小时期间问答服务还用着旧数据体验很差。建议做成增量同步定时扫描文件目录或对接文档平台API比较文件的最后修改时间或内容哈希只处理变更过的文档。对于删掉的文档对应的向量块也要清理。Spring AI的VectorStore接口目前只提供了deleteByIds方法你需要先按metadata里的documentId查出旧块再调用delete。7.2 效果评估怎么知道我的RAG好不好RAG效果不好是个玄学痛点但你完全可以量化。我把实践经验分成三个层次第一层用一组百来条的真实问题集人工标注哪些知识库块是正确参考然后跑一遍检索算一下命中率。这一步主要评估检索质量。第二层把问题和期望答案喂给大模型用另一个更强的模型来打分LLM-as-a-Judge自动评估生成质量。第三层上生产后记录用户的点赞点踩、停留时长、追问率这些真实反馈比任何离线指标都靠谱。如果你不想自己从零写评估工具也可以用LangSmith或Dify这类平台来辅助管理但Java团队内部其实写个简单脚本维护一个测试集就够了重点是把“评测”这件事坚持下来而不是靠感觉调参。7.3 安全与权限知识库问答不能裸奔知识库里的内容大概率含有内部敏感信息RAG服务接进来后必须考虑越权问题。最简单的方式是检索阶段就根据当前用户角色加上MetadataFilter让不同的角色只检索到有权限的那部分文档。Spring AI的SearchRequest支持filter条件实现起来并不复杂但需要你在入库时就规划好文档的权限标签。如果权限模型很复杂比如文档A的某个章节只对特定部门开放那就要在切块阶段就把权限信息打在每个块的Metadata上而不是靠文档级权限拦截。这一步不做好RAG上线后迟早要出事。7.4 日志、监控与失败兜底基于大模型应用出了问题不太好查所以日志尤为重要。每次问答建议把用户问题、检索到的文档块ID、相似度分数、模型回答、耗时全部记录下来。这样用户反馈回答不对时你能快速定位是检索问题还是生成问题而不是一头雾水地重启服务。另外大模型接口在真实场景下会超时、会报错、会被限流服务端必须有兜底逻辑。我常用的做法是捕获异常后返回“当前服务开小差请稍后重试”同时把完整异常信息打到日志里。必要时可以降级为只返回检索到的知识库原文虽然消费者体验差了点但比一片空白强。8. 常见问题与排查技巧实录这里挑几个我实战中反复遇到的坑按问题、原因、解决方案的顺序列出来方便你日后对照排查。8.1 检索结果为空或相似度极低刚跑通时最容易遇到这种问题。先检查Embedding模型是否正常工作——拿两条明显语义相关的文本单独调用一下embeddingModel.embed看返回的向量是否正常。然后检查切块后的文本是否太短或有大量乱码比如PDF扫描件解析出来是空文本自然检索不到。最后再检查提问时和入库时用的Embedding模型是否一致这个最隐蔽但最常见换了模型维度都对不上结果一定跳闸。8.2 回答看起来不错但内容是编造的这是RAG最危险的表现形式。问题出在Prompt和检索两处要么检索出来的参考块相关性不足模型在“尽力”; 要么Prompt没有强制约束模型只能依据参考内容回答。我的建议是双管齐下调高相似度阈值、增加Rerank精排同时在Prompt里明确写“若参考资料没有提到请直接回答不了解”并且把相似度分数较低的检索结果直接丢弃不传给模型。8.3 回答慢得像老牛拉车RAG链路慢瓶颈通常在三个地方向量检索慢、Embedding慢、大模型生成慢。可以按顺序排查向量库有没有建好索引pgvector默认的索引类型是否合适Embedding服务能不能支持并发大模型本地部署的算力是否足够。另外还有个常见优化点把检索过程和大模型生成过程分开计时你才能知道时间到底花在了谁身上。日志里把这些耗时项拆开打出来优化时才有依据。8.4 常见问题速查表现象可能原因建议排查方向检索结果为空文档未成功入库、Embedding接口报错检查入库日志、直接embed测试相似度分数普遍极低中文Embedding模型不匹配、文档是扫描件换bge-m3、加OCR预处理回答内容编造Prompt缺少约束、检索相关性不足强化Prompt约束、加Rerank精排回答引用了过时内容增量更新逻辑缺失补上文档变更检测与增量入库并发一高就超时本地模型算力不足加限流、换更强GPU或转API8.5 调试RAG时一个特别好用的技巧由于RAG链路每一步都可能出错我调试时习惯在测试环境把“检索文档块”和“最终回答”同时打印出来。只看最终回答你很难判断问题出在哪个环节但一旦能看到模型实际拿到了哪些参考材料问题原因就一目了然——要么是材料不对要么是模型没用好材料。Spring AI的Advisor机制里可以自定义一个日志Advisor在链路上拦截并把检索结果dump出来这个小技巧能让你的排错速度快好几倍。写在最后的几句体会跑通一个单机版的RAG Demo可能只需要一个下午。但要让它在团队内部稳定可靠地服务花的时间會是这个数的十倍不止。这不是劝退而是想说明知识库搭建只是起点真正体现Java程序员价值的地方在于后续的数据处理、检索调优、工程化保障这一整套“隐形工作”。Spring AI把对接大模型的门槛降了下来但门槛降低不等于没有门槛理解RAG每一环的原理、踩过几个真实案例的坑之后你才算真正把这套技术内化了。如果看完这篇文章你打算从自己团队的知识库开始动手实验我先给个实用建议别一上来就搞全量知识库挑一个范围最聚焦的模块或目录比如“研发环境部署手册”把几十篇文档跑通全流程验证效果后再逐步扩展。范围越小问题越容易定位效果也越容易感知。等你把这一条路走顺了再用同样的方法论铺开到其他知识域那才是稳扎稳打的推进方式。
返回列表