LangChain4j RAG 实战完整指南:从入门到踩坑

🚀 LangChain4j RAG 实战完整指南:从入门到踩坑

RAG = 检索增强生成,让大模型告别幻觉,精准回答你的文档内容!


📚 什么是 RAG?

简单来说,RAG 是一种在发送给大模型(LLM)之前,从你的数据中找到并注入相关信息片段到提示中的方法。

这样 LLM 将获得相关信息,并能够使用这些信息回复------这应该会降低产生幻觉的概率

相关信息片段可以使用各种信息检索方法找到,最流行的方法有三种:

1️⃣ 全文(关键词)搜索

使用 TF-IDF 和 BM25 等技术,通过匹配查询中的关键词与文档数据库进行搜索。

根据每个文档中这些关键词的频率和相关性对结果进行排名。

2️⃣ 向量搜索(语义搜索)

文本文档使用嵌入模型转换为数字向量。

然后根据查询向量和文档向量之间的余弦相似度或其他相似度度量找到并排序文档,从而捕捉更深层次的语义含义。

3️⃣ 混合搜索

结合多种搜索方法(例如,全文 + 向量)通常可以提高搜索的有效性。


🔄 RAG 的两个核心阶段

RAG 过程分为两个不同的阶段:索引检索

LangChain4j 为这两个阶段提供了完整的工具支持。

📥 阶段一:索引(Indexing)

在索引阶段,文档会被预处理,以便在检索阶段进行高效搜索。

