【LlamaIndex核心组件指南 | 数据加载篇】从原始数据到向量的全链路深度解析

Langchain系列文章目录

01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
05-玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
06-从 0 到 1 掌握 LangChain Agents:自定义工具 + LLM 打造智能工作流!
07-【深度解析】从GPT-1到GPT-4:ChatGPT背后的核心原理全揭秘
08-【万字长文】MCP深度解析:打通AI与世界的“USB-C”,模型上下文协议原理、实践与未来

Python系列文章目录

PyTorch系列文章目录

机器学习系列文章目录

深度学习系列文章目录

Java系列文章目录

JavaScript系列文章目录

Llamaindex系列文章目录

01-【LlamaIndex核心组件指南 | 模型篇】一文通晓 LlamaIndex 模型层:LLM、Embedding 及多模态应用全景解析
02-【LlamaIndex核心组件指南 | Prompt篇】深度解析LlamaIndex提示模板的设计与实战
03-【LlamaIndex核心组件指南 | 数据加载篇】从原始数据到向量的全链路深度解析



前言

在人工智能技术快速发展的背景下,大语言模型(LLM)虽然能力强大,但其知识往往局限于训练数据,无法直接访问我们私有的、实时的外部数据源。如何安全、高效地将 LLM 与我们的数据连接起来,构建强大的检索增强生成(RAG)应用,已成为开发者的核心议题。LlamaIndex正是为解决这一问题而生的。

LlamaIndex 是一个领先的开源数据框架,旨在帮助开发者轻松构建、优化和部署基于自定义数据的 LLM 应用。通过 LlamaIndex,开发者可以无缝集成数据加载、索引构建、查询引擎、响应合成等一系列复杂功能,极大地简化了 RAG 应用的开发流程。

LlamaIndex 的核心组件

LlamaIndex 由一系列高度模块化的组件构成,每个组件都专注于 RAG 流程中的特定任务:

  1. Loading (数据加载)

    • 提供数百个数据连接器(Data Connectors),用于从各种来源(如本地文件、Notion、数据库、API)摄取数据。
    • 将加载的数据统一转换为标准的 Document 对象,便于后续处理。
  2. Indexing (索引构建)

    • 负责将非结构化或结构化的 Document 数据转换为 LLM 能够高效查询的数据结构(即索引)。
    • 支持多种索引类型,如向量存储索引 (VectorStoreIndex)知识图谱索引 (PropertyGraphIndex)等。
  3. Storing (持久化存储)

    • 管理数据和索引的持久化,确保应用的可扩展性和状态保持。
    • 包含文档存储 (Docstore)索引存储 (IndexStore)向量存储 (VectorStore)三大组件。
  4. Querying (查询引擎)

    • 是 RAG 的核心执行者,接收用户查询,并从索引中检索相关信息。
    • 通过响应合成 (Response Synthesis)模块将检索到的上下文和用户查询整合,生成最终答案。
  5. Models (模型)

    • 提供与各种大语言模型 (LLMs)嵌入模型 (Embeddings)多模态模型 (Multi-modal Models)交互的统一接口。
    • 是驱动整个应用理解、表示和生成信息的大脑。
  6. Agents (智能体)

    • 赋予 LLM 超越简单问答的能力,使其能够使用外部工具(如 API 调用、数据库查询)来执行多步骤的复杂任务。
    • 是构建自动化工作流和实现更高级别自主性的关键。

LlamaIndex 的应用场景

LlamaIndex 的模块化和 RAG 专注设计使其在以下场景中表现出色:

  • 智能问答与知识库:构建基于海量私有文档(PDF、Word、Notion)、数据库或企业知识图谱的智能问答机器人。
  • 文档理解与摘要:对大量、复杂的非结构化文档进行深度分析、信息提取和自动化摘要。
  • 结构化数据分析:连接到 SQL 或图数据库,用自然语言查询结构化数据并获得分析结果。
  • 自主研究智能体:创建能够主动查询外部数据源、执行代码、并综合信息生成研究报告的自动化智能体。
  • 多模态 RAG 应用:构建能够同时理解文本和图像内容,并基于图文信息进行问答的应用。

