# 【学习记录】LlamaIndex 四行代码背后的完整 RAG 工作流:从文档解析到生成答案 > 很多人第一次接触 LlamaIndex 时,会被它的简洁 API 震撼——短短三四行代码就能搭建一
·
【学习记录】LlamaIndex 四行代码背后的完整 RAG 工作流:从文档解析到生成答案
很多人第一次接触 LlamaIndex 时,会被它的简洁 API 震撼——短短三四行代码就能搭建一个 RAG(检索增强生成)系统。但在这背后,框架默默完成了文档解析、文本分块、向量化、索引构建、检索、提示词组装、LLM 调用等一系列复杂操作。本文逐行拆解 LlamaIndex 的典型代码,揭示每一步的底层原理与数据流转。读完能真正理解 RAG 的内部机制。
📌 前置说明
本文使用的示例代码:
import os
from llama_index.core import SimpleDirectoryReader, VectorStoreIndex
os.environ['OPENAI_API_KEY'] = 'your-api-key'
documents = SimpleDirectoryReader('data').load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()
response = query_engine.query("总结一下这篇文章,用中文")
print(response)
⚠️ 注意:实际运行时需设置有效的 OpenAI API 密钥(或替换为本地模型)。本文假设密钥有效,按照 LlamaIndex 正常工作流程说明。
步骤一:文档解析
documents = SimpleDirectoryReader('data').load_data()
底层做了什么?
-
SimpleDirectoryReader('data')- 创建一个目录读取器实例,指向本地的
data文件夹。 - 内部使用
fsspec或pathlib遍历目录,获取所有文件路径。
- 创建一个目录读取器实例,指向本地的
-
.load_data()- 对每个文件,根据文件扩展名自动选择合适的
BaseReader子类:.pdf→PDFReader(依赖PyPDF2或pdfplumber).txt→TextFileReader.docx→DocxReader(依赖python-docx).csv→CSVReader- 等等。
- 调用读取器的
load_data()方法提取文件中的纯文本和元数据(如文件名、页数、创建时间等)。 - 将所有读取结果合并成一个 Python 列表,列表元素为
Document对象。
- 对每个文件,根据文件扩展名自动选择合适的
-
输出
List[Document]- 每个
Document包含:text:提取的文本内容(字符串)metadata:字典,如{"file_name": "a.pdf", "page_label": "1"}
- 每个
💡 扩展:LlamaIndex 还支持通过
SimpleDirectoryReader(input_dir="...", recursive=True, required_exts=[".pdf"])等参数精细控制。
步骤二:构建索引
index = VectorStoreIndex.from_documents(documents)
这是最复杂的一步,内部包含以下子步骤:
2.1 文本分块(Node Parsing)
- 目的:将一个长
Document切分成多个节点(Node),每个节点长度适中。这样做可以提高检索的细粒度和相关性。 - 默认参数:
chunk_size=1024(token 数),chunk_overlap=20。 - 默认解析器:
SimpleNodeParser。 - 底层流程:
- 遍历每个
Document,将其文本按 token 或字符切分(实际调用tokenizer或字符计数)。 - 相邻块之间保留
chunk_overlap个 token,防止信息在边界处割裂。 - 为每个节点生成唯一 ID,并继承原 Document 的元数据(可增加
chunk_index等字段)。
- 遍历每个
2.2 为每个节点生成嵌入向量
- 目的:将节点文本映射到高维语义空间,使语义相似的文本向量距离更近。
- 默认嵌入模型:OpenAI
text-embedding-ada-002(维度 1536)。 - 底层流程:
- 调用
Settings.embed_model的get_text_embedding_batch()方法,将节点文本批量发送到 OpenAI API(或本地模型)。 - 每个节点返回一个固定维度的浮点数向量(列表或 numpy 数组)。
- 如果使用 OpenAI,会消耗 API 费用;本地模型(如
HuggingFaceEmbedding)则无需联网。
- 调用
2.3 存储到向量数据库
- 目的:持久化向量和节点信息,支持后续快速检索。
- 默认向量存储:
SimpleVectorStore(内存中的简单索引,关机即丢失)。生产环境可换为 FAISS、Chroma、Pinecone 等。 - 底层流程:
- 创建一个
VectorStoreIndex实例。 - 初始化一个
DocumentStore(默认SimpleDocumentStore)用于存储节点文本和元数据。 - 将节点 ID、节点向量、节点文本一并存入向量存储和文档存储。
- 维护一个
IndexStruct,记录索引的元信息(如节点 ID 列表)。
- 创建一个
2.4 构建索引对象
- 返回一个
VectorStoreIndex实例,内部持有:vector_storedocstoreindex_struct
- 该索引对象可以直接用于检索和查询。
步骤三:构建查询引擎
query_engine = index.as_query_engine()
底层做了什么?
-
创建检索器(Retriever)
- 默认类型:
VectorIndexRetriever。 - 作用:根据用户问题从索引中检索最相关的 k 个节点(默认
k=2)。 - 工作方式:将用户问题向量化,然后在向量存储中进行相似度搜索(余弦相似度或内积),返回 top-k 节点。
- 默认类型:
-
创建合成器(Response Synthesizer)
- 默认类型:通过
get_response_synthesizer()获得,使用compact模式。 - 作用:将检索到的节点内容和用户问题组合成提示词(prompt),并调用 LLM 生成最终答案。
compact模式会尽量合并节点文本,减少 token 消耗。
- 默认类型:通过
-
组装查询引擎
- 创建一个
RetrieverQueryEngine实例,将检索器和合成器组合在一起。 - 返回的
query_engine对象具有query()方法,用于执行查询。
- 创建一个
步骤四:执行查询
response = query_engine.query("总结一下这篇文章,用中文")
print(response)
内部流程详解
4.1 问题向量化
- 将用户问题
"总结一下这篇文章,用中文"通过相同的嵌入模型(与构建索引时一致)转换为向量。
4.2 检索相关节点
- 检索器在向量存储中进行相似度搜索,找出与问题向量最相似的 k 个节点(默认 k=2)。
- 返回节点列表,每个节点包含
text和metadata,以及相似度分数score。
4.3 构建提示词
- 合成器将检索到的节点文本和用户问题组合成一个结构化的提示词。典型的
compact模式提示词格式:上下文信息: [1] 节点1的文本... [2] 节点2的文本... 根据以上上下文信息,回答用户的问题: 总结一下这篇文章,用中文 - 还可以包含指令如“如果你不知道答案,请说‘不知道’”,具体取决于合成器配置。
4.4 调用大语言模型生成答案
- 将提示词发送给 LLM(默认 OpenAI
gpt-3.5-turbo或gpt-4)。 - LLM 根据上下文生成自然语言回答。
- 返回的
response对象包含:response:生成的答案文本(字符串)。source_nodes:检索到的源节点列表,可用于溯源或展示引用。metadata:其他信息(如 token 使用量、响应耗时等)。
4.5 打印答案
print(response)输出答案文本。
📊 整体流程图(文本版)
data/ 目录
│
▼ (SimpleDirectoryReader)
List[Document] (含文本+元数据)
│
▼ (VectorStoreIndex.from_documents)
├─ 文本分块 → List[Node]
├─ 嵌入模型 → 每个Node的向量
├─ 存入向量存储 + 文档存储
└─ 返回 VectorStoreIndex
│
▼ (index.as_query_engine)
├─ 构建 Retriever (VectorIndexRetriever)
├─ 构建 Response Synthesizer (compact模式)
└─ 返回 RetrieverQueryEngine
│
▼ (query_engine.query("问题"))
├─ 问题向量化
├─ 检索 top-k 节点
├─ 组装提示词
├─ LLM 生成答案
└─ 返回 Response 对象
│
▼
打印答案
⚠️ 常见问题与优化建议
| 问题 | 说明 | 解决方案 |
|---|---|---|
| 默认使用 OpenAI | 需要有效 API 密钥,且产生费用 | 替换为本地模型:Settings.llm = HuggingFaceLLM(...)、Settings.embed_model = HuggingFaceEmbedding(...) |
| 分块大小不合适 | 默认 1024 token,太短可能丢失上下文,太长可能降低检索精度 | 根据文档类型调整:技术文档可用 512,长文可用 2048 |
| 检索数量 k=2 | 可能漏掉相关信息 | 增加 similarity_top_k=5(通过 as_query_engine(similarity_top_k=5)) |
| 内存向量存储不持久 | SimpleVectorStore 关机即丢失 |
改用 ChromaVectorStore 或 FAISS 并持久化到磁盘 |
| 无中文优化 | 默认嵌入模型和 LLM 对中文效果一般 | 使用 BAAI/bge-large-zh 等中文嵌入模型,LLM 用 Qwen 或 ChatGLM |
🎯 总结
| 步骤 | 核心操作 | 输入 | 输出 | 关键依赖 |
|---|---|---|---|---|
| 一 | 读取本地文件 → 提取文本 | 目录路径 | List[Document] |
SimpleDirectoryReader + 文件解析库 |
| 二 | 分块 → 嵌入 → 存入向量存储 | List[Document] |
VectorStoreIndex |
嵌入模型 API / 本地模型,向量数据库 |
| 三 | 组装检索器 + 合成器 | VectorStoreIndex |
QueryEngine |
默认 RetrieverQueryEngine |
| 四 | 问题向量化 → 检索 → 提示词 → LLM 生成 | 问题字符串 | Response 对象 |
嵌入模型,LLM API |
LlamaIndex 用极其简洁的 API 封装了 RAG 的复杂流程,让开发者能够快速构建原型。但理解其底层原理,能帮助你在遇到性能瓶颈或定制需求时,精准地调整参数、替换组件。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)