当前位置:

首页 > 编程开发 > 使用Java实现RAG(检索增强生成)的完整指南

使用Java实现RAG(检索增强生成)的完整指南

适合人群:需要构建知识库问答系统的 Ja va 开发者 核心技术:RAG、向量数据库 Milvus、文本 Embedding 什么是 RAG? 说实话,RAG(Retrieval-Augmented Generation,检索增强生成)在当下的企业级 AI 应用中,已经快成为标配了。它解决了一个很实

适合人群:需要构建知识库问答系统的 Ja va 开发者

使用Ja va实现RAG(检索增强生成)的完整指南

核心技术:RAG、向量数据库 Milvus、文本 Embedding

什么是 RAG?

说实话,RAG(Retrieval-Augmented Generation,检索增强生成)在当下的企业级 AI 应用中,已经快成为标配了。它解决了一个很实际的问题:大模型再聪明,也看不到你公司内部的文档、产品手册、规章制度这些私有数据。而且,它的知识是有“截止日期”的。

所以,RAG 的思路特别直接:

用户提问 → 先去你的私有知识库里翻一翻,找到相关的内容 → 把找到的材料和问题一起喂给大模型 → 让它基于这些材料给出一个靠谱的回答。

这招的好处显而易见:不用花大价钱去微调模型,成本极低;知识库可以随时更新,今天改,明天就能生效;最关键的是,答案有根有据,你能告诉用户这个结论是从哪份文档里扒出来的。

完整的 RAG 流程长什么样?

如果把整个流程画出来,大概是这么一条线:

PDF/Word文档

↓

[文档加载器] PdfboxLoader / ApachePoiDocxLoader

↓

[文本切分器] StanfordNLPTextSplitter

↓

[Embedding 模型] OllamaEmbeddings

↓

[向量数据库] Milvus(存储)

↓ (查询时)

用户问题 → [向量检索] → 相关文档块 → [LLM] → 最终回答

别被这一堆组件吓到,一步步拆解开来,其实并不复杂。

先把环境搭起来:前置配置

在正式开始之前,得先确保一些关键组件能被 Spring Boot 容器识别。这里依赖 Milvus 和 Tesseract OCR,需要在 application.yml 里明确告诉 Spring:“嘿,我要用它们了”:

rag:
  ocr:
    tesseract:
      use: true   # 开启 Tesseract OCR,用于 PDF 中的图片文字识别
  vector:
    milvus:
      use: true   # 启用 Milvus 向量数据库

多说一句,j-langchain 框架很聪明,它通过 @ConditionalOnProperty 来控制这些组件的“出生”。只有当你配了 use: true,TesseractActuator 和 MilvusContainer 这两个 Bean 才会被注册到 Spring 容器里。如果你没开这个开关就去运行 RAG 相关的代码,立马会收到一个 Bean 找不到的报错,别问我怎么知道的。

Step 1:把文档“吃”进来

万事开头难,第一步是把你的 PDF 或 Word 文档加载成程序能理解的结构。j-langchain 提供了现成的加载器,很贴心。

加载 PDF

比如加载一个 PDF,代码非常简洁:

@Test
public void loadPdfDocuments() {
    PdfboxLoader loader = PdfboxLoader.builder()
        .filePath("./files/pdf/en/Transformer.pdf")
        .build();
    loader.setExtractImages(false);  // 我们只关心文本,图片先忽略

    List documents = loader.load();

    System.out.println("总页数:" + documents.size());
    // 注意:这里每个 Document 对象,正好对应 PDF 的一页
}

加载 Word 文档

加载 Word 也是类似的套路:

ApachePoiDocxLoader loader = ApachePoiDocxLoader.builder()
    .filePath("./files/docx/en/Transformer.docx")
    .build();

List documents = loader.load();

加载完后,每个 Document 对象都包含了两个关键信息:一是 pageContent 文本内容,二是 metadata 元数据,比如来自哪个文件、第几页等,方便后续溯源。

Step 2:切分文本,别让大模型“噎着”

想象一下,一份 PDF 可能一页就有几千个字。如果直接把这么大一段文本扔给 Embedding 模型,效果会很差,而且大模型的上下文窗口(context window)也有限。所以,我们需要把长文档切成一个一个的小块。

@Test
public void splitDocuments() {
    List documents = loader.load();
    System.out.println("切分前:" + documents.size() + " 页");

    StanfordNLPTextSplitter splitter = StanfordNLPTextSplitter.builder()
        .chunkSize(1000)    // 每块最多 1000 个字符
        .chunkOverlap(100)  // 相邻两块重叠 100 个字符
        .build();

    List splits = splitter.splitDocument(documents);
    System.out.println("切分后:" + splits.size() + " 块");
}

这里有个小细节:为什么需要 chunkOverlap(重叠部分)?

这个问题很关键。如果一句话恰好跨了两个块,没有重叠,那这句话的意思就被拦腰斩断了,无论落在哪个块里都不完整。有了这 100 个字符的重叠,就能确保这句关键的话至少完整地出现在其中一个块中,不会丢失语义。

