首页 / AI应用开发 / 多模态 RAG 系统实战:使用 LangChain4j 构建支持图文混合检索的企业级知识库 9 次阅读
多模态 RAG 系统实战:使用 LangChain4j 构建支持图文混合检索的企业级知识库
AI 应用开发

多模态 RAG 系统实战:使用 LangChain4j 构建支持图文混合检索的企业级知识库

融合文本与图像理解的智能检索系统,让企业知识库支持跨模态语义搜索

2026 年 3 月 21 日 · 约 12 分钟阅读

痛点:企业知识库的"多模态鸿沟"

想象这个场景:产品团队想查找去年某次评审会上的设计图,销售需要定位合同扫描件中的特定条款,客服想检索培训视频里的问题处理片段——但传统关键词搜索对这些非结构化多模态数据束手无策。

根据 2026 年 McKinsey 调研,71% 的企业已在至少一个业务功能中采用生成式 AI,但只有 23% 实现了多模态数据的统一检索。RAG(检索增强生成)市场预计到 2030 年将达到 110 亿美元,年复合增长率 49.1%,而多模态 RAG正是下一代企业知识基础设施的核心。

本教程将带你使用 LangChain4j(Java 生态的 LangChain)构建一个生产级多模态 RAG 系统,支持:

  • 📄 PDF 文档文本提取与向量化
  • 🖼️ 图像内容理解与描述生成
  • 🔍 文本 + 图像的混合语义检索
  • 💬 基于检索结果的自然语言回答

核心架构:多模态 RAG 系统拆解

多模态 RAG 系统由两大核心模块组成:

🔎 检索模块
对企业内文本、图片、PDF 等多模态数据进行索引和向量化,通过语义搜索快速定位相关内容
✍️ 生成模块
基于检索结果,利用大语言模型(如 Google Gemini)生成符合业务上下文的自然语言回答
🖼️ 图像理解
使用视觉语言模型(VLM)为图像生成描述文本,实现图像内容的向量化表示
📊 向量存储
使用 PostgreSQL + PgVector 扩展存储向量数据,支持高效相似度检索

准备工作:环境搭建与依赖配置

开始之前,确保你的开发环境满足以下要求:

☕ JDK 17+
LangChain4j 基于 Java 17 构建,推荐使用 Amazon Corretto 17
📦 Maven 3.8+
项目构建与依赖管理工具
🐳 Docker
用于容器化部署 PostgreSQL + PgVector 向量数据库
🔑 API Keys
GEMINI_API_KEY(Google AI)和 HF_API_KEY(Hugging Face)

创建 Maven 项目,pom.xml 核心依赖如下:

<?xml version="1.0" encoding="UTF-8"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>multimodal-rag</artifactId>
    <version>1.0.0</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0</version>
    </parent>

    <dependencies>
        <!-- LangChain4j 核心库 -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j</artifactId>
            <version>0.35.0</version>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-google-ai-gemini</artifactId>
            <version>0.35.0</version>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-pgvector</artifactId>
            <version>0.35.0</version>
        </dependency>

        <!-- PDF 解析与图像处理 -->
        <dependency>
            <groupId>org.apache.pdfbox</groupId>
            <artifactId>pdfbox</artifactId>
            <version>3.0.2</version>
        </dependency>

        <!-- Spring Boot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
</project>
多模态 RAG 系统架构图:展示数据输入层(PDF/图片/文本)→ 处理层(文本提取/图像描述)→ 向量化层 → 存储层(PgVector)→ 检索层 → 生成层(LLM)的完整流程

架构图展示了数据从输入到最终回答的完整流转过程,每个模块都可通过 LangChain4j 的组件接口灵活替换。

实战步骤:从零构建多模态 RAG 系统

1

配置向量数据库

使用 Docker Compose 快速部署 PostgreSQL + PgVector:

# docker-compose.yml
version: '3.8'
services:
  postgres:
    image: pgvector/pgvector:pg16
    container_name: rag-vector-db
    environment:
      POSTGRES_USER: raguser
      POSTGRES_PASSWORD: ragpass123
      POSTGRES_DB: ragdb
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data
volumes:
  pgdata:

启动服务:

docker-compose up -d
2

初始化 Embedding Store

创建 LangChain4j 的 PgVector 连接配置:

@Configuration
public class VectorStoreConfig {

    @Bean
    public EmbeddingStore embeddingStore() {
        return PgVectorEmbeddingStore.builder()
            .host("localhost")
            .port(5432)
            .database("ragdb")
            .user("raguser")
            .password("ragpass123")
            .table("embeddings")
            .dimension(768)  // BGE-M3 模型维度
            .build();
    }
}
3

实现 PDF 解析与文本提取

使用 Apache PDFBox 提取 PDF 内容:

public class PdfProcessor {

