LangChain4j RAG检索增强生成

未完待续

关于知识库以及RAG、向量相似度和向量数据库有关前置知识,已经在下面文章提到:

1.LangChain4j RAG概述

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

  • 索引 Indexing

    将文档切分为片段,并嵌入转换为向量,将文档片段和对应向量成对一并保存到向量数据库。

  • 检索 Retrieval

    当用户提交一个应该使用索引文档回答的问题时,将用户提问内容进行嵌入转换为向量,在将转换成的向量在向量数据库中检索出相似的片段,连同提示词一块发送给大模型。

LangChain4j为这两个阶段提供了工具,主要包括以下几种概念和对应API

  • 文档 Document

    一个文件,office,txt,pdf等等

  • 文档加载器 Document Loader

    从本地或网络加载文档数据的工具

  • 文档解析器 Document Parser

    用于将文档转换为纯文本数据的工具

  • 文档分割器 Document Splitter

    用于将文档转换为的纯文本进行分割的工具,按照行,段落等

  • 向量模型 Embedding Model

  • 向量数据库操作对象 Embedding Store

LangChain4j RAG简单实现

这里先用一个简单实现介绍LangChain4j RAG的大致用法

要实现一个最简单的langchain4j rag功能,需要导入langchain4j-easy-rag依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.5.4</version>
    <relativePath/>
</parent>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-bom</artifactId>
            <version>1.8.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>

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

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

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

    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-reactor</artifactId>
    </dependency>

    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-easy-rag</artifactId>
    </dependency>


</dependencies>

<repositories>
    <repository>
        <name>Central Portal Snapshots</name>
        <id>central-portal-snapshots</id>
        <url>https://central.sonatype.com/repository/maven-snapshots/</url>
        <releases>
            <enabled>false</enabled>
        </releases>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
</repositories>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>21</source>
                <target>21</target>
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
    </plugins>
</build>

在之前配置的bean的基础上,新增一个EmbeddingStore<TextSegment>类,并设置为@Primary替换spring的自动装配,创建bean时会将一段普通文本文件使用自带的向量模型进行嵌入,再将数据保存到内存向量数据库InMemoryEmbeddingStore中。

新增ContentRetriever用于在向量数据库中进行检索。

package org.example.config;

import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.ClassPathDocumentLoader;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import dev.langchain4j.store.memory.chat.InMemoryChatMemoryStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import java.util.List;

@Configuration
public class RagConfig {

    @Bean
    public StreamingChatModel streamingChatModel() {

        return OpenAiStreamingChatModel.builder()
                .baseUrl("https://api.deepseek.com/")
                .apiKey(System.getProperty("OPEN_API_KEY"))
                .modelName("deepseek-reasoner")
                .logRequests(true)
                .logResponses(true)
                .returnThinking(true)
                .build();
    }

    @Bean
    public ChatMemoryStore chatMemoryStore() {
        return new InMemoryChatMemoryStore();
    }


    @Bean
    public ChatMemoryProvider chatMemoryProvider () {
        return new ChatMemoryProvider() {
            @Override
            public ChatMemory get(Object id) {
                return MessageWindowChatMemory.builder()
                        .id(id)
                        .maxMessages(1000)
                        .chatMemoryStore( chatMemoryStore() )
                        .build();
            }
        };
    }



    @Bean
    @Primary
    public EmbeddingStore<TextSegment> embeddingStore() {
        List<Document> documents = ClassPathDocumentLoader.loadDocuments("document");

        InMemoryEmbeddingStore embeddingStore = new InMemoryEmbeddingStore();

        EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
                .embeddingStore(embeddingStore)
                .build();

        ingestor.ingest(documents);

        return embeddingStore;

    }


    @Bean
    public ContentRetriever contentRetriever(EmbeddingStore embeddingStore) {
        return EmbeddingStoreContentRetriever.builder()
                .embeddingStore(embeddingStore)
                .maxResults(7) //返回片段数
                .minScore(0.5) //最小余弦相似度
                .build();
    }

}

@AiService注解上新增属性值contentRetriever = "contentRetriever",使chat功能具备RAG能力

@AiService(
        wiringMode = AiServiceWiringMode.EXPLICIT,
        streamingChatModel = "streamingChatModel",
        chatMemoryProvider = "chatMemoryProvider",
        contentRetriever = "contentRetriever"
)
public interface RagAssistance {
    Flux<String> chat(@UserMessage String prompt, @MemoryId String msgId);
}

将一段最近发生的热点信息作为测试文字放入文本文件,并保存在src\main\resources\document\1.txt