摘要

本文深度剖析了 LlamaIndex 框架中最为核心的数据处理链路,旨在为 RAG (Retrieval-Augmented Generation) 应用开发者提供一份详尽、专业且易于理解的技术指南。文章对 LlamaIndex 官网的组件文档进行了系统性的结构重组与价值提升,从基础的数据抽象 DocumentNode 讲起,逐步深入到多样化的数据加载器(如 SimpleDirectoryReaderLlamaHub)、精细化的文本切分与节点解析器 (Node Parser),再到利用大语言模型(LLM)进行自动化元数据提取,最后聚焦于终极利器——Ingestion Pipeline,它如何将整个数据处理流程串联、缓存并实现高效的自动化。本文通过丰富的代码示例、操作步骤和应用场景,帮助初学者快速上手,同时为进阶者提供高级特性的深度解读,如图文档管理、并行处理和自定义转换等。无论您是刚刚接触 LlamaIndex,还是希望优化现有 RAG 应用的数据处理流程,本文都将为您提供坚实的理论基础和宝贵的实战经验。

一、LlamaIndex 概览:构建 RAG 应用的基石

LlamaIndex 是一个强大的数据框架,旨在帮助开发者轻松地将自定义数据源与大型语言模型(LLM)连接起来。它提供了一整套工具,用于数据的获取(Ingestion)、索引(Indexing)、查询(Querying),是构建和部署生产级 RAG 应用的关键。其核心思想在于,通过高效地处理和组织外部数据,为 LLM 提供精准、相关的上下文,从而显著提升回答的准确性和相关性。

LlamaIndex 的架构是模块化的,由一系列可插拔的组件构成。理解这些核心组件是高效使用 LlamaIndex 的前提。下表概览了其主要构成部分:

核心模块 (Category)组件 (Component)核心功能
模型 (Models)LLMs, Embeddings, Multi Modal封装语言模型、嵌入模型以及多模态模型,负责文本生成、推理和数据向量化。
提示词 (Prompts)Prompt Templates提供灵活的提示词工程能力,指导 LLM 如何使用上下文并生成回应。
数据加载 (Loading)Data Connectors, Node Parsers从各种来源加载数据,并将其解析、切分为标准化的 DocumentNode 结构。
数据索引 (Indexing)Vector Store Index, Property Graph Index构建数据索引,以便于后续的高效检索。最常见的是基于向量的语义索引。
数据存储 (Storing)Vector Stores, Document Stores, Index Stores持久化存储嵌入向量、文档内容以及索引元数据。
数据查询 (Querying)Query Engines, Chat Engines, Retrievers负责接收用户查询、从索引中检索相关上下文、并合成最终的、连贯的回答。
高级组件 (Advanced)Agents, Workflows, Evaluation提供构建智能体、复杂工作流以及评估 RAG 应用性能的高级功能。

本文将聚焦于 数据加载 (Loading) 这一关键阶段,它是一切后续步骤的基础。我们将深入探讨从原始文件到可供索引的 Node 对象的完整生命周期。

二、数据基石:深入理解 DocumentNode

在 LlamaIndex 中,所有的数据处理都围绕两个核心的抽象概念展开:DocumentNode

2.1.1 Document:通用数据容器

Document 是 LlamaIndex 中表示任意数据源的通用容器。它可以是一个 PDF 文件、一个 API 的返回结果、或者数据库中的一条记录。Document 对象主要包含文本内容以及一系列描述性属性。

  • 核心属性:
    • text: 文档的主要文本内容。
    • metadata: 一个字典,用于存储关于数据的任意附加信息(元数据),如文件名、类别、创建日期等。这些元数据可以用于过滤、增强检索和溯源。
    • relationships: 一个字典,定义了该 Document 与其他 DocumentNode 之间的关系。
(1) 创建 Document 对象

Document 对象既可以由数据加载器自动创建,也可以手动进行实例化。

自动创建 (推荐):使用 LlamaIndex 提供的数据加载器(如 SimpleDirectoryReader 或 LlamaHub 上的连接器)可以自动将原始文件或数据源转换为 Document 对象列表。

