跳到主要内容

ETL 管道

Deepseek 3.2 中英对照 ETL Pipeline

提取、转换和加载(ETL)框架是检索增强生成(RAG)应用场景中数据处理的支柱。

ETL流水线统筹管理从原始数据源到结构化向量存储的整个流程,确保数据处于供AI模型检索的最佳格式。

RAG(检索增强生成)用例是通过从数据体中检索相关信息来增强生成模型的能力,从而提高生成输出的质量和相关性。

API 概览

ETL流水线负责创建、转换和存储Document实例。

Spring AI 消息 API

Document 类包含文本、元数据,以及可选的额外媒体类型,如图像、音频和视频。

ETL(提取、转换、加载)流水线包含三个主要组成部分,

  • 实现 Supplier<List<Document>> 接口的 DocumentReader
  • 实现 Function<List<Document>, List<Document>> 接口的 DocumentTransformer
  • 实现 Consumer<List<Document>> 接口的 DocumentWriter

Document类的内容是通过DocumentReader的帮助,从PDF、文本文件和其他文档类型中创建的。

要构建一个简单的ETL流水线,你可以将每种类型的实例串联起来。

ETL 管道

假设我们有这三种ETL类型的以下实例

  • PagePdfDocumentReaderDocumentReader 的一个实现

  • TokenTextSplitterDocumentTransformer 的一个实现

  • VectorStoreDocumentWriter 的一个实现

为了将数据基本加载到向量数据库中,以便用于检索增强生成模式,请在Java函数风格语法中使用以下代码。

vectorStore.accept(tokenTextSplitter.apply(pdfReader.get()));

或者,你可以使用更符合领域自然表达的方法名称

vectorStore.write(tokenTextSplitter.split(pdfReader.read()));

ETL 接口

ETL 流水线由以下接口和实现组成。详细的 ETL 类图展示在 ETL 类图 部分。

DocumentReader

提供来自不同来源的文档集合。

public interface DocumentReader extends Supplier<List<Document>> {

default List<Document> read() {
return get();
}
}

DocumentTransformer

在文档处理工作流中,对一批文档进行转换。

public interface DocumentTransformer extends Function<List<Document>, List<Document>> {

default List<Document> transform(List<Document> transform) {
return apply(transform);
}
}

DocumentWriter

管理ETL流程的最终阶段,为文档存储做好准备。

public interface DocumentWriter extends Consumer<List<Document>> {

default void write(List<Document> documents) {
accept(documents);
}
}

ETL 类图

以下类图展示了ETL的接口与实现。

ETL 类图

DocumentReaders

JSON

JsonReader 用于处理JSON文档,将其转换为Document对象的列表。

示例

@Component
class MyJsonReader {

private final Resource resource;

MyJsonReader(@Value("classpath:bikes.json") Resource resource) {
this.resource = resource;
}

List<Document> loadJsonAsDocuments() {
JsonReader jsonReader = new JsonReader(this.resource, "description", "content");
return jsonReader.get();
}
}

构造函数选项

JsonReader 提供了多个构造函数选项:

  1. JsonReader(Resource resource)

  2. JsonReader(Resource resource, String…​ jsonKeysToUse)

  3. JsonReader(Resource resource, JsonMetadataGenerator jsonMetadataGenerator, String…​ jsonKeysToUse)

参数

  • resource: 一个指向JSON文件的Spring Resource 对象。

  • jsonKeysToUse: 一个来自JSON的键数组,这些键将用作生成的 Document 对象中的文本内容。

  • jsonMetadataGenerator: 一个可选的 JsonMetadataGenerator,用于为每个 Document 创建元数据。

行为

JsonReader 按如下方式处理 JSON 内容:

  • 它能够处理 JSON 数组和单个 JSON 对象。

  • 对于每个 JSON 对象(无论是在数组中还是单个对象):

    • 它会根据指定的 jsonKeysToUse 提取内容。
    • 如果未指定任何键,它将使用整个 JSON 对象作为内容。
    • 它会使用提供的 JsonMetadataGenerator(如果未提供,则使用空的生成器)来生成元数据。
    • 它会使用提取的内容和元数据创建一个 Document 对象。

使用 JSON 指针

