张子阳的博客

首页 读书 技术 店铺 关于
张子阳的博客 首页 读书 技术 关于

LlamaIndex 概念解析

2025-06-15 张子阳 分类: 大语言模型

LLamaIndex 采用一种被称为“逐步揭示复杂性“(progressive disclosure of complexity)的设计原则。基于这个原则,完成一个任务,只需要短短的几行代码就能够实现。然而,如果需要对任务进行一些配置,或者实现一些更深入、更细致的功能,就需要对组件进行自定义,或者添加更深入的配置。此时,对于相关概念和组件的理解,就显得尤为重要。因为LlamaIndex涉及了相当多的概念,针对这些概念官方站点上又提供了上百的示例程序,所以想要深入理解和掌握仍是有一定难度的。本文将从定义和作用两个方面,对LLamaIndex的核心概念进行阐述。本文的内容,大体上是:官方文档、AI模型问答、《Building Data-Driven Applications with LlamaIndex》,以及我个人理解的综合。

RAG 处理管线

RAG 的作用就是为大模型引入本地知识,使得大模型不再只能基于预训练的数据进行回答,而是能够将本地私有知识库考虑在内。RAG 的处理过程,大体分为下面几个步骤:

  1. 对本地文档构建向量数据库
  2. 用户提问,根据提问问题,在向量数据库中检索出合适的段落作为上下文
  3. 对用户提问和本地数据进行装饰,添加提示语
  4. 将整理后的内容(包含了用户问题、本地相关的上下文、提示语)一起发送给大模型
  5. 大模型进行处理后,将结果返回给用户

文档(document)

文档是一个通用的容器,适用于任何数据源——例如,PDF、API 输出或从数据库中检索的数据。文档可以手动构建,也可以通过数据加载器自动创建。默认情况下,文档存储文本及其他一些属性。文档比较重要的几个属性是:

数据连接器(Data Connector)

我们可以在代码中手动创建文档对象(Documment),但是更多的情况下,数据原本就存在于各式各样的文档中,例如:PDF、网页、文本文件、CSV、Word文档、API、数据库 等等。数据连接器 负责从各种数据源中提取和加载数据,将异构数据统一转换为框架可处理的 Document 对象。

数据连接器的主要作用如下:

  1. 数据提取:从各种格式和来源的数据中提取文本内容,包括文件、数据库、API、网页等。
  2. 格式解析:处理不同的文件格式,如 PDF、Word、Excel、Markdown、HTML 等,将其转换为纯文本或结构化数据。
  3. 元数据提取:自动提取文件属性、创建时间、作者信息等元数据,并将其附加到生成的 Document 中。
  4. 批量处理:支持一次性处理多个文件或数据源,提高数据加载的效率。

常见的数据连接器例如:

文件型:

数据型:

网页型:

节点(Nodes)

LlamaIndex 中的节点(Nodes)是系统的核心数据结构之一,它比 Document 更加细粒度,是实际参与索引构建和检索的基本单元。和Document类似,可以在代码中手动构建Node,但Node通常是通过将 Document 进行分块(chunking)后生成的。它代表文档分割后的语义片段,直接影响检索精度与效率。节点是文档被分割后的​​最小语义单元​​,通常对应文本段落、表格或图像片段。

节点的属性包括:

节点的核心作用:

节点的生成过程:

节点解析器(Node Parser)

前面有说,节点通常是将文档进行分块(chunking)后生成的。而 节点解析器 就是负责将文档分块,并创建节点的组件。换言之,处理流程类似这样:document --> node parser --> nodes。换言之:节点是一个结果,Node Parser则负责转换和生成节点。

NodeParser的核心功能

关键参数

对于处理文本的解析器,常见的关键参数包括:

常见的解析器

显然,不同的文档结构完全不同。例如:普通的文本、代码文件(python、go)、HTML文件 结构不同。因此,需要选择合适的解析器进行处理,常见的解析器如下:

这里类型有些是以Parser结尾,有些是以Splitter结尾,但它们均继承自 NodeParser 基类,核心任务是将原始文档(Document)分割为节点(Nodes)。Parser强调的是将文档解析为节点,而 Splitter 是通用切分策略​​,解决文本物理切割的边界问题。两者在LlamaIndex中可以结合使用。

向量(Vector)和嵌入(Embedding)

向量

向量是数学中的基本概念,在计算机科学中用来表示多维数据。向量是一个有序的数字列表,例如:[0.2, -0.5, 0.8, 0.1, -0.3]。在LlamaIndex中,向量的作用如下:

嵌入

嵌入是将离散的、高维的或复杂的数据转换为连续的、低维的向量表示的过程。嵌入是过程,向量是结果。

原始数据 -> 嵌入过程 -> 向量 苹果 -> Embedding -> [0.2, -0.5, 0.8, 0.1, -0.3]

数据向量化之后,最大的好处就是可以进行相似度运算:

# 用户查询 查询: "如何做红烧肉?" 查询向量: [0.1, 0.7, -0.2, 0.4, ...] # 文档库中的文档 文档1: "红烧肉的制作方法" → [0.2, 0.6, -0.1, 0.5, ...] 文档2: "计算机编程教程" → [-0.5, 0.1, 0.8, -0.3, ...] # 通过向量相似性找到最相关的文档 文档1 相似度: 0.92 ← 最相关 文档2 相似度: 0.15 ← 不相关

嵌入模型(embedding-model)