from llama_index.core import SimpleDirectoryReader

# 加载器会自动为目录下的每个文件创建一个 Document 对象
documents = SimpleDirectoryReader("./data").load_data()

手动创建:在某些场景下,您可能需要根据内存中的文本列表或其他数据源手动构建 Document

from llama_index.core import Document

text_list = ["这是第一段文本。", "这是第二段文本。", "这是第三段文本。"]
documents = [Document(text=t) for t in text_list]

# 为了快速原型验证,也可以创建一个示例 Document
document = Document.example()
(2) 自定义 Document

Document 对象提供了丰富的自定义选项,以满足复杂的应用需求。

1. 添加元数据 (Metadata)

元数据对于后续的检索和响应生成至关重要。默认情况下,元数据会与文本内容一起被用于生成嵌入(Embedding)和传入 LLM。

# 方式一:在构造函数中指定
document = Document(
    text="这是一段关于金融的文本...",
    metadata={"filename": "finance_report.txt", "category": "finance"},
)

# 方式二:创建后动态添加
document.metadata = {"filename": "finance_report.txt"}

# 方式三:使用 SimpleDirectoryReader 的 file_metadata 钩子函数自动添加
# 该函数会为每个读取的文件自动调用,并将返回值作为 metadata
filename_fn = lambda filename: {"file_name": filename}
documents = SimpleDirectoryReader(
    "./data", file_metadata=filename_fn
).load_data()

2. 自定义文档ID (doc_id)

doc_idDocument 的唯一标识符,对于在索引中更新或刷新文档至关重要。

from llama_index.core import SimpleDirectoryReader

# 使用 SimpleDirectoryReader 时,可将文件路径作为 doc_id
documents = SimpleDirectoryReader("./data", filename_as_id=True).load_data()
print([x.doc_id for x in documents])

# 也可以直接手动设置
doc = Document(text="...")
doc.doc_id = "my_custom_doc_id_123"

注意doc_id 也可以通过 id_node_id 属性进行设置,因为它继承自 TextNode

3. 高级元数据定制

有时,我们希望某些元数据(如文件名)能影响嵌入的生成(以提高检索相关性),但又不希望它被 LLM 读取(以避免干扰回答的生成)。LlamaIndex 提供了精确控制元数据可见性的功能。

  • excluded_llm_metadata_keys: 设置在此列表中的元数据键将不会被 LLM 看到。
  • excluded_embed_metadata_keys: 设置在此列表中的元数据键将不会被用于生成嵌入。

此外,元数据注入文本时的格式也可以自定义:

  • metadata_seperator: 多个元数据键值对之间的分隔符,默认为 "\n"
  • metadata_template: 每个键值对的格式化模板,默认为 "{key}: {value}"
  • text_template: 最终元数据字符串与原始文本内容结合的模板,默认为 {metadata_str}\n\n{content}

下面是一个综合示例:

from llama_index.core import Document
from llama_index.core.schema import MetadataMode

document = Document(
    text="这是一个高度定制化的文档。",
    metadata={
        "file_name": "super_secret_document.txt",
        "category": "finance",
        "author": "LlamaIndex",
    },
    # LLM 将看不到 file_name
    excluded_llm_metadata_keys=["file_name"],
    # 自定义元数据格式
    metadata_seperator=" | ",
    metadata_template="{key} -> {value}",
    text_template="[元数据]: {metadata_str}\n-----\n[正文]: {content}",
)

# 查看 LLM 将会看到的内容
print(
    "LLM 看到的内容: \n",
    document.get_content(metadata_mode=MetadataMode.LLM),
)

# 查看嵌入模型将看到的内容
print(
    "\nEmbedding 模型看到的内容: \n",
    document.get_content(metadata_mode=MetadataMode.EMBED),
)

2.1.2 Node:文档的数据块

Node 代表源 Document 的一个“块”(chunk)。它可以是文本块、图像块或其他类型的数据。Node 是 LlamaIndex 中的一等公民,它继承了其父 Document 的元数据,并包含与其他 Node 的关系信息。将 Document 分割成 Node 是构建索引前的关键一步,因为 LLM 的上下文窗口大小有限,且更小的、更具针对性的文本块通常能带来更好的检索效果。