Step 3:把文本变成向量,存入 Milvus

现在文本已经被切好了,下一步就是把这些文本块转换成计算机能理解的语言——向量(一长串浮点数),然后存到向量数据库 Milvus 里。

@Test
public void embedAndStore() {
    // ... 假设上面加载、切分的步骤已经完成,得到了 splits ...

    VectorStore vectorStore = Milvus.fromDocuments(
        splits,
        OllamaEmbeddings.builder()
            .model("nomic-embed-text")  // 本地运行的 Embedding 模型,完全免费
            .vectorSize(768)            // 生成的向量维度
            .build(),
        "MyKnowledgeBase"               // 在 Milvus 中创建的 Collection 名称
    );

    System.out.println("向量化完成!");
}

为什么选择本地的 nomic-embed-text 模型?理由很充分:

  • 零成本:不用去调 OpenAI 的付费 API。
  • 隐私安全:所有数据都在你自己的机器上处理,没有泄露风险。
  • 效果好:这个模型在中文和英文的 Embedding 任务上表现都相当不错,足够用了。

当然,前提是你得先在本地通过 Docker 把 Milvus 跑起来:

docker run -d --name milvus \
  -p 19530:19530 \
  milvusdb/milvus:latest standalone

Step 4:组装核心链路——RAG 问答

到这里,就进入了最核心的环节。我们需要用一个“管道”把整个流程串起来:用户提问 -> 去 Milvus 中检索 -> 拼接上下文 -> 让大模型基于上下文给出答案。

代码会稍微长一点,但逻辑非常清晰:

@Test
public void retrieveAndAsk() {
    // 假设之前已经把文档存入 Milvus,拿到了 vectorStore...
    BaseRetriever retriever = vectorStore.asRetriever();

    // 1. 定义好 Prompt 模板,告诉大模型:你要基于我给你的材料来回答
    BaseRunnable prompt = PromptTemplate.fromTemplate(
        """
        请根据以下文档内容回答问题。如果文档中没有相关信息,请说"文档中未找到相关信息"。
        
        文档内容:
        ${context}
        
        问题:${question}
        
        回答:
        """
    );

    // 2. 定义一个函数,把检索到的多个文档块拼接成一个长字符串
    Function formatDocs = input -> {
        List docs = (List) input;
        StringBuilder sb = new StringBuilder();
        for (Document doc : docs) {
            sb.append(doc.getPageContent()).append("\n\n");
        }
        return sb.toString();
    };

    // 3. 组装整个调用链路
    FlowInstance ragChain = chainActor.builder()
        .next(retriever)   // 输入问题,返回最相关的文档块
        .next(formatDocs)  // 将文档块列表拼接成一个字符串
        .next(input -> Map.of(
            "context",  input,
            "question", ContextBus.get().getFlowParam()  // 获取原始的提问
        ))
        .next(prompt)      // 组装 Prompt
        .next(llm)         // 交给大模型处理
        .next(new StrOutputParser()) // 从结果中提取出最终的文本
        .build();

    // 4. 执行链路,提出你的问题
    ChatGeneration result = chainActor.invoke(
        ragChain,
        "Transformer 模型中的注意力机制是如何工作的?"
    );

    System.out.println(result.getText());
}

这个链路的运行过程像是一条流水线:

“Transformer注意力机制...”

→ retriever(去 Milvus 里做相似度检索,捞出最相关的 5 个文档块)

→ formatDocs(把5个文档块拼成一大段文字)

→ prompt(组装成 Prompt:上下文 + 问题)

→ LLM(大模型开始阅读材料并思考)

→ StrOutputParser(把思考结果提取成纯文本)

→ “注意力机制通过计算 Query、Key、Value...”

Step 5:轻量级玩法——直接让大模型做摘要

有时候,我们可能不需要完整的 RAG 流程,只是想让大模型快速总结一下某个文档的大意。这时候,甚至不需要向量库,直接把文档内容喂给大模型就行。

@Test
public void documentSummary() {
    // 加载 PDF
    List documents = loader.load();
    String content = documents.stream()
        .map(Document::getPageContent)
        .collect(Collectors.joining("\n"));

    // 如果文档太长,只取首尾部分,避免超出上下文窗口
    String textToSummarize = content.length() < 2000 ? content
        : content.substring(0, 1000) + "\n...\n" + content.substring(content.length() - 1000);

    FlowInstance chain = chainActor.builder()
        .next(PromptTemplate.fromTemplate("请对以下内容摘要(100字以内):\n\n${text}"))
        .next(ChatOllama.builder().model("qwen2.5:0.5b").build())  // 甚至可以用个小模型
        .next(new StrOutputParser())
        .build();

    ChatGeneration result = chainActor.invoke(chain, Map.of("text", textToSummarize));
    System.out.println(result.getText());
}

RAG 凭什么比直接问大模型强?

最后,我们做个对比,一目了然:

对比项直接问 LLMRAG
私有知识不知道知道(来自你的文档)
知识时效性训练截止日期实时更新
回答可溯源不行可以(返回来源文档)
成本低稍高(Embedding + 向量库)
幻觉风险高低(基于真实文档)

