LangChain4j-RAG

LangChain4j-RAG

起男 109 2025-03-26

LangChain4j-RAG

RAG检索增强生成(retrieval-augmented-generation)

向量文本化

        //向量模型
        QwenEmbeddingModel model = QwenEmbeddingModel.builder()
                .apiKey("xxxx")
                .build();
        Response<Embedding> embed = model.embed("你好");
        System.out.println(embed.content());
        System.out.println(embed.content().vector().length);//向量维度

向量检索

        //embedding阶段
        //内存的向量数据库
        InMemoryEmbeddingStore<TextSegment> store = new InMemoryEmbeddingStore();
        //向量模型
        QwenEmbeddingModel model = QwenEmbeddingModel.builder()
                .apiKey("xxxx")
                .build();
        //向量化
        TextSegment segment1 = TextSegment.from("""
                预定航班:
                - 通过我们的网站或移动应用程序预定
                - 预定时需要全额付款
                - 确保个人信息(姓名、id)的准确性,会产生25的费用
                """);
        Embedding embedding1 = model.embed(segment1).content();
        TextSegment segment2 = TextSegment.from("""
                取消预定:
                - 最晚在航班起飞前48小时取消
                - 取消费用:50
                - 退款将在7个工作日内处理
                """);
        Embedding embedding2 = model.embed(segment2).content();
        //存储到向量数据库
        store.add(embedding1,segment1);
        store.add(embedding2,segment2);

        //数据检索阶段
        //查询内容向量化
        Embedding queryEmbedding = model.embed("退票多少钱").content();
        //构建查询条件
        EmbeddingSearchRequest request = EmbeddingSearchRequest.builder()
                .queryEmbedding(queryEmbedding)
                .maxResults(1)//最多查询几条数据
//                .minScore(0.8)//最小分数
                .build();
        //从向量数据库中查询
        EmbeddingSearchResult<TextSegment> result = store.search(request);
        //查询结果
        result.matches().forEach(embeddingMatch -> {
            System.out.println(embeddingMatch.score());//相似度分数,最高1
            System.out.println(embeddingMatch.embedded().text());//检索到的文本
        });

检索增强

将检索出来的向量携带到大模型中

langchain4j中使用AiServices创建动态代理来简化操作

        //embedding阶段
        //内存的向量数据库
        InMemoryEmbeddingStore<TextSegment> store = new InMemoryEmbeddingStore();
        //向量模型
        QwenEmbeddingModel model = QwenEmbeddingModel.builder()
                .apiKey("xxxx")
                .build();
        //向量化
        TextSegment segment1 = TextSegment.from("""
                预定航班:
                - 通过我们的网站或移动应用程序预定
                - 预定时需要全额付款
                - 确保个人信息(姓名、id)的准确性,会产生25的费用
                """);
        Embedding embedding1 = model.embed(segment1).content();
        TextSegment segment2 = TextSegment.from("""
                取消预定:
                - 最晚在航班起飞前48小时取消
                - 取消费用:50
                - 退款将在7个工作日内处理
                """);
        Embedding embedding2 = model.embed(segment2).content();
        //存储到向量数据库
        store.add(embedding1,segment1);
        store.add(embedding2,segment2);

        //检索增强
        //查询模型
        QwenChatModel qw = QwenChatModel.builder()
                .apiKey("xxxxx")
                .modelName("qwen-max")
                .build();
        EmbeddingStoreContentRetriever retriever = EmbeddingStoreContentRetriever.builder()
                .embeddingStore(store)//向量数据库
                .embeddingModel(model)//向量化模型
                .maxResults(5)//最大条数
                .minScore(0.6)//最低分数
                .build();
        //生成代理
        Assistant assistant = AiServices.builder(Assistant.class)
                .chatLanguageModel(qw)
                .contentRetriever(retriever)
                .build();
        System.out.println(assistant.chat("退票多少钱"));

文档解析器

