惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 三生石上(FineUI控件)
雷峰网
雷峰网
爱范儿
爱范儿
P
Proofpoint News Feed
Security Archives - TechRepublic
Security Archives - TechRepublic
Latest news
Latest news
The Hacker News
The Hacker News
Cyberwarzone
Cyberwarzone
博客园 - 【当耐特】
Project Zero
Project Zero
小众软件
小众软件
T
Tailwind CSS Blog
量子位
博客园 - 聂微东
I
Intezer
美团技术团队
S
SegmentFault 最新的问题
T
Tor Project blog
Spread Privacy
Spread Privacy
V
Vulnerabilities – Threatpost
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Jina AI
Jina AI
罗磊的独立博客
B
Blog RSS Feed
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
T
Troy Hunt's Blog
有赞技术团队
有赞技术团队
Google DeepMind News
Google DeepMind News
宝玉的分享
宝玉的分享
C
Cisco Blogs
L
LINUX DO - 热门话题
Last Week in AI
Last Week in AI
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
AI
AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Microsoft Azure Blog
Microsoft Azure Blog
L
LINUX DO - 最新话题
Know Your Adversary
Know Your Adversary
GbyAI
GbyAI
Engineering at Meta
Engineering at Meta
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Recent Commits to openclaw:main
Recent Commits to openclaw:main
L
Lohrmann on Cybersecurity
The Register - Security
The Register - Security
L
LangChain Blog
博客园 - 叶小钗
T
Tenable Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC

Liu Zijian's Blog | 一个技术博客

使用Certbot自动续签HTTPS证书 使用Filebeat采集Nginx日志到ES Python的协程 Python中的异常 Python中的类和对象 Python的函数 Python的数据结构,推导式、迭代器和生成器 Spring AI集成多模态模型 LangChain4j多模态 LangChain Tools工具使用 Python中的模块和包 Python全局环境和虚拟环境(venv) LangChain Prompt提示词工程 LangChain4j Tools工具使用 基于Dify搭建AI智能体应用 Spring AI实现MCP Server Spring AI集成MCP Client LangChain4j Prompt提示词工程 Spring AI使用知识库增强对话功能 Spring AI实现一个智能客服 Spring AI实现一个简单的对话机器人 实现MinIO数据的每日备份 自己实现一个DNS服务 简单理解AI智能体 大模型和大模型应用 LangChain开篇 LangChain4j开篇 一个解析Excel2007的POI工具类 DataPermissionInterceptor源码解读 TenantLineInnerInterceptor源码解读 BaseMultiTableInnerInterceptor源码解读 Spring AI开篇 SQL解析工具JSQLParser 芋道源码解读之多租户 芋道源码解读之数据权限 芋道源码解读开篇 Java实现将数据导出为Word文档 OA系统的天数该怎样计算 安装MySQL8 安装MySQL5.7 RockyLinux9环境下编译MySQL8 MySQL字符集及底层原理 Java实现LDAP登录 Docker Compose IPv4和IPv6 使用虚拟机安装一个K8s集群 使用GraalVM原生编译打包SpringBoot工程 Nginx防止目录穿越 Java线程的状态 Nginx防盗链设置 使用python将excel表格转换为SQL INSERT Redis的公共操作命令 Redis数据结构之Bitfleid Redis数据结构之Bitmap Redis数据结构之GEO Redis数据结构之Hash Redis数据结构之HyperLogLog Redis数据结构之List Redis数据结构之Set Redis数据结构之Stream Redis数据结构之String Redis数据结构之ZSet 使用python压缩图片 利用Python实现Hexo站点的持续集成 Nginx设置HTTPS监听 firewalld防火墙工具的使用 Linux信号(signal)机制 MySQL5.7x 主从复制 用IP自签发一个HTTPS证书 基于Hexo实现一个静态的个人博客 RockyLinux9环境下编译MySQL5.7 Docker离线安装 MySQL数据定义语言 Docker与联合文件系统 Docker的网络 Docker的镜像操作 MySQL存储过程 MyBatis-Plus开篇 MySQL变量 MySQL视图 MySQL事务 MySQL插入修改和删除 MySQL查询 MySQL系统命令 Docker的容器操作 Docker的安装和配置 Docker容器数据卷 浅谈OAuth2.0授权原理 JVM开篇 浅谈Linux(Unix)的I/O模型 一个通用的CloseableHttpClient工厂类 JUC可重入锁ReentrantLock JUC读写锁ReadWriteLock Java的单例 Java泛型 Java8的新特性 最近最少使用算法(LRU) MySQL函数 SpringBoot配置和启动 volatile作用分析
LangChain4j RAG检索增强生成
Liu Zijian · 2025-12-01 · via Liu Zijian's Blog | 一个技术博客

未完待续

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

1.概述

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

  • 索引 Indexing

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

  • 检索 Retrieval

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

2.API详解