JsonReader 现在支持使用 JSON 指针检索 JSON 文档的特定部分。此功能使您能够轻松地从复杂的 JSON 结构中提取嵌套数据。

get(String pointer) 方法
public List<Document> get(String pointer)

该方法允许您使用JSON指针来检索JSON文档的特定部分。

参数
  • pointer: 一个 JSON Pointer 字符串(遵循 RFC 6901 标准定义),用于在 JSON 结构内定位目标元素。
返回值
  • 返回一个 List<Document>,其中包含从 JSON 指针定位的 JSON 元素解析得到的文档。
行为
  • 该方法使用提供的 JSON Pointer 来导航到 JSON 结构中的特定位置。

  • 如果指针有效并指向一个现有元素:

    • 对于 JSON 对象:它返回一个包含单个 Document 的列表。

    • 对于 JSON 数组:它返回一个 Document 列表,数组中的每个元素对应一个 Document。

  • 如果指针无效或指向一个不存在的元素,它将抛出 IllegalArgumentException

示例
JsonReader jsonReader = new JsonReader(resource, "description");
List<Document> documents = this.jsonReader.get("/store/books/0");

示例JSON结构

[
{
"id": 1,
"brand": "Trek",
"description": "A high-performance mountain bike for trail riding."
},
{
"id": 2,
"brand": "Cannondale",
"description": "An aerodynamic road bike for racing enthusiasts."
}
]

在这个例子中,如果JsonReader被配置为将"description"作为jsonKeysToUse,它将创建Document对象,其中内容对应于数组中每辆自行车的"description"字段值。

备注

  • JsonReader 使用 Jackson 进行 JSON 解析。

  • 它通过为数组使用流式处理,能够高效地处理大型 JSON 文件。

  • 如果在 jsonKeysToUse 中指定了多个键,内容将是这些键对应值的拼接。

  • 该读取器非常灵活,可以通过自定义 jsonKeysToUseJsonMetadataGenerator 来适应各种 JSON 结构。

文本

TextReader 处理纯文本文档,将其转换为 Document 对象列表。

示例

@Component
class MyTextReader {

private final Resource resource;

MyTextReader(@Value("classpath:text-source.txt") Resource resource) {
this.resource = resource;
}

List<Document> loadText() {
TextReader textReader = new TextReader(this.resource);
textReader.getCustomMetadata().put("filename", "text-source.txt");

return textReader.read();
}
}

构造函数选项

TextReader 提供了两种构造函数选项:

  1. TextReader(String resourceUrl)

  2. TextReader(Resource resource)

参数

  • resourceUrl: 一个字符串,表示待读取资源的 URL。

  • resource: 一个指向文本文件的 Spring Resource 对象。

配置

  • setCharset(Charset charset): 设置用于读取文本文件的字符集。默认为 UTF-8。

  • getCustomMetadata(): 返回一个可变映射表,您可以在其中为文档添加自定义元数据。

行为

TextReader 按以下方式处理文本内容:

  • 它将文本文件的全部内容读取到一个单一的 Document 对象中。

  • 文件的内容成为 Document 的内容。

  • 元数据会自动添加到 Document 中:

    • charset:用于读取文件的字符集(默认值:"UTF-8")。
    • source:源文本文件的文件名。
  • 通过 getCustomMetadata() 添加的任何自定义元数据也会包含在 Document 中。

注意事项

  • TextReader 会将整个文件内容读取到内存中,因此可能不适用于非常大的文件。

  • 如果需要将文本拆分成较小的块,可以在读取文档后使用文本分割器,例如 TokenTextSplitter

List<Document> documents = textReader.get();
List<Document> splitDocuments = new TokenTextSplitter().apply(this.documents);
  • 该读取器使用Spring的Resource抽象,允许从多种来源(类路径、文件系统、URL等)进行读取。

  • 通过getCustomMetadata()方法,可以向读取器创建的所有文档添加自定义元数据。

HTML (JSoup)

JsoupDocumentReader 使用 JSoup 库处理 HTML 文档,并将其转换为 Document 对象列表。

示例