如果要开发一个知识库系统,这些资料可能在各种文件中,比如word、txt、pdf、image、html等等,所以langchain4j提供了不同的文档解析器

  • TextDocumentParser:可用解析纯文本格式(txt、html、md等),来自langchain4j模块
  • ApachePdfBoxDocumentParser:可以解析pdf文件,来自langchain4j-document-parser-apache-pdfbox
  • ApachePoiDocumentParser:可以解析Office文件,来自langchain4j-document-parser-apache-poi
  • ApacheTikaDocumentParser:可以自动检测和解析所以的文件格式,来自langchain4j-document-parser-apache-tika模块

使用:

        //classpath
		Document document = ClassPathDocumentLoader.loadDocument("rag/test01.txt", new TextDocumentParser());
        System.out.println(document.text());

		//绝对路径
		Document document = FileSystemDocumentLoader.loadDocument("D:\\IDEAProject\\langchain-demo\\src\\main\\resources\\img\\test01.txt");
        System.out.println(document);

文档拆分器

由于文本读取过来后,还需要分成一段一段的片段(分块chunk),分块是蔚来更好的拆分语义单元,这样在后面可以更精确的进行语义相似性检索,也可以避免llm的token限制

langchain4j分割器

分割器 能力
DocumentByCharacterSplitter 无符号分割器(按照字数)
DocumentByRegexSplitter 正则表达式分割
DocumentByParagraphSplitter 删除大段空白内容
DocumentByLineSplitter 删除单个换行符周围的空白
DocumentByWordSplitter 删除连续的空白字符
DocumentBySentenceSplitter 按句子分割

使用:

        Document document = ClassPathDocumentLoader.loadDocument("rag/test01.txt", new TextDocumentParser());
        DocumentByCharacterSplitter splitter = new DocumentByCharacterSplitter(
                50,//每段最长字数
                5);//自然语义最大重叠数,避免断句(这个分割器会忽略这个参数)
        List<TextSegment> split = splitter.split(document);//对文档进行分割
        split.forEach(System.out::println);

整合springboot

导入依赖

    <dependencies>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j</artifactId>
            <version>1.0.0-beta1</version>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>dev.langchain4j</groupId>
                <artifactId>langchain4j-community-bom</artifactId>
                <version>1.0.0-beta1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

配置文件

langchain4j:
  community:
    dashscope:
      chat-model:
        api-key: xxxx
        model-name: qwen-max
      embedding-model:
        api_key: xxxx

配置bean

    //向量数据库
    @Bean
    public EmbeddingStore embeddingStore(){
        return new InMemoryEmbeddingStore();
    }

    @Bean
    public Assistant assistant(ChatLanguageModel chatLanguageModel,
                               StreamingChatLanguageModel streamingChatLanguageModel,
                               QwenEmbeddingModel qwenEmbeddingModel) {
        //记忆对话
        MessageWindowChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);

        //内容检索器
        EmbeddingStoreContentRetriever retriever = EmbeddingStoreContentRetriever.builder()
                .embeddingStore(embeddingStore())
                .embeddingModel(qwenEmbeddingModel)
                .maxResults(5)
                .minScore(0.6)
                .build();

        Assistant assistant = AiServices.builder(Assistant.class)
                .chatLanguageModel(chatLanguageModel)
                .streamingChatLanguageModel(streamingChatLanguageModel)
                .chatMemory(chatMemory)
                .contentRetriever(retriever)//指定检索器
                .build();

        return assistant;
    }

    //初始化数据
    @Bean
    public CommandLineRunner commandLineRunner(QwenEmbeddingModel qwenEmbeddingModel){
        return args -> {
            //获取文档数据
            Document document = ClassPathDocumentLoader.loadDocument("rag/test01.txt", new TextDocumentParser());
            //分割
            DocumentByLineSplitter splitter = new DocumentByLineSplitter(500, 200);
            List<TextSegment> textSegments = splitter.split(document);
            //向量化
            List<Embedding> embeddingList = qwenEmbeddingModel.embedAll(textSegments).content();
            //存入向量数据库
            embeddingStore().addAll(embeddingList, textSegments);
        };
    }

controller

    @Autowired
    private AssistantUnique assistantUnique;

	@RequestMapping("assistant/chat")
    public String assistantChat(@RequestParam String message){
        return assistant.chat(message);
    }