数据引入

数据引入是收集、读取和准备来自不同源(如文件、数据库、API 或云服务)的数据的过程,以便在下游应用程序中使用它。 实际上,此过程遵循 Extract-Transform-Load (ETL) 工作流:

  • 从其原始源(无论是 PDF、Word 文档、音频文件还是 Web API)中提取数据。
  • 通过清理、分块、扩充或转换格式来转换数据。
  • 将数据加载到目标(如数据库、矢量存储或 AI 模型)中,以便检索和分析。

对于 AI 和机器学习方案(尤其是检索扩充生成(RAG),数据引入不仅仅是将数据从一种格式转换为另一种格式。 它用于使数据可用于智能应用程序。 这意味着,以保留其结构和含义的方式表示文档,将其拆分为可管理的区块,使用元数据或嵌入内容丰富文档,并存储文档,以便快速准确地检索它们。

为什么数据引入对于 AI 应用程序很重要

假设你正在构建一个 RAG 驱动的聊天机器人,以帮助员工查找公司大量文档集合中的信息。 这些文档可能包括 PDF、Word 文件、PowerPoint 演示文稿和分散在不同系统中的网页。

聊天机器人需要了解并搜索数千个文档,以提供准确的上下文答案。 但原始文档不适用于 AI 系统。 你需要将它们转换为保留含义的格式,同时使其可搜索和可检索。

这是数据引入变得至关重要的地方。 你需要从不同的文件格式中提取文本,将大型文档分解为适合 AI 模型限制的较小区块,使用元数据丰富内容,生成语义搜索的嵌入内容,以及以启用快速检索的方式存储所有内容。 每个步骤都需要仔细考虑如何保留原始含义和上下文。

数据摄取构建模块

Microsoft.Extensions.DataIngestion 库是围绕多个关键组件构建的,这些组件共同创建完整的数据处理管道。 本部分介绍每个组件及其组合方式。

文档和文档阅读器

在库的基础是 IngestionDocument 类型,它提供了一种统一的方式来表示任何文件格式,而不会丢失重要信息。 IngestionDocument 以 Markdown 为中心,因为大型语言模型最适合 Markdown 格式。

IngestionDocumentReader抽象层负责从各种来源加载文档,无论是本地文件还是流。 有一些读卡器可供使用:

将来将添加更多读者(包括 LlamaParseAzure 文档智能)。

此设计意味着你可以使用同一一个一致的 API 处理来自不同源的文档,使代码更易于维护且更灵活。

文档处理

文档处理器在文档级别应用转换以增强和准备内容。 该库提供ImageAlternativeTextEnricher类作为内置处理器,利用大型语言模型为文档中的图像生成描述性替代文本。

区块和分块策略

加载文档后,通常需要将其分解为称为区块的较小部分。 区块表示可由 AI 系统有效处理、存储和检索的文档的子节。 此分块过程对于检索扩充的生成方案至关重要,需要快速查找最相关的信息片段。

该库提供了多个分块策略来适应不同的用例:

  • 用于拆分标头的基于标头的分块
  • 基于分区的分块 ,用于按分区进行拆分(例如页面)。
  • 语义感知分块 以保留完整的想法。

这些分块策略基于 Microsoft.ML.Tokenizers 库构建,以智能地将文本拆分为适合大型语言模型的适当大小的片段。 正确的分块策略取决于文档类型和计划检索信息的方式。

Tokenizer tokenizer = TiktokenTokenizer.CreateForModel("gpt-5");
IngestionChunkerOptions options = new(tokenizer)
{
    MaxTokensPerChunk = 2000,
    OverlapTokens = 0
};
IngestionChunker<string> chunker = new HeaderChunker(options);

区块处理和扩充

将文档拆分为区块后,可以应用处理器来增强和丰富内容。 区块处理器处理各个部分,可以执行以下作:

  • 内容扩充 包括自动摘要(SummaryEnricher)、情绪分析(SentimentEnricher)和关键字提取(KeywordEnricher)。
  • 基于预定义类别()自动内容分类的ClassificationEnricher

这些处理器使用 Microsoft.Extensions.AI.Abstractions 利用大型语言模型进行智能内容转换,使区块更适用于下游 AI 应用程序。

文档编写器和存储

IngestionChunkWriter<T> 将已处理的区块存储在数据存储中供以后检索。 使用Microsoft.Extensions.AIMicrosoft.Extensions.VectorData的库提供VectorStoreWriter<T>类。 此编写器支持将区块存储在受支持的任何Microsoft.Extensions.VectorData中。

矢量存储包括常用的选项,如 QdrantSQL ServerCosmosDBMongoDBElasticSearch。 有关提供程序的详细信息,请参阅现成的矢量存储提供程序。 (尽管包名称中包含“SemanticKernel”,但这些提供程序与语义内核无关,并且可用于 .NET 中的任何位置,包括 Agent Framework)。

编写器还可以用Microsoft.Extensions.AI自动为您的数据块生成嵌入式向量,从而为语义搜索和检索方案做好准备。

OpenAIClient openAIClient = new(
    new ApiKeyCredential(Environment.GetEnvironmentVariable("GITHUB_TOKEN")!),
    new OpenAIClientOptions { Endpoint = new Uri("https://models.github.ai/inference") });

IEmbeddingGenerator<string, Embedding<float>> embeddingGenerator =
    openAIClient.GetEmbeddingClient("text-embedding-3-small").AsIEmbeddingGenerator();

using SqliteVectorStore vectorStore = new(
    "Data Source=vectors.db;Pooling=false",
    new()
    {
        EmbeddingGenerator = embeddingGenerator
    });

// The writer requires the embedding dimension count to be specified.
// For OpenAI's `text-embedding-3-small`, the dimension count is 1536.
using VectorStoreWriter<string> writer = new(vectorStore, dimensionCount: 1536);

文档处理管道

API IngestionPipeline<T> 允许将各种数据引入组件链接到完整的工作流中。 可以组合:

  • 读取器 从各种源加载文档。
  • 用于转换和扩充文档内容的处理器
  • 分块器 将文档分解为可管理的部分。
  • 用于将最终结果存储在所选数据存储中的编写器

此管道方法可减少样本代码,并可轻松生成、测试和维护复杂的数据引入工作流。

using IngestionPipeline<string> pipeline = new(reader, chunker, writer, loggerFactory: loggerFactory)
{
    DocumentProcessors = { imageAlternativeTextEnricher },
    ChunkProcessors = { summaryEnricher }
};

await foreach (var result in pipeline.ProcessAsync(new DirectoryInfo("."), searchPattern: "*.md"))
{
    Console.WriteLine($"Completed processing '{result.DocumentId}'. Succeeded: '{result.Succeeded}'.");
}

单个文档引入失败不应导致整个管道失败。 这就是IngestionPipeline<T>.ProcessAsync通过返回IAsyncEnumerable<IngestionResult>来实现部分成功的原因。 调用方负责处理任何失败(例如,通过重试失败的文档或在第一个错误时停止处理)。