序
本文主要研究一下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。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。