(1) 创建 Node 对象

Node 对象通常由 NodeParser(文本切分器)从 Document 列表中自动生成。

from llama_index.core import Document
from llama_index.core.node_parser import SentenceSplitter

# 假设 documents 已经加载
documents = [Document(text="这是一个很长很长的文档,需要被切分成多个节点...")]

# 使用句子切分器创建 Nodes
parser = SentenceSplitter(chunk_size=100, chunk_overlap=10)
nodes = parser.get_nodes_from_documents(documents)

当然,您也可以完全手动创建 Node 对象,并定义它们之间的关系,这在构建复杂的索引结构(如图索引)时非常有用。

from llama_index.core.schema import TextNode, NodeRelationship, RelatedNodeInfo

node1 = TextNode(text="这是第一个节点。", id_="node-1")
node2 = TextNode(text="这是第二个节点。", id_="node-2")

# 定义节点关系:node1 的下一个节点是 node2
node1.relationships[NodeRelationship.NEXT] = RelatedNodeInfo(
    node_id=node2.node_id
)
# 定义节点关系:node2 的上一个节点是 node1
node2.relationships[NodeRelationship.PREVIOUS] = RelatedNodeInfo(
    node_id=node1.node_id
)
# 关系信息中还可以附加元数据
node2.relationships[NodeRelationship.PARENT] = RelatedNodeInfo(
    node_id="document_id_abc", metadata={"source_type": "pdf_page"}
)

nodes = [node1, node2]

三、数据加载:从万物到 Document

数据加载是将外部世界的数据转换为 LlamaIndex 内部 Document 对象的入口。LlamaIndex 提供了多种灵活的加载方式。

3.1 SimpleDirectoryReader:本地文件加载利器

SimpleDirectoryReader 是从本地文件系统加载数据最简单直接的方式,非常适合快速启动项目。

(1) 支持的文件类型

它默认支持多种常见文件类型,并能根据文件扩展名自动选择合适的解析器:

  • 文本文件: .txt, .md
  • 文档: .csv, .docx, .pdf, .epub, .hwp
  • 演示文稿: .ppt, .pptm, .pptx
  • 其他: .ipynb (Jupyter Notebook), .mbox (邮件存档)
  • 图像/音视频: .jpeg, .jpg, .png, .mp3, .mp4

对于 .json 文件,推荐使用专门的 JSONLoader

(2) 核心用法
from llama_index.core import SimpleDirectoryReader

# 1. 基本用法:加载指定目录下的所有支持文件
reader = SimpleDirectoryReader(input_dir="./my_data")
documents = reader.load_data()

# 2. 并行加载:通过 num_workers 加速多文件加载
documents = reader.load_data(num_workers=4)

# 3. 递归加载:读取所有子目录中的文件
recursive_reader = SimpleDirectoryReader(input_dir="./my_data", recursive=True)

# 4. 迭代加载:逐个文件加载并处理
all_docs = []
for docs_per_file in recursive_reader.iter_data():
    # 对单个文件的文档列表进行处理
    all_docs.extend(docs_per_file)

# 5. 文件筛选
# 只加载指定文件
file_reader = SimpleDirectoryReader(input_files=["./data/file1.pdf", "./data/file2.txt"])
# 排除指定文件
exclude_reader = SimpleDirectoryReader(input_dir="./data", exclude=["./data/temp.txt"])
# 只加载特定扩展名的文件
ext_reader = SimpleDirectoryReader(input_dir="./data", required_exts=[".pdf", ".docx"])

# 6. 指定文件编码
encoding_reader = SimpleDirectoryReader(input_dir="./data", encoding="latin-1")
(3) 扩展与定制

自定义元数据提取:如前所述,可以使用 file_metadata 参数传入一个函数来定制每个文件的元数据。

扩展支持新文件类型:通过 file_extractor 参数,您可以为 SimpleDirectoryReader 添加对新文件类型的支持。

