ETL 管道
在检索增强生成(RAG)用例中,提取、转换和加载(ETL)框架是数据处理的支柱。
ETL 管道协调从原始数据源到结构化向量存储的数据流,确保数据以最佳格式供 AI 模型检索。
RAG(Retrieval-Augmented Generation)用例是通过从数据体中检索相关信息来增强生成模型的能力,从而提高生成输出的质量和相关性。
API 概览
ETL 管道创建、转换并存储 Document 实例。

Document 类包含文本、元数据以及可选的附加媒体类型,如图像、音频和视频。
ETL 管道主要由三个组件组成,
- 
DocumentReader,实现了Supplier<List<Document>>接口
- 
DocumentTransformer,实现了Function<List<Document>, List<Document>>接口
- 
DocumentWriter,实现了Consumer<List<Document>>接口
Document 类的内容是通过 DocumentReader 从 PDF、文本文件和其他文档类型中创建的。
为了构建一个简单的 ETL 管道,你可以将每种类型的实例链接在一起。

假设我们有以下三种 ETL 类型的实例
- 
PagePdfDocumentReader是DocumentReader的一个实现
- 
TokenTextSplitter是DocumentTransformer的一个实现
- 
VectorStore是DocumentWriter的一个实现
为了将数据基本加载到 Vector Database 中以用于 Retrieval Augmented Generation 模式,请在 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 接口及其实现。

文档读取器
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 提供了多个构造函数选项:
- 
JsonReader(Resource resource)
- 
JsonReader(Resource resource, String… jsonKeysToUse)
- 
JsonReader(Resource resource, JsonMetadataGenerator jsonMetadataGenerator, String… jsonKeysToUse)
翻译为中文:
- 
JsonReader(Resource resource)
- 
JsonReader(Resource resource, String… jsonKeysToUse)
- 
JsonReader(Resource resource, JsonMetadataGenerator jsonMetadataGenerator, String… jsonKeysToUse)
由于这些内容是代码片段,且包含专有名词和方法签名,因此保持原文不变。
参数
- 
resource: 一个指向 JSON 文件的 SpringResource对象。
- 
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 Pointer 来检索 JSON 文档的特定部分。
参数
- pointer: 一个 JSON Pointer 字符串(如 RFC 6901 中所定义),用于在 JSON 结构中定位所需的元素。
返回值
- 返回一个 List<Document>,包含从指针定位的 JSON 元素解析出的文档。
行为
- 
该方法使用提供的 JSON Pointer 在 JSON 结构中导航到特定位置。 
- 
如果指针有效并指向一个现有元素: - 
对于 JSON 对象:它返回一个包含单个 Document 的列表。 
- 
对于 JSON 数组:它返回一个 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中指定了多个键,内容将是这些键对应值的连接。
- 
该读取器具有灵活性,可以通过自定义 jsonKeysToUse和JsonMetadataGenerator来适应各种 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 提供了两种构造函数选项:
- 
TextReader(String resourceUrl)
- 
TextReader(Resource resource)
参数
- 
resourceUrl: 一个字符串,表示要读取的资源的 URL。
- 
resource: 一个 SpringResource对象,指向文本文件。
配置
- 
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()方法向读取器创建的所有文档添加自定义元数据。
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 允许你自定义 MarkdownDocumentReader 的行为:
- 
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 内容并根据配置创建 Document 对象:
- 
标题成为 Document 对象中的元数据。 
- 
段落成为 Document 对象的内容。 
- 
代码块可以被分离成它们自己的 Document 对象,或者与周围的文本一起包含。 
- 
引用块可以被分离成它们自己的 Document 对象,或者与周围的文本一起包含。 
- 
水平分割线可以用来将内容分割成单独的 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();
    }
}
变换器
文本分割器
TextSplitter 是一个抽象基类,用于帮助将文档分割以适应 AI 模型的上下文窗口。
TokenTextSplitter
TokenTextSplitter 是 TextSplitter 的一个实现,它使用 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 提供了两个构造函数选项:
- 
TokenTextSplitter(): 使用默认设置创建一个分割器。
- 
TokenTextSplitter(int defaultChunkSize, int minChunkSizeChars, int minChunkLengthToEmbed, int maxNumChunks, boolean keepSeparator)
参数
- 
defaultChunkSize: 每个文本块的目标大小,以 token 为单位(默认值:800)。
- 
minChunkSizeChars: 每个文本块的最小大小,以字符为单位(默认值:350)。
- 
minChunkLengthToEmbed: 包含的文本块的最小长度(默认值:5)。
- 
maxNumChunks: 从文本生成的最大块数(默认值:10000)。
- 
keepSeparator: 是否在块中保留分隔符(如换行符)(默认值:true)。
行为
TokenTextSplitter 处理文本内容的方式如下:
- 
它使用 CL100K_BASE 编码将输入文本编码为 tokens。 
- 
它根据 defaultChunkSize将编码后的文本分割成块。
- 
对于每个块: - 
它将块解码回文本。 
- 
它尝试在 minChunkSizeChars之后找到一个合适的分割点(句号、问号、感叹号或换行符)。
- 
如果找到分割点,它会在该点处截断块。 
- 
它会修剪块,并根据 keepSeparator设置选择性地移除换行符。
- 
如果生成的块长度大于 minChunkLengthToEmbed,则将其添加到输出中。
 
