从 Demo 到生产:用 Spring AI Alibaba + Ollama 落地本地 RAG 知识库的工程实战指南
从 Demo 到生产:用 Spring AI Alibaba + Ollama 落地本地 RAG 知识库的工程实战指南关键词:Spring AI Alibaba、Ollama、RAG、本地知识库、混合检索、生产化、高并发、可扩展架构摘要很多团队第一次做 RAG,往往都从一个“能问能答”的 Demo 开始:本地起一个 Ollama,塞几篇 Markdown 到向量库,写一个接口调用模型,十几分钟就能跑起来。但真正上线后,问题会立刻出现:文档一多,切块和索引速度急剧下降检索命中率不稳定,答案时好时坏多租户无法隔离,权限边界模糊并发一上来,Ollama 与向量检索链路同时成为瓶颈无法定位问题出在切块、召回、重排,还是 Prompt文章里的 Demo 代码能跑,但离“可维护、可扩展、可审计”还差很远这篇文章不再停留在“本地跑通”,而是从原理、架构、工程实现、性能治理、生产运维五个层面,完整讲清楚如何基于Spring AI Alibaba + Ollama构建一个真正能落地的本地 RAG 知识库系统。本文会给出:一套适合企业内部知识问答的生产级 RAG 架构一条完整的离线索引流水线与在线查询链路一套可直接改造成项目骨架的 Spring Boot 代码高并发、可扩展、可观测、可灰度的工程治理方案一个贴近真实业务的场景化案例一、为什么很多 RAG Demo 一上线就失效1.1 Demo 解决的是“可行性”,生产系统解决的是“稳定性”一个最小化 RAG Demo 通常只有四步:读取文档文档切块向量化后写入向量库查询时 TopK 检索,再把上下文拼进 Prompt这个路径本身没错,但它默认了很多理想条件:文档规模小数据更新慢查询并发低不需要权限控制不要求引用溯源不要求监控、审计和回放而生产环境恰恰相反。企业知识库是一个持续变化、权限敏感、查询模式复杂的系统,RAG 本质上不是一个“模型调用问题”,而是一个检索系统 + 生成系统 + 工程系统的复合问题。1.2 真正的难点不在“接上模型”,而在“控制回答质量”RAG 最核心的价值不是让模型“更会说”,而是让模型:只在有证据时回答尽量基于证据回答能把证据给出来没证据时明确拒答所以一个生产级 RAG 系统的优化重点,通常不是 Prompt 花活,而是下面四件事:检索召回是否足够准上下文拼装是否足够稳文档索引是否持续可更新整条链路是否可观测、可回放、可治理二、先把底层原理讲透:RAG 不只是“检索 + 生成”从架构视角看,RAG 至少包括两条主链路:离线链路:文档采集 - 清洗 - 切块 - 向量化 - 建索引在线链路:问题理解 - 检索召回 - 重排过滤 - Prompt 组装 - 生成回答 - 引用返回这两条链路都要稳定,系统才算稳定。2.1 文档切块不是越碎越好,也不是越大越好很多 Demo 直接用固定长度切块,例如 500 或 1000 tokens。它简单,但不一定合理。切块过大时:单块包含多个主题,语义噪声增多检索召回命中后,注入模型的上下文容易跑偏Token 成本和响应时延都更高切块过小时:语义不完整,尤其是技术文档中的“定义 + 约束 + 示例”可能被拆开TopK 需要取更大,召回结果碎片化严重上下文拼接后可读性变差,模型更容易丢失逻辑工程上更推荐三类策略:固定窗口切块:适合 FAQ、工单模板、短说明文递归分块:按标题、段落、句子逐级切分,适合技术文档结构化分块:保留章节、标题、表格、代码块边界,适合设计文档、接口文档、SOP一个实用经验是:正文块长控制在300~800 tokens重叠区控制在50~120 tokens为每个 chunk 保留title、section、source、tenantId、docVersion、chunkNo这些元数据在检索过滤、引用展示、故障排查时非常重要。2.2 Embedding 的本质是“把语义映射到可计算空间”Embedding 模型并不理解业务,只是把文本编码成一个高维向量,使语义相近的文本在向量空间里更接近。这里有三个常见误区:误区一:向量维度越高越好并不是。维度更高通常意味着存储更大、索引更慢、计算更贵,不一定带来稳定收益。误区二:只要用了 embedding,召回自然就准也不是。召回质量还和文档切块、领域术语、别名、缩写、数字实体、过滤条件密切相关。误区三:同一份文档永远不用重建向量错。文档修改、切块策略变化、embedding 模型升级,都应触发重建或局部重建。在本地方案中,Ollama 常见搭配是:Chat 模型:qwen2.5、deepseek-r1、llama3.xEmbedding 模型:nomic-embed-text如果是中文文档占比高的企业场景,建议一定做离线评估,而不是只看单次问答效果。2.3 检索不是单点能力,而是“召回 + 排序”的组合能力真正决定 RAG 上限的,往往不是生成模型,而是检索系统。一个成熟的检索链路一般分三层:粗召回:向量相似度检索,快速找出语义相关块补召回:BM25 或关键词检索,召回包含术语、接口名、错误码、版本号的文本重排序:对候选片段做 rerank,减少“看起来像、其实不对”的噪声块这也是为什么生产系统通常不会只做纯向量检索,而会采用混合检索。2.4 生成不是“把检索结果丢给模型”,而是“受约束的答案合成”生成阶段真正要解决的是三个问题:给模型多少上下文用什么格式给上下文模型何时回答、何时拒答一个生产可用的 Prompt 通常至少要明确:角色约束:你是企业知识助手,不是自由聊天机器人证据约束:只能依据参考资料回答拒答策略:没有证据就明确拒答引用格式:回答中引用文档名、章节、片段编号输出风格:简明、可执行、避免泛化描述三、从单机 Demo 到生产系统:推荐的整体架构3.1 分层架构图┌─────────────────────────────────────────────────────────────────────┐ │ 接入层 / API Gateway │ │ Web / App / IM Bot / OpenAPI / SSO / 限流 / 鉴权 / 灰度 │ └──────────────────────────────┬──────────────────────────────────────┘ │ ┌──────────────────────────────▼──────────────────────────────────────┐ │ RAG Query Orchestrator │ │ 会话管理 / Query 重写 / 检索编排 / Prompt 装配 / 引用聚合 / SSE │ └───────────────┬──────────────────────────────┬──────────────────────┘ │ │ ┌───────────────▼───────────────┐ ┌──────────▼───────────────────────┐ │ Retrieval Service │ │ Generation Service │ │ 向量召回 / BM25 / Rerank / │ │ Ollama Chat / Prompt 模板 / │ │ 多租户过滤 / 阈值控制 │ │ 流式输出 / Token 统计 │ └───────────────┬───────────────┘ └──────────┬───────────────────────┘ │ │ ┌───────────────▼──────────────────────────────────────────────────────┐ │ 数据与存储层 │ │ PgVector / Elasticsearch / Redis / MySQL / MinIO / Kafka │ └───────────────┬──────────────────────────────────────────────────────┘ │ ┌───────────────▼──────────────────────────────────────────────────────┐ │ Offline Index Pipeline │ │ 文档上传 - 清洗解析 - 分块 - 向量化 - 建索引 - 版本切换 │ └──────────────────────────────────────────────────────────────────────┘3.2 为什么我推荐“在线查询链路”和“离线索引链路”分离因为这两类流量的特征完全不同。在线查询链路关注:低时延可预测延迟熔断降级SSE 流式体验离线索引链路关注:批量处理吞吐异步削峰幂等重试失败补偿文档版本管理如果把这两类链路混在一个服务里,通常会出现两个问题:大批量导入文档时拖慢在线问答查询高峰时挤压索引任务,导致知识更新延迟因此,生产环境建议至少拆成三个逻辑模块:rag-api:对外提供查询接口、上传接口、会话接口rag-indexer:异步消费文档,做解析、切块、embedding、写索引rag-admin:知识库管理、重建索引、文档版本切换、运营配置如果团队规模不大,也可以物理不拆服务,但逻辑上必须拆链路。四、技术选型建议:为什么是 Spring AI Alibaba + Ollama4.1 组合定位这个组合的核心价值不是“功能堆砌”,而是职责边界清晰:Spring Boot:承载 Web、配置、监控、线程池、事务、工程框架Spring AI / Spring AI Alibaba:统一模型调用抽象、Prompt、Advisor、RAG 集成能力Ollama:本地运行 LLM 与 embedding 模型,降低外部依赖和调用成本PgVector / Elasticsearch:负责向量检索与混合检索Redis:缓存热点问题、会话摘要、租户配置Kafka:承接异步文档入库任务4.2 为什么本地 RAG 适合企业内部知识库本地部署并不只是为了省钱,它还有几个非常现实的价值:内部文档不出域,安全边界更清晰模型响应更可控,不依赖公网抖动成本结构从“按调用计费”转向“固定资源投入”方便做定制化管控,例如文档白名单、租户隔离、内容审计当然,本地方案也有代价:模型效果更依赖本地硬件并发能力受推理资源限制更明显需要自己处理模型热更新、资源调度和容量规划所以它更适合:企业内部知识助手私有部署场景研发、运维、客服、法务类文档问答对数据安全和可控性要求较高的组织五、项目目录设计:先把工程骨架搭对建议按“查询域”和“索引域”拆分包结构,而不是把所有类都塞进service:rag-knowledge/ ├── pom.xml ├── src/main/java/com/example/rag │ ├── RagKnowledgeApplication.java │ ├── config │ │ ├── RagProperties.java │ │ ├── OllamaConfig.java │ │ ├── VectorStoreConfig.java │ │ ├── ExecutorConfig.java │ │ └── ObservationConfig.java │ ├── controller │ │ ├── ChatController.java │ │ └── DocumentController.java │ ├── app │ │ ├── ChatApplicationService.java │ │ └── DocumentIndexApplicationService.java │ ├── domain │ │ ├── model │ │ ├── service │ │