from llama_index.core.readers.base import BaseReader
from llama_index.core import Document, SimpleDirectoryReader

# 自定义一个读取 .myfile 文件的 Reader
class MyFileReader(BaseReader):
    def load_data(self, file, extra_info=None):
        with open(file, "r", encoding="utf-8") as f:
            text = f.read()
        return [Document(text=text + " [Custom Loaded]", extra_info=extra_info or {})]

# 将 .myfile 扩展名映射到自定义的 Reader 实例
reader = SimpleDirectoryReader(
    input_dir="./data", file_extractor={".myfile": MyFileReader()}
)
documents = reader.load_data()
print(documents)

支持远程文件系统:通过 fs 参数,SimpleDirectoryReader 可以读取如 AWS S3、Google Drive 等远程文件系统中的数据,前提是安装了相应的 fsspec 库(如 s3fs)。

# 示例:从 S3 加载数据
from s3fs import S3FileSystem
from llama_index.core import SimpleDirectoryReader

s3_fs = S3FileSystem(key="...", secret="...")
bucket_name = "my-document-bucket"

reader = SimpleDirectoryReader(
    input_dir=bucket_name,
    fs=s3_fs,
    recursive=True,
)
documents = reader.load_data()

3.2 LlamaHub 数据连接器:连接无限数据源

当您的数据源不是本地文件时,LlamaHub 就是您的宝库。它是一个开源的、由社区贡献的庞大数据加载器(Reader)仓库,支持数百种数据源,如 Notion, Slack, Discord, Google Docs, 数据库等。

使用模式:
使用 LlamaHub 的核心是 download_loader 函数,它会从仓库下载并加载指定的 Reader 模块。

from llama_index.core import VectorStoreIndex, download_loader

# 假设我们要从 Google Docs 加载
# from llama_index.readers.google import GoogleDocsReader # 新版本可以直接导入

# 使用 download_loader (适用于社区贡献的加载器)
GoogleDocsReader = download_loader("GoogleDocsReader")

# 你的 Google Doc ID 列表
gdoc_ids = ["your_google_doc_id_here"]
loader = GoogleDocsReader()
documents = loader.load_data(document_ids=gdoc_ids)

# 后续可以构建索引
index = VectorStoreIndex.from_documents(documents)

3.3 LlamaParse:专业级文档解析服务

LlamaParse 是 LlamaIndex 官方推出的一项托管API服务,专为高效解析复杂文档(特别是PDF)而设计。它能够更好地处理表格、图表和复杂的布局,生成更适合RAG应用的结构化表示。它支持 PDF, Word, PowerPoint, Excel 等多种文件格式,并能与 LlamaIndex 无缝集成。

四、文本切分与节点解析 (Node Parser)

加载 Document 之后,下一步是将其切分为更小的 Node 块,这个过程由 NodeParser(也称为 TextSplitter)完成。选择合适的切分策略对 RAG 的最终效果至关重要。

通用使用模式:
NodeParser 可以独立使用,也可以作为转换(Transformation)步骤集成到索引构建或 Ingestion Pipeline 中。

from llama_index.core import Document, VectorStoreIndex, Settings
from llama_index.core.node_parser import SentenceSplitter

# 1. 独立使用
node_parser = SentenceSplitter(chunk_size=512, chunk_overlap=20)
nodes = node_parser.get_nodes_from_documents([Document(text="...")])

# 2. 在构建索引时作为转换步骤
index = VectorStoreIndex.from_documents(
    documents,
    transformations=[SentenceSplitter(chunk_size=512, chunk_overlap=20)],
)

# 3. 设置为全局默认切分器
Settings.text_splitter = SentenceSplitter(chunk_size=512, chunk_overlap=20)
# 之后所有 .from_documents() 调用都会默认使用此切分器
index_global = VectorStoreIndex.from_documents(documents)

LlamaIndex 提供了多种类型的 NodeParser

4.1 基于文本内容的切分器