总结一下整个架构

离线阶段(建库):

文档 → 加载 → 切分 → Embedding → Milvus

在线阶段(问答):

问题 → Embedding → Milvus检索 → 拼接上下文 → LLM → 回答

整个流程就是这两个阶段,离线把知识库建好,在线直接开问。思路清晰,实现起来也不复杂,上手试试看吧。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
作者最新文章
编程开发
相关文章 更多
codekit环境配置指南从安装到环境搭建完整教程
codekit环境配置指南从安装到环境搭建完整教程

详解 CodeKit 在 macOS 下的安装步骤、项目导入方法、Sass与JavaScript编译设置及浏览器自动刷新功能,助您快速搭建高效的前端开发环境。

codex安装windows 命令行完整操作教程
codex安装windows 命令行完整操作教程

详解Windows环境下安装OpenAI Codex CLI的步骤,包括WSL环境检查、Node.js/npm配置、npm全局安装命令及首次启动验证,适合开发者快速上手。

NativeRest环境配置要求与完整操作教程
NativeRest环境配置要求与完整操作教程

学习如何配置 NativeRest REST API 客户端。涵盖 Windows/macOS/Linux 安装后的工作区创建、环境变量管理、请求编辑及响应查看步骤,帮助开发者快速完成基础环境搭建与连通性测试。

CSS设置透明度的注意事项有哪些?opacity属性详解
CSS设置透明度的注意事项有哪些?opacity属性详解

深入解析CSS中设置透明度的核心属性opacity,剖析子元素继承、事件穿透、层叠上下文等关键注意事项,并提供与rgba、hsla的实用选型对比。

flutter页面传值到后台的方法及示例代码
flutter页面传值到后台的方法及示例代码

flutter页面传值到后台的完整实现方法及示例代码,帮助读者快速掌握相关技术要点。

Java 8至21新特性代码写法对比:Lambda、Record与Switch
Java 8至21新特性代码写法对比:Lambda、Record与Switch

本文通过具体的旧版与新版代码对比,详细剖析Java 8引入的Lambda表达式、Java 14/16引入的Record类,以及Java 12至21逐步演进完善的Switch表达式与模式匹配,展示代码简化路径与避坑要点。

AI智能体开发培训课程学什么及实战内容介绍
AI智能体开发培训课程学什么及实战内容介绍

系统梳理AI智能体开发培训的核心知识模块、技术栈选型与典型实战项目,解析低代码平台与纯代码框架的差异,提供从零构建可落地智能体的完整学习与实施路径。

Java子类未实现抽象方法编译错误修复指南
Java子类未实现抽象方法编译错误修复指南

针对Java开发中常见的“子类未实现抽象方法”编译错误,深入分析报错原因,提供重写实现、声明抽象子类两种标准修复路径,并总结参数签名、访问修饰符等典型避坑要点。

解决PHP递归报错:max_nesting_level限制与内存溢出处理
解决PHP递归报错:max_nesting_level限制与内存溢出处理

遇到PHP递归报错时,不要盲目调大max_nesting_level。本文教你区分Xdebug限制、内存耗尽和正则递归错误,提供代码级的终止条件优化与迭代替代方案,彻底解决栈溢出问题。

PHP递归中static变量与引用传递的常见陷阱及调试
PHP递归中static变量与引用传递的常见陷阱及调试

本文分析PHP递归中static变量导致的状态污染及引用传递引发的共享数据修改问题。提供具体的代码复现、缓存键设计建议及调试打印技巧,帮助开发者避免隐蔽的逻辑错误。

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件
macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

Mac软件 更多
Figma macOS版
Figma macOS版
Mac

Figma 是面向产品团队的界面与原型设计平台,Mac 客户端支持 Intel 和 Apple 芯片,可直接调用本机字体。它将矢量绘制、自动布局、组件库、交互原型、评论及多人实时编辑集中在统一工作区。

Final Cut Pro macOS版
Final Cut Pro macOS版
Mac

Final Cut Pro 是 Apple 面向 Mac 用户推出的专业视频剪辑软件,提供磁性时间线、媒体资源库、多机位剪辑、字幕生成、对象跟踪、调色、音频处理和多格式输出等工具。软件针对 Apple 芯片优化。

FreeCAD macOS版
FreeCAD macOS版
Mac

FreeCAD是一款免费开源的三维参数化建模软件,可通过草图、约束和特征历史构建尺寸精确且便于修改的模型。软件提供零件设计、装配、工程制图、建筑信息模型、有限元分析、数控加工及三维打印等工作台,并支持插件、宏和Python脚本扩展。

WINDOWS 更多
3dmax(3ds max)
3dmax(3ds max)
Windows

Autodesk 3ds Max 是一款专业的三维建模、动画与渲染软件,广泛应用于建筑可视化、游戏开发、影视动画、广告设计和产品展示等领域。

photoshop
photoshop
Windows、macOS 、 iPad

Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

Blender
Blender
Windows、macOS 和 Linux

Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。