户晨风,男,汉族,1998年10月11日出生,成都卖有回音文化传媒有限公司法定代表人, [7]网络视频博主,泛娱乐领域自媒体创作者,截至2025年9月,哔哩哔哩粉丝数达67.4万,抖音粉丝数达93.7万 [1-2],微博粉丝数达20万。2025年9月16日,话题 “户晨风疑似被封” 登上微博热搜,其微博、抖音、B站等多个社交平台的账号遭到封禁或功能限制。
户晨风成长于一个贫穷农村家庭,后随父母搬到城里。2023年11月27日,他发布了首个作品《100元人民币,在泰国首都曼谷的购买力到底有多强?》,从此开始进行自媒体创作。2024年6月21日,发布作品《新加坡街边卖艺,84 岁老人的一生》。2025年9月7日,发布作品《呼和浩特,真假乞讨?——户晨风全球真假乞讨系列》。
2025年,户晨风以“苹果安卓”为标签代指消费、学历上的鄙视链等言论引发巨大争议 [5]。9月16日,其微博、抖音、B站等多个社交平台的账号遭到封禁或功能限制 [3]。9月20日下午,有网友报料,网红“户晨风”在抖音、微博等多个平台账号已被封禁,其账号内容被清空 [5]。9月30日晚,极目新闻记者搜索发现,户晨风全网账号被彻底封禁,且无法通过搜索找到账号,账号主页已无法查看信息 [6]。11月5日,户晨风账号被封详情披露,从展示跨国消费差异到制造阶层对立,户晨风以“苹果、安卓论”收割流量,最终突破监管红线 [9]。
2025年12月消息,网络账号“户晨风”在多个平台长期编造所谓“安卓人”“苹果人”等煽动群体对立言论,各平台相关账号已关闭。

调用RagAssistance的方法,可以看到会在发送提示词给大模型前检索向量数据库查询相似片段进行拼接

2025-12-14T15:06:25.110+08:00  INFO 14848 --- [nio-8080-exec-1] d.l.http.client.log.LoggingHttpClient    : HTTP request:
- method: POST
- url: https://api.deepseek.com/chat/completions
- headers: [Authorization: Beare...07], [User-Agent: langchain4j-openai], [Content-Type: application/json]
- body: {
  "model" : "deepseek-reasoner",
  "messages" : [ {
    "role" : "user",
    "content" : "你知道安卓苹果相对论吗吗\n\nAnswer using the following information:\n2025年12月消息,网络账号“户晨风”在多个平台长期编造所谓“安卓人”“苹果人”等煽动群体对立言论,各平台相关账号已关闭。\n\n2025年,户晨风以“苹果安卓”为标签代指消费、学历上的鄙视链等言论引发巨大争议 [5]。9月16日,其微博、抖音、B站等多个社交平台的账号遭到封禁或功能限制 [3]。9月20日下午,有网友报料,网红“户晨风”在抖音、微博等多个平台账号已被封禁,其账号内容被清空 [5]。9月30日晚,极目新闻记者搜索发现,户晨风全网账号被彻底封禁,且无法通过搜索找到账号,账号主页已无法查看信息 [6]。11月5日,户晨风账号被封详情披露,从展示跨国消费差异到制造阶层对立,户晨风以“苹果、安卓论”收割流量,最终突破监管红线 [9]。\n\n户晨风,男,汉族,1998年10月11日出生,成都卖有回音文化传媒有限公司法定代表人, [7]网络视频博主,泛娱乐领域自媒体创作者,截至2025年9月,哔哩哔哩粉丝数达67.4万,抖音粉丝数达93.7万 [1-2],微博粉丝数达20万。2025年9月16日,话题 “户晨风疑似被封” 登上微博热搜,其微博、抖音、B站等多个社交平台的账号遭到封禁或功能限制。\n\n户晨风成长于一个贫穷农村家庭,后随父母搬到城里。2023年11月27日,他发布了首个作品《100元人民币,在泰国首都曼谷的购买力到底有多强?》,从此开始进行自媒体创作。2024年6月21日,发布作品《新加坡街边卖艺,84 岁老人的一生》。2025年9月7日,发布作品《呼和浩特,真假乞讨?——户晨风全球真假乞讨系列》。"
  } ],
  "stream" : true,
  "stream_options" : {
    "include_usage" : true
  }
}

一个简单的RAG实现就完成了,但是实际开发过程中往往需要采用真实的向量数据库永久保存数据,而且数据存储是异步的,实时的,需要嵌入的文档也是多种多样的,因此langchain4j提供了很多文档解析支持和向量数据库支持。


"如果文章对您有帮助,可以请作者喝杯咖啡吗?"

微信二维码

微信支付

支付宝二维码

支付宝


LangChain4j RAG检索增强生成
https://blog.liuzijian.com/post/langchain4j/2025/12/01/langchain4j-rag/
作者
Liu Zijian
发布于
2025年12月1日
许可协议