@Component
class MyHtmlReader {

private final Resource resource;

MyHtmlReader(@Value("classpath:/my-page.html") Resource resource) {
this.resource = resource;
}

List<Document> loadHtml() {
JsoupDocumentReaderConfig config = JsoupDocumentReaderConfig.builder()
.selector("article p") // Extract paragraphs within <article> tags
.charset("ISO-8859-1") // Use ISO-8859-1 encoding
.includeLinkUrls(true) // Include link URLs in metadata
.metadataTags(List.of("author", "date")) // Extract author and date meta tags
.additionalMetadata("source", "my-page.html") // Add custom metadata
.build();

JsoupDocumentReader reader = new JsoupDocumentReader(this.resource, config);
return reader.get();
}
}

JsoupDocumentReaderConfig 允许您自定义 JsoupDocumentReader 的行为:

  • charset: 指定 HTML 文档的字符编码(默认为 "UTF-8")。

  • selector: 一个 JSoup CSS 选择器,用于指定从哪些元素提取文本(默认为 "body")。

  • separator: 用于连接多个选定元素文本的字符串(默认为 "\n")。

  • allElements: 如果为 true,则从 <body> 元素提取所有文本,忽略 selector(默认为 false)。

  • groupByElement: 如果为 true,则为 selector 匹配的每个元素创建一个单独的 Document(默认为 false)。

  • includeLinkUrls: 如果为 true,则提取绝对链接 URL 并将其添加到元数据中(默认为 false)。

  • metadataTags: 要从中提取内容的 <meta> 标签名称列表(默认为 ["description", "keywords"])。

  • additionalMetadata: 允许您向所有创建的 Document 对象添加自定义元数据。

示例文档:my-page.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Web Page</title>
<meta name="description" content="A sample web page for Spring AI">
<meta name="keywords" content="spring, ai, html, example">
<meta name="author" content="John Doe">
<meta name="date" content="2024-01-15">
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>Welcome to My Page</h1>
</header>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
<article>
<h2>Main Content</h2>
<p>This is the main content of my web page.</p>
<p>It contains multiple paragraphs.</p>
<a href="https://www.example.com">External Link</a>
</article>
<footer>
<p>&copy; 2024 John Doe</p>
</footer>
</body>
</html>

行为:

JsoupDocumentReader 会处理HTML内容并根据配置创建Document对象:

  • selector 决定了哪些元素用于文本提取。

  • 如果 allElementstrue,则 <body> 内的所有文本将被提取到一个单独的 Document 中。

  • 如果 groupByElementtrue,则每个匹配 selector 的元素都会创建一个独立的 Document

  • 如果 allElementsgroupByElement 都不为 true,则匹配 selector 的所有元素的文本将使用 separator 连接起来。

  • 文档标题、指定 <meta> 标签的内容以及(可选的)链接 URL 会被添加到 Document 的元数据中。

  • 用于解析相对链接的基础 URI 将从 URL 资源中提取。

阅读器会保留所选元素的文本内容,但会移除其中的所有HTML标签。

Markdown

MarkdownDocumentReader 处理 Markdown 文档,将其转换为 Document 对象列表。

示例

@Component
class MyMarkdownReader {

private final Resource resource;

MyMarkdownReader(@Value("classpath:code.md") Resource resource) {
this.resource = resource;
}

List<Document> loadMarkdown() {
MarkdownDocumentReaderConfig config = MarkdownDocumentReaderConfig.builder()
.withHorizontalRuleCreateDocument(true)
.withIncludeCodeBlock(false)
.withIncludeBlockquote(false)
.withAdditionalMetadata("filename", "code.md")
.build();

MarkdownDocumentReader reader = new MarkdownDocumentReader(this.resource, config);
return reader.get();
}
}

MarkdownDocumentReaderConfig 允许你自定义 Markdown 文档读取器的行为:

  • horizontalRuleCreateDocument:当设置为 true 时,Markdown 中的水平分隔符将创建新的 Document 对象。

  • includeCodeBlock:当设置为 true 时,代码块将与周围的文本包含在同一 Document 中。当设置为 false 时,代码块会创建单独的 Document 对象。

  • includeBlockquote:当设置为 true 时,引用块将与周围的文本包含在同一 Document 中。当设置为 false 时,引用块会创建单独的 Document 对象。

  • additionalMetadata:允许你向所有创建的 Document 对象添加自定义元数据。

示例文档:code.md

This is a Java sample application:

