本文主要研究一下langchain4j的核心RAG APIs

核心RAG APIs

langchain4j提供了一套丰富的API来构建自定义的RAG(检索增强生成)pipelines,从简单的到高级的都有涵盖。

Document

Document类表示整个文档,例如单个 PDF 文件或网页。目前,Document 只能表示文本信息,但未来的更新将使其能够支持图像和表格。

Metadata

每个文档都有Metadata,例如其名称、来源、最后更新日期、所有者或其他相关信息。Metadata以键值对的形式存储,其中键为字符串类型,值可以是以下类型之一:字符串、整数、长整数、浮点数、双精度浮点数。Metadata具有以下用途:

  • 当将文档内容包含在对LLM(大型语言模型)的提示中时,也可以包含元数据条目,为LLM提供额外的信息以考虑。例如,提供文档名称和来源可以帮助提高LLM对内容的理解。
  • 在搜索相关内容以包含在提示中时,可以通过元数据条目进行过滤。例如,可以将语义搜索缩小到仅属于特定所有者的文档。
  • 当文档的来源更新时(例如,特定页面的文档),可以通过其元数据条目(例如,"id"、"来源"等)轻松定位相应的文档,并在EmbeddingStore中同步更新它以保持同步。

DocumentLoader

langchain4j提供了FileSystemDocumentLoader、ClassPathDocumentLoader、UrlDocumentLoader、AmazonS3DocumentLoader(langchain4j-document-loader-amazon-s3)、AzureBlobStorageDocumentLoader(langchain4j-document-loader-azure-storage-blob)、GitHubDocumentLoader(langchain4j-document-loader-github)、GoogleCloudStorageDocumentLoader(langchain4j-document-loader-google-cloud-storage)、SeleniumDocumentLoader(langchain4j-document-loader-selenium)、TencentCosDocumentLoader(langchain4j-document-loader-tencent-cos)

DocumentParser

文档有诸如PDF, DOC, TXT等类型,需要对应的DocumentParser去解析,langchain4j提供了TextDocumentParser、ApachePdfBoxDocumentParser(langchain4j-document-parser-apache-pdfbox)、ApachePoiDocumentParser(langchain4j-document-parser-apache-poi,可以解析MS Office文件,诸如DOC, DOCX, PPT, PPTX, XLS, XLSX)、ApacheTikaDocumentParser(langchain4j-document-parser-apache-tika,可以自动检测和解析大部分文件格式)

DocumentTransformer

DocumentTransformer实现可以执行多种文档转换任务:

  • 清理(Cleaning):
    清理涉及从文档文本中移除不必要的噪音,这可以节省token并减少干扰。
  • 过滤(Filtering):
    过滤可以完全排除特定文档,从而避免这些文档参与搜索。
  • 丰富(Enriching):
    可以向文档中添加附加信息,以增强搜索结果。
  • 总结(Summarizing):
    文档可以被总结,其简短摘要可以存储在元数据中,并稍后包含在每个TextSegment 中,以改善搜索结果。
也可以添加、修改或删除元数据条目。目前唯一提供的开箱即用实现是HtmlToTextDocumentTransformer,它位于langchain4j-document-transformer-jsoup模块中,可以从原始HTML中提取所需文本内容和元数据条目

TextSegment

一旦文档加载完成,接下来就是将它们分割(分块)成更小的片段(部分)。LangChain4j 的领域模型中包含一个TextSegment 类,该类表示文档的一个片段。正如其名称所示,TextSegment 只能表示文本信息。

DocumentSplitter

langchain4j提供了多个开箱即用的DocumentSplitter实现,比如DocumentByParagraphSplitter、DocumentByLineSplitter、DocumentBySentenceSplitter、DocumentByWordSplitter、DocumentByCharacterSplitter、DocumentByRegexSplitter以及递归的DocumentSplitters.recursive(...)。它们可以通过如下方式配合在一起工作:

  • 实例化一个DocumentSplitter,并指定所需的TextSegments大小,以及可选的字符或token重叠量。
  • 调用DocumentSplitter的split(Document) 或 splitAll(List<Document>) 方法
  • DocumentSplitter将给定的文档分割成更小的单元,这些单元的性质取决于具体的DocumentSplitter实现。例如,DocumentByParagraphSplitter将文档按段落(由两个或更多连续换行符定义)分割,而DocumentBySentenceSplitter 使用OpenNLP库的句子检测器将文档按句子分割,等等。
  • DocumentSplitter 将这些较小的单元(段落、句子、单词等)组合成TextSegments,尝试在单个TextSegment中包含尽可能多的单元,而不超过步骤1中设置的限制。如果某些单元仍然太大,无法适应TextSegment,它会调用子分割器。这是另一个DocumentSplitter,能够将不适应的单元分割成更细粒度的单元。所有元数据条目都会从文档复制到每个TextSegment。每个TextSegment会添加一个唯一的元数据条目 "index"。第一个TextSegment将包含index=0,第二个 index=1,依此类推。