这类切分器关注文本本身,而不关心其原始文件格式。

  • TokenTextSplitter: 基于 Token 数量进行切分,是最基础的切分方式。
    from llama_index.core.node_parser import TokenTextSplitter
    splitter = TokenTextSplitter(chunk_size=256, chunk_overlap=20, separator=" ")
    
  • SentenceSplitter: 尝试在句子边界进行切分,尽可能保持句子的完整性,是推荐的通用切分器。
    from llama_index.core.node_parser import SentenceSplitter
    splitter = SentenceSplitter(chunk_size=1024, chunk_overlap=20)
    
  • SemanticSplitterNodeParser: “语义切分”,这是一种更智能的切分方法。它不依赖固定的块大小,而是通过计算句子之间嵌入向量的相似度来决定断点,从而确保每个 Node 内部的句子在语义上是高度相关的。
    from llama_index.core.node_parser import SemanticSplitterNodeParser
    from llama_index.embeddings.openai import OpenAIEmbedding
    
    splitter = SemanticSplitterNodeParser(
        buffer_size=1, breakpoint_percentile_threshold=95, embed_model=OpenAIEmbedding()
    )
    
  • CodeSplitter: 专门用于切分代码文件,支持多种编程语言。
    from llama_index.core.node_parser import CodeSplitter
    splitter = CodeSplitter(language="python", chunk_lines=40, chunk_lines_overlap=15)
    

4.2 基于文件结构的解析器

这类解析器理解特定文件的结构,如 HTML, JSON, Markdown。

  • HTMLNodeParser: 使用 BeautifulSoup 解析HTML,并根据指定的标签(如 p, h1, li)提取内容块。
  • JSONNodeParser: 解析 JSON 数据。
  • MarkdownNodeParser: 解析 Markdown 文本。

4.3 高级切分策略

  • SentenceWindowNodeParser: 此解析器将文档切分为单个句子,但在每个 Node 的元数据中包含了该句子前后的“上下文窗口”(即周围的句子)。这使得在生成嵌入时可以聚焦于单个句子的精确语义,而在后续传递给 LLM 时,可以通过后处理器(MetadataReplacementNodePostProcessor)将上下文窗口替换回来,为 LLM 提供更完整的信息。
    from llama_index.core.node_parser import SentenceWindowNodeParser
    parser = SentenceWindowNodeParser.from_defaults(window_size=3)
    
  • HierarchicalNodeParser: “层级节点解析器”,它会将文档切分成多个不同尺寸的块(例如,2048、512、128个字符),并建立它们之间的父子关系。当与 AutoMergingRetriever 结合使用时,如果检索器发现多个较小的子节点被命中,它会自动向上合并,返回它们的父节点,从而为 LLM 提供一个更宏观、更完整的上下文。
    from llama_index.core.node_parser import HierarchicalNodeParser
    parser = HierarchicalNodeParser.from_defaults(chunk_sizes=[2048, 512, 128])
    

五、自动化元数据提取

除了手动添加元数据,LlamaIndex 还可以利用 LLM 的能力来自动从文本中提取有价值的元数据,进一步增强 Node 的信息含量。

MetadataExtractor 模块可以实现这一功能,它包含多种“特征提取器”:

  • SummaryExtractor: 为每个 Node 生成一段摘要。
  • QuestionsAnsweredExtractor: 为每个 Node 提取它能够回答的一系列问题。
  • TitleExtractor: 为每个 Node 的内容提取一个标题。
  • EntityExtractor: 从 Node 的内容中提取实体(如人名、地名、组织名)。

这些提取器可以无缝地集成到数据处理流程中,通常是在文本切分之后。

from llama_index.core.extractors import TitleExtractor, QuestionsAnsweredExtractor
from llama_index.core.node_parser import TokenTextSplitter
from llama_index.core.ingestion import IngestionPipeline

# 1. 定义转换链
text_splitter = TokenTextSplitter(separator=" ", chunk_size=512, chunk_overlap=128)
title_extractor = TitleExtractor(nodes=5) # nodes=5表示每5个节点生成一个标题
qa_extractor = QuestionsAnsweredExtractor(questions=3) # questions=3表示每个节点提取3个问题

