Java 开发者零成本构建 RAG 知识库:Spring AI Alibaba + Ollama 搭建本地 RAG 知识库
“
大模型再聪明,也不了解你的公司内部文档。RAG 就是让通用模型学会"你的知识"的最佳方案。
为什么需要 RAG?
假设你是一家公司的技术负责人,团队积累了大量内部文档——产品手册、运维手册、技术规范、FAQ。现在你想做一个智能问答系统,让员工可以用自然语言提问。
直接把这些文档丢给大模型?不现实。原因有三:
- 上下文窗口有限:即使是百万 Token 的模型,也无法一次性塞入整个公司的知识库
- 成本高昂:每次对话都发送大量文档,Token 费用难以承受
- 知识更新困难:文档变更时需要重新训练或重新输入
RAG(检索增强生成,Retrieval-Augmented Generation)完美解决了这个问题。它的核心思路很简单:
用户提问 → 从知识库中检索相关片段 → 把片段 + 问题一起交给大模型 → 大模型基于上下文回答
整个过程不需要训练模型,只需要在每次对话时"临时补充"相关知识。
一、技术架构
我们要搭建的系统由四个核心组件组成:
┌─────────────────────────────────────────────────────────┐│ 用户请求 ││ │ ││ ▼ ││ ┌───────────────────────────────────────────────────┐ ││ │ Spring AI Alibaba 应用 (Java) │ ││ │ │ ││ │ ① 接收问题 │ ││ │ ② 将问题转为向量 (Embedding) │ ││ │ ③ 在向量库中检索最相关的知识片段 │ ││ │ ④ 组装 Prompt (知识片段 + 用户问题) │ ││ │ ⑤ 调用大模型生成回答 │ ││ │ ⑥ 流式返回结果 │ ││ └───────┬───────────────────────┬───────────────────┘ ││ │ │ ││ ▼ ▼ ││ ┌───────────────┐ ┌───────────────────┐ ││ │ Ollama │ │ 向量数据库 │ ││ │ (本地模型) │ │ (Chroma/Milvus) │ ││ │ │ │ │ ││ │ · Embedding │ │ 存储文档向量 │ ││ │ · Chat │ │ 支持语义检索 │ ││ └───────────────┘ └───────────────────┘ │└─────────────────────────────────────────────────────────┘
组件选型
| 组件 | 选择 | 理由 |
|---|---|---|
| 应用框架 | Spring AI Alibaba | Java 生态原生,API 统一,企业级能力丰富 |
| 模型运行时 | Ollama | 一条命令启动模型,封装所有复杂性 |
| Chat 模型 | deepseek-r1:8b | 中文能力强,8B 版本在消费级硬件可运行 |
| Embedding 模型 | nomic-embed-text | 轻量、效果好,专门用于文本向量化 |
| 向量数据库 | Chroma | 轻量级,零配置,适合快速原型验证 |
二、环境搭建
2.1 启动 Ollama
如果你还没有安装 Ollama,可以参考之前的本地部署教程。安装完成后,拉取我们需要的两个模型:
# 对话模型(负责生成最终回答)ollama pull deepseek-r1:8b# 向量模型(负责将文本转为向量)ollama pull nomic-embed-text
验证模型是否就绪:
ollama list
你应该能看到两个模型都出现在列表中。
2.2 启动向量数据库
这里选择 Chroma,因为它足够轻量——甚至不需要单独部署服务,可以通过嵌入式模式直接在 Java 进程中运行。但为了演示更通用的场景,我们用 Docker 启动一个独立服务:
docker run -d \ --name chroma \ -p 8000:8000 \ -v chroma-data:/chroma/chroma \ chromadb/chroma:latest
启动后访问 http://localhost:8000/api/v1/heartbeat,如果返回心跳信息说明服务正常。
“
向量数据库的其他选择: 如果你的场景需要生产级部署,可以考虑 Milvus(支持分布式、亿级向量)、Elasticsearch(向量 + 全文混合检索)、或 PostgreSQL + pgvector(已有 PG 基础设施的团队)。Spring AI 对这些都有官方集成。
三、项目实现
3.1 创建 Spring Boot 项目
使用 Spring Initializr 创建一个 Spring Boot 3.x 项目,添加 Web 依赖后,引入以下依赖:
<dependencies> <!-- Spring AI Ollama 集成 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-ollama-spring-boot-starter</artifactId> <version>1.0.0</version> </dependency> <!-- Chroma 向量存储 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-chroma-store-spring-boot-starter</artifactId> <version>1.0.0</version> </dependency> <!-- PDF 文档读取 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-pdf-document-reader</artifactId> <version>1.0.0</version> </dependency></dependencies>
3.2 配置文件
spring: ai: ollama: base-url:http://localhost:11434 chat: model:deepseek-r1:8b options: temperature:0.7 num-predict:4096 embedding: model:nomic-embed-text vectorstore: chroma: client: host:http://localhost port:8000 collection-name:knowledge-base initialize-schema:true
配置解读:
chat.model:指定用于对话的模型embedding.model:指定用于文本向量化的模型vectorstore.chroma:向量数据库连接信息,initialize-schema: true表示自动创建集合
3.3 知识库初始化——将文档变为向量
RAG 的第一步是把你的文档"喂"给系统。这个过程分为四个阶段:
原始文档 → 文本提取 → 文本分块 → 向量化 → 存入向量库
在 Spring AI 中,这一切可以用很简洁的代码完成:
import org.springframework.ai.document.Document;import org.springframework.ai.reader.pdf.PagePdfDocumentReader;import org.springframework.ai.transformer.splitter.TokenTextSplitter;import org.springframework.ai.vectorstore.VectorStore;import org.springframework.boot.ApplicationArguments;import org.springframework.boot.ApplicationRunner;import org.springframework.core.io.FileSystemResource;import org.springframework.stereotype.Component;import java.util.List;@Componentpublicclass KnowledgeBaseInitializer implements ApplicationRunner { privatefinal VectorStore vectorStore; public KnowledgeBaseInitializer(VectorStore vectorStore) { this.vectorStore = vectorStore; } @Override public void run(ApplicationArguments args) throws Exception { // 1. 读取 PDF 文档 PagePdfDocumentReader reader = new PagePdfDocumentReader( new FileSystemResource("knowledge/product-manual.pdf") ); List<Document> documents = reader.get(); System.out.println("读取到 " + documents.size() + " 页文档"); // 2. 文本分块(按 Token 切分,避免单个块过大) TokenTextSplitter splitter = new TokenTextSplitter(); List<Document> chunks = splitter.apply(documents); System.out.println("切分为 " + chunks.size() + " 个文本块"); // 3. 向量化并存入向量库(Spring AI 自动完成 Embedding + 存储) vectorStore.add(chunks); System.out.println("知识库初始化完成"); }}
关键点说明:
- **
PagePdfDocumentReader**:按页读取 PDF,每页作为一个 Document - **
TokenTextSplitter**:按 Token 数量切分文本,默认每块约 800 Token,重叠 20%(避免关键信息被截断) - **
vectorStore.add()**:这一步会自动调用 Embedding 模型将文本转为向量,然后存入 Chroma
“
关于分块策略: 分块大小直接影响检索效果。块太小→上下文不足;块太大→引入噪音。一般建议 500-1000 Token,重叠 10-20%。对于代码文档,建议按函数/类边界切分;对于问答文档,可以按问答对切分。
3.4 RAG 核心服务
知识库就绪后,接下来实现问答逻辑:
import org.springframework.ai.chat.client.ChatClient;import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor;import org.springframework.ai.vectorstore.SearchRequest;import org.springframework.ai.vectorstore.VectorStore;import org.springframework.core.io.Resource;import org.springframework.stereotype.Service;import reactor.core.publisher.Flux;@Servicepublicclass RagChatService { privatefinal ChatClient chatClient; privatefinal VectorStore vectorStore; public RagChatService(ChatClient.Builder chatClientBuilder, VectorStore vectorStore) { this.vectorStore = vectorStore; this.chatClient = chatClientBuilder.build(); } /** * RAG 问答(流式输出) */ public Flux<String> chat(String question) { // 构建检索请求:取最相关的 3 个文本块 SearchRequest searchRequest = SearchRequest.builder() .topK(3) .build(); return chatClient.prompt() .advisors(new QuestionAnswerAdvisor(vectorStore, searchRequest)) .user(question) .stream() .content(); }}
这里的核心是 **QuestionAnswerAdvisor**。它的作用是在每次对话时自动执行以下操作:
- 将用户问题转为向量
- 在向量库中检索最相关的文本块
- 将检索到的文本块注入到 Prompt 中
- 调用大模型生成回答
你不需要手动拼接 Prompt——Spring AI 已经帮你处理好了。
3.5 自定义系统提示词
默认的 Prompt 模板可能不够贴合你的业务场景。可以通过自定义系统提示词来约束模型的行为:
在 src/main/resources/prompts/system-rag.st 中创建提示词模板:
你是一个专业的技术文档助手。请严格基于以下参考资料回答问题。参考资料:{question_answer_context}要求:1. 仅使用参考资料中的信息作答2. 如果参考资料中没有相关信息,请明确告知3. 回答要准确、简洁、有条理4. 使用中文回答
然后在代码中加载它:
import org.springframework.ai.chat.client.advisor.QuestionAnswerAdvisor;import org.springframework.core.io.Resource;import org.springframework.beans.factory.annotation.Value;@Servicepublicclass RagChatService { @Value("classpath:/prompts/system-rag.st") private Resource promptTemplate; // ... public Flux<String> chat(String question) { SearchRequest searchRequest = SearchRequest.builder() .topK(3) .build(); // 加载自定义提示词模板 String template = new String(promptTemplate.getInputStream().readAllBytes()); return chatClient.prompt() .advisors(new QuestionAnswerAdvisor( vectorStore, searchRequest, template)) .user(question) .stream() .content(); }}
3.6 对外暴露 REST 接口
最后,提供一个 HTTP 接口供前端调用:
import org.springframework.http.MediaType;import org.springframework.web.bind.annotation.*;import reactor.core.publisher.Flux;@RestController@RequestMapping("/api/rag")publicclass RagChatController { privatefinal RagChatService ragChatService; public RagChatController(RagChatService ragChatService) { this.ragChatService = ragChatService; } @GetMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> chat(@RequestParam String question) { if (question == null || question.isBlank()) { return Flux.just("请输入问题"); } return ragChatService.chat(question); }}
使用 Server-Sent Events(SSE)格式 TEXT_EVENT_STREAM_VALUE,前端可以实时接收流式响应。
四、测试验证
启动应用后,用 curl 测试:
curl -N "http://localhost:8080/api/rag?question=产品A的保修期是多久?"
你会看到回答像打字机一样逐字输出,而不是等待全部内容生成后才返回。
对比效果
| 场景 | 直接问模型 | RAG 增强后 |
|---|---|---|
| 问通用知识 | ✅ 回答准确 | ✅ 回答准确 |
| 问公司内部政策 | ❌ 不知道/编造 | ✅ 基于文档回答 |
| 问产品技术细节 | ❌ 泛泛而谈 | ✅ 引用具体参数 |
五、生产级优化
上面的实现已经能跑通完整的 RAG 流程。但如果要用于生产环境,还需要考虑以下优化方向:
5.1 混合检索(向量 + 全文)
纯向量检索擅长语义匹配,但对精确关键词(如产品型号、错误码)可能不够准确。可以结合全文检索:
SearchRequest searchRequest = SearchRequest.builder() .topK(5) .similarityThreshold(0.6) // 设置相似度阈值,过滤低质量匹配 .build();
如果使用的向量数据库支持(如 Elasticsearch、Milvus),可以配置混合检索策略。
5.2 重排序(Rerank)
检索到的文本块按相关性排序后,可以再用一个 Rerank 模型进行精排,确保最相关的内容被优先使用:
ChatClient runtimeChatClient = ChatClient.builder(chatModel) .defaultAdvisors(new RetrievalRerankAdvisor( vectorStore, rerankModel, // 重排序模型 searchRequest, promptTemplate, 0.2 // 最低分数阈值 )) .build();
5.3 云端模型混合使用
本地模型推理速度受硬件限制。可以采取"本地 Embedding + 云端 Chat"的混合策略:
spring: ai: # 本地 Embedding(免费、快速) ollama: embedding: model:nomic-embed-text enabled:true chat: enabled:false# 关闭本地 Chat # 云端 Chat 模型(高质量回答) dashscope: api-key:${DASHSCOPE_API_KEY} chat: model:qwen-plus
向量检索是计算密集型操作,本地跑 Embedding 模型完全够用;而最终的回答生成交给云端更强的模型,兼顾成本和效果。
5.4 元数据过滤
为文档块添加元数据,可以实现更精细的检索控制:
Document doc = new Document(content, Map.of( "source", "product-manual", "version", "2.0", "department", "engineering", "language", "zh"));
检索时按元数据过滤:
SearchRequest.builder() .topK(3) .filterExpression("source = 'product-manual' AND department = 'engineering'") .build();
这在多部门、多产品的场景中非常实用——可以确保检索结果只来自相关的文档范围。
5.5 增量更新
生产环境中知识库会持续更新。不需要每次都全量重建,可以按需增删:
// 添加新文档vectorStore.add(newDocuments);// 按 ID 删除旧文档vectorStore.delete(List.of("doc-id-1", "doc-id-2"));
学AI大模型的正确顺序,千万不要搞错了
🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!
有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!
就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋

📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇
学习路线:
✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经
以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!
我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~
这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

更多推荐

所有评论(0)