Spring AI RAG实战:Java开发者构建知识库问答系统的完整指南

发布时间:2026/10/22 18:11:48
Spring AI RAG实战:Java开发者构建知识库问答系统的完整指南 我去年在一个内部知识库项目里尝试接入RAG当时最头疼的就是怎么选框架——LangChain生态丰富但学习曲线陡直接调OpenAI接口又得自己写一堆检索逻辑。后来换了Spring AI整个开发节奏明显顺了很多尤其是它把向量检索、模型调用、提示词管理这些脏活封装得相当干净。这篇就围绕Spring AI RAG实战把我踩过的坑、调试过程和最终落地方案完整记录下来希望能帮你少走弯路。1. 项目背景与方案选型为什么最终锁定Spring AI先说结论如果你所在的团队以Java为主且现有系统已经重度依赖Spring生态Spring AI几乎是目前融入成本最低的RAG落地方式。这个判断是我对比了多种实现路径之后得出的下面说说具体考量。1.1 RAG到底解决了什么问题RAGRetrieval-Augmented Generation检索增强生成本质上解决的是大模型“不知道”的问题。纯靠模型参数里的固有知识回答私有知识库、实时数据、企业内部文档这类问题时要么答非所问要么一本正经地编造——也就是业内常说的幻觉问题。RAG的思路很直接在模型回答问题之前先从外部知识库中检索出相关内容片段然后把这些片段和用户问题一起拼进提示词让模型基于给定的上下文作答。这样做有两个明显好处一是回答可以追根溯源因为答案是基于检索到的实际文档生成的不是凭空捏造二是知识更新成本低需要更新知识时只需刷新向量库不用重新训练模型。1.2 对比LangChain、LlamaIndex和Spring AI的选型过程我最初调研时重点看了三条技术路线各有特点最终结合团队情况做了取舍方案核心优势主要问题适用场景LangChain组件全、社区活跃、生态丰富Python栈与Java团队技术栈割裂版本迭代快API变动频繁纯Python团队快速验证原型LlamaIndex数据索引和检索能力强组件化程度高同样受限于Python栈文档较复杂偏重数据管道、文档分析场景Spring AI原生融入Spring生态抽象统一Java类型安全生产级配置完善起步相对晚部分进阶能力还在演进Java/Spring技术栈的团队落地RAG我们团队的技术栈是Java Spring Boot如果强行引入Python的RAG框架等于要在架构里额外维护一套Python服务还要处理跨语言调用的序列化协议、部署链路和监控成本。Spring AI则可以让RAG能力直接嵌入现有Spring服务复用原有的配置中心、日志链路和部署体系运维心智负担小很多。1.3 Spring AI的核心抽象Spring AI借鉴了Spring生态一贯的“约定优于配置”理念提供了一套统一的接口抽象核心就三个词Model、VectorStore、Advisor。Model是对模型提供方的抽象不管是OpenAI、通义千问还是本地部署的Ollama对外都以统一的ChatClient接口暴露切换模型只需改配置。VectorStore是对向量数据库的抽象Pincone、Redis、PGVector、Milvus都实现了统一的存储和相似度检索接口。Advisor是拦截器机制类似Spring MVC里的Interceptor可以在调用模型前后插入处理逻辑比如自动检索、记录日志、追加安全审查等。这三层抽象让RAG流水线的搭建变成了一种“组合式”开发定义好多维向量字段、指定Embedding模型、注册向量存储、配置Advisor链一个具备RAG能力的最小服务就搭起来了。2. 环境准备与基础依赖配置开始动手之前先把开发环境和依赖配好。这块看似简单但版本兼容问题能让人卡一整天我把验证过的组合直接列出来。2.1 技术栈版本组合我实测下来比较稳的一套组合是JDK 17Spring Boot 3.x要求JDK 17起步Spring Boot 3.3.x / 3.2.xSpring AI 0.8.1版本变化快建议锁定一个已验证的稳定版本PostgreSQL 14我用的PGVector插件来存向量向量维度建议与Embedding模型输出对齐我使用的是常见BGE系列或OpenAI embedding维度一般是1024或1536依赖配置方面如果使用Maven在pom.xml中加入dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-pgvector-store/artifactId version0.8.1/version /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai/artifactId version0.8.1/version /dependency因为Spring AI目前有些模块还没有完全发布到Maven中央仓库如果你拉不到依赖需要在pom中额外引入Spring仓库repositories repository idspring-milestones/id nameSpring Milestones/name urlhttps://repo.spring.io/milestone/url snapshots enabledfalse/enabled /snapshots /repository /repositories2.2 向量数据库选型为什么优先PGVector我知道市面上的向量数据库不少——专门的Milvus、轻量的Chroma、云原生的Pinecone。但我们最终选了PGVector理由很朴素PostgreSQL本来就是业务数据的主库把向量直接存在同一套数据库里不需要额外部署一个基础组件也省掉了两套数据之间的同步痛点。PGVector本质上是PostgreSQL的一个扩展插件提供了vector数据类型和HNSW/IVFFlat两种索引。对于百万级以下的向量规模PGVector性能完全够用更重要的是对DBA友好备份、权限、监控都能沿用现有体系。如果你的向量量级更大千万级以上、查询并发很高那需要认真考虑Milvus这类专用引擎但如果只是企业内部知识库、个人项目验证PGVector性价比是最高的。安装PGVector扩展只需一条SQLCREATE EXTENSION IF NOT EXISTS vector;Spring AI在启动时会自动建表并初始化向量字段默认的表结构我后面会展示你基本不需要手写建表语句。3. 构建RAG流水线的完整实操过程理论和配置都备好了现在进入核心环节——把一条可用的RAG链路搭起来。我按实际开发的顺序来写这样更容易理解每一步在整条链路里的位置和作用。3.1 定义向量存储结构Spring AI使用VectorStore接口屏蔽底层差异对接PGVector时它会自动管理一张名为vector_store的表。如果你想调整表名或基础结构可以通过PgVectorStore的构造参数定制。在代码中我们通过配置类把它注册成Spring BeanConfiguration public class VectorStoreConfig { Bean public VectorStore vectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel) { return new PgVectorStore(jdbcTemplate, embeddingModel); } }这里EmbeddingModel是Spring AI对Embedding模型的抽象底层可以是OpenAI接口、本地Ollama也可以是HuggingFace上的模型。定义完成后调用vectorStore.add(ListDocument)即可将文档写入向量库Spring AI内部会把文档内容切块并逐批调用Embedding模型生成向量整个过程对业务方透明。3.2 文档加载与切块策略RAG的检索质量很大程度取决于文档怎么切。切块splitting直接把控着“检索到的片段是否够用”。我记得第一次测试时用的是几百字的默认切块大小结果检索效果很一般。后来研究了一下Spring AI的文档切分器发现它支持按固定长度、按段落、按递归字符切分。核心参数有三个maxChunkSize单个切块的最大字符数overlap相邻切块之间的重叠字符数separators分隔符优先级我的经验是默认值适合英文文本中文文档需要调整切分参数。因为中文没有天然的空格分词按字符切容易切断语义。实测中对于中文技术文档每块300到500字、重叠80到100字的效果相对理想。TokenTextSplitter splitter new TokenTextSplitter(500, 100, true); ListDocument documents splitter.split(document);这里TokenTextSplitter按Token数切分比按字符切更符合LLM的上下文窗口逻辑。参数里第一个是目标Token数我通常按500配第二个是重叠Token数第三个表示是否启用边界感知。切割完成后一定要给文档设置自定义元数据metadata比如来源文件名、章节标题、所属部门等这样后续在prompt中才能展示引用来源也方便按条件过滤检索范围。Document doc new Document(content); doc.getMetadata().put(source, 产品手册-2024v3.pdf); doc.getMetadata().put(category, 产品文档);3.3 Embedding模型选择与向量化Embedding是RAG的质量底座选什么样Embedding模型直接影响检索准确率。Spring AI这边对Embedding模型的接入方式比较灵活配置在application.yml里即可。如果你使用OpenAI的text-embedding-3-small配置如下spring: ai: openai: api-key: ${OPENAI_API_KEY} embedding: model: text-embedding-3-small如果你想用完全本地化的方案数据不出内网可以通过Ollama跑bge-m3或nomic-embed-text配置方式类似。我个人在当前项目里因为对数据安全要求比较高最终用了本地的Ollama bge-m3spring: ai: ollama: base-url: http://localhost:11434 embedding: model: bge-m3需要强调的是切换Embedding模型时最容易翻车的一个坑是向量维度不一致导致已入库数据无法被检索。一旦换了模型历史向量的维度大概率会变老数据就全废了。所以项目早期就应该锁定Embedding模型后续尽量不切换。3.4 检索与问答链路的组装Spring AI把“从向量库检索TopK片段 → 拼接到系统提示词 → 调用LLM生成答案”这整套流程封装成了Advisor链。我们只需要构建一个带检索能力的ChatClient即可Bean ChatClient ragChatClient(ChatClient.Builder builder, VectorStore vectorStore) { return builder .defaultAdvisors(QuestionAnswerAdvisor.builder(vectorStore).order(1).build()) .build(); }这个QuestionAnswerAdvisor就是RAG的核心——它会把每次用户提问转换成向量去向量库检索最相似的K个文档片段然后把片段作为上下文注入系统提示词再交给LLM回答。整个调用形式非常简洁String answer ragChatClient.prompt() .user(question) .call() .content();调用入口只有一行但内部完成了检索、拼接、生成三步。这就是Spring AI这种框架最“爽”的地方——框架帮你处理了80%的样板代码你只需要关注业务和调优。3.5 系统提示词设计让模型“会说话”如果不设计提示词模型直接把检索到的片段摆出来回答效果会非常生硬。我建议自定义系统提示词明确几个规则优先使用给定的上下文回答不要臆造如果上下文中没有相关内容直接说明“知识库中未找到答案”回答时标注引用来源从元数据中取在Spring AI里可以通过Advisor自定义提示词模板也可以直接覆盖默认的QuestionAnswerAdvisor系统提示词。我的方案是基于QuestionAnswerAdvisor.builder()传入自定义文本QuestionAnswerAdvisor advisor QuestionAnswerAdvisor.builder(vectorStore) .systemText( 你是一名严谨的知识库助手。请基于以下【参考资料】内容回答问题。 规则 1. 只使用参考资料中的信息作答。 2. 如果参考资料中没有答案请直接回复“知识库中暂未找到相关信息”。 3. 回答原文使用【参考资料】中对应的语言和格式。 4. 在回答末尾列出信息来源文件名。 ) .build();这段提示词是我在不断试错中调整出来的最终版本基本可以满足大多数内部知识库问答场景。实际效果中“知识库中暂未找到相关信息”这句兜底话术非常重要它大幅降低了模型瞎编的概率。4. 常见问题与排查技巧实录这部分我认为是最有价值的实战沉淀。我把自己在开发Spring AI RAG过程中真实踩过的坑按排查过程完整整理出来。4.1 检索结果不准答非所问症状检索出来的片段和用户问题明明语义无关模型回答也就胡说八道。排查步骤先看Embedding模型是否是同一个。我踩过的一个坑是入库时用的BGE模型检索时配置被切成了OpenAI模型导致两个向量的语义空间完全不一致相似度计算结果全部失真。再看TopK设置。Spring AI里QuestionAnswerAdvisor默认检索的片段数量是4如果知识库文档比较碎4个片段可能不够我可以调整K值QuestionAnswerAdvisor.builder(vectorStore) .topK(8) ... .build()最后检查元数据过滤。如果你的知识库有多个类别、多种文档不加过滤条件的话检索范围是全库。如果文档差异很大建议按category字段过滤。Spring AI里可以通过动态表达式过滤.prompt() .advisor(检索过滤表达式, category 产品文档)4.2 OpenAPI Key或连接异常导致的500错误症状首次请求RAG接口报错日志里能看到连接超时或API Key未认证。排查方向我会优先确认配置项有没有被Spring正确加载——Spring AI对OpenAI的API Key有默认的占位符配置spring.ai.openai.api-key如果你只配置了裸token没配完整前缀或者用了错误的环境变量名启动是不会报错的但调用模型时会401。建议在启动日志里看Spring配置加载情况确认实际生效的配置文件。另一个常见问题是网络策略本地开发环境如果用了代理调用OpenAI接口时有可能出现连接超时或证书错误需要在JVM参数中配置代理信息或临时关闭代理调试。4.3 中文文档检索效果差切块导致语义断裂我反复测试过切块策略对中文文档的结果影响非常明显。踩了两次坑之后我总结出的经验是不要盲信默认参数针对中文内容手动指定分隔符优先级优先按章、节、段落边界切分对表格类内容尽量整表保留在一个切块里避免表格被拦腰截断对代码示例尽量按代码块切分避免代码中间被切断调整切分器时我固定了一个思路先用段落标题/换行符分割出大块再对大块内部按长度二次切分。实现上可以用TokenTextSplitter 自定义分隔符TokenTextSplitter.builder() .maxTokens(500) .overlapTokens(100) .separators(Arrays.asList(\n\n, \n, 。, , )) .build();4.4 PGVector检索性能慢或索引失效问题场景向量数据量到了几十万条之后检索开始明显变慢。排查发现PGVector插件虽然默认帮你建索引但如果数据量小的时候可能不会自动构建HNSW索引或者建索引时没有指定合适的distance函数。需要在建索引时注意向量距离类型与检索时的一致性CREATE INDEX ON vector_store USING hnsw (embedding vector_cosine_ops);同时表数据量过大时建议定期清理重复或已失效的向量数据避免检索范围无意义膨胀。5. 生产化改造与性能调优能跑通Demo和能上线是两回事。RAG项目到了生产环节还需要关注几个容易被忽略的点。5.1 流式输出改造原型阶段的call()是阻塞式的用户要在后台等几百毫秒甚至几秒才能看到结果。这在真实使用场景里是不可接受的。Spring AI对ChatClient提供了流式调用的方式改为stream()即可FluxString answerStream ragChatClient.prompt() .user(question) .stream() .content();前端通过SSE或WebSocket承接这个流就能实现打字机式输出体验感完全不一样。5.2 检索策略继续升级从单路检索到融合检索用过一段时间后你会发现纯靠向量检索在某些场景下还是不够准——比如姓名、编号这类精确匹配词语义检索匹配不到。为此我引入了混合检索向量检索 关键词BM25检索然后通过结果融合算法如RRF排序合并。Spring AI原生目前不直接提供混合检索但可以自己实现一个Advisor在内部并行调用向量检索和全文检索后做合并。这块改造虽然增加了一点开发量但检索召回率提升非常明显。5.3 缓存与可观测性生产环境里同义或重复的问题会频繁出现每次触发向量检索和LLM调用既费钱又费时。加一层Redis缓存以用户问题的Hash为Key能显著降低成本我遇到的热点知识问答缓存命中率大约有35%到45%。运维侧建议把每次RAG问答链路里涉及的检索片段、Token数、耗时、引用来源都记录下来。Spring AI虽然自带简单的观测日志但生产级还需要接上链路追踪工具把advisorName和检索到的文档ID作为标签上报这对于追查回答质量问题和成本分析太重要了。6. 实践经验总结与下一步想法这个项目从搭骨架到跑通我用了一周多一点的时间——前三天配置环境踩坑后面主要花在切块和提示词调优上。回头看Spring AI确实是Java技术栈做RAG应用当前最顺手的一条路但它的“顺手”不只体现在封装度上更体现在它保留了足够的灵活性我想换Embedding模型、想覆盖系统提示词、想自定义检索逻辑都有对应的扩展点不会被框架卡住。另一个体会是RAG项目的性能瓶颈往往不是框架本身而是你对文档切片和检索策略的调优深度。做得好的RAG检索结果精准到用户提的问题恰好能在Top1、Top2的片段里找到答案做得不好的再强的LLM也救不回来。目前我还在尝试的方向是把多轮对话历史也纳入RAG的检索上下文让用户追问上一轮问题时检索能结合历史提问意图去召回相关内容这个点Spring AI也有对应的Advisor可以扩展后续有成果了我再写一篇续文分享。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询