# 2. 在 Ingestion Pipeline 中使用
pipeline = IngestionPipeline(
    transformations=[text_splitter, title_extractor, qa_extractor]
)
nodes = pipeline.run(documents=documents, show_progress=True)

# 3. 或者直接在构建索引时使用
index = VectorStoreIndex.from_documents(
    documents, transformations=[text_splitter, title_extractor, qa_extractor]
)

执行后,提取出的标题、问题等会自动添加到每个 Nodemetadata 字典中。

六、终极武器:构建高效 Ingestion Pipeline

IngestionPipeline (摄取流水线) 是 LlamaIndex 中用于实现端到端、可重复、可缓存的数据处理流程的核心组件。它将一系列的 Transformation (转换)应用于输入的 Document,最终生成 Node 列表,并可以选择性地将其存入向量数据库。

6.1 核心用法

(1) 定义并运行流水线

最简单的用法是定义一个包含多个转换步骤的流水线。

from llama_index.core import Document
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.extractors import TitleExtractor
from llama_index.core.ingestion import IngestionPipeline

# 定义流水线,包含切分、标题提取和嵌入生成三个步骤
pipeline = IngestionPipeline(
    transformations=[
        SentenceSplitter(chunk_size=256, chunk_overlap=20),
        TitleExtractor(),
        OpenAIEmbedding(), # 注意:如果要存入向量库,必须包含嵌入生成
    ]
)

# 运行流水线
# 在真实场景中,documents 来自 SimpleDirectoryReader 等加载器
nodes = pipeline.run(documents=[Document.example()])
(2) 连接向量数据库

您可以将流水线直接连接到一个向量数据库,处理后的 Node 会被自动存入。

import qdrant_client
from llama_index.vector_stores.qdrant import QdrantVectorStore

# 假设使用 Qdrant 作为向量库
client = qdrant_client.QdrantClient(location=":memory:")
vector_store = QdrantVectorStore(client=client, collection_name="my_collection")

pipeline = IngestionPipeline(
    transformations=[
        SentenceSplitter(chunk_size=256, chunk_overlap=20),
        TitleExtractor(),
        OpenAIEmbedding(),
    ],
    vector_store=vector_store, # 指定向量库
)

# 直接运行,节点将被自动处理并存入 Qdrant
pipeline.run(documents=[Document.example()])

# 后续可以直接从向量库构建索引,无需再次处理数据
from llama_index.core import VectorStoreIndex
index = VectorStoreIndex.from_vector_store(vector_store)

6.2 高级特性

(1) 缓存 (Caching)

IngestionPipeline 的一大优势是其内置的缓存机制。它会为每个(输入节点 + 转换步骤)的组合计算一个哈希值,并将结果缓存起来。当再次运行流水线处理相同的数据时,它会直接从缓存中读取结果,从而极大地节省了时间和API调用成本(特别是对于嵌入生成)。

  • 本地缓存管理:

    # 将流水线状态(包括缓存)持久化到本地磁盘
    pipeline.persist("./pipeline_storage")
    
    # 在新的会话中,加载已有的流水线状态
    new_pipeline = IngestionPipeline(transformations=[...]) # 需定义相同的转换
    new_pipeline.load("./pipeline_storage")
    
    # 再次运行相同的数据,会瞬间完成
    nodes = new_pipeline.run(documents=[Document.example()])
    
  • 远程缓存管理: 支持 Redis, MongoDB, Firestore 等后端。

    from llama_index.storage.kvstore.redis import RedisKVStore as RedisCache
    from llama_index.core.ingestion import IngestionCache
    
    # 使用 Redis 作为缓存后端
    ingest_cache = IngestionCache(
        cache=RedisCache.from_host_and_port(host="127.0.0.1", port=6379),
        collection="my_cache_collection",
    )
    
    pipeline = IngestionPipeline(
        transformations=[...],
        cache=ingest_cache,
    )
    # 运行流水线,缓存会自动写入 Redis,无需手动 persist
    
(2) 文档管理 (Document Management)