```java
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
```

Markdown also provides the possibility to `use inline code formatting throughout` the entire sentence.

---

Another possibility is to set block code without specific highlighting:

```
./mvnw spring-javaformat:apply
```

行为:MarkdownDocumentReader处理Markdown内容并根据配置创建文档对象:

  • 标题(Headers)成为 Document 对象中的元数据。

  • 段落(Paragraphs)成为 Document 对象的内容。

  • 代码块(Code blocks)可以分离成独立的 Document 对象,也可以与周围的文本一起包含。

  • 引用块(Blockquotes)可以分离成独立的 Document 对象,也可以与周围的文本一起包含。

  • 水平分割线(Horizontal rules)可用于将内容分割成单独的 Document 对象。

读取器会保留文档对象内容中的内联代码、列表和文本样式等格式。

PDF 页面

PagePdfDocumentReader 使用 Apache PdfBox 库来解析 PDF 文档

使用Maven或Gradle将依赖项添加到您的项目中。

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pdf-document-reader</artifactId>
</dependency>

或将其添加到您的 Gradle build.gradle 构建文件中。

dependencies {
implementation 'org.springframework.ai:spring-ai-pdf-document-reader'
}

示例

@Component
public class MyPagePdfDocumentReader {

List<Document> getDocsFromPdf() {

PagePdfDocumentReader pdfReader = new PagePdfDocumentReader("classpath:/sample1.pdf",
PdfDocumentReaderConfig.builder()
.withPageTopMargin(0)
.withPageExtractedTextFormatter(ExtractedTextFormatter.builder()
.withNumberOfTopTextLinesToDelete(0)
.build())
.withPagesPerDocument(1)
.build());

return pdfReader.read();
}

}

PDF 段落

ParagraphPdfDocumentReader 利用PDF目录(例如,目录)信息将输入的PDF文档分割为文本段落,并为每个段落输出一个独立的 Document。请注意:并非所有PDF文档都包含PDF目录。

依赖项

使用 Maven 或 Gradle 将依赖项添加到您的项目中。

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pdf-document-reader</artifactId>
</dependency>

或者添加到您的Gradle build.gradle 构建文件中。

dependencies {
implementation 'org.springframework.ai:spring-ai-pdf-document-reader'
}

示例

@Component
public class MyPagePdfDocumentReader {

List<Document> getDocsFromPdfWithCatalog() {

ParagraphPdfDocumentReader pdfReader = new ParagraphPdfDocumentReader("classpath:/sample1.pdf",
PdfDocumentReaderConfig.builder()
.withPageTopMargin(0)
.withPageExtractedTextFormatter(ExtractedTextFormatter.builder()
.withNumberOfTopTextLinesToDelete(0)
.build())
.withPagesPerDocument(1)
.build());

return pdfReader.read();
}
}

Tika (DOCX, PPTX, HTML…)

TikaDocumentReader 使用 Apache Tika 从多种文档格式中提取文本,例如 PDF、DOC/DOCX、PPT/PPTX 和 HTML。有关支持的完整格式列表,请参阅 Tika 文档

依赖关系

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-tika-document-reader</artifactId>
</dependency>

或添加到您的 Gradle build.gradle 构建文件中。

dependencies {
implementation 'org.springframework.ai:spring-ai-tika-document-reader'
}

示例

@Component
class MyTikaDocumentReader {

private final Resource resource;

MyTikaDocumentReader(@Value("classpath:/word-sample.docx")
Resource resource) {
this.resource = resource;
}

List<Document> loadText() {
TikaDocumentReader tikaDocumentReader = new TikaDocumentReader(this.resource);
return tikaDocumentReader.read();
}
}

Transformers

TextSplitter

TextSplitter 是一个抽象基类,用于将文档分割以适应 AI 模型的上下文窗口。

TokenTextSplitter

TokenTextSplitterTextSplitter 的一种实现,它使用 CL100K_BASE 编码,根据令牌(token)数量将文本分割成块。

用法

@Component
class MyTokenTextSplitter {

public List<Document> splitDocuments(List<Document> documents) {
TokenTextSplitter splitter = new TokenTextSplitter();
return splitter.apply(documents);
}

public List<Document> splitCustomized(List<Document> documents) {
TokenTextSplitter splitter = new TokenTextSplitter(1000, 400, 10, 5000, true);
return splitter.apply(documents);
}
}

