当前位置: 首页 > news >正文

用 LangChain4j 从零实现 RAG:基于 PDF 文档的智能问答系统

在大语言模型时代,让 AI 能够理解并基于本地文档回答问题的 RAG(检索增强生成)技术成为热门。本文将结合实际代码,详细介绍如何使用 LangChain4j 框架快速实现一个基于 PDF 文档的 RAG 系统,让 AI 能够 "读懂" 你的本地文档并精准回答相关问题。

什么是 RAG?为什么选择 LangChain4j?

RAG(Retrieval-Augmented Generation,检索增强生成)是一种将外部知识检索与大语言模型生成相结合的技术。它解决了大语言模型 "知识过时" 和 "幻觉生成" 的问题,通过在生成回答前检索相关文档内容,让 AI 基于真实可信的来源生成答案。

LangChain4j 是 Java 生态中优秀的大语言模型应用开发框架,它提供了简洁的 API 封装,简化了 RAG 流程中文档处理、嵌入生成、向量存储、检索匹配等核心环节的实现,让开发者可以用最少的代码搭建生产级 RAG 系统。

实战:用 LangChain4j 实现 PDF 文档问答

环境准备:核心依赖

首先需要在pom.xml中引入 LangChain4j 的核心依赖,包括框架核心、内存向量存储、文档解析器和大语言模型集成(这里以 OpenAI 为例):

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-open-ai</artifactId><version>1.1.0</version></dependency><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j</artifactId><version>1.1.0</version></dependency><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-reactor</artifactId><version>1.1.0-beta7</version></dependency><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-easy-rag</artifactId><version>1.1.0-beta7</version></dependency>

核心组件配置:搭建 RAG 基础框架

我们需要配置三个核心组件:向量存储(用于存储文档嵌入)、嵌入模型(将文本转换为向量)、RAG 聊天助手(整合检索与生成)。以下是 Spring 环境下的配置类实现:

import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.openai.OpenAiEmbeddingModel;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.InMemoryEmbeddingStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RAGConfig {// 1. 配置嵌入模型(将文本转换为向量)@Beanpublic OpenAiEmbeddingModel embeddingModel() {return OpenAiEmbeddingModel.withApiKey("你的OpenAI API密钥");}// 2. 配置向量存储(存储文档向量)@Beanpublic EmbeddingStore<TextSegment> embeddingStore() {// 实际生产环境可替换为Pinecone、Weaviate等向量数据库return new InMemoryEmbeddingStore<>();}// 3. 配置大语言模型(用于生成回答)@Beanpublic ChatModel chatModel() {return OpenAiChatModel.withApiKey("你的OpenAI API密钥").modelName("gpt-4o-mini") // 可替换为gpt-4等模型.temperature(0.7); // 控制回答随机性,0表示更确定}// 4. 配置RAG聊天助手(整合检索与生成)@Bean(name = "ragChatAssistant")public ChatMemoryAssistant ragChatAssistant(EmbeddingStore<TextSegment> embeddingStore,ChatModel chatModel) {// 创建内容检索器(从向量存储中检索相关文档)EmbeddingStoreContentRetriever contentRetriever = EmbeddingStoreContentRetriever.from(embeddingStore,embeddingModel(), // 用于将查询转换为向量3 // 每次检索返回3个最相关的文档片段);// 构建RAG聊天助手return AiServices.builder(ChatMemoryAssistant.class).chatModel(chatModel) // 生成回答的大模型.contentRetriever(contentRetriever) // 检索相关文档.chatMemory(MessageWindowChatMemory.withMaxMessages(50)) // 保留对话历史.build();}
}

关键组件说明

  • EmbeddingStore:存储文档的向量表示,这里使用内存存储(适合演示),生产环境建议使用分布式向量数据库。
  • EmbeddingModel:将文本(文档片段和用户查询)转换为向量,实现语义匹配。
  • ChatModel:大语言模型,基于检索到的文档生成自然语言回答。
  • ChatMemoryAssistant:LangChain4j 提供的接口式助手,自动处理检索 - 生成流程,支持对话记忆。

文档处理:解析 PDF 并存入向量存储

接下来需要实现文档的加载、解析和嵌入存储。以下是控制器中处理 PDF 文档并提供问答接口的代码:

import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.parser.apache.tika.ApacheTikaDocumentParser;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import java.io.FileInputStream;
import java.io.FileNotFoundException;@RestController
@Slf4j
public class RAGController {// 注入RAG聊天助手@Resourceprivate ChatMemoryAssistant ragChatAssistant;// 注入向量存储@Resourceprivate EmbeddingStore<TextSegment> embeddingStore;/*** 加载PDF文档并执行RAG问答*/@GetMapping("/rag/query")public String ragQuery(@RequestParam String query) {try {// 1. 读取本地PDF文件(这里以"Java开发错误码.pdf"为例)FileInputStream fileInputStream = new FileInputStream("src/main/resources/static/Java开发错误码.pdf");// 2. 使用Apache Tika解析PDF文档(支持多种格式:PDF/Word/Excel等)Document document = new ApacheTikaDocumentParser().parse(fileInputStream);// 3. 将文档分块并存入向量存储// 自动分块(默认按字符长度分块,可自定义分块策略)EmbeddingStoreIngestor.ingest(document, embeddingStore);log.info("文档解析完成,已存入向量存储");// 4. 调用RAG助手生成回答(自动检索相关文档)String answer = ragChatAssistant.chat(query);log.info("RAG回答:{}", answer);return answer;} catch (FileNotFoundException e) {log.error("文档未找到", e);return "文档不存在,请检查路径";} catch (Exception e) {log.error("RAG处理失败", e);return "处理失败:" + e.getMessage();}}
}

