使用 LangChain4j 分析 PDF 文档

在我的咨询工作中,客户经常向我们提出具有挑战性的问题,需要创新的解决方案。最近,我们接到一项任务,要求通过自动分析从 PDF 文档中提取结构化元数据。在下文中,我将分享这个真实世界挑战的简化版本,以及我们如何解决它。

用例

我们的客户收到包含多达数百份需要审核的可移植文档格式 (PDF) 租赁文档的压缩档案(.zip 文件)。每份文档包含必须验证准确性的财产租赁详细信息。审核过程包括检查各种业务规则 - 例如,识别租期短于 2 年的租赁。目前,此文档验证是手动完成的,非常耗时。客户希望自动化和简化此审核工作流程,以提高效率。

这些租赁文档的一些复杂性包括:

  • 文档不是标准格式,因此每份租赁合同可能由不同的物业经理以不同的方式编写。

  • 文档可能经过扫描,因此文本有时是手写而不是打字的。

  • 文档可能包含多个页面,这些页面的顺序并不总是相同的。

  • 租赁期限可能不是实际日期,而是写为“从开始日期起五年到期”或“在开始日期的周年纪念日到期”。

  • 我们的客户需要土地面积和税号信息等元数据来验证租赁详细信息。

您可以理解为什么人工审核和验证文档非常耗时。

我们的解决方案

在咨询 Dmytro Liubarskyi 并与 Quarkus 团队合作后,我们实施了一个使用 LangChain4j 进行文档元数据提取的解决方案。我们选择 Google Gemini 作为我们的大型语言模型 (LLM),因为它擅长通过其内置的光学字符识别 (OCR) 功能进行 PDF 分析,从而能够从数字文档和扫描文档中准确提取文本。

技术细节

该应用程序使用以下技术构建:

  • Quarkus - Kubernetes 原生 Java 框架

  • LangChain4j - LangChain 的 Java 绑定,用于与 LLM 交互

  • Google Gemini AI - 用于 PDF 文档分析和信息提取

  • Quarkus REST - 用于处理多部分文件上传

  • HTML/JavaScript 前端 - 用于文件上传和结果显示的简单 UI

后端通过以下步骤处理 PDF:

  1. 通过多部分表单数据接受 PDF 上传

  2. 将 PDF 内容转换为 base64 编码

  3. 将具有结构化 JSON 模式的消息发送到 Gemini AI 以进行响应格式化

  4. 以标准化格式返回已解析的租赁信息

  5. 在 Web 界面上以表格格式显示结果

主要组件包括:

  • LeaseAnalyzerResource - 用于 PDF 分析的 REST 端点

  • LeaseReport - 租赁信息的数据结构

  • 用于文件上传和结果显示的 Web 界面

工作原理

首先,我们需要一个 Google Gemini API 密钥。您可以免费获得一个,请在此处查看更多详细信息:Gemini API 密钥文档

export QUARKUS_LANGCHAIN4J_AI_GEMINI_API_KEY=<your-google-ai-gemini-api-key>

接下来,我们需要安装 LangChain4j 依赖项

<dependency>
    <groupId>io.quarkiverse.langchain4j</groupId>
    <artifactId>quarkus-langchain4j-ai-gemini</artifactId>
    <version>0.25.0</version>
</dependency>

配置 Gemini LLM

接下来,我们需要将 Gemini LLM 连接到应用程序(使用您的 Google AI Gemini API 密钥)。

quarkus.langchain4j.ai.gemini.chat-model.model-id=gemini-2.0-flash
quarkus.langchain4j.log-requests=true
quarkus.langchain4j.log-responses=true

记录请求和响应是可选的,但有助于调试。

注册 AI 服务

我们必须注册 AI 服务才能使用 LeaseAnalyzer 接口。

import dev.langchain4j.data.pdf.PdfFile;
import dev.langchain4j.service.UserMessage;
import io.quarkiverse.langchain4j.PdfUrl;
import io.quarkiverse.langchain4j.RegisterAiService;

@RegisterAiService(chatMemoryProviderSupplier = RegisterAiService.NoChatMemoryProviderSupplier.class)
public interface LeaseAnalyzer {

    @UserMessage("Analyze the given document")
    LeaseReport analyze(@PdfUrl PdfFile pdfFile);
}

定义您的数据结构

现在我们需要为租赁信息建模数据结构,我们希望 LLM 从任何租赁文档中提取这些信息。您可以根据您需要从 PDF 文档中获取的信息自定义这些字段,但在我们的用例中,我们提取以下信息:

public record LeaseReport(
    LocalDate agreementDate,
    LocalDate termStartDate,
    LocalDate termEndDate,
    LocalDate developmentTermEndDate,
    String landlordName,
    String tenantName,
    String taxParcelId,
    BigDecimal acres,
    Boolean exclusiveRights) {
}

创建 REST 端点

最后,我们需要创建一个 LeaseAnalyzerResource 类,它将使用 LLM 从 PDF 文档中提取租赁信息。

@Inject
LeaseAnalyzer analyzer;

@PUT
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.TEXT_PLAIN)
public String upload(@RestForm("file") FileUpload fileUploadRequest) {
    final String fileName = fileUploadRequest.fileName();
    log.infof("Uploading file: %s", fileName);

    try {
        // Convert input stream to byte array for processing
        byte[] fileBytes = Files.readAllBytes(fileUploadRequest.filePath());

        // Encode PDF content to base64 for transmission
        String documentEncoded = Base64.getEncoder().encodeToString(fileBytes);

        log.info("Google Gemini analyzing....");
        long startTime = System.nanoTime();

        LeaseReport result = analyzer.analyze(PdfFile.builder().base64Data(documentEncoded).build());

        long endTime = System.nanoTime();
        log.infof("Google Gemini analyzed in %.2f seconds: %s", (endTime - startTime) / 1_000_000_000.0, result);

        return result;
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

有一个简单的 HTML/JavaScript 前端,允许您上传 PDF 文档并查看结果。在下面的示例中,上传并分析了 3 个不同的租赁文档。

Lease Analyzer Results
图 1. 租赁分析器结果

您可以在 GitHub 上找到完整的示例代码。

结论

本文演示了如何利用 LangChain4j 和 AI 自动从 PDF 文档中提取结构化元数据。通过实施此解决方案,我们的客户将显著减少手动文档处理时间,每年可能节省数千个工时。LangChain4j 和 Google Gemini AI 的结合证明是自动化文档分析工作流程的强大方法。