构造函数选项

TokenTextSplitter 提供了两种构造选项:

  1. TokenTextSplitter(): 使用默认设置创建分割器。

  2. TokenTextSplitter(int defaultChunkSize, int minChunkSizeChars, int minChunkLengthToEmbed, int maxNumChunks, boolean keepSeparator)

参数

  • defaultChunkSize: 每个文本块的目标大小,以令牌为单位(默认值:800)。

  • minChunkSizeChars: 每个文本块的最小大小,以字符为单位(默认值:350)。

  • minChunkLengthToEmbed: 要包含的文本块的最小长度(默认值:5)。

  • maxNumChunks: 从一个文本生成的最大块数(默认值:10000)。

  • keepSeparator: 是否在块中保留分隔符(如换行符)(默认值:true)。

行为

TokenTextSplitter处理文本内容的过程如下:

  1. 它使用 CL100K_BASE 编码将输入文本编码为令牌。

  2. 根据 defaultChunkSize 将编码后的文本分割成块。

  3. 对于每个块:

    1. 将块解码回文本。

    2. 尝试在 minChunkSizeChars 之后找到一个合适的断点(句号、问号、感叹号或换行符)。

    3. 如果找到断点,则在该点截断块。

    4. 根据 keepSeparator 设置修剪块,并可选地移除换行符。

    5. 如果生成的块长度超过 minChunkLengthToEmbed,则将其添加到输出中。

  4. 此过程持续进行,直到处理完所有令牌或达到 maxNumChunks

  5. 任何剩余的文本,如果长度超过 minChunkLengthToEmbed,则作为最终块添加。

示例

Document doc1 = new Document("This is a long piece of text that needs to be split into smaller chunks for processing.",
Map.of("source", "example.txt"));
Document doc2 = new Document("Another document with content that will be split based on token count.",
Map.of("source", "example2.txt"));

TokenTextSplitter splitter = new TokenTextSplitter();
List<Document> splitDocuments = this.splitter.apply(List.of(this.doc1, this.doc2));

for (Document doc : splitDocuments) {
System.out.println("Chunk: " + doc.getContent());
System.out.println("Metadata: " + doc.getMetadata());
}

注释

  • TokenTextSplitter 使用来自 jtokkit 库的 CL100K_BASE 编码,该编码与较新的 OpenAI 模型兼容。

  • 该分割器尝试通过在可能的句子边界处进行断开来创建语义上连贯的文本块。

  • 原始文档的元数据会被保留并复制到源自该文档的所有文本块中。

  • 如果 copyContentFormatter 设置为 true(默认行为),原始文档的内容格式化器(如果已设置)也会被复制到派生的文本块中。

  • 对于为有令牌限制的大型语言模型准备文本,此分割器特别有用,它能确保每个文本块都在模型的处理能力范围内。

ContentFormatTransformer

确保所有文档中的内容格式统一。

KeywordMetadataEnricher

KeywordMetadataEnricher 是一种 DocumentTransformer,它使用生成式 AI 模型从文档内容中提取关键词,并将其作为元数据添加。

用法

@Component
class MyKeywordEnricher {

private final ChatModel chatModel;

MyKeywordEnricher(ChatModel chatModel) {
this.chatModel = chatModel;
}

List<Document> enrichDocuments(List<Document> documents) {
KeywordMetadataEnricher enricher = KeywordMetadataEnricher.builder(chatModel)
.keywordCount(5)
.build();

// Or use custom templates
KeywordMetadataEnricher enricher = KeywordMetadataEnricher.builder(chatModel)
.keywordsTemplate(YOUR_CUSTOM_TEMPLATE)
.build();

return enricher.apply(documents);
}
}

构造函数选项

KeywordMetadataEnricher 提供两种构造函数选项:

  1. KeywordMetadataEnricher(ChatModel chatModel, int keywordCount): 使用默认模板提取指定数量的关键词。

  2. KeywordMetadataEnricher(ChatModel chatModel, PromptTemplate keywordsTemplate): 使用自定义模板进行关键词提取。

行为

