Langchain4j(5)RAG之多格式文档加载(PDF / Word / TXT / 批量文件夹)

张开发
2026/4/18 20:39:41 15 分钟阅读

分享文章

Langchain4j(5)RAG之多格式文档加载(PDF / Word / TXT / 批量文件夹)
概念官方标准 RAG 架构1. Document Loading 文档加载 2. Parsing 格式解析 3. Text Cleaning 文本清洗官方推荐 4. Splitting 文档分块 5. Embedding 向量生成 6. Storage 向量存储 7. Retrieval 检索 8. Generation 回答生成支持的文档类型格式加载器特性PDFApachePdfDocumentLoader, PdfBoxDocumentLoader文本、表格、元数据WordApacheTikaDocumentLoader.doc, .docxExcelApacheTikaDocumentLoader.xls, .xlsxHTMLHtmlDocumentLoader网页解析MarkdownMarkdownDocumentLoader.md 文件TXTTextDocumentLoader纯文本DEMO1.准备文件2.添加依赖用于读取 PDF Word!-- 读取 PDF -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-document-parser-apache-pdfbox/artifactId /dependency !-- 读取 Word DOCX -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-document-parser-apache-poi/artifactId /dependency完整版的 pom?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion !-- 使用 Spring Boot 父项目自动管理版本 -- parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.2.5/version relativePath/ /parent groupIdorg.example/groupId artifactIdlangchain4j-demo/artifactId version1.0-SNAPSHOT/version properties java.version17/java.version maven.compiler.source${java.version}/maven.compiler.source maven.compiler.target${java.version}/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding langchain4j.version1.3.0/langchain4j.version /properties dependencies !-- 1. Spring Boot 核心依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter/artifactId /dependency !-- 2. LangChain4j 核心依赖 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j/artifactId /dependency !-- 3. OpenAI 适配依赖 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-open-ai/artifactId /dependency !-- 4. 流式依赖 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-reactor/artifactId /dependency !-- 5.读取 PDF -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-document-parser-apache-pdfbox/artifactId /dependency !-- 6. 读取 Word DOCX -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-document-parser-apache-poi/artifactId /dependency /dependencies dependencyManagement dependencies dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-bom/artifactId version${langchain4j.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement build plugins !-- Spring Boot 打包插件 -- plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin /plugins /build /project3.实现package org.deepseek.demo11; import dev.langchain4j.data.document.Document; import dev.langchain4j.data.document.parser.apache.pdfbox.ApachePdfBoxDocumentParser; import dev.langchain4j.data.document.splitter.DocumentSplitters; import dev.langchain4j.data.segment.TextSegment; import dev.langchain4j.model.chat.ChatModel; import dev.langchain4j.model.embedding.EmbeddingModel; import dev.langchain4j.model.openai.OpenAiChatModel; import dev.langchain4j.model.openai.OpenAiEmbeddingModel; import dev.langchain4j.rag.content.retriever.ContentRetriever; import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever; import dev.langchain4j.service.AiServices; import dev.langchain4j.service.SystemMessage; import dev.langchain4j.store.embedding.EmbeddingStore; import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; public class RAGMultiDocDemo { interface Assistant { SystemMessage(你必须严格根据文档内容回答不知道就说未找到绝对不能编造。) String ask(String question); } public static void main(String[] args) { try { // 1. 模型配置 ChatModel chatModel OpenAiChatModel.builder() .apiKey(sk-xxxxxxxxxxxxxx) .baseUrl(https://api.deepseek.com) .modelName(deepseek-chat) .temperature(0.0) .build(); EmbeddingModel embeddingModel OpenAiEmbeddingModel.builder() .apiKey(sk-xxxxxxxxxxxxxxxx) .baseUrl(https://dashscope.aliyuncs.com/compatible-mode/v1) .modelName(text-embedding-v1) .build(); // 2. 批量加载 所有 PDF / Word / TXT String folderPath D:\\Project\\public\\langchain4j\\demo\\docs; ListDocument documents new ArrayList(); ApachePdfBoxDocumentParser pdfParser new ApachePdfBoxDocumentParser(); Files.walk(Paths.get(folderPath)) .filter(Files::isRegularFile) .forEach(path - { try { String fileName path.getFileName().toString().toLowerCase(); // 【PDF 解析】使用你提供的 parse(InputStream) if (fileName.endsWith(.pdf)) { documents.add(pdfParser.parse(Files.newInputStream(path))); System.out.println(✅ 加载PDF fileName); } // Word / TXT 直接读取文本 else if (fileName.endsWith(.docx) || fileName.endsWith(.doc) || fileName.endsWith(.txt)) { String text Files.readString(path); documents.add(Document.from(text)); System.out.println(✅ 加载文档 fileName); } } catch (Exception e) { System.err.println(❌ 加载失败 path); } }); // 3. 文档分块短文本不分块保证关键信息完整 ListTextSegment segments new ArrayList(); for (Document doc : documents) { String cleanText doc.text() .replace(\n, ) .replace(\r, ) .trim(); if (cleanText.length() 300) { segments.add(TextSegment.from(cleanText)); } else { segments.addAll(DocumentSplitters.recursive(512, 128).split(Document.from(cleanText))); } } // 4. 向量入库分批避开通义限制 EmbeddingStoreTextSegment embeddingStore new InMemoryEmbeddingStore(); int batchSize 20; for (int i 0; i segments.size(); i batchSize) { int end Math.min(i batchSize, segments.size()); var batch segments.subList(i, end); embeddingStore.addAll(embeddingModel.embedAll(batch).content(), batch); } // 5. 检索器 ContentRetriever retriever EmbeddingStoreContentRetriever.builder() .embeddingStore(embeddingStore) .embeddingModel(embeddingModel) .maxResults(10) .build(); // 6. AI 助手 Assistant assistant AiServices.builder(Assistant.class) .chatModel(chatModel) .contentRetriever(retriever) .build(); // 7. 测试提问 System.out.println(------------------------------------------------); System.out.println(创始人: assistant.ask(公司创始人是谁)); System.out.println(成立时间: assistant.ask(公司成立时间是)); System.out.println(公司名称: assistant.ask(公司名称是)); } catch (Exception e) { e.printStackTrace(); } } }知识点RAG 核心原理RAG检索增强生成先检索外部知识库再结合检索内容生成回答解决大模型幻觉、知识滞后问题流程用户问题 → 向量检索 → 上下文注入 → 大模型回答向量嵌入Embedding将文本转为高维向量用于语义相似度计算相同语义文本向量距离更近实现语义检索向量检索问题向量化后与向量库匹配召回最相关文本片段控制召回数量平衡检索精度与效率Prompt 工程系统提示词约束回答行为禁止编造内容温度系数temperature0.0保证回答严谨、无发散LangChain4j 核心组件文档模型Document统一封装 PDF、Word、TXT 等文档内容TextSegment- 文档分块后的最小处理单元是向量生成、向量存储、检索的核心对象包含文本内容、元数据等信息与向量一一对应。文档解析器ApachePdfBoxDocumentParser基于Apache PDFBox开源库实现专门用于解析PDF文档支持复杂PDF如带图片、表格的PDF但表格文本可能无法完全解析通用文本处理TXT、DOCX、DOC等文本文件无需专用解析器可直接通过文件读取方法获取文本内容再封装为Document对象。文档分块器核心类与方法DocumentSplitters.recursive(maxSegmentSize, overlapSize)属于递归分块器是项目中使用的核心分块工具。官方职责将长文档切分为多个小片段TextSegment适配模型的上下文窗口避免因文本过长超出模型输入限制。参数详情512每个片段的最大长度单位字符需根据向量模型的输入限制调整。128相邻片段的重叠长度单位字符核心作用是保证语义连续性避免一句话被拆分导致语义断裂。实践建议长文本必须切分短文本200字符建议不切分数字、时间、关键词等短关键信息需保持完整避免切分后丢失核心信息。模型相关ChatModel对话大模型接口负责最终的问答生成项目中使用OpenAiChatModel实现类兼容DeepSeek接口调用DeepSeek Chat模型。EmbeddingModel向量模型接口负责将文本片段转为高维向量项目中使用OpenAiEmbeddingModel实现类兼容阿里云接口调用阿里云text-embedding-v1模型。向量存储与检索EmbeddingStore向量存储抽象接口用于存储“向量-文本片段”的映射关系本例使用InMemoryEmbeddingStore内存向量库轻量、无需部署适合演示和小规模测试生产环境可替换为ElasticSearch、PGVector等。EmbeddingStoreContentRetriever内容检索器RAG检索端核心组件负责将用户问题向量化、在向量库中检索相关片段无需手动实现检索逻辑简化开发。向量检索官方行为根据用户问题生成向量在向量库中进行相似度检索返回最相关的Top-K片段项目中K10。AI 服务封装AiServices简化 AI 服务构建绑定模型与检索器SystemMessage注解式定义系统提示词工程实践与优化批处理优化向量生成分批调用项目中batchSize20避免单次请求超出向量模型的接口频率/数量限制提升接口调用的稳定性。文本分块策略差异化分块短文本直接保留长文本自动切块既保证短文本的语义完整性又避免长文本超出模型输入限制。关键注意短文本15字语义向量表达较弱检索相似度偏低建议不切分并适当提高maxResults结合关键词检索提升精度。低耦合设计组件可替换模型对话模型、向量模型、向量库、文档解析器均可灵活替换无需修改整体代码结构便于后续扩展和迭代。模块化设计将RAG流程拆分为模型初始化、文档加载、分块、向量生成、检索、问答等独立模块职责清晰便于维护和调试。鲁棒性处理文件遍历容错通过try-catch捕获单个文件的加载、解析异常确保单个文件处理失败不影响整体流程的执行。格式兼容针对不同格式的文档PDF、Word、TXT采用差异化的解析方案提升项目的兼容性。加载文件遍历文件夹 → 判断后缀 → 选择解析器 → 解析 → 加入列表Files.walk(文件夹) .filter(是文件) .forEach(文件 - { if (pdf) 用 PDF 解析器 if (docx) 用 Word 解析器 if (txt) 用 纯文本解析器 })FileSystemDocumentLoaderFileSystemDocumentLoader 支持自动加载 PDF / Word / TXTPDF 自动加载必须满足两个条件引入 langchain4j-document-parser-apache-pdfbox 依赖解析器通过 SPI 自动注册手动加载 PDF 是兼容写法适用于所有版本批量加载标准实现1. 遍历目标目录获取所有文件 2. 根据文件后缀判断文件类型 3. 获取系统中注册的对应DocumentParser 4. 调用parser.parse(inputStream)解析单个文件生成Document对象 5. 收集所有Document对象到列表完成批量加载。核心总结批量加载 文件遍历 循环调用单个解析器DocumentParser仅负责单个文件解析不具备批量处理能力。PDF 解析与文本清洗PPDF 格式内部按行 / 块存储解析后天然包含大量换行、空格、分段这些符号会降低向量相似度导致检索匹配失败属于PDF 格式特性非解析器错误必须进行文本清洗去除换行符 \n去除回车符 \r多个空格合并为单个空格去除首尾空白DocumentParserDocumentParser 是单个文件解析器不提供批量能力、不提供遍历能力职责将 InputStream → 解析为 1 个 Document文档分块Document SplittingDocumentSplitter 官方职责官方定义将长文档切分为多个小片段 TextSegment适配模型上下文窗口。recursive(512, 128) 官方行为512最大片段长度128片段重叠长度保证语义连续性实践长文本必须切分短文本200建议不切分数字、时间、关键词等短关键信息应保持完整Embedding Retrieval 规则向量检索官方行为根据用户问题生成向量在向量库中做相似度检索返回最相关的 Top-K 片段对短文本的检索说明短文本15 字语义向量表达较弱检索相似度偏低建议不切分适当提高 maxResults结合关键词检索流程图开始 ↓ 【初始化模型】 ├─ 对话模型DeepSeek Chat └─ 向量模型阿里云 text-embedding-v1 ↓ 【遍历指定文件夹】 ↓ 判断文件类型 ├─ .pdf → Apache PdfBox 解析 → 生成 Document ├─ .doc/.docx/.txt → 直接读取文本 → 生成 Document └─ 其他文件 → 跳过 ↓ 【文档清洗】 去换行、去空格、统一格式 ↓ 【智能分块】 ├─ 文本长度 300 → 直接作为片段 └─ 文本长度 ≥ 300 → 按 512 字符分块重叠 128 ↓ 【批量生成向量】 调用向量模型 → 得到文本片段向量 ↓ 【存入内存向量库】 InMemoryEmbeddingStore ↓ 【构建检索器】 EmbeddingStoreContentRetriever ↓ 【创建 AI 助手】 绑定对话模型 检索器 ↓ 【用户提问】 例创始人、成立时间、公司名称 ↓ 【RAG 检索】 问题向量化 → 向量库匹配 → 返回最相关10条片段 ↓ 【AI 生成回答】 仅依据检索内容作答不编造 ↓ 输出答案 ↓ 结束

更多文章