对于向量搜索,这通常涉及:

  • 清理文档
  • 用额外数据和元数据丰富文档
  • 将文档分割成更小的片段(也称为分块
  • 嵌入这些片段
  • 最后将它们存储在嵌入存储(向量数据库)中

💡 提示 :索引阶段通常是离线进行 的,最终用户不需要等待其完成。

例如:可以通过定时任务在周末每周重新索引一次公司内部文档。

但在某些情况下,最终用户可能希望上传自己的自定义文档,使 LLM 能够访问这些文档。

在这种情况下,索引应该在线进行,并成为主应用程序的一部分。

索引阶段流程图:


🔍 阶段二:检索(Retrieval)

检索阶段通常在线进行,当用户提交一个应该使用索引文档回答的问题时。

对于向量搜索,这通常涉及:

  1. 嵌入用户的查询(问题)
  2. 在嵌入存储中执行相似度搜索
  3. 将相关片段(原始文档的片段)注入到提示中
  4. 发送给 LLM 生成回答

检索阶段流程图:


💻 RAG 实战案例:Java 8 文档问答

📄 步骤一:准备资料

首先准备一份 PDF 文档作为知识来源------《Java 8 从入门到精通》。

我们的目标是:投喂这个 PDF,让大模型回答文档中的问题


🏗️ 步骤二:创建项目结构

创建标准的 Spring Boot 项目结构:

pom.xml 依赖配置

核心依赖包括:

  • Spring Boot Web
  • LangChain4j 核心库
  • 阿里百炼平台(DashScope)集成
  • Qdrant 向量数据库
  • Easy RAG 模式
  • AllMiniLmL6V2 本地嵌入模型
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.whc</groupId>
        <artifactId>langChain4j-whc</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>langchain4j-whc-rag</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- LangChain4j 核心 -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-open-ai</artifactId>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j</artifactId>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-reactor</artifactId>
        </dependency>

        <!-- 阿里百炼平台 -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId>
        </dependency>

        <!-- 工具库 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.22</version>
        </dependency>

        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-spring-boot-starter</artifactId>
        </dependency>

        <!-- Qdrant 向量数据库 -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-qdrant</artifactId>
            <version>1.5.0-beta11</version>
        </dependency>

        <!-- Easy RAG 模式 -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-easy-rag</artifactId>
        </dependency>
        
        <!-- AllMiniLmL6V2 本地嵌入模型 -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-embeddings-all-minilm-l6-v2</artifactId>
            <version>1.5.0-beta11</version>
        </dependency>
        
    </dependencies>

</project>

📂 注意 :将 PDF 文件放到 resources/doc 目录下。


⚙️ 步骤三:YAML 配置

配置服务端口、DashScope API 和 Qdrant 连接信息:

yaml 复制代码
server:
  port: 9009
  servlet:
    encoding:
      enabled: true
      force: true
      charset: UTF-8

spring:
  application:
    name: langchain_whc_rag

ai:
  dashScope:
    apiKey: ${AI_DASHSCOPE_API_KEY}
    modelName: ${AI_DASHSCOPE_MODEL_NAME}
    baseUrl: ${AI_DASHSCOPE_BASE_URL}

qdrant:
  host: ${QDRANT_HOST}
  port: ${QDRANT_PORT}
  collectionName: ${QDRANT_COLLECTION_NAME}

🔧 步骤四:大模型配置类

这是最核心的配置部分,需要配置三个关键 Bean:

  1. 向量存储实例(Qdrant)
  2. 嵌入模型实例(本地 AllMiniLmL6V2)
  3. 聊天模型实例(DashScope)
  4. 内容检索器实例(重点!)
java 复制代码
@Slf4j
@Getter
@Configuration
public class LlmConfig {

    @Value("${ai.dashScope.apiKey}")
    private String dashScopeApiKey;

    @Value("${ai.dashScope.modelName}")
    private String dashScopeModelName;

    @Value("${ai.dashScope.baseUrl}")
    private String dashScopeBaseUrl;

    @Value("${qdrant.host}")
    private String qdrantHost;

    @Value("${qdrant.port}")
    private int qdrantPort;

    @Value("${qdrant.collectionName}")
    private String qdrantCollectionName;

    /**
     * 创建向量存储实例
     * 使用 Qdrant 作为向量数据库
     */
    @Bean
    public EmbeddingStore<TextSegment> embeddingStore() {
        return QdrantEmbeddingStore.builder()
                .host(qdrantHost)
                .port(qdrantPort)
                .collectionName(qdrantCollectionName)
                .build();
    }

    /**
     * 创建嵌入模型实例
     * 使用本地的 AllMiniLmL6V2 嵌入模型,避免 DashScope 兼容性问题
     */
    @Bean
    public EmbeddingModel embeddingModel() {
        return new dev.langchain4j.model.embedding.onnx.allminilml6v2.AllMiniLmL6V2EmbeddingModel();
    }

    /**
     * 创建聊天模型实例
     * 使用 OpenAI 兼容的聊天模型,通过 DashScope 提供的 API 服务进行对话
     */
    @Bean
    public ChatModel chatModel() {
        return OpenAiChatModel.builder()
                .apiKey(dashScopeApiKey)
                .modelName(dashScopeModelName)
                .baseUrl(dashScopeBaseUrl)
                .logRequests(true)
                .build();
    }

    /**
     * 创建内容检索器实例
     * ⚠️ 重点:必须明确指定嵌入模型,避免冲突!
     */
    @Bean
    public ContentRetriever contentRetriever(EmbeddingStore<TextSegment> embeddingStore, EmbeddingModel embeddingModel) {
        return EmbeddingStoreContentRetriever.builder()
                .embeddingStore(embeddingStore)
                .embeddingModel(embeddingModel)
                .build();
    }
}

🤖 步骤五:定义 AI 服务接口

使用 @AiService 注解,显式指定要使用的模型和检索器:

java 复制代码
@AiService(wiringMode = AiServiceWiringMode.EXPLICIT,
        chatModel = "chatModel",
        contentRetriever = "contentRetriever")
public interface RagAssist {

    /**
     * 聊天接口
     * @param message 用户问题
     * @return AI 回答
     */
    String chat(String message);
}

🌐 步骤六:Controller 实现

实现 REST API,完成文档解析、嵌入存储和问答的完整流程:

java 复制代码
@RestController
@RequiredArgsConstructor
public class RagController {

    private final EmbeddingModel embeddingModel;
    private final EmbeddingStore<TextSegment> embeddingStore;
    private final RagAssist ragAssist;

    @GetMapping(value = "/rag/chat")
    public String java8(@RequestParam String message) throws IOException {

        // 1. 读取并解析 PDF 文档
        ClassPathResource resource = new ClassPathResource("doc/java8从入门到精通.pdf");
        InputStream inputStream = resource.getInputStream();
        Document document = new ApacheTikaDocumentParser().parse(inputStream);

        // 2. 创建文档嵌入存储处理器
        EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
                .documentSplitter(DocumentSplitters.recursive(1000, 0))
                .embeddingStore(embeddingStore)
                .embeddingModel(embeddingModel)
                .build();
        
        // 3. 将文档处理并存储到向量数据库
        ingestor.ingest(document);

        // 4. 使用 RAG 助手回答问题
        String result = ragAssist.chat(message);
        System.out.println(result);
        
        return result;
    }
}

✅ 测试效果验证

测试一:询问第九章内容

文档第九章是关于「本地时间和时间戳」的内容:

接口调用:

复制代码
GET http://localhost:9009/rag/chat?message=第九章主要讲了啥

AI 回答:

第九章主要讲述了本地时间和时间戳的相关概念。内容包括如何在程序中处理不同地区的本地时间,时区的转换,以及时间戳(Timestamp)的定义和使用方法。本章还介绍了如何将时间戳与本地时间进行相互转换,帮助开发者更好地理解和操作时间数据,确保在跨时区应用中的时间一致性与准确性。

🎉 完美!达到预期效果!


测试二:询问 Stream 创建方法

接口调用:

复制代码
GET http://localhost:9009/rag/chat?message=如何创建Stream

调用结果:

很显然,AI 回答参考了文档中的内容,并明确说明了对应的章节来源。

再次验证成功!


📊 向量数据库存储效果

文档被分割成了一个又一个的 text_segment 存储在 Qdrant 中:


⚠️ 踩坑记录与解决方案

坑一:多个嵌入模型冲突

问题:

配置内容检索器时,如果不明确指定 model,会报错:

复制代码
Conflict: multiple embedding models have been found in the classpath

解决方案:

EmbeddingStoreContentRetriever必须显式指定要使用的嵌入模型:

java 复制代码
@Bean
public ContentRetriever contentRetriever(EmbeddingStore<TextSegment> embeddingStore, EmbeddingModel embeddingModel) {
    return EmbeddingStoreContentRetriever.builder()
            .embeddingStore(embeddingStore)
            .embeddingModel(embeddingModel)  // ✅ 关键:明确指定模型
            .build();
}

坑二:DashScope 兼容性问题

问题:

尝试使用百炼平台的向量模型(TEXT-EMBEDDING-V4)时,报 OpenAI 不兼容异常。

解决方案:

改用本地的 AllMiniLmL6V2EmbeddingModel 嵌入模型,完美避开兼容性问题:

java 复制代码
@Bean
public EmbeddingModel embeddingModel() {
    // ✅ 使用本地嵌入模型,避免兼容性问题
    return new dev.langchain4j.model.embedding.onnx.allminilml6v2.AllMiniLmL6V2EmbeddingModel();
}

Vibecoding开端

LangChain4j RAG 实战完整指南:从入门到踩坑

一文讲透向量 Embedding:从数学概念到 LangChain4j + Qdrant 实战

Qdrant 向量数据库安装与入门指南

langchain4j 工具调用:让大模型从"会说"走向"会做"

LangChain4j 提示词完全指南:SystemMessage、UserMessage 与 PromptTemplate

LangChain4j 实战:ChatMemory 聊天记忆完全指南

手把手教你用 LangChain4j 集成图片理解与图片生成

LangChain4j 流式模式实战指南

LangChain4j 与 Spring Boot集成指南

相关推荐
yaoxin5211231 小时前
476. Java 反射 - 调用方法
java·开发语言
Ivanqhz2 小时前
Rust Lazy浅析
java·javascript·rust
bksczm2 小时前
Linux之日志和线程池、内存池
java·开发语言
笨蛋不要掉眼泪2 小时前
Java虚拟机:对象复活、引用强度与Stop-The-World
java·开发语言·jvm
uncle_ll2 小时前
LangGraph 深度解析:用图结构构建下一代智能代理与多智能体系统
langchain·llm·agent·graph·langgraph
凤山老林3 小时前
SpringBoot实战:构建优雅的全局异常处理机制
java·springboot·异常处理
小Ti客栈3 小时前
Spring Boot 整合 Swagger2 和 Knife4j实现接口文档与可视化调试
java·spring boot·后端
梅头脑3 小时前
ThreadLocalMap里几十万个死Entry——排查了半天OOM,根因就一行finally没写
java
_Jimmy_3 小时前
Tool Calling 与 Function Calling 区别
人工智能·python·langchain