KeywordMetadataEnricher 处理文档的流程如下:

  1. 针对每个输入文档,它会利用文档内容创建提示。

  2. 将此提示发送给所提供的 ChatModel 以生成关键词。

  3. 将生成的关键词添加到文档的元数据中,对应的键为 "excerpt_keywords"。

  4. 返回经过丰富处理的文档。

自定义功能

您可以使用默认模板,或通过 keywordsTemplate 参数自定义模板。默认模板如下:

\{context_str}. Give %s unique keywords for this document. Format as comma separated. Keywords:

其中 {context_str} 被替换为文档内容,%s 被替换为指定的关键词计数。

示例

ChatModel chatModel = // initialize your chat model
KeywordMetadataEnricher enricher = KeywordMetadataEnricher.builder(chatModel)
.keywordCount(5)
.build();

// Or use custom templates
KeywordMetadataEnricher enricher = KeywordMetadataEnricher.builder(chatModel)
.keywordsTemplate(new PromptTemplate("Extract 5 important keywords from the following text and separate them with commas:\n{context_str}"))
.build();

Document doc = new Document("This is a document about artificial intelligence and its applications in modern technology.");

List<Document> enrichedDocs = enricher.apply(List.of(this.doc));

Document enrichedDoc = this.enrichedDocs.get(0);
String keywords = (String) this.enrichedDoc.getMetadata().get("excerpt_keywords");
System.out.println("Extracted keywords: " + keywords);

备注

  • KeywordMetadataEnricher 需要一个正常运行的 ChatModel 来生成关键词。

  • 关键词数量必须大于等于 1。

  • 该增强器会为每个处理的文档添加 "excerpt_keywords" 元数据字段。

  • 生成的关键词以逗号分隔的字符串形式返回。

  • 此增强器对于提高文档的可搜索性以及为文档生成标签或类别特别有用。

  • 在 Builder 模式中,如果设置了 keywordsTemplate 参数,则 keywordCount 参数将被忽略。

SummaryMetadataEnricher

SummaryMetadataEnricher 是一个 DocumentTransformer,它使用生成式 AI 模型为文档创建摘要,并将其添加为元数据。它可以为当前文档以及相邻文档(前一个和后一个)生成摘要。

用法

@Configuration
class EnricherConfig {

@Bean
public SummaryMetadataEnricher summaryMetadata(OpenAiChatModel aiClient) {
return new SummaryMetadataEnricher(aiClient,
List.of(SummaryType.PREVIOUS, SummaryType.CURRENT, SummaryType.NEXT));
}
}

@Component
class MySummaryEnricher {

private final SummaryMetadataEnricher enricher;

MySummaryEnricher(SummaryMetadataEnricher enricher) {
this.enricher = enricher;
}

List<Document> enrichDocuments(List<Document> documents) {
return this.enricher.apply(documents);
}
}

构造函数

SummaryMetadataEnricher 提供了两个构造函数:

  1. SummaryMetadataEnricher(ChatModel chatModel, List<SummaryType> summaryTypes)

  2. SummaryMetadataEnricher(ChatModel chatModel, List<SummaryType> summaryTypes, String summaryTemplate, MetadataMode metadataMode)

参数

  • chatModel: 用于生成摘要的 AI 模型。

  • summaryTypes: 一个 SummaryType 枚举值列表,指明要生成哪些摘要(PREVIOUS, CURRENT, NEXT)。

  • summaryTemplate: 用于摘要生成的自定义模板(可选)。

  • metadataMode: 指定在生成摘要时如何处理文档元数据(可选)。

行为

SummaryMetadataEnricher 按以下方式处理文档:

  1. 对于每个输入文档,它会使用文档内容和指定的摘要模板创建提示。

  2. 将此提示发送给提供的 ChatModel 以生成摘要。

  3. 根据指定的 summaryTypes,它会向每个文档添加以下元数据:

    • section_summary:当前文档的摘要。
    • prev_section_summary:前一个文档的摘要(如果可用且被请求)。
    • next_section_summary:下一个文档的摘要(如果可用且被请求)。
  4. 返回经过增强的文档。

定制

摘要生成提示可以通过自定义 summaryTemplate 进行定制。默认模板为:

"""
Here is the content of the section:
{context_str}

Summarize the key topics and entities of the section.

Summary:
"""

