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”,模型上下文协议原理、实践与未来
01-【LlamaIndex核心组件指南 | 模型篇】一文通晓 LlamaIndex 模型层:LLM、Embedding 及多模态应用全景解析
02-【LlamaIndex核心组件指南 | Prompt篇】深度解析LlamaIndex提示模板的设计与实战
03-【LlamaIndex核心组件指南 | 数据加载篇】从原始数据到向量的全链路深度解析
在人工智能技术快速发展的背景下,大语言模型(LLM)
虽然能力强大,但其知识往往局限于训练数据,无法直接访问我们私有的、实时的外部数据源。如何安全、高效地将 LLM 与我们的数据连接起来,构建强大的检索增强生成(RAG)
应用,已成为开发者的核心议题。LlamaIndex
正是为解决这一问题而生的。
LlamaIndex 是一个领先的开源数据框架,旨在帮助开发者轻松构建、优化和部署基于自定义数据的 LLM 应用。通过 LlamaIndex,开发者可以无缝集成数据加载、索引构建、查询引擎、响应合成等一系列复杂功能,极大地简化了 RAG 应用的开发流程。
LlamaIndex 的核心组件
LlamaIndex 由一系列高度模块化的组件构成,每个组件都专注于 RAG 流程中的特定任务:
Loading (数据加载)
Document
对象,便于后续处理。Indexing (索引构建)
Document
数据转换为 LLM 能够高效查询的数据结构(即索引)。向量存储索引 (VectorStoreIndex)
、知识图谱索引 (PropertyGraphIndex)
等。Storing (持久化存储)
文档存储 (Docstore)
、索引存储 (IndexStore)
和向量存储 (VectorStore)
三大组件。Querying (查询引擎)
响应合成 (Response Synthesis)
模块将检索到的上下文和用户查询整合,生成最终答案。Models (模型)
大语言模型 (LLMs)
、嵌入模型 (Embeddings)
和多模态模型 (Multi-modal Models)
交互的统一接口。Agents (智能体)
LlamaIndex 的应用场景
LlamaIndex 的模块化和 RAG 专注设计使其在以下场景中表现出色:
本文深度剖析了 LlamaIndex 框架中最为核心的数据处理链路,旨在为 RAG (Retrieval-Augmented Generation) 应用开发者提供一份详尽、专业且易于理解的技术指南。文章对 LlamaIndex 官网的组件文档进行了系统性的结构重组与价值提升,从基础的数据抽象 Document
与 Node
讲起,逐步深入到多样化的数据加载器(如 SimpleDirectoryReader
、LlamaHub
)、精细化的文本切分与节点解析器 (Node Parser
),再到利用大语言模型(LLM)进行自动化元数据提取,最后聚焦于终极利器——Ingestion Pipeline
,它如何将整个数据处理流程串联、缓存并实现高效的自动化。本文通过丰富的代码示例、操作步骤和应用场景,帮助初学者快速上手,同时为进阶者提供高级特性的深度解读,如图文档管理、并行处理和自定义转换等。无论您是刚刚接触 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 |
从各种来源加载数据,并将其解析、切分为标准化的 Document 和 Node 结构。 |
数据索引 (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
对象的完整生命周期。
Document
与Node
在 LlamaIndex 中,所有的数据处理都围绕两个核心的抽象概念展开:Document
和 Node
。
Document
是 LlamaIndex 中表示任意数据源的通用容器。它可以是一个 PDF 文件、一个 API 的返回结果、或者数据库中的一条记录。Document
对象主要包含文本内容以及一系列描述性属性。
text
: 文档的主要文本内容。metadata
: 一个字典,用于存储关于数据的任意附加信息(元数据),如文件名、类别、创建日期等。这些元数据可以用于过滤、增强检索和溯源。relationships
: 一个字典,定义了该 Document
与其他 Document
或 Node
之间的关系。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()
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_id
是 Document
的唯一标识符,对于在索引中更新或刷新文档至关重要。
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),
)
Node
代表源 Document
的一个“块”(chunk)。它可以是文本块、图像块或其他类型的数据。Node
是 LlamaIndex 中的一等公民,它继承了其父 Document
的元数据,并包含与其他 Node
的关系信息。将 Document
分割成 Node
是构建索引前的关键一步,因为 LLM 的上下文窗口大小有限,且更小的、更具针对性的文本块通常能带来更好的检索效果。
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 提供了多种灵活的加载方式。
SimpleDirectoryReader
:本地文件加载利器SimpleDirectoryReader
是从本地文件系统加载数据最简单直接的方式,非常适合快速启动项目。
它默认支持多种常见文件类型,并能根据文件扩展名自动选择合适的解析器:
.txt
, .md
.csv
, .docx
, .pdf
, .epub
, .hwp
.ppt
, .pptm
, .pptx
.ipynb
(Jupyter Notebook), .mbox
(邮件存档).jpeg
, .jpg
, .png
, .mp3
, .mp4
对于 .json
文件,推荐使用专门的 JSONLoader
。
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")
自定义元数据提取:如前所述,可以使用 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()
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)
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
:
这类切分器关注文本本身,而不关心其原始文件格式。
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)
这类解析器理解特定文件的结构,如 HTML, JSON, Markdown。
HTMLNodeParser
: 使用 BeautifulSoup
解析HTML,并根据指定的标签(如 p
, h1
, li
)提取内容块。JSONNodeParser
: 解析 JSON 数据。MarkdownNodeParser
: 解析 Markdown 文本。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]
)
执行后,提取出的标题、问题等会自动添加到每个 Node
的 metadata
字典中。
Ingestion Pipeline
IngestionPipeline
(摄取流水线) 是 LlamaIndex 中用于实现端到端、可重复、可缓存的数据处理流程的核心组件。它将一系列的 Transformation
(转换)应用于输入的 Document
,最终生成 Node
列表,并可以选择性地将其存入向量数据库。
最简单的用法是定义一个包含多个转换步骤的流水线。
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()])
您可以将流水线直接连接到一个向量数据库,处理后的 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)
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
通过为流水线配置 docstore
,可以激活文档管理功能。它利用 document.doc_id
来跟踪和管理文档的更新。
doc_id
到文档哈希值的映射。doc_id
时,它会比较新旧文档的哈希值。from llama_index.core.storage.docstore import SimpleDocumentStore
pipeline = IngestionPipeline(
transformations=[...],
docstore=SimpleDocumentStore(),
vector_store=vector_store,
)
通过设置 num_workers
参数,可以利用多进程并行执行数据转换,显著提升处理大量文档时的速度。
pipeline.run(documents=documents, num_workers=4)
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
对象的完整路线图。
核心要点回顾:
Document
作为通用数据容器,Node
作为其原子化的数据块,是 LlamaIndex 数据处理的基础。深入理解并善用它们的 metadata
和 relationship
属性是优化 RAG 应用的关键。SimpleDirectoryReader
满足本地文件加载需求,LlamaHub
连接海量第三方数据源,而 LlamaParse
为复杂文档的解析提供了专业解决方案。NodeParser
的选择直接影响检索效果。从基础的 Token/SentenceSplitter
,到智能的 SemanticSplitter
,再到面向特定场景的 SentenceWindow
和 Hierarchical
解析器,LlamaIndex 提供了丰富的工具箱。MetadataExtractor
可以借助 LLM 自动为数据块生成摘要、标题、可回答问题等丰富元数据,极大地提升了数据的可利用价值。IngestionPipeline
将上述所有步骤(加载、切分、提取、嵌入)有机地串联起来,并通过缓存、文档管理、并行处理等高级功能,构建了一个高效、可维护、可扩展的生产级数据摄取流水线。Transformation
抽象允许开发者轻松创建自定义处理逻辑,并将其无缝集成到现有工作流中,体现了 LlamaIndex 框架高度的灵活性和可扩展性。掌握 LlamaIndex 的数据加载模块,是构建高性能 RAG 应用的第一步,也是最重要的一步。希望本文能帮助您在这条路上走得更稳、更远。