TextSegmentTransformer

TextSegmentTransformer跟DocumentTransformer有点类似,但是他转换的是TextSegment。与DocumentTransformer一样,没有一种万能的解决方案,因此建议实现自己的TextSegmentTransformer。有一种在提高检索效果方面非常有效的方案是:在每个TextSegment中包含文档标题或简短摘要。

Embedding

Embedding封装了一个数值向量,该向量表示已嵌入内容(通常是文本,比如TextSegment)的语义意义.

EmbeddingModel

EmbeddingModel表示一种特殊类型的模型,用于将文本转换为嵌入(Embedding)。目前支持的嵌入模型可以在这里找到Embedding Models

EmbeddingStore

EmbeddingStore表示一个用于存储嵌入(Embeddings)的存储系统,也被称为向量数据库。它允许存储嵌入并高效地搜索相似的(在嵌入空间中靠近的)嵌入。目前支持的嵌入存储可以在这里找到Comparison table of all supported Embedding Stores。EmbeddingStore可以单独存储嵌入,也可以与相应的 TextSegment一起存储:

  • 它可以通过ID只存储嵌入。原始嵌入数据可以存储在其他地方,并通过ID进行关联。
  • 它可以同时存储嵌入和已嵌入的原始数据(通常是 TextSegment)。
EmbeddingSearchRequest代表一个向EmbeddingStore的搜索请求,它有maxResults可选参数,返回的最大结果数量,默认值为3;minScore最小分数可选参数,范围从0到1(含0和1)。只有分数大于等于minScore 的嵌入才会被返回,默认值为0;Filter参数,在搜索过程中应用于元数据的过滤器,只有其元数据与过滤器匹配的TextSegments才会被返回,不过并不是所有的EmbeddingStore都支持元数据过滤,也有的虽然支持但是不支持Filter的所有操作,比如ContainsString操作目前仅仅是Milvus, PgVector和Qdrant这三种向量数据库支持。
EmbeddingSearchResult代表在EmbeddingStore中搜索的结果,它包含一个EmbeddingMatch列表;EmbeddingMatch表示一个匹配的嵌入(Embedding),以及其相关性分数、ID和原始嵌入数据(通常是 TextSegment)。

EmbeddingStoreIngestor

EmbeddingStoreIngestor负责将文档(Documents)摄取到嵌入存储(EmbeddingStore)中。在最简单的配置中,EmbeddingStoreIngestor使用指定的嵌入模型(EmbeddingModel)对提供的文档进行嵌入,并将它们及其嵌入存储在指定的嵌入存储中。示例代码:

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

ingestor.ingest(document1);
ingestor.ingest(document2, document3);
IngestionResult ingestionResult = ingestor.ingest(List.of(document4, document5, document6));
  • 所有ingest() 方法在EmbeddingStoreIngestor中返回一个IngestionResult对象。IngestionResult包含有用的信息,包括TokenUsage,它显示了用于嵌入的令牌数量。
  • EmbeddingStoreIngestor可选地可以使用指定的DocumentTransformer转换文档,这在希望在嵌入之前清理、丰富或格式化文档时非常有用。
  • EmbeddingStoreIngestor可选地可以使用指定的DocumentSplitter将文档拆分为文本段(TextSegments)。这在文档较大时非常有用,您可以将它们拆分为更小的文本段,以提高相似性搜索的质量,并减少发送到LLM的提示的大小和成本。
  • EmbeddingStoreIngestor可选地可以使用指定的TextSegmentTransformer转换文本段。这在希望在嵌入之前清理、丰富或格式化文本段时非常有用。

示例如下

EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()

    // adding userId metadata entry to each Document to be able to filter by it later
    .documentTransformer(document -> {
        document.metadata().put("userId", "12345");
        return document;
    })

    // splitting each Document into TextSegments of 1000 tokens each, with a 200-token overlap
    .documentSplitter(DocumentSplitters.recursive(1000, 200, new OpenAiTokenizer()))

    // adding a name of the Document to each TextSegment to improve the quality of search
    .textSegmentTransformer(textSegment -> TextSegment.from(
            textSegment.metadata("file_name") + "\n" + textSegment.text(),
            textSegment.metadata()
    ))

    .embeddingModel(embeddingModel)
    .embeddingStore(embeddingStore)
    .build();

小结

langchain4j提供了一套丰富的API来构建自定义的RAG(检索增强生成)pipelines,包括DocumentLoader、DocumentParser、DocumentTransformer、DocumentSplitter、TextSegmentTransformer、EmbeddingStoreIngestor。

doc


codecraft
11.9k 声望2k 粉丝

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下...


引用和评论

0 条评论