ETL 管道

提取、转换和加载 (ETL) 框架是检索增强生成 (RAG) 使用案例中数据处理的基石。

ETL 管道协调从原始数据源到结构化向量存储的数据流,确保数据以最佳格式供 AI 模型检索。

RAG 使用案例是文本,通过从数据主体检索相关信息来增强生成模型的功能,从而提高生成输出的质量和相关性。

API 概述

ETL 管道创建、转换和存储 Document 实例。

Spring AI Message API

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

ETL 管道主要包含三个组件:

  • DocumentReader 实现 Supplier<List<Document>>

  • DocumentTransformer 实现 Function<List<Document>, List<Document>>

  • DocumentWriter 实现 Consumer<List<Document>>

Document 类的内容是借助 DocumentReader 从 PDF、文本文件和其他文档类型创建的。

要构建一个简单的 ETL 管道,您可以将每个类型的实例链接在一起。

etl pipeline

假设我们有以下三个 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 class diagram

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 指针字符串(如 RFC 6901 中所定义),用于在 JSON 结构中定位所需元素。

返回值
  • 返回一个 List<Document>,其中包含从指针定位的 JSON 元素解析的文档。

行为
  • 该方法使用提供的 JSON 指针在 JSON 结构中导航到特定位置。

  • 如果指针有效且指向现有元素

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

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

  • 如果指针无效或指向不存在的元素,则会抛出 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() 方法将自定义元数据添加到读取器创建的所有文档中。

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 对象。

读取器保留格式,如内联代码、列表和 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 目录(例如 TOC)信息将输入 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

TextSplitter 是一个抽象基类,有助于将文档划分为适合 AI 模型上下文窗口的大小。

TokenTextSplitter

TokenTextSplitterTextSplitter 的一个实现,它根据令牌数量将文本拆分为块,使用 CL100K_BASE 编码。

用法

@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 = new KeywordMetadataEnricher(this.chatModel, 5);
        return enricher.apply(documents);
    }
}

构造函数

KeywordMetadataEnricher 构造函数采用两个参数

  1. ChatModel chatModel:用于生成关键词的 AI 模型。

  2. int keywordCount:为每个文档提取的关键词数量。

行为

KeywordMetadataEnricher 按如下方式处理文档

  1. 对于每个输入文档,它使用文档的内容创建一个提示。

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

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

  4. 返回增强的文档。

自定义

可以通过修改类中的 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 提供两个构造函数

  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 参数允许控制如何将现有元数据合并到摘要生成过程中。

编写器

文件

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 参数允许控制如何将现有元数据合并到写入的内容中。

  • 此编写器对于调试或创建文档集合的人类可读输出特别有用。

向量存储

提供与各种向量存储的集成。有关完整列表,请参阅 向量数据库文档