具体执行 embedding 这一过程,将文本进行向量化的工具与算法,就是嵌入模型(embedding-model)。嵌入模型的输入是一段文本,输出是一个向量。

使用大语言模型来执行embedding,是因为大语言模型具有强大的文本理解和表示能力。

索引(Indexing)

索引由nodes构建,用于快速获取和用户提问相关的上下文。简单来说,就是从所有的nodes中找出和用户提问最相关的节点(Nodes)。索引由下面几部分构成:

常见的索引类型

索引类型 简要说明
VectorStoreIndex(向量存储索引) 基于向量相似性检索,查询时通过计算向量相似度找到最相关内容
ListIndex(列表索引) 按顺序存储文档节点;查询时会考虑所有或大部分节点;不依赖向量相似性
TreeIndex(树形索引) 自底向上构建摘要树; 支持层级式信息检索; 可以获得不同粒度的信息
DocumentSummaryIndex(文档摘要索引) 预生成文档摘要; 基于摘要进行初步筛选; 结合摘要和原文内容

不同索引类型的工作流程

ListIndex

用户查询 → 遍历所有节点 → 将相关节点发送给LLM → 生成回答

VectorStoreIndex

用户查询 → 向量化 → 向量相似性搜索 → 返回top-k相似节点 → 发送给LLM → 生成回答

TreeIndex

构建时: 文档 → 分块成多个节点(叶子节点) → 对叶子节点进行分组 → LLM 为每组生成摘要(父节点) → 递归向上构建,直到根节点 → 存储完整的树形结构 查询时: 用户查询 → 与根节点摘要比较相关性 → 选择最相关的子节点路径 → 递归向下遍历子树 → 到达最相关的叶子节点 → 将相关节点内容发送给LLM → 生成最终回答

DocumentSummaryIndex

构建时: 文档 → 分块成节点(按文档分组) → LLM 为每个完整文档生成摘要 → 建立摘要到原文档节点的映射关系 → 存储摘要索引和原文档内容 查询时: 用户查询 → 与所有文档摘要进行比较 → 根据相关性排序选择top-k文档 → 获取选中文档的完整原文内容 → 将相关文档内容发送给LLM → 基于原文生成详细回答

存储上下文(StorageContext)

如果文档内容比较多,构建索引不仅是一个耗时的过程,而且还是一个花钱的事情(调用LLM接口的费用)。在默认情况下,索引构建完成后,存在于内存当中。但是,LlamaIndex也支持将索引保存在外部(磁盘、数据库),再下一次使用时,直接从外部进行读取就可以了。

StorageContext就是统一管理数据存储组件的核心容器,负责协调索引构建与查询所需的各类存储后端。除了索引以外,还细分成了下面几种数据的存储:

检索器(Retriever)

检索器具体负责根据用户查询从索引中检索相关信息的核心组件。它是连接用户查询和文档内容之间的桥梁,决定了哪些文档片段会被送给LLM进行最终的答案生成。当需要基于特定情况对查询结果进行过滤,例如根据权限进行访问控制的时候,就可以通过自定义检索器来完成。

Index是数据的组织和存储方式,关注的是”数据怎么存“;Retriever是从Index中检索数据的执行机制,关注的是”数据怎么取“。Retriever的主要作用是:从节点中筛选出预查询最相关的内容,从而减少发送给LLM的无关信息,提高响应质量。

不同的索引(index),对应着不同的检索器(Retriever)。例如下面的检索器:

后处理器(Postprocessor)

Postprocessor(后处理器) 是 LlamaIndex 中用于对检索结果进行二次处理和优化的组件。它在 Retriever 检索出候选节点后、将结果传递给 LLM 生成答案前发挥作用。其简要流程如下:

用户查询 → Retriever检索 → Postprocessor后处理 → 优化后的节点 → LLM生成答案

其核心作用,包含了下面几个方面:

常见后处理器的类型:

响应合成器(Response Synthesizer)

用户的一个问题,可能关联多个节点(Nodes),而这些节点的长度,超过了LLM模型的最大上下文长度。此时,一个合理的方案就是:1、分批次将用户查询和节点发送给LLM;2、再将每次LLM的返回的结果进行合并,最终构成给用户显示的完整回复。

这就是 响应合成器 的主要作用。​​所有与LLM的交互均由响应合成器发起和调度,用户仅发起​​一次查询​​,但 响应合成器 可能进行​​多次LLM调用​​(不同模式策略决定)。简言之,它的职责如下:

  1. 决定如何组织和分批发送上下文
  2. 控制与 LLM 的交互策略
  3. 合并多次 LLM 调用的结果

查询引擎(Query Engine)

查询引擎是连接 用户查询 与 数据 的主要接口。用户发起查询,查询引擎负责将自然语言问题转换为结构化检索任务,协调检索器、后处理器和响应合成器,最终返回给用户一个完整的答案。

简单理解:查询引擎相当于是对底层组件的一个封装,相当于一个更高级的接口,且直接面向用户,接受用户的查询。

查询引擎适合处理单次问询(一问一答),它不具备记忆功能。

聊天引擎(Chat Engine)

聊天引擎 是 查询引擎(Query Engine)的“有状态”版本。通过持续追踪对话历史,系统能够结合上下文语境给出更为精确的答案。聊天引擎 底层依赖 查询引擎执行单轮检索与生成,但在此基础上添加了对话状态管理。聊天引擎 可以将复杂对话拆解成为子问题,调用 查询引擎 获取分步答案。

它的核心能力有下面几项:

感谢阅读,希望这篇文章能给你带来帮助!