通过为流水线配置 docstore,可以激活文档管理功能。它利用 document.doc_id 来跟踪和管理文档的更新。

  • 工作机制:
    1. 流水线维护一个 doc_id 到文档哈希值的映射。
    2. 当遇到一个已存在的 doc_id 时,它会比较新旧文档的哈希值。
    3. 如果哈希值已改变,说明文档已更新,流水线将重新处理该文档并更新(upsert)向量库中的相应节点。
    4. 如果哈希值未变,说明文档未更新,则跳过处理,节省资源。
from llama_index.core.storage.docstore import SimpleDocumentStore

pipeline = IngestionPipeline(
    transformations=[...],
    docstore=SimpleDocumentStore(),
    vector_store=vector_store,
)
(3) 并行处理 (Parallel Processing)

通过设置 num_workers 参数,可以利用多进程并行执行数据转换,显著提升处理大量文档时的速度。

pipeline.run(documents=documents, num_workers=4)
(44) 异步支持 (Async Support)

IngestionPipeline 也完全支持异步操作。

nodes = await pipeline.arun(documents=documents)

七、Transformation 抽象:自定义你的处理流程

IngestionPipeline 的背后,是统一的 Transformation 抽象。任何实现了 TransformComponent基类的组件都可以被视为一个转换步骤,并被无缝地集成到流水线中。

目前,以下核心组件都是 Transformation 的实例:

  • TextSplitter / NodeParser
  • MetadataExtractor
  • Embedding 模型

这意味着你可以轻松地创建自定义的数据处理步骤。

示例:创建一个自定义的文本清理转换

下面的 TextCleaner 会移除节点文本中的所有非字母数字字符。

import re
from llama_index.core.schema import TransformComponent

class TextCleaner(TransformComponent):
    def __call__(self, nodes, **kwargs):
        for node in nodes:
            # 使用正则表达式移除特殊字符和标点
            node.text = re.sub(r"[^0-9A-Za-z ]", "", node.text)
        return nodes
    
    async def acall(self, nodes, **kwargs):
        # 异步版本(可选实现)
        return self.__call__(nodes, **kwargs)

# 在流水线中使用自定义转换
pipeline = IngestionPipeline(
    transformations=[
        SentenceSplitter(chunk_size=256, chunk_overlap=0),
        TextCleaner(), # 将自定义组件加入流水线
        OpenAIEmbedding(),
    ],
)

八、总结

本文系统地梳理了 LlamaIndex 框架中数据加载和处理的核心链路,通过对官方文档的重构和深化,我们描绘了一幅从异构数据源到结构化、可检索的 Node 对象的完整路线图。

核心要点回顾:

  1. 两大基石: Document 作为通用数据容器,Node 作为其原子化的数据块,是 LlamaIndex 数据处理的基础。深入理解并善用它们的 metadatarelationship 属性是优化 RAG 应用的关键。
  2. 三类加载器: SimpleDirectoryReader 满足本地文件加载需求,LlamaHub 连接海量第三方数据源,而 LlamaParse 为复杂文档的解析提供了专业解决方案。
  3. 多维切分策略: NodeParser 的选择直接影响检索效果。从基础的 Token/SentenceSplitter,到智能的 SemanticSplitter,再到面向特定场景的 SentenceWindowHierarchical 解析器,LlamaIndex 提供了丰富的工具箱。
  4. 自动化元数据: 利用 MetadataExtractor 可以借助 LLM 自动为数据块生成摘要、标题、可回答问题等丰富元数据,极大地提升了数据的可利用价值。
  5. 一个终极武器: IngestionPipeline 将上述所有步骤(加载、切分、提取、嵌入)有机地串联起来,并通过缓存、文档管理、并行处理等高级功能,构建了一个高效、可维护、可扩展的生产级数据摄取流水线。
  6. 无限扩展性: Transformation 抽象允许开发者轻松创建自定义处理逻辑,并将其无缝集成到现有工作流中,体现了 LlamaIndex 框架高度的灵活性和可扩展性。

掌握 LlamaIndex 的数据加载模块,是构建高性能 RAG 应用的第一步,也是最重要的一步。希望本文能帮助您在这条路上走得更稳、更远。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吴师兄大模型

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值