
知识库已经建好了是不是就能直接做一个像样的问答助手了我以前也这么以为。直到自己把 Obsidian 里的几百篇笔记导入向量库、又把公司 wiki 的文档全塞进去之后才发现聊天机器人依然答非所问明明资料里有答案模型就是翻不到。那段时间我反复查日志、调参数最后才意识到一件事——知识库只是 RAG 的原料仓库Java 程序员真正要做的是把存储变成检索再把检索和生成正确地接起来。这篇实战笔记就以知识库已经有了为前提把 Spring AI 做 RAG 时那些文档里不会写清楚的环节一条条拆开讲。1. 知识库≠RAG先看清还缺哪几环1.1 知识库已经有了到底意味着什么很多人提到的知识库其实有两种完全不同的形态。一种是用 Dify、Obsidian、语雀或者自己写的内容管理系统把文档整理成了有目录、有标签、有版本的内容仓库。这种知识库解决的是内容管理问题文件在哪、谁写的、怎么分类但模型没法直接读。另一种是已经做完了向量化文档被切成块、每块算出了向量、写进了向量数据库。这种知识库解决的是内容可被向量检索的问题但它依然不等于一个能用的 RAG 应用。关键区别在于知识库是静态资产RAG 是动态流水线。知识库回答不了请结合最近一周的文档总结项目风险因为它只有数据没有流程只有当你把用户问题转化为检索条件、把检索结果组织成上下文、再把上下文交给大模型生成答案这一整套闭环跑起来知识才真正活了。如果你的知识库还停在第一种形态那工作重点是把文档导出来、做清洗、做切分如果你的知识库已经向量化那工作重心就转移到检索质量和生成策略上。这个判断决定了你接下来花时间的方向千万不要一上来就埋头写代码。1.2 一条完整的 RAG 产线由六个环节组成RAG 的标准链路我习惯拆成六段文档加载Document Loader从 PDF、Word、Markdown、网页里取出文本内容。文本切分Splitter把长文档切成适合检索的片段同时保留上下文关联。向量化Embedding把每个文本片段转成高维向量让语义相近的内容在向量空间里靠近。向量存储Vector Store保存向量和原文支持相似度检索。检索Retriever根据用户问题召回最相关的若干片段。生成Generation把检索结果拼进 Prompt交给大模型产出最终答案。你的知识库已经帮你完成了第 3 和第 4 步甚至第 1、2 步也做了。那 Java 程序员还要做什么答案是你要补上从第 5 步到第 6 步这段衔接逻辑并且回头优化第 2 步的切分质量。因为前端用户感知到的回答质量主要取决于检索环节能不能精准召回以及生成环节有没有把上下文用对。我见过不少团队花大力气把文档全量向量化然后直接在代码里调一个similaritySearch把 Top 5 的结果全拼给大模型。结果回答经常前后矛盾或者被无关片段带偏。问题不在于向量化而在于检索策略太粗糙和 Prompt 设计太随意。1.3 核心认知知识库解决存RAG 解决取准和用好用一个仓库来类比知识库是库房里面堆满了货RAG 是配货员加导购员。库房货物再多配货员拿错货、导购员讲不清客户照样不满意。Spring AI 这类框架本质上就是帮你把配货和导购这两段流程标准化。它不解决你的知识库内容对不对也不解决你选的模型聪不聪明它解决的是 Java 程序员如何用最少的胶水代码把检索和生成稳定地串起来。所以后面所有讨论都围绕两个问题如何取准如何用好。取准是检索侧的事包括切块、召回、重排序、过滤用好是生成侧的事包括 Prompt 模板、多轮记忆、上下文组织。这两个问题想清楚了代码反而是最简单的一层。2. Java 生态选型Spring AI 究竟替你省掉了哪些活2.1 RAG 需要哪些零件Spring AI 怎样帮你拼在 Spring AI 出现之前Java 程序员做 RAG 的基本姿势是用 HTTP 客户端调用 embedding 接口把返回的向量自己塞进某个向量库查询的时候再自己算余弦相似度然后把结果拼 Prompt再调大模型接口。每一步都要处理认证、重试、超时、JSON 解析、类型转换写出来的代码八成是那种只有自己能看懂的胶水屎山。Spring AI 把这个过程抽象成了几个核心接口Document代表文档TextSplitter负责切分EmbeddingModel负责向量化VectorStore负责存储和检索ChatModel负责对话生成。你只需要注入对应的 Bean剩下的网络细节、参数封装、重试逻辑框架帮你处理。对 Java 程序员来说这意味着 RAG 不再是 Python 圈的专属玩具。你可以用熟悉的 Spring Boot 配置项、依赖注入、AOP 和测试工具来构建 AI 应用也可以把现成的知识库服务直接包一层 REST 接口给前端调用。这是 Spring AI 最大的价值它让 Java 团队在 AI 应用开发上不至于从零造轮子。2.2 选型表格模型与向量库的常规组合做一个 RAG 项目你需要拍板三件事用哪个对话模型、用哪个 embedding 模型、用哪个向量库。下面是我个人实践中比较稳的组合参考角色可选方案适用场景备注对话模型OpenAI GPT 系列通用能力强生态完善需要海外网络成本偏高对话模型智谱 GLM 系列中文场景表现好国内直连官方提供 Spring AI Starter对话模型通义千问DashScope中文场景、阿里云生态通过 Spring AI Alibaba 接入对话模型Ollama 本地模型开发调试、数据敏感场景免费qwen2.5 等模型可选EmbeddingOpenAI text-embedding-3-small通用效果好中文表现可以Embedding智谱 embedding-3中文优成本低和智谱对话模型配套方便Embedding通义 text-embedding-v3中文优阿里云生态支持多种向量维度EmbeddingOllama nomic-embed-text本地调试够用生产不建议向量库SimpleVectorStore原型验证、测试只存内存重启即失向量库Redis中小规模、已有 Redis 场景支持向量检索部署简单向量库Milvus大规模、高并发运维重一点向量库PGVector和 PostgreSQL 一起用事务和检索兼顾向量库Elasticsearch已有 ES 的团队混合检索更方便我自己的经验是开发调试阶段用 Ollama 加 SimpleVectorStore跑通流程正式环境再切换到智谱或通义的模型配合 Redis 或 Milvus。不要一开始就上复杂架构RAG 烂不烂通常跟向量库关系不大跟检索策略关系更大。2.3 版本差异比 API 本身更值得关注用 Spring AI 最让人抓狂的不是概念多而是版本变化太快。如果你搜到一篇三个月前的教程里面的坐标和 API 很可能已经过时。Spring AI 在 1.0 之前经历了很长一段 0.x 阶段API 变动频繁2025 年年中发布了 1.0 GA整体稳定下来随后 2.0 又在 2025 年下半年登场包结构、Starter 命名和部分 API 都做了调整。网上大量的教程是基于 0.8.x 写的里面的spring-ai-openai-spring-boot-starter、AiClient这些用法在 2.x 里多半对不上。所以我的建议是以官方文档标注的版本文档为准不要盲目照抄老博客的依赖坐标。你写代码时只要抓住几个稳定概念——ChatClient、EmbeddingModel、VectorStore、Advisor——无论在 1.x 还是 2.x 里这些核心抽象的演进方向是一致的查文档时更容易对号入座。2.4 中文场景智谱、通义这类国产模型怎么接如果你的知识库内容是中文为主我的经验是优先考虑国产模型。原因很实际一是网络延迟和合规更省心二是中文语义理解在本地化场景下通常比通用模型更贴合。智谱 AI 在官方 Spring AI 里有对应的 Starter坐标大概是spring-ai-starter-model-zhipuai这一类的命名配置api-key之后ChatModel和EmbeddingModel就能自动装配。通义千问则可以通过 Spring AI Alibaba 项目接入这个项目由阿里维护把 DashScope 上的模型统一包装成 Spring AI 的接口风格。这里给个实际提醒Spring AI Alibaba 的 Maven 坐标和版本号跟官方 Spring AI 的发布节奏不完全同步artifactId 在不同版本之间改过名字。你如果照着旧教程配很容易出现依赖冲突或者 Bean 找不到的情况。最稳妥的方法是进项目的官方仓库看最新 release 的示例代码或者直接用官方 BOMBill of Materials管理版本避免自己写死版本号。2.5 开发调试期用 Ollama 本地模型最省事做 RAG 开发时频繁调用云端模型接口既花钱又慢特别是在调试切分参数和 Prompt 的时候。我的做法是开发环境用 Ollama 拉一个参数规模适中的本地模型——比如 qwen2.5 系列的 7B 或 14B——配合本地 embedding 模型把整个链路先在本地跑通。Ollama 的好处是兼容 OpenAI 的接口风格Spring AI 里可以直接配置 base-url 指向本地服务。这样你可以在不花一分钱的前提下测试文档切分方式是否合理检索召回是否准确Prompt 里的上下文顺序是否有效这些核心问题。确认没问题了再切到生产级模型做一轮回归测试。这个习惯帮我省了不少钱也大幅缩短了调试循环。你在本地把链路调到能稳定答对 80% 的问题这个水平再上生产模型体验会顺滑很多。3. 代码级闭环把已入库的知识接进对话流3.1 最简实现Advisor 让聊天自动带出知识如果你的知识库已经向量化用 Spring AI 做 RAG 最偷懒的姿势是使用QuestionAnswerAdvisor。这个 Advisor 会自动完成向量检索 → 拼进 Prompt → 调用大模型的流程你只需要给它一个VectorStore。Configuration public class RagConfiguration { Bean public VectorStore vectorStore(EmbeddingModel embeddingModel) { return new SimpleVectorStore(embeddingModel); } Bean public ChatClient chatClient(ChatModel chatModel, VectorStore vectorStore) { return ChatClient.builder(chatModel) .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore)) .build(); } }调用的时候一个方法就完成了问答RestController public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient chatClient) { this.chatClient chatClient; } PostMapping(/ask) public String ask(RequestBody String question) { return chatClient.prompt(question) .call() .content(); } }这套实现的核心逻辑是QuestionAnswerAdvisor接管了用户问题先用问题去VectorStore做相似度检索把召回的文档拼成 Prompt 里的参考材料再让模型基于这些材料生成答案。这一版代码非常短但对于知识库已经有了的人来说它已经能把知识真正用起来了。我建议先用这个跑通最小闭环确认检索、生成、响应链路都没问题再去研究更精细的检索策略。3.2 手动检索自己控制召回结果的写法QuestionAnswerAdvisor虽方便但它的检索策略偏固定。如果你想自己控制召回的条数、相似度阈值或者想在 Prompt 里用不同的模板那就手动拆开写。ListDocument documents vectorStore.similaritySearch( SearchRequest.builder() .query(公司年假政策是什么) .topK(5) .similarityThreshold(0.5) .build() ); String context documents.stream() .map(Document::getText) .collect(Collectors.joining(\n\n)); String response chatClient.prompt() .user(u - u .text( 你是一个企业知识助手。请基于以下资料回答用户问题。 资料中如果没有答案请直接说明不要编造。 资料 {context} 问题{question} ) .param(context, context) .param(question, question)) .call() .content();手动写的好处是你能看清楚每一步发生了什么。特别是出了答非所问的情况时Advisor那套黑盒会让你很难排查——到底是向量库没召回还是召回了但模型没用对手动拆开之后你可以在中间打印documents的内容一眼就能定位问题。3.3 入库侧的代码与配置如果你的知识库还只是文档目录没有向量化那入库侧的代码也要自己写。Spring AI 提供了TikaDocumentReader和TokenTextSplitter可以快速完成读取和切分// 读取目录下的文档递归加载返回Document列表 DocumentReader reader new TikaDocumentReader( new FileSystemResource(/data/knowledge/docs)); ListDocument docs reader.get(); // 按token切分块大小800重叠100 TextSplitter splitter new TokenTextSplitter(800, 100); ListDocument chunks splitter.split(docs); // 写入向量库Embedding在此过程中自动完成 vectorStore.write(chunks);这段代码的流程是读取文档 → 切分成适合检索的块 → 向量化并写入。TokenTextSplitter的两个参数分别代表目标块大小和块间重叠。很多人会忽略一个细节不同格式的文档切分前最好做去噪。比如 PDF 里的页眉页脚、表格抽出后的断行、Markdown 里的代码块这些内容如果直接进向量库检索时经常产生莫名其妙的干扰。我在入库前会跑一遍轻量清洗把多余空行和无效字符去掉效果提升非常明显。3.4 从 SimpleVectorStore 升级到 Redis/MilvusSimpleVectorStore只适合原型阶段因为数据存在内存里服务一重启就全没了。生产环境我一般建议先上 Redis理由很直接很多 Java 项目本来就有 Redis运维不需要引入新组件RedisVectorStore的配置也不算复杂只是要额外引入对应的依赖并在 Redis 侧开好向量检索模块。如果知识库规模真的大到百万级文档或者并发查询压力很高再考虑 Milvus 这类专用向量数据库。大规模场景下单机 Redis 的内存和检索性能都会成瓶颈。从我的经验看绝大多数企业内部知识库几千到几万篇文档的规模Redis 已经绰绰有余。不要在起步阶段为了架构先进引入 Milvus那是给自己找运维麻烦。4. 检索质量切块、多路召回与重排序4.1 切块参数块小了没上下文块大了全是噪音我最早做 RAG 时以为切块就是按固定长度切就行。后来被实际问题教育了块切得越小召回越精准但块里缺失上下文模型看到的是断章取义块切得越大上下文完整但检索时无关信息增多相似度分数被稀释。举个直观例子。一份产品说明里写本产品不支持离线模式请确保网络连接稳定。如果切出来的块只包含请确保网络连接稳定模型很可能回答用户需要自己保证网络完全丢失了产品不支持离线模式这个关键信息。我的通用经验是中文文档用 500800 token 作为目标块大小重叠窗口 100150 token。这个区间既能保留一个主题的完整表达又不至于让单次检索塞进太多噪声。当然具体数字跟文档类型强相关规章制度类可以更大问答对、FAQ 类可以更小代码示例类则需要按代码块结构切。4.2 重叠窗口与父子块两个高频补救手段重叠窗口是为了解决关键信息正好被切在边界上的问题。相邻两个块之间保留一段重复文本信息就不容易被拦腰截断。这是投入最小、收益最稳定的切分优化手段。另一个更高级的手段是父子块结构先把文档切成比较大的父块比如 1500 token再把每个父块切成若干小儿子块用于检索检索时命中子块但送给模型的上下文是子块对应的父块。父子块的好处是检索精度和上下文完整性兼得检索发生在小粒度上保证准确性生成时拿到的是大粒度的完整上下文避免断章取义。实现起来也不复杂入库时维护一个子块 ID → 父块 ID的映射关系查询阶段先用子块库召回再根据映射取父块内容拼 Prompt。这个技巧效果很明显推荐做内部文档型知识库的朋友优先尝试。4.3 混合检索向量加关键词的双通道召回向量检索擅长语义匹配——用户问怎么请假它能找到休假申请流程但向量检索也有软肋专有名词、精确编号、产品型号这类文本向量相似度往往不如关键词匹配直接。比如用户搜BUG-2024-001语义向量可能把它匹配到一堆无关的 bug 描述上。混合检索的思路是向量检索和关键词检索比如 BM25各跑一路分别召回 Top N 结果合并去重后再统一打分。这样语义匹配和精确匹配能互补。在 Spring AI 里向量检索走VectorStore关键词检索则需要借助 Elasticsearch 或数据库的全文索引。如果你的向量库正好是 ES混合检索会方便很多如果是 Redis 或 Milvus就需要额外维护一份倒排索引。实现成本高不高取决于你原本的存储选型。我个人的建议是如果知识库里有大量型号、编号、人名这类强标识文本混合检索非常值得做如果内容以概念性、说明性为主先做好向量检索和重排序性价比更高。4.4 重排序花小钱办大事的检索优化向量检索召回 Top 510 个结果这里面的排序是按向量相似度算的并不完全等于对回答的贡献度。很多时候真正有用的答案排在第三、第四位而排在第一位的是主题相近但内容无用的片段。重排序Reranker就是用一个专门的模型把召回的候选片段重新打分排序。这个模型通常比 embedding 模型更精细它会同时考虑查询和文档之间的相关性而不是单纯的向量距离。有些实现直接让大模型参与重排效果也很好只是成本更高。我自己实测的数据加了一个轻量级 rerank 模型之后回答的准确性从偶尔靠谱提升到了基本稳定提升幅度比我调了一下午切块参数还大。所以我会把重排序列为 RAG 优化的第一优先级。在 Spring AI 生态里目前没有统一的内置 Reranker 抽象常见的做法是检索完拿到候选列表后调用重排序 API 重新打分再取前 35 个结果拼 Prompt。代码上不复杂但需要你自己组织一次额外的 HTTP 调用。4.5 元数据过滤让检索范围一开始就收敛知识库里通常不只有一种文档。公司 wiki 可能既有产品资料又有技术方案还有行政通知。如果检索时不做区分用户问报销流程系统可能把技术方案里提到报销模块架构的内容也召回来。解决办法是给每个文档块在入库时打好元数据标签比如部门、文档类型、发布日期、适用范围。检索时通过SearchRequest的过滤条件先把范围限定在相关类别里再做相似度计算。SearchRequest.builder() .query(报销流程) .topK(5) .filterExpression(docType hr department finance) .build();这个过滤条件的作用是在向量检索之前缩小候选集。它不需要额外引入复杂组件但能显著降低噪音。很多人忽视这一步其实元数据过滤往往是最便宜、见效最快的检索优化手段之一——只要建库的时候顺手把标签打上检索时加一行过滤条件就行。5. 多轮对话下的 RAG记忆、改写与检索配合5.1 多轮问答里最常见的检索失败RAG 应用上线一段时间后你会遇到一个很典型的场景用户先问华为 Mate 70 的电池容量是多少系统答对了接着用户追问那 Pro 版本呢系统去向量库检索那 Pro 版本呢结果什么都匹配不到回答变成了我没看懂您的问题。这就是多轮对话下的指代消解问题——用户的问题依赖前文而检索器拿到的是残缺的当前问题。很多 RAG 项目在单轮问答上表现不错一到多轮就露馅根因基本都在这里。解决思路有两条一是把对话历史一起参与检索二是先改写用户问题再检索。前者实现简单但容易引入噪声后者效果更好但需要多一次模型调用。我倾向于两者结合用改写为主辅以少量历史关键信息。5.2 ChatMemory把历史对话变成模型上下文Spring AI 里处理多轮对话最基础的是ChatMemory。它能把最近的对话消息存下来并作为上下文传给模型。ChatMemory chatMemory MessageWindowChatMemory.builder() .maxMessages(20) .build(); ChatClient chatClient ChatClient.builder(chatModel) .defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory)) .build();MessageChatMemoryAdvisor会自动把最近的对话历史拼进请求模型也就拥有了理解指代的基础。但这里有个容易踩的坑对话历史里的噪声也会被带进检索。比如用户前面聊了三句无关话题历史全拼进去检索关键词被稀释召回自然不准。所以我在实践中的做法是ChatMemory解决的只是模型侧的理解RAG 侧的检索不应该直接拿完整历史去查而是应该拿改写后的当前问题去查。5.3 查询改写让那个变成一句完整问题查询改写是让 RAG 支持多轮对话的关键一步。思路是在真正检索之前先让大模型根据对话历史和当前问题生成一条独立的、含义完整的查询语句。String rewritePrompt 你是一个查询改写助手。请结合对话历史和当前用户问题 把当前问题改写为一条不依赖上下文、含义完整的独立搜索查询。 只输出改写后的查询不要解释。 对话历史 {history} 当前问题 {question} ; String rewrittenQuery chatClient.prompt() .user(u - u.text(rewritePrompt) .param(history, historyText) .param(question, currentQuestion)) .call() .content(); // 用改写后的查询做向量检索 ListDocument documents vectorStore.similaritySearch( SearchRequest.builder() .query(rewrittenQuery) .topK(5) .build() );这样用户追问那 Pro 版本呢会被改写成华为 Mate 70 Pro 的电池容量是多少检索精度一下子就上来了。这个方案唯一的代价是每次多一轮大模型调用成本和延迟略有上升。但考虑到多轮问答本来就是用户高频使用场景这笔开销值得。我自己做的时候还顺手把改写结果缓存了起来同一个话题连续追问时降低调用频次。5.4 从 RAG 到 Agentic RAG什么时候需要它热词里有个概念叫 Agentic RAG。简单说传统 RAG 是一次检索一次回答Agentic RAG 是模型可以决定要不要检索、检索几次、检索不出来要不要换策略。它把检索从固定流程升级成了模型可操控的工具。举个实际例子用户问我们公司去年所有项目的预算总和如果你直接做向量检索只能召回一堆项目文档但没法自动算总和。Agentic RAG 的思路是让模型先判断该检索哪些文档再决定是否调用一个工具做数值计算甚至多轮检索、多工具配合之后才生成答案。什么时候需要 Agentic RAG我的判断是当你的知识库场景开始涉及跨文档汇总、条件查询、多步推理时传统 RAG 的一次检索拼 Prompt已经明显不够用了。但 Agentic RAG 的复杂度也高不少——你需要设计工具调用、编排逻辑、错误处理。对大多数知识库问答场景来说先把检索质量打磨到位再考虑 Agentic路会更顺。顺带提一句热词里的RAG 和 MCP 区别MCP 解决的是大模型如何通过标准化协议调用外部工具和数据源RAG 解决的是如何让模型利用私有知识库的内容生成答案。两者定位不同实践中经常配合使用——MCP 可以提供一个知识库查询工具而工具内部用 RAG 实现。Java 程序员可以先把 RAG 跑明白再往 MCP 上扩展。6. 生产化之前评测、成本与常见翻车点6.1 一套简单可复制的评测方法技术圈有个奇怪现象大家愿意花大量时间调参数却不愿意花半小时建评测集。没有评测集的 RAG 优化本质上就是凭感觉调参改坏了都不知道。我建议从第一天开始就建一个轻量评测集针对你的知识库准备 2030 条真实问答对覆盖不同文档类型和难度。每条问答对标注该问题的答案来自哪篇文档。评测时跑一遍全量问答看两个指标指标含义判断标准检索命中率检索出的 Top 5 文档里是否包含标注文档越高越好理想 80% 以上答案正确率模型最终回答是否准确完整人工判断可接受 80% 以上注意先单独看检索命中率。如果检索就没命中生成再强也没用如果检索命中了但答错问题在生成侧或 Prompt。这个分层定位方法能让你少走很多弯路。6.2 召回率与答案正确率两个核心指标刚开始做评测时我只看答案对不对导致经常分不清是检索的锅还是模型的锅。后来我改成两段式评测第一段把向量检索召回的 Top N 文档打印出来看标准答案是否在里面。在标记检索命中不在标记检索失败。第二段把命中情况下的最终回答质量按正确、部分正确、错误三档人工打分。这个评测集也许不严谨但足够指导迭代。每次改切块参数、换 embedding 模型、加重排序之后都跑一遍记录检索命中率和答案正确率的变化。长期积累下来你就能清楚知道每种改动带来的真实影响。我还发现一个规律检索命中率上去了答案正确率不一定跟着上去但检索命中率上不去答案正确率一定上不去。所以优化顺序永远是先保检索再管生成。6.3 token 成本、延迟和降级策略RAG 应用上线后成本大头不在 embedding而在于每次请求把几千 token 的上下文送给模型。如果知识库文档特别长上下文塞得又多token 费用和响应延迟都会肉眼可见地上升。三个省钱动作我亲测有效第一响应缓存。对高频问题比如年假有几天命中缓存后直接返回不经过模型。第二动态截断。召回结果越多越好是错觉相关的内容两三条就够把 Top 10 全塞进去只会增加 token 消耗并稀释注意力。第三降级策略。当检索结果相似度普遍很低时明确告诉用户知识库里没有找到相关内容而不是硬凑答案。这个降级策略既能省 token又能避免幻觉。另一个思路是调整向量检索的相似度阈值。阈值设得太低比如 0.3模型会拿到大量无关上下文既贵又误导阈值设得太高比如 0.8又容易什么都召不回。我的经验值是中文文档 0.50.6 作为起点再根据评测集微调。6.4 我踩过的几个坑和对应解法先说版本坑。Spring AI 的版本差异真的能坑死人。我最早照着 0.8.x 的博客搭环境导入依赖后一连串的类找不到后来翻官方文档才发现是新版改了包名。现在我的习惯是打开官方文档直接看当前 stable 版本的快速开始跑通官方示例之后再叠加自己的代码。再说 Embedding 模型不一致的坑。如果入库时用的 embedding 模型和查询时用的模型不一样向量空间对不上检索效果会断崖式下跌。这个坑特别隐蔽因为代码不报错但召回结果就是差。解决方案是把 embedding 模型的名称和版本固化到配置中心升级时强制走重新向量化流程。第三是中文环境的坑。一些英文模型对中文编码不友好中文文档用这类模型向量化后语义距离明显不准。我的建议是中文场景直接选中文优化过的 embedding 模型别拿通用英文模型硬扛。第四是依赖冲突的坑。引入 Spring AI 相关 Starter 时它可能传递引入指定版本的 Spring Boot 依赖如果项目里已有其他模块依赖了不同版本很容易出现诡异的启动失败。解法是统一用 BOM 管理版本尽量保持 Spring Boot 主版本与 Spring AI 的要求一致。6.5 给 Java 程序员的起步路线图最后整理一下我建议的行动顺序你照着走基本不会迷路先用QuestionAnswerAdvisor加SimpleVectorStore跑通最小闭环。把 2030 条真实问答整理成评测集记录检索命中率和答案正确率。优化切块参数用评测集验证重点尝试父子块结构。加入元数据过滤缩小检索范围。接入重排序这是性价比最高的一步。处理多轮对话引入查询改写。切换生产级向量库和模型做成本和延迟优化。我在实际项目里按这套顺序推进基本上每一步都能看到明确的效果提升。最后说一个我自己的体会在 RAG 这件事上代码量真的不是关键你花在检索策略上的思考时间直接决定最终效果。知识库已经有了剩下的功课就是把这些检索和生成的细节一个一个补扎实。