    public List<TextSegment> processPdf(Path pdfPath) throws IOException {
        try (PDDocument document = PDDocument.load(pdfPath.toFile())) {
            PDFTextStripper stripper = new PDFTextStripper();
            String fullText = stripper.getText(document);

            // 按段落分割并进行语义分块
            return chunkBySemantics(fullText);
        }
    }

    private List<TextSegment> chunkBySemantics(String text) {
        // 使用 LangChain4j 的分块策略
        DocumentSplitter splitter = DocumentSplitters.recursive(300, 30);
        List<TextSegment> chunks = splitter.split(text);

        // 为每个 chunk 添加元数据
        return chunks.stream()
            .map(segment -> TextSegment.from(segment.text(),
                Metadata.from("source", "pdf")))
            .collect(Collectors.toList());
    }
}
4

实现图像描述生成

使用 Google Gemini Vision 为图像生成描述:

public class ImageProcessor {

    private final VisionModel visionModel;

    public ImageProcessor(String geminiApiKey) {
        this.visionModel = GeminiVisionModel.builder()
            .apiKey(geminiApiKey)
            .modelName("gemini-2.0-flash")
            .build();
    }

    public String generateImageDescription(Path imagePath) throws IOException {
        byte[] imageBytes = Files.readAllBytes(imagePath);

        String prompt = """
            请用中文详细描述这张图片的内容,包括:
            1. 主要物体和场景
            2. 颜色、布局等视觉特征
            3. 图片可能表达的信息或用途
            限制在 200 字以内。
            """;

        VisionContentRequest request = VisionContentRequest.builder()
            .addUserMessage(prompt)
            .addImage(imageBytes)
            .build();

        return visionModel.generate(request);
    }
}
5
图像描述生成流程图:原始图片 → Gemini Vision 模型 → 描述文本 → 嵌入向量 → 向量数据库
6

构建向量化流水线

将文本和图像描述转换为向量并存入数据库:

@Service
public class IngestionService {

    private final EmbeddingModel embeddingModel;
    private final EmbeddingStore embeddingStore;
    private final PdfProcessor pdfProcessor;
    private final ImageProcessor imageProcessor;

    // 使用 Hugging Face BGE-M3 模型
    public IngestionService() {
        this.embeddingModel = new HuggingFaceEmbeddingModel("BAAI/bge-m3");
    }

    public void ingestDocument(Path docPath) {
        List<TextSegment> segments = new ArrayList<>();

        // 处理 PDF
        if (docPath.toString().endsWith(".pdf")) {
            segments.addAll(pdfProcessor.processPdf(docPath));
        }

        // 处理图片
        if (isImage(docPath)) {
            String description = imageProcessor.generateImageDescription(docPath);
            segments.add(TextSegment.from(description,
                Metadata.from("type", "image").add("filename", docPath.getFileName())));
        }

        // 生成向量并存入
        for (TextSegment segment : segments) {
            Embedding embedding = embeddingModel.embed(segment);
            embeddingStore.add(embedding, segment);
        }
    }
}
7

实现检索 - 生成流水线

创建 RAG 查询服务:

@Service
public class RagQueryService {

    private final EmbeddingStore embeddingStore;
    private final ChatLanguageModel chatModel;
    private final EmbeddingModel embeddingModel;

    public String query(String question) {
        // 1. 将查询向量化
        Embedding queryEmbedding = embeddingModel.embed(question);

        // 2. 检索最相关的文档片段
        List<EmbeddingMatch> matches = embeddingStore.findRelevant(
            queryEmbedding,
            5  // 返回 top 5 结果
        );

        // 3. 构建增强 prompt
        String context = buildContext(matches);
        String augmentedPrompt = buildRagPrompt(question, context);

        // 4. 生成回答
        return chatModel.generate(augmentedPrompt);
    }

    private String buildContext(List<EmbeddingMatch> matches) {
        return matches.stream()
            .map(match -> match.embedded().text())
            .collect(Collectors.joining("\n\n---\n\n"));
    }

    private String buildRagPrompt(String question, String context) {
        return """
            你是一个企业知识库助手。请基于以下上下文信息回答问题:

            【上下文】
            %s

            【问题】
            %s

            如果上下文中没有相关信息,请如实告知。
            """.formatted(context, question);
    }
}
8

提供 RESTful API

基于 Spring Boot 暴露查询接口:

@RestController
@RequestMapping("/api/rag")
public class RagController {

    @Autowired
    private RagQueryService ragQueryService;

    @Autowired
    private IngestionService ingestionService;

    @PostMapping("/query")
    public ResponseEntity<QueryResponse> query(@RequestBody QueryRequest request) {
        String answer = ragQueryService.query(request.question());
        return ResponseEntity.ok(new QueryResponse(answer));
    }