示例

ChatModel chatModel = // initialize your chat model
SummaryMetadataEnricher enricher = new SummaryMetadataEnricher(chatModel,
List.of(SummaryType.PREVIOUS, SummaryType.CURRENT, SummaryType.NEXT));

Document doc1 = new Document("Content of document 1");
Document doc2 = new Document("Content of document 2");

List<Document> enrichedDocs = enricher.apply(List.of(this.doc1, this.doc2));

// Check the metadata of the enriched documents
for (Document doc : enrichedDocs) {
System.out.println("Current summary: " + doc.getMetadata().get("section_summary"));
System.out.println("Previous summary: " + doc.getMetadata().get("prev_section_summary"));
System.out.println("Next summary: " + doc.getMetadata().get("next_section_summary"));
}

提供的示例展示了预期的行为:

  • 对于一个包含两份文档的列表,两份文档都会收到一个 section_summary

  • 第一份文档会收到一个 next_section_summary,但不会收到 prev_section_summary

  • 第二份文档会收到一个 prev_section_summary,但不会收到 next_section_summary

  • 第一份文档的 section_summary 与第二份文档的 prev_section_summary 相匹配。

  • 第一份文档的 next_section_summary 与第二份文档的 section_summary 相匹配。

注释

  • SummaryMetadataEnricher 需要一个正常运行的 ChatModel 来生成摘要。

  • 该增强器可以处理任意大小的文档列表,并能妥善处理第一个和最后一个文档的边缘情况。

  • 该增强器在创建上下文感知的摘要方面特别有用,有助于更好地理解序列中文档之间的关系。

  • MetadataMode 参数允许控制现有元数据如何被纳入摘要生成过程。

Writers

文件

FileDocumentWriter 是一种 DocumentWriter 实现,它将 Document 对象列表的内容写入文件。

用法

@Component
class MyDocumentWriter {

public void writeDocuments(List<Document> documents) {
FileDocumentWriter writer = new FileDocumentWriter("output.txt", true, MetadataMode.ALL, false);
writer.accept(documents);
}
}

构造函数

FileDocumentWriter 提供了三种构造函数:

  1. FileDocumentWriter(String fileName)

  2. FileDocumentWriter(String fileName, boolean withDocumentMarkers)

  3. FileDocumentWriter(String fileName, boolean withDocumentMarkers, MetadataMode metadataMode, boolean append)

参数

  • fileName:要写入文档的文件名。

  • withDocumentMarkers:是否在输出中包含文档标记(默认值:false)。

  • metadataMode:指定要写入文件的文档内容类型(默认值:MetadataMode.NONE)。

  • append:如果为 true,数据将写入文件末尾而非开头(默认值:false)。

行为

FileDocumentWriter 按以下方式处理文档:

  1. 它为指定的文件名打开一个 FileWriter。

  2. 对于输入列表中的每个文档:

    1. 如果 withDocumentMarkers 为 true,它会写入一个文档标记,包括文档索引和页码。

    2. 根据指定的 metadataMode,它写入文档的格式化内容。

  3. 在所有文档写入完毕后,文件被关闭。

文档标记

withDocumentMarkers 设置为 true 时,写入器会为每个文档包含以下格式的标记:

### Doc: [index], pages:[start_page_number,end_page_number]

元数据处理

作者使用了两个具体的元数据键:

  • page_number:表示文档的起始页码。

  • end_page_number:表示文档的结束页码。

这些用于编写文档标记。

示例

List<Document> documents = // initialize your documents
FileDocumentWriter writer = new FileDocumentWriter("output.txt", true, MetadataMode.ALL, true);
writer.accept(documents);

这将把所有文档写入"output.txt"文件,包括文档标记,使用所有可用的元数据,并且如果文件已存在,则追加内容。

注释

  • 作者使用 FileWriter,因此它会使用操作系统的默认字符编码来写入文本文件。

  • 如果在写入过程中发生错误,则会抛出一个 RuntimeException,其原因为原始的异常。

  • metadataMode 参数允许控制如何将现有的元数据整合到写入的内容中。

  • 该写入器特别适用于调试或创建文档集合的人类可读输出。

VectorStore

提供与多种向量数据库的集成。完整列表请参阅向量数据库文档