LangChain4j为RAG提供的工具,主要包括以下几种概念和对应API

  • 文档 Document 一个文件,office,txt,pdf等等

  • 文档加载器 Document Loader 从本地或网络加载文档

  • 文档解析器 Document Parser 用于将文档(ms office, pdf)转换为纯文本数据的工具

  • 文档分割器 Document Splitter 用于将文档按照行,段落等转换为的纯文本

  • 向量模型 Embedding Model 将文本等数据转换为向量坐标

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

2.1 文档加载器

  • FileSystemDocumentLoader 从本地磁盘绝对路径加载
  • ClassPathDocumentLoader 从工程类路径加载
  • UrlDocumentLoader 从URL加载

2.2 文档解析器

  • TextDocumentParser 默认的解析器

  • ApachePdfBoxDocumentParser pdf解析器

    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-document-parser-apache-pdfbox</artifactId>
    </dependency>
  • ApacheTikaDocumentParser 几乎可以解析所有文档,但是可能解析PDF不专业

    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-document-parser-apache-poi</artifactId>
    </dependency>
  • ApachePoiDocumentParser 解析ms office文档,同样要引入langchain4j-document-parser-apache-poi

2.3 文档分割器

  • DocumentByParagraphSplitter 根据段落分割

  • DocumentByLineSplitter 根据行分割

  • DocumentBySentenceSplitter 根据句子分割

  • DocumentByWordSplitter 根据词分割

  • DocumentByCharacterSplitter 根据固定数量字符分割

  • DocumentByRegexSplitter 按照正则表达式分割

  • DocumentSplitters.recursive() 递归分割(默认分割器,单片段最大300字符)将文本按照段,行,句,词,字的优先级顺序分割


💡关于递归分割器:

将文本按照段,行,句,词,字顺序分割,能尽量多的截取信息。例如,如果限制截取最多300字符,要分割一个每段200字共两段的文档,使用段落分割器,会丢失整个的第二段。但如果使用递归分割器,可以尽可能将信息截取为第一段200字外加第二段的前几个行/句子组成的100字的形式。

如果默认的单片段最大300字符不能满足需求,还可以定制自己的递归分割器

DocumentSplitters.recursive(每片段最多字符, 两片段之间重叠字符个数);

2.4 向量模型

langchain4j中使用EmbeddingModel接口操作向量模型,前面的例子中,文本向量化使用的是EmbeddingStoreIngestor默认的向量模型BgeSmallEnV15QuantizedEmbeddingModel,但是它的功能有限,实际项目中应当替换为其他的向量模型,例如阿里巴巴千问text-embedding-v4,它兼容open-ai的API协议,因此和ChatModel一样进行配置

langchain4j:
  open-ai:
    embedding-model:
      base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
      api-key: ${QWKEY}
      model-name: text-embedding-v4
      log-requests: true
      log-responses: true
      dimensions: 1024

配置类中注入即可

@Resource
private EmbeddingModel embeddingModel;

2.5 向量数据库

langchain4j操作向量数据库使用EmbeddingStore接口,默认会采用一个内存向量数据库,实际项目中数据的持久化存储,需要整合外置向量数据库,比如redis-stack,milvus, Qdrant等

以Qdrant为例,首先要引入Qdrant的依赖

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

用Docker启动一个Qdrant示例

docker run -d -p 6333:6333 -p 6334:6334 qdrant/qdrant

6333端口是浏览器访问控制台用的,6334是grpc协议,用于客户端连接。浏览器打开对应ip的6333端口,访问/dashboard,就可以看见Qdrant的控制台了

在Dashboard的Console中执行命令,创建一个test-qdrant库,这个步骤和ES还是很相似的

创建的向量库的维度size=1024,要和嵌入模型配置dimensions:1024一致,否则保存数据会出错

PUT /collections/test-qdrant
{
    "vectors": {
      "size": 1024,
      "distance": "Cosine"
    }
}

配置类中使用EmbeddingStore接口操作对应向量数据库QdrantEmbeddingStore

@Bean
@Primary
public EmbeddingStore<TextSegment> embeddingStore() {

    return QdrantEmbeddingStore.builder()
            .host("192.168.228.104")
            .port(6334)
            .collectionName("test-qdrant")
            .build();

}

3.实现案例

这里先用一个简单实现介绍LangChain4j RAG的大致用法,采用阿里巴巴text-embedding-v4向量模型和Qdrant向量数据库,使用过程大致步骤是:

  1. 文档经Document Loader加载为Document对象到内存中,通过解析器Document Parser解析为文本,通过分割器Document Splitter进一步转换为片段Text Segment,最终,经向量模型Embedding Model转换为向量Embedding

  2. 当用户询问问题,ContentRetriever会将问题向量化,从向量数据库查询出相关片段夹在提示词中一并发送给大模型。

要实现一个最简单的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>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </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>

application.yml配置基本的日志,嵌入模型等

server:
  port: 8080

logging:
  level:
    dev.langchain4j: debug