    @PostMapping("/ingest")
    public ResponseEntity<?> ingest(@RequestParam("file") MultipartFile file)
            throws IOException {
        Path tempFile = Files.createTempFile("upload", file.getOriginalFilename());
        file.transferTo(tempFile);

        ingestionService.ingestDocument(tempFile);
        Files.delete(tempFile);

        return ResponseEntity.ok(Map.of("status", "success"));
    }
}
RAG 查询时序图:用户提问 → 向量化 → 相似度检索 → 构建增强 Prompt → LLM 生成 → 返回答案

进阶技巧:提升检索质量的最佳实践

混合检索策略

单一向量检索可能遗漏关键词精确匹配的结果。结合 BM25(关键词)+ 向量(语义)的混合检索能显著提升召回率:

public List<EmbeddingMatch> hybridSearch(String query, int k) {
    // 向量检索
    Embedding queryEmbedding = embeddingModel.embed(query);
    List<EmbeddingMatch> vectorResults = embeddingStore.findRelevant(queryEmbedding, k);

    // BM25 关键词检索(使用 PostgreSQL 全文搜索)
    List<EmbeddingMatch> bm25Results = bm25Search(query, k);

    // 合并并去重,按分数加权排序
    return mergeResults(vectorResults, bm25Results, 0.7, 0.3);
}

查询重写与扩展

用户查询往往简短模糊,使用 LLM 进行查询重写能提升检索精度:

public String rewriteQuery(String originalQuery) {
    return chatModel.generate("""
        请将以下用户查询重写为更适合检索的形式:
        - 扩展同义词和相关概念
        - 明确隐含的时间、实体等信息
        - 保持简洁,不超过 50 字

        原始查询:%s
        重写后:
        """.formatted(originalQuery));
}

重排序(Re-ranking)

检索后使用 Cross-Encoder 模型对结果重排序,能进一步提升相关性:

// 使用 BGE Re-Ranker 对初步检索结果重排序
public List<EmbeddingMatch> rerank(String query, List<EmbeddingMatch> candidates) {
    List<ScoredResult> scored = reranker.score(query, candidates);
    return scored.stream()
        .sorted(Comparator.comparingDouble(ScoredResult::score).reversed())
        .map(ScoredResult::toEmbeddingMatch)
        .limit(3)  // 只取 top 3 给 LLM,节省 token
        .collect(Collectors.toList());
}
混合检索 + 重排序流程图:用户查询 → 查询重写 → 向量检索 + BM25 检索 → 结果合并 → Re-ranker 重排序 → Top N 结果 → LLM 生成

常见问题 FAQ

Q: 如何处理大文件的内存溢出问题?
A: 采用流式处理 + 分页策略:PDF 解析时使用 PDFBox 的增量加载,向量生成时批量处理(每批 32 个 chunk),数据库插入使用批量事务。对于超大文件(>100MB),建议先拆分再处理。
Q: 图像描述的质量如何保证?
A: 使用 Prompt Engineering 技巧:1)明确描述维度(主体、场景、文字、颜色等);2)限制字数避免冗长;3)针对特定业务场景定制 prompt。建议使用 Gemini 2.0 Flash 或 GPT-4V 等最新多模态模型。
Q: 如何实现细粒度权限控制?
A: 在向量元数据中添加权限标签(如departmentsecurity_level),查询时根据用户身份过滤。PgVector 支持在 SQL 层面添加 WHERE 条件实现权限过滤。
Q: 检索结果不准确怎么办?
A: 系统排查链路:1)检查 embedding 模型是否适合业务领域(可考虑微调);2)调整 chunk 大小(200-500 字为宜);3)引入查询重写;4)添加重排序步骤;5)收集用户反馈建立评估集。

总结:构建企业级多模态 RAG 的关键要点

  • ✓ 多模态 RAG 融合文本、图像、PDF 等多种数据源,突破传统搜索的局限
  • ✓ LangChain4j 为 Java 开发者提供与 LangChain 对等的 AI 应用开发能力
  • ✓ PgVector 作为开源向量数据库,支持高效相似度检索和权限过滤
  • ✓ 混合检索(向量 +BM25)+ 重排序策略显著提升检索质量
  • ✓ 生产环境需关注分块策略、缓存机制、评估体系等工程细节

本教程展示了从零构建多模态 RAG 系统的完整流程。实际生产中,还需根据具体业务场景调整模型选型、优化检索策略、建立质量评估体系,让 AI 真正成为企业知识的"智能入口"。

选择栏目
今日简报 播客电台 实战教程 AI挣钱计划 关于我
栏目
全球AI日报国内AI日报全球金融日报国内金融日报全球大新闻日报国内大新闻日报Claude Code 玩法日报OpenClaw 动态日报GitHub 热门项目日报AI工具实战AI应用开发编程实战工作流自动化AI原理图解AI Agent开发AI变现案例库AI工具创收AI内容变现AI接单提效变现前沿研究
我的收藏