1. 为什么企业需要一个私有化的RAG知识库最近几年大语言模型LLM的能力大家有目共睹但真正在企业里用起来总会遇到几个绕不开的坎。比如你问它公司最新的产品定价策略它可能给你编一个去年的旧数据你让它根据内部技术手册解答一个具体问题它可能答非所问甚至“一本正经地胡说八道”。这种“幻觉”问题在需要精准、可靠信息的业务场景里是致命的。这时候RAG检索增强生成技术就成了“解药”。简单来说RAG就是给大模型配了一个“外接硬盘”和一个“智能秘书”。当用户提问时系统不是让模型凭空想象而是先让“秘书”检索器去“外接硬盘”你的知识库比如产品文档、合同、会议纪要里快速找到最相关的资料然后把问题和这些资料一起交给模型让它基于这些“实锤”信息来生成答案。这样一来答案的准确性、时效性和可追溯性都得到了极大提升。对于企业而言把知识库和AI能力私有化部署更是刚需。数据安全是第一生命线客户信息、技术专利、财务数据绝不能上传到公有云。其次响应速度和稳定性必须自己掌控不能受制于外部API的限速或服务中断。最后系统需要能灵活地与企业现有的OA、CRM、ERP等系统打通形成闭环。我过去在几个项目里尝试过不同的技术栈最终发现Spring AI Ollama DeepSeek ChromaDB这套组合拳对于Java技术背景的团队来说是落地私有化RAG系统非常平滑、高效的选择。Spring AI让你能用熟悉的Spring Boot风格来调用AI能力Ollama让你在本地就能轻松运行像DeepSeek这样的优秀开源模型而ChromaDB作为一个轻量级向量数据库负责知识的存储和检索集成起来非常方便。接下来我就带你从零开始把这套系统搭建起来并分享一些在生产环境中踩过的坑和优化经验。2. 搭建你的本地AI基础设施Ollama与模型部署工欲善其事必先利其器。在开始编码之前我们需要先把底层的基础设施准备好核心就是两件事一个能本地运行模型的引擎和一个能理解中文的强力模型。2.1 安装与配置OllamaOllama可以说是本地运行大模型的“瑞士军刀”它把模型下载、加载、服务化这些繁琐步骤都打包成了简单的命令行操作。安装极其简单去官网下载对应操作系统的安装包一路下一步就行。安装完成后打开终端或命令行输入ollama --version确认安装成功。Ollama默认会在本地启动一个服务通常是http://localhost:11434我们的Spring AI应用之后就会通过这个地址与模型对话。这里有个小坑需要注意如果你的Spring Boot应用是跑在Docker容器里或者需要被同一网络下的其他机器访问你可能需要修改Ollama的启动配置让它监听0.0.0.0而不仅仅是localhost。不过对于我们初期的本地开发测试默认设置就足够了。2.2 选择并拉取适合的模型DeepSeek与Granite-Embedding模型选型是效果的关键。对于生成式对话我们选择DeepSeek。它是由深度求索公司开源的模型在中文理解和生成能力上表现非常出色完全免费可商用并且对硬件要求相对友好。在Ollama中拉取它只需要一行命令ollama pull deepseek-r1:7b这里的7b指的是70亿参数版本在消费级显卡如RTX 4060 16G上就能流畅运行。如果你的服务器显卡内存更大比如24G以上可以尝试deepseek-r1:14b或deepseek-r1:32b版本效果会更好。仅仅有对话模型还不够RAG系统的另一个核心是Embedding模型它负责将我们的一段段文本比如一个段落转换成计算机能理解的“向量”一串有语义意义的数字。检索的本质就是计算问题向量和文档向量之间的相似度。这里我们选用Granite-embedding模型它同样支持多语言包括中文并且由IBM开源商业使用无忧。ollama pull granite-embedding这个模型下载后不会像对话模型那样提供一个聊天接口它静静地待在后台等待被调用来做文本的“向量化”工作。完成这两步后执行ollama list你应该能看到两个模型都已经就绪。这样我们的本地模型算力就准备好了全程无需联网数据不出私域。3. 构建知识的大脑ChromaDB向量数据库实战如果说大模型是系统的“思考者”那么向量数据库就是它的“记忆库”。所有非结构化的文档PDF、Word、TXT都需要经过Embedding模型加工成向量然后存到这个数据库里以备快速检索。3.1 部署与启动ChromaDBChromaDB的设计理念就是简单。它可以通过Python包快速安装pip install chromadb安装完成后我们以服务模式启动它这样我们的Java程序才能通过网络访问。我建议为数据创建一个单独的目录比如D:\chroma_data然后运行chroma run --path D:\chroma_data --host 0.0.0.0 --port 8000--path指定向量数据持久化存储的路径重启服务也不会丢失。--host 0.0.0.0允许所有网络接口访问方便后续集成。--port 8000指定服务端口。启动成功后浏览器访问http://localhost:8000/api/v1/heartbeat如果返回一个包含“心跳”时间的JSON说明服务运行正常。在生产环境你可能会用Docker Compose或Kubernetes来部署和管理这个服务并配置好身份验证这里我们先以开发模式进行。3.2 在Spring AI中集成ChromaDB客户端现在回到我们的Spring Boot项目。首先在pom.xml中添加必要的依赖。除了基础的spring-ai-ollama-spring-boot-starter我们还需要引入ChromaDB和RAG相关的模块dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-ollama-spring-boot-starter/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-chroma-store/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-advisors-vector-store/artifactId /dependency接下来创建一个配置类ChromaConfig。这里面的门道不少我逐一解释Configuration public class ChromaConfig { Bean public RestClient.Builder restClientBuilder() { // 配置一个通用的RestClient构造器用于HTTP通信 return RestClient.builder(); } Bean public ChromaApi chromaApi(RestClient.Builder restClientBuilder) { // 指向我们刚刚启动的ChromaDB服务地址 String chromaUrl http://localhost:8000; // 创建ChromaApi客户端这是Spring AI封装好的底层操作接口 return new ChromaApi(chromaUrl, restClientBuilder, null); } Bean public VectorStore chromaVectorStore(EmbeddingModel embeddingModel, ChromaApi chromaApi) { return ChromaVectorStore.builder(chromaApi, embeddingModel) .collectionName(enterprise_knowledge_base) // 集合名称类似数据库的表 .initializeSchema(true) // 首次启动时自动创建集合 .build(); } }关键点在于EmbeddingModel这个Bean。Spring AI会根据我们在application.yml中的配置自动注入。我们需要配置它使用我们本地Ollama服务上的granite-embedding模型spring: ai: ollama: base-url: http://localhost:11434 embedding: model: granite-embedding这样当我们需要将文档存入向量库时VectorStore会自动调用granite-embedding模型来生成向量。整个配置过程Spring AI做了大量的封装我们只需要关注几个核心的配置项这正是其强大之处。4. 从文档到智能实现完整的RAG流水线基础设施和存储都搞定后我们进入最核心的环节构建一个自动化的流水线把一堆原始文档变成AI可以调用的知识并能智能地回答用户问题。这个过程可以分为“灌库”和“问答”两个阶段。4.1 文档解析与向量化入库企业里的知识通常是PDF报告、Word方案、Excel表格或者网页文章。Spring AI提供了多种DocumentReader来解析它们。这里我以解析PDF和Word为例使用TikaDocumentReader它能自动识别文件格式。 首先引入依赖dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-tika-document-reader/artifactId /dependency然后我们编写一个文档处理服务。这里有个非常重要的概念文本分块Chunking。你不能把一整本100页的产品手册直接转换成向量存进去那样检索效率低且返回的信息可能不精确。正确的做法是按语义把它切成一个个小片段比如每段200-500个字符。Service public class DocumentIngestionService { Autowired private VectorStore vectorStore; public void ingestDocument(String filePath, MapString, Object metadata) { // 1. 使用Tika读取文档 TikaDocumentReader reader new TikaDocumentReader(filePath); ListDocument documents reader.read(); // 2. 使用文本分割器进行分块 TextSplitter splitter new TokenTextSplitter(512, 100, 0, true); // 参数说明chunkSize512 tokens, chunkOverlap100 tokens, 保留分隔符 ListDocument chunks splitter.apply(documents); // 3. 为每个块附加元数据如来源、日期、部门等便于后续筛选 chunks.forEach(chunk - chunk.getMetadata().putAll(metadata)); // 4. 批量存入向量数据库 vectorStore.add(chunks); log.info(成功入库文档: {} 共分割为 {} 个知识块, filePath, chunks.size()); } }你可以通过一个定时任务或者一个上传接口来调用这个服务源源不断地把企业文档“喂”给系统。元数据metadata非常有用比如你可以给所有“财务部”的文档打上department: finance的标签这样在检索时可以限定范围让答案更精准。4.2 集成检索与生成实现问答接口知识库灌装好后实现问答就是水到渠成。Spring AI提供了一个非常优雅的抽象Advisor顾问。QuestionAnswerAdvisor就是一个专门为问答场景设计的顾问它封装了“检索-增强-生成”的完整逻辑。 我们创建一个简单的REST接口RestController RequestMapping(/api/knowledge) public class KnowledgeQAController { Autowired private ChatClient chatClient; // 注入配置了DeepSeek模型的ChatClient Autowired private VectorStore vectorStore; GetMapping(/ask) public String askQuestion(RequestParam String question) { String answer chatClient.prompt() .advisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults())) .user(question) .call() .content(); return answer; } }就这么简单当你调用/api/knowledge/ask?question我们公司的年假政策是怎样的时背后发生了一系列精妙的过程检索QuestionAnswerAdvisor会使用同样配置的Embedding模型将你的问题转换成向量然后在ChromaDB的“enterprise_knowledge_base”集合中查找与这个问题向量最相似的几个文档块默认是5个。增强Advisor会将这些检索到的文档块文本作为“上下文”或“参考依据”巧妙地拼接到发送给大模型的最终提示词Prompt中。这个Prompt模板通常是“请基于以下信息回答问题[检索到的文档文本] 问题[用户的问题]”。生成这个增强后的Prompt被发送给Ollama服务上的DeepSeek模型。模型严格地基于你提供的上下文来组织语言生成最终答案。如果上下文中没有相关信息它甚至会诚实地回答“根据现有资料无法回答该问题”从而有效避免了幻觉。你可以看到我们几乎没有写什么复杂的检索和Prompt拼接逻辑Spring AI的Advisor模式已经为我们提供了一套最佳实践。这正是框架的价值所在。5. 迈向生产环境部署、优化与集成考量一个能在本地跑通的Demo和一個能支撑企业级应用的生产系统中间还有很长的路要走。下面我结合实战经验聊聊几个关键的提升点。5.1 系统部署与高可用架构在生产环境我们不会把所有组件都扔在一台机器上。一个典型的分布式部署架构如下Ollama模型服务可以部署在拥有高性能GPU的服务器上甚至可以是多台服务器组成集群通过负载均衡对外提供API。需要考虑模型的版本管理和热更新。ChromaDB向量数据库建议使用Docker容器化部署并配置持久化卷。对于数据量巨大千万级以上向量的场景可能需要考虑ChromaDB的集群模式或者评估切换到Milvus、Weaviate等支持分布式架构的向量数据库。Spring Boot应用这是无状态的服务可以轻松地水平扩展部署在Kubernetes或云服务器集群中。它通过内网调用Ollama和ChromaDB服务。缓存层在Spring Boot应用和向量数据库之间可以引入Redis等缓存对于一些常见、热点问题的检索结果进行缓存能极大降低向量数据库的负载并提升响应速度。5.2 性能与效果优化策略RAG系统的效果很大程度上取决于“检索”的质量。检索不准后面生成得再漂亮也是白搭。这里有几个调优方向分块策略调优TokenTextSplitter的参数块大小、重叠大小需要根据你的文档类型调整。技术文档可能适合较小的块256 token而报告文学可能需要较大的块1024 token。重叠部分可以保证语义的连贯性避免一个关键信息被割裂在两个块中。检索优化SearchRequest对象提供了丰富的参数。SearchRequest.builder() .query(question) .topK(5) // 返回最相关的K个结果不是越多越好 .similarityThreshold(0.7) // 设置相似度阈值过滤掉低质量结果 .filterExpression(metadata[department] hr) // 基于元数据过滤实现多租户或分类检索 .build();Prompt工程优化虽然QuestionAnswerAdvisor提供了默认Prompt但在复杂场景下你可能需要定制。例如你可以要求模型“如果信息不足请列出可能相关的文档来源并请求用户澄清”或者“以表格形式总结答案”。Spring AI支持自定义PromptTemplate你可以设计更符合业务场景的指令。混合检索除了语义检索向量相似度还可以结合关键词检索BM25。例如先通过关键词快速筛选出一批文档再在这批文档中用向量检索做精排。这种“混合检索”策略能兼顾召回率和精确率Spring AI社区也有一些相关的实验性项目可供参考。5.3 与企业现有系统集成知识库系统不是孤岛。真正的价值在于与工作流结合。与OA/CRM集成可以通过企业微信、钉钉或飞书的机器人接口将RAG问答能力嵌入到日常通讯工具中。员工在群里就能直接机器人提问。或者在CRM系统的客户详情页集成一个知识问答侧边栏自动检索该客户所属行业、产品的相关文档辅助销售跟进。知识更新与同步建立自动化的知识同步管道。例如监听公司Confluence或SharePoint的文档更新事件通过Webhook触发你的文档处理服务实现知识库的实时或定时增量更新。审计与溯源这是企业级应用的必备功能。在返回答案的同时系统必须能够返回引用的源文档片段甚至具体到页码。这不仅能增加可信度也方便用户追溯和核实。Spring AI的Document对象和响应对象里都包含了元数据可以很方便地将来源信息一并返回给前端展示。从我实际落地的经验来看从一个技术Demo到一个稳定可用的系统最大的挑战往往不在技术本身而在对业务场景的深入理解和对细节的持续打磨。比如法务部门的文档和研发部的技术手册最优的分块策略和检索参数可能完全不同。这就需要我们与业务部门紧密合作不断收集反馈进行A/B测试和效果评估。这套基于Spring AI的技术栈最大的优势在于它为Java开发者提供了一个极其熟悉的编程范式。你不需要去学习Python的机器学习框架用你写Spring MVC、Spring Data JPA的经验就能构建出强大的AI应用。它降低了智能化的门槛让企业能够更快速地将内部知识资产转化为实际的生产力。在后续的迭代中你可以考虑加入对话历史管理实现多轮问答、答案评分与反馈收集用于持续优化检索效果等更高级的功能让这个系统越来越智能。