LangChain4j(12)——Naive RAG

上篇文章,我们介绍了RAG的基本,并且使用的是LangChain4j中提到的Easy RAG,Easy RAG只是提供了对RAG认知的一个基本方式,对外隐藏了很多实现细节。本文讲解LangChain4j中提到的Naive RAG,之所以称为Naive RAG,只是为了和Advanced RAG做个区分,表示其没有使用到Advanced RAG的一些高级语法。

Naive RAG代码

package com.renr.langchain4jnew.app4;

import com.renr.langchain4jnew.constant.CommonConstants;
import dev.langchain4j.community.model.zhipu.ZhipuAiChatModel;
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.DocumentParser;
import dev.langchain4j.data.document.DocumentSplitter;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.document.parser.TextDocumentParser;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.embedding.onnx.bgesmallenv15q.BgeSmallEnV15QuantizedEmbeddingModel;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
import lombok.extern.slf4j.Slf4j;

import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.List;

@Slf4j
public class NaiveRAG {

    public static void main(String[] args) throws URISyntaxException {

        ChatLanguageModel CHAT_MODEL = ZhipuAiChatModel.builder()
                // 模型key
                .apiKey(CommonConstants.API_KEY)
                // 精确度
                .temperature(0.9)
                .model("GLM-4-Flash")
                .maxRetries(3)
                .callTimeout(Duration.ofSeconds(60))
                .connectTimeout(Duration.ofSeconds(60))
                .writeTimeout(Duration.ofSeconds(60))
                .readTimeout(Duration.ofSeconds(60))
                .logRequests(true)
                .logResponses(true)
                .build();

        // 获取待加载的文档路径
        URL fileUrl = NaiveRAG.class.getClassLoader().getResource("document/a.txt");
        Path path = Paths.get(fileUrl.toURI());

        // 指定文档解析器
        DocumentParser documentParser = new TextDocumentParser();
        // 加载文档数据
        Document document = FileSystemDocumentLoader.loadDocument(path, documentParser);

        // 文档拆分器
        DocumentSplitter splitter = DocumentSplitters.recursive(300, 0);
        List segments = splitter.split(document);

        // 嵌入模型对象
        EmbeddingModel embeddingModel = new BgeSmallEnV15QuantizedEmbeddingModel();
        List embeddings = embeddingModel.embedAll(segments).content();

        // 嵌入存储对象
        EmbeddingStore embeddingStore = new InMemoryEmbeddingStore<>();
        embeddingStore.addAll(embeddings, segments);

        ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder()
                .embeddingStore(embeddingStore)
                .embeddingModel(embeddingModel)
                // 每次最多检索2个最符合要求的分段
                .maxResults(2)
                // 指定检索时的最低相似度分数
                .minScore(0.5)
                .build();

        Assistant assistant = AiServices.builder(Assistant.class)
                .chatLanguageModel(CHAT_MODEL)
                .contentRetriever(contentRetriever)
                .build();

        String answer = assistant.chat("河南省直第三人民医院的地址");
        System.out.println(answer);
    }

}

文档解析器DocumentParser

RAG可以加载多种文档格式,比如例如 PDF、DOC、TXT 等。 要解析不同格式的文档,需要相关的实现类都实现DocumentParseter接口。

LangChain4j内置的文档解析器有:

  • TextDocumentParser,该解析器可以解析纯文本格式(例如TXT、HTML、MD 等);
  • ApachePdfBoxDocumentParser,该解析器可以解析 PDF 文件,该解析器来自langchain4j-document-parser-apache-pdfbox对应的jar;
  • ApachePoiDocumentParser,该解析器可以解析MS Office 文件格式 (例如DOC、DOCX、PPT、PPTX、XLS、XLSX 等),该解析器来自langchain4j-document-parser-apache-poi对应的jar;
  • ApacheTikaDocumentParser, 它可以自动检测和解析几乎所有现有的文件格式,该解析器来自langchain4j-document-parser-apache-tika对应的jar。

注意:本文的案例使用的是TextDocumentParser

文档加载器

用来加载不同来源的数据,比如使用FileSystemDocumentLoader加载来自不同格式文件的数据,UrlDocumentLoader加载来自url路径的网页数据。

  • FileSystemDocumentLoader,来自langchain4j对应的jar
  • ClassPathDocumentLoader,来自langchain4j对应的jar
  • UrlDocumentLoader,来自langchain4j对应的jar
  • AmazonS3DocumentLoader,来自langchain4j-document-loader-amazon-s3对应的jar
  • AzureBlobStorageDocumentLoader,来自langchain4j-document-loader-azure-storage-blob对应的jar
  • GitHubDocumentLoader,来自langchain4j-document-loader-github对应的jar
  • GoogleCloudStorageDocumentLoader,来自langchain4j-document-loader-google-cloud-storage对应的jar
  • SeleniumDocumentLoader,来自langchain4j-document-loader-selenium对应的jar
  • TencentCosDocumentLoader,来自langchain4j-document-loader-tencent-cos对应的jar

注意:本文的案例使用的是FileSystemDocumentLoader

文档拆分器DocumentSplitter

按照规则对文档进行拆分,拆分为多个文本段。其内置的实现方式有:

  • DocumentByParagraphSplitter
  • DocumentByLineSplitter
  • DocumentBySentenceSplitter
  • DocumentByWordSplitter
  • DocumentByCharacterSplitter
  • DocumentByRegexSplitter
  • DocumentSplitters.recursive()

注意:本文的案例使用的是DocumentSplitters.recursive(300, 0),递归文档拆分器,第一个参数用来指定每段的token数,第二个参数用于指定不同的段之前允许重合的token数。

递归文档拆分器最初尝试以按段落拆分。如果段落太大而无法放入单个段落,则递归地将其按照换行符,句子,单词的顺序拆分。

针对文档分段,有两种处理方式:

第一种,不对文档进行分割,使用RAG时,会将整个文档进行检索后放入提示词。这种方式不会丢失上下文,但是消耗的token会很多,而且检索质量不能保证。

第二种,拆分为较小的分段,比如按照段落、句子等进行拆分。这种方式减少了token,提高了检索质量,但是容易丢失一些关键的上下文信息,这就需要我们采用其他的手段进行优化。

嵌入模型EmbeddingModel

通过嵌入模型,可以对文档进行向量化处理,LangChain4j目前支持10种以上的嵌入模型。

Embedding Models | LangChain4j

注意:本例使用的是LangChain4j集成的bge-small-zh-v1.5模型,该嵌入模型是由智源研究院(BAAI)开发的中文文本嵌入模型。在LangChain4j中对应的类为BgeSmallZhV15EmbeddingModel,LangChain4j中直接将该模型加载到本地内存中使用。

需要额外导入的jar:


    dev.langchain4j
    langchain4j-embeddings-bge-small-zh-v15
    1.0.0-beta2

嵌入存储

嵌入模型向量化处理后的数据需要进行嵌入存储,用于检索相似的分段信息,一般可以将分段信息和向量化后的数据存入向量数据库中。Langchain4j目前支持超过15 种的嵌入存储方式Embedding (Vector) Stores | LangChain4j。

注意:本例使用的是InMemoryEmbeddingStore,表示将向量化后的数据存入内容中。

内容检索器ContentRetriever

内容检索器负责根据用户的提问进行相关内容的检索,目前支持检索文本段。数据源可以是:

  • Embedding Store (嵌入存储)
  • 全文搜索引擎
  • 向量搜索和全文搜索的混合
  • Web 搜索引擎
  • 知识图谱
  • SQL 数据库等

你可能感兴趣的:(开发语言,LangChain4j,人工智能)