langchain4j:
  open-ai:
    embedding-model:
      base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
      api-key: ${QWKEY}
      model-name: text-embedding-v4
      log-requests: true
      log-responses: true
      dimensions: 1024

新建配置类:

  1. EmbeddingStore<TextSegment>:向量数据库,设置为@Primary替换spring的自动装配
  2. EmbeddingStoreIngestor:用于分割文档,转换向量和存储
  3. ContentRetriever:用于检索与提示词相关的知识,一并发送给大模型,通过.dynamicFilter的配置回调方法在查找到相关知识片段后,还要根据某个元信息进行匹配,这里简单的将传过来的会话ID:chatMemoryId当作用户ID,用于区分用户,实际项目中可以实现根据chatMemoryId得到userId的方法,最终用userId进行查询。
package org.example.config;

import dev.langchain4j.data.document.DocumentSplitter;
import dev.langchain4j.data.document.splitter.*;
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.embedding.EmbeddingModel;
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.filter.MetadataFilterBuilder;
import dev.langchain4j.store.embedding.qdrant.QdrantEmbeddingStore;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import dev.langchain4j.store.memory.chat.InMemoryChatMemoryStore;

import jakarta.annotation.Resource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;


@Configuration
public class RagConfig {

    @Resource
    private EmbeddingModel embeddingModel;

    @Bean
    public StreamingChatModel streamingChatModel() {

        return OpenAiStreamingChatModel.builder()
                .baseUrl("https://api.deepseek.com/")
                .apiKey(System.getenv("DSKEY"))
                .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
    public EmbeddingStoreIngestor ingestor() {
        return EmbeddingStoreIngestor.builder()
                .embeddingStore(embeddingStore() )
                .documentSplitter( DocumentSplitters.recursive(1000, 100) ) //指定分割器
                .embeddingModel(embeddingModel)
                .build();
    }


    @Bean
    public ContentRetriever contentRetriever(EmbeddingStore embeddingStore) {
        return EmbeddingStoreContentRetriever.builder()
                .embeddingStore(embeddingStore)
                .maxResults(7) //返回片段数
                .minScore(0.5) //最小余弦相似度
                .embeddingModel(embeddingModel) //使用自己定义的向量模型
                .dynamicFilter(query -> {
                    Object chatMemoryId = query.metadata().chatMemoryId();

                    String userId = chatMemoryId.toString();
                    return MetadataFilterBuilder.metadataKey("author").isEqualTo(userId);
                })
                .build();
    }

 

    @Bean
    @Primary
    public EmbeddingStore<TextSegment> embeddingStore() {

        return QdrantEmbeddingStore.builder()
                .host("192.168.228.104")
                .port(6334)
                .collectionName("test-qdrant")
                .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);
}

新建一个测试类,将信息向量化,然后写入刚刚创建的向量数据库

实际项目中,这个过程就是用户自己上传文档到自己的知识库,供大模型参考,同时还附带了一些知识片段的元信息,主要用于标识知识片段,例如:用于区分所属用户。

package org.example.test;

import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.document.parser.apache.pdfbox.ApachePdfBoxDocumentParser;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.output.Response;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import jakarta.annotation.Resource;
import org.example.Main;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;


@SpringBootTest(classes = Main.class)
public class RagTest {

    @Resource
    private EmbeddingStore embeddingStore;

    @Resource
    private EmbeddingModel  embeddingModel;

    @Resource
    EmbeddingStoreIngestor ingestor;

    //文字
    @Test
    public void index() {

        String msg = """
                户晨风,男,汉族,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月消息,网络账号“户晨风”在多个平台长期编造所谓“安卓人”“苹果人”等煽动群体对立言论,各平台相关账号已关闭。
                """;

        Response<Embedding> response = embeddingModel.embed(msg);
        Embedding embedding = response.content();

        TextSegment segment = TextSegment.from(msg);
        segment.metadata().put("author", "lzj");
        segment.metadata().put("doc", "1.txt");

        embeddingStore.add(embedding, segment);
    }

    //模拟pdf文档
    @Test
    public void pdf() {
        Document document = FileSystemDocumentLoader.loadDocument("D:/毕业设计/装订/答辩.pdf", new ApachePdfBoxDocumentParser());

        document.metadata().put("author", "lzj");
        document.metadata().put("doc", "答辩.pdf");
        ingestor.ingest(document);
    }
}

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

GET http://127.0.0.1:8080/rag-chat/stream?msg=你知道安卓苹果相对论吗&msgId=lzj

package org.example.controller;

import jakarta.annotation.Resource;
import org.example.ai.RagAssistance;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RestController
@RequestMapping("rag-chat")
public class RagController {

    @Resource
    private RagAssistance assistance;
    
    @GetMapping(value = "stream", produces = "text/html; charset=utf-8")
    public Flux<String> stream(String msg, String msgId) {
        return assistance.chat(msg, msgId);

    }


}
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实现就完成了,实际项目中按照实际情况切换向量数据库和文档加载器和分割器即可。