文档处理流程解析

  1. 文档加载:通过FileInputStream读取本地 PDF 文件(支持其他格式如 Word、TXT 等)。
  2. 文档解析:使用ApacheTikaDocumentParser解析文档内容(Tika 支持多种格式,无需单独处理 PDF 解析逻辑)。
  3. 文档分块EmbeddingStoreIngestor自动将文档分割为适合嵌入的小片段(默认策略:每块 200 字符,重叠 0 字符,可自定义)。
  4. 嵌入存储:分块后的文本片段通过嵌入模型转换为向量,存入EmbeddingStore

定义聊天助手接口

最后需要定义ChatMemoryAssistant接口,LangChain4j 会通过动态代理自动实现接口逻辑:

import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;public interface ChatMemoryAssistant {// 系统提示:告诉AI如何使用检索到的文档回答问题@SystemMessage("""你是一个基于文档的问答助手。请根据提供的文档内容回答用户问题。如果文档中没有相关信息,请明确说明"文档中未找到相关内容",不要编造答案。回答要简洁明了,基于文档事实。""")String chat(@UserMessage String query);
}

接口说明

  • @SystemMessage:定义 AI 的角色和行为准则,这里要求 AI 严格基于检索到的文档回答。
  • @UserMessage:标记用户输入的查询参数。
  • LangChain4j 会自动将检索到的文档内容作为上下文传入大模型,生成符合系统提示的回答。

运行与测试

  1. 准备工作

    • 替换代码中的 OpenAI API 密钥(可在 OpenAI 官网申请)。
    • 将 PDF 文档放入src/main/resources/static目录(或修改代码中的文件路径)。
  2. 启动应用:运行 Spring Boot 主类,访问接口http://localhost:8080/rag/query?query=你的问题

  3. 示例

    • 若 PDF 中包含 "错误码 1001 表示参数无效" 的内容,查询http://localhost:8080/rag/query?query=错误码A0001是什么意思,会返回基于文档的准确回答。

优化与扩展方向

  1. 替换向量存储:将InMemoryEmbeddingStore替换为生产级向量数据库(如 Pinecone、Milvus、Weaviate),支持大规模文档存储。

  2. 自定义分块策略:默认分块可能不适合长文档,可自定义分块器:

    DocumentSplitter splitter = new CharacterDocumentSplitter(500, // 块大小50,  // 块重叠"\n"  // 分隔符
    );
    EmbeddingStoreIngestor.builder().documentSplitter(splitter).embeddingStore(embeddingStore).build().ingest(document);
    
  3. 使用本地模型:若需隐私保护,可替换为本地嵌入模型(如 BGE)和大语言模型(如 Llama 3):

    // 本地嵌入模型
    EmbeddingModel embeddingModel = new BgeSmallEnV15QuantizedEmbeddingModel();
    // 本地大语言模型
    ChatModel chatModel = new Llama3ChatModel(...);
    
  4. 添加文档元数据:解析文档时可添加元数据(如作者、日期),检索时支持过滤:

    Document document = Document.from(text, Map.of("source", "Java开发手册"));
    

总结

本文通过实际代码演示了如何用 LangChain4j 快速搭建 RAG 系统,核心流程包括:文档解析→分块→嵌入存储→检索→生成回答。LangChain4j 通过封装复杂的底层逻辑,让开发者只需关注业务需求,大幅降低了 RAG 的实现门槛。

无论是企业内部知识库问答、产品手册查询还是法律文档分析,这种架构都能快速适配,为 AI 应用注入 "读懂本地文档" 的能力。

http://www.dtcms.com/a/272501.html

相关文章:

  • 《信号与系统》学习笔记——第八章(补充部分)
  • 缺乏日常项目进度例会机制,如何系统推进
  • 基于大模型的膀胱癌全周期精准诊疗方案研究
  • GitHub敏感信息收集与防御指南
  • 【音视频】TS协议解析
  • 音频 SDP 文件格式
  • 基于多模态感知的裂缝2D及3D检测方案
  • Boost.Asio学习(3):异步读写
  • windows对\和/敏感吗?
  • 小白成长之路-NFS文件存储及论坛项目搭建(php)
  • C++之unordered_set和unordered_map基本介绍
  • jmeter如何让一个线程组中的多个请求同时触发
  • PyTorch中torch.eq()、torch.argmax()函数的详解和代码示例
  • 多线程交替打印ABC
  • Windows安装DevEco Studio
  • 解决问题:在cmd中能查看到pnpm版本,在vscode终端中却报错
  • [5种方法] 如何将iPhone短信保存到电脑
  • 搜索算法在前端的实践
  • G5打卡——Pix2Pix算法
  • Vue前端导出页面为PDF文件
  • 【HDLBits习题 2】Circuit - Sequential Logic(4)More Circuits
  • AI驱动的业务系统智能化转型:从静态配置到动态认知的范式革命
  • 基础 IO
  • Spring Boot中的中介者模式:终结对象交互的“蜘蛛网”困境
  • JAVA JVM的内存区域划分
  • Redis的常用命令及`SETNX`实现分布式锁、幂等操作
  • Redis Stack扩展功能
  • K8S数据流核心底层逻辑剖析
  • AI进化论06:连接主义的复兴——神经网络的“蛰伏”与“萌动”
  • k8s集群--证书延期