- 
- 
此过程会持续进行,直到所有 tokens 都被处理完毕或达到 maxNumChunks。
- 
如果剩余文本的长度大于 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 = new KeywordMetadataEnricher(this.chatModel, 5);
        return enricher.apply(documents);
    }
}
构造函数
KeywordMetadataEnricher 构造函数接受两个参数:
- 
ChatModel chatModel: 用于生成关键词的 AI 模型。
- 
int keywordCount: 为每个文档提取的关键词数量。
行为
KeywordMetadataEnricher 处理文档的过程如下:
- 
对于每个输入文档,它会使用文档的内容创建一个提示(prompt)。 
- 
它将这个提示发送给提供的 ChatModel以生成关键词。
- 
生成的关键词被添加到文档的元数据中,键为 "excerpt_keywords"。 
- 
返回经过增强的文档。 
自定义
关键词提取提示可以通过修改类中的 KEYWORDS_TEMPLATE 常量来自定义。默认模板如下:
\{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 = new KeywordMetadataEnricher(chatModel, 5);
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" 元数据字段。 
- 
生成的关键词以逗号分隔的字符串形式返回。 
- 
该丰富器特别适用于提高文档的可搜索性以及为文档生成标签或类别。 
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 提供了两个构造函数:
- 
SummaryMetadataEnricher(ChatModel chatModel, List<SummaryType> summaryTypes)
- 
SummaryMetadataEnricher(ChatModel chatModel, List<SummaryType> summaryTypes, String summaryTemplate, MetadataMode metadataMode)
参数
- 
chatModel: 用于生成摘要的 AI 模型。
- 
summaryTypes: 一个SummaryType枚举值的列表,指示要生成哪些摘要(PREVIOUS、CURRENT、NEXT)。
- 
summaryTemplate: 用于生成摘要的自定义模板(可选)。
- 
metadataMode: 指定在生成摘要时如何处理文档元数据(可选)。
行为
SummaryMetadataEnricher 处理文档的流程如下:
- 
对于每个输入文档,它使用文档内容和指定的摘要模板创建一个提示。 
- 
它将此提示发送到提供的 ChatModel以生成摘要。
- 
根据指定的 summaryTypes,它向每个文档添加以下元数据:- 
section_summary:当前文档的摘要。
- 
prev_section_summary:前一个文档的摘要(如果可用且请求)。
- 
next_section_summary:下一个文档的摘要(如果可用且请求)。
 
- 
- 
返回经过增强的文档。 
自定义
摘要生成的提示可以通过提供自定义的 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来生成摘要。
- 
该 enricher 能够处理任意大小的文档列表,并正确处理第一个和最后一个文档的边缘情况。 
- 
该 enricher 特别适用于创建上下文感知的摘要,从而更好地理解文档序列中的关系。 
- 
MetadataMode参数允许控制现有元数据如何被整合到摘要生成过程中。
写入器
文件
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 提供了三个构造函数:
- 
FileDocumentWriter(String fileName)
- 
FileDocumentWriter(String fileName, boolean withDocumentMarkers)
- 
FileDocumentWriter(String fileName, boolean withDocumentMarkers, MetadataMode metadataMode, boolean append)
参数
- 
fileName: 要写入文档的文件名。
- 
withDocumentMarkers: 是否在输出中包含文档标记(默认值:false)。
- 
metadataMode: 指定要写入文件的文档内容(默认值:MetadataMode.NONE)。
- 
append: 如果为 true,数据将写入文件的末尾而不是开头(默认值:false)。
行为
FileDocumentWriter 处理文档的流程如下:
- 
它为指定的文件名打开一个 FileWriter。
- 
对于输入列表中的每个文档: - 
如果 withDocumentMarkers为true,则写入一个文档标记,包括文档索引和页码。
- 
根据指定的 metadataMode写入文档的格式化内容。
 
- 
- 
在所有文档写入完成后,关闭文件。 
文档标记
当 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参数允许控制如何将现有元数据合并到写入的内容中。
- 
此写入器特别适用于调试或创建文档集合的人类可读输出。 
向量存储
提供与各种向量存储的集成。完整列表请参见向量数据库文档。