大模型再聪明,也不了解你的公司内部文档。RAG 就是让通用模型学会"你的知识"的最佳方案。


为什么需要 RAG?

假设你是一家公司的技术负责人,团队积累了大量内部文档——产品手册、运维手册、技术规范、FAQ。现在你想做一个智能问答系统,让员工可以用自然语言提问。

直接把这些文档丢给大模型?不现实。原因有三:

  1. 上下文窗口有限:即使是百万 Token 的模型,也无法一次性塞入整个公司的知识库
  2. 成本高昂:每次对话都发送大量文档,Token 费用难以承受
  3. 知识更新困难:文档变更时需要重新训练或重新输入

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**。它的作用是在每次对话时自动执行以下操作:

  1. 将用户问题转为向量
  2. 在向量库中检索最相关的文本块
  3. 将检索到的文本块注入到 Prompt 中
  4. 调用大模型生成回答

你不需要手动拼接 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%免费

在这里插入图片描述

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