LlamaIndex 语法详解与 RAG 系统构建指南
·
LlamaIndex 语法详解与 RAG 系统构建指南
前言
LlamaIndex 是一个专为大语言模型设计的数据框架,核心能力是将私有数据(文档、数据库、API)与 LLM 连接起来。本文基于 4 个实战脚本,系统讲解 LlamaIndex 的核心语法和关键概念。

1. 环境配置与全局设置
1.1 基础依赖
from dotenv import load_dotenv
load_dotenv()
import os
from llama_index.core import Settings
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.llms.openai_like import OpenAILike
from llama_index.embeddings.dashscope import DashScopeEmbedding, DashScopeTextEmbeddingModels
1.2 Settings 全局配置
Settings 是 LlamaIndex 的核心配置对象,设置一次,全局生效:
# LLM 配置
Settings.llm = OpenAILike(
model="qwen-plus",
api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
api_key=os.getenv("DASHCOPE_KEY"),
is_chat_model=True
)
# Embedding 配置
Settings.embed_model = DashScopeEmbedding(
model_name=DashScopeTextEmbeddingModels.TEXT_EMBEDDING_V3,
embed_batch_size=6,
embed_input_length=8192
)
| 参数 | 含义 | 说明 |
|---|---|---|
model |
LLM 模型名 | qwen-plus / gpt-4 / claude 等 |
api_base |
API 地址 | 兼容 OpenAI 协议的地址 |
is_chat_model |
是否对话模型 | True 使用 chat 接口 |
model_name |
Embedding 模型 | text-embedding-v3 等 |
embed_batch_size |
批量大小 | DashScope 限制最大 10 |
embed_input_length |
最大输入长度 | 单条文本 token 上限 |
踩坑:DashScopeEmbedding 底层 SDK 需要
DASHSCOPE_API_KEY环境变量,如果.env中变量名不同,需显式设置dashscope.api_key = os.getenv("DASHCOPE_KEY")。
2. 文档加载(Reader)
2.1 SimpleDirectoryReader:基础加载器
# 基础用法
documents = SimpleDirectoryReader("data").load_data()
# 进阶用法
documents = SimpleDirectoryReader(
"data",
required_exts=[".txt", ".pdf"], # 只读取特定格式
recursive=True, # 递归子目录
exclude_hidden=True # 排除隐藏文件
).load_data()
支持格式:.txt、.pdf、.docx、.csv、.pptx、.html、.json 等。
2.2 SmartPDFLoader:智能 PDF 解析
普通 PDF 解析只提取纯文本,丢失布局信息。SmartPDFLoader 通过 LLMSherpa 服务识别 PDF 的章节、段落、表格结构:
from llama_index.readers.smart_pdf_loader import SmartPDFLoader
llmsherpa_api_url = "https://readers.llmsherpa.com/api/document/developer/parseDocument?renderFormat=all"
pdf_url = "data/1910.13461v1.pdf" # 支持 URL 或本地路径
documents = SmartPDFLoader(llmsherpa_api_url=llmsherpa_api_url).load_data(pdf_url)
布局识别效果:
| 普通解析 | SmartPDFLoader |
|---|---|
| 纯文本,丢失结构 | 保留章节、段落、表格层级 |
| 表格变纯文本 | 保留表格行列关系 |
| 无元数据 | 每个 chunk 带 chunk_type 标签 |
2.3 自定义 Reader
当内置加载器不满足需求时,继承 BaseReader 实现自定义加载器:
from llama_index.core.readers.base import BaseReader
from llama_index.core.schema import Document
from typing import List, Optional, Dict
class CustomReader(BaseReader):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def load_data(
self, file_path: str, extra_info: Optional[Dict] = None
) -> List[Document]:
# 自定义解析逻辑
results = []
# ... 解析文件 ...
document = Document(
text="解析后的文本",
extra_info={"source": file_path, "type": "custom"}
)
results.append(document)
return results
关键规则:
- 继承
BaseReader - 实现
load_data()方法 - 返回
List[Document] - 通过
extra_info传递元数据
3. 文档与节点(Document vs Node)
3.1 Document:完整文档对象
from llama_index.core import Document
doc = Document(
text="CEO 可以直接请假,无需向直接领导汇报",
metadata={
"author": "wilson yin",
"title": "CEO 请假申请",
"id": "1234567890"
}
)
Document 的属性:
| 属性 | 类型 | 说明 |
|---|---|---|
text |
str | 文档文本内容 |
metadata |
Dict | 元数据(作者、标题、来源等) |
id_ |
str | 自动生成或手动指定的唯一 ID |
embedding |
List[float] | 向量表示(构建索引后生成) |
3.2 TextNode:文档片段
from llama_index.core.schema import TextNode
# 手动创建节点
n1 = TextNode(text=doc.text[0:8], doc_id=doc.id_)
n2 = TextNode(text=doc.text[9:16], doc_id=doc.id_)
Document 与 Node 的关系:
Document(完整文档)
↓ NodeParser 切分
Node(文档片段)
↓ Embedding 向量化
向量索引中的条目
| 对比项 | Document | Node |
|---|---|---|
| 粒度 | 完整文档 | 文档的一个片段 |
| 创建方式 | 手动或 Reader 加载 | 切分器自动生成或手动创建 |
| 用途 | 输入给切分器 | 输入给索引和检索 |
| 元数据 | 原始元数据 | 继承自 Document + 切分器添加的元数据 |
4. 文本切分器(NodeParser)
切分器决定文档如何被切分为 Node,直接影响检索质量。
4.1 TokenTextSplitter:按 Token 切分
from llama_index.core.node_parser import TokenTextSplitter
splitter = TokenTextSplitter(
chunk_size=32, # 每块最多 32 个 token
chunk_overlap=4, # 块间重叠 4 个 token
separator="\n" # 优先在换行符处切分
)
nodes = splitter.get_nodes_from_documents([doc])
for node in nodes:
print(node.text) # 切分后的文本
print(node.metadata) # 继承自 Document 的元数据
参数说明:
chunk_size=32 → 每块最多 32 个 token(约 50-100 中文字符)
chunk_overlap=4 → 相邻块重叠 4 个 token(避免关键信息被切断)
separator="\n" → 优先在换行符处切分(保持段落完整性)
切分效果示例:
原文:
### 第七条 事假
1. 员工因私事必须本人处理的,可申请事假。
2. 事假需提前申请并获直属主管批准...
切分后(chunk_size=32):
Node 1: "### 第七条 事假\n1. 员工因私事必须本人处理的,可申请事假。"
Node 2: "2. 事假需提前申请并获直属主管批准,紧急情况可事后补办手续。"
Node 3: "3. 事假为无薪假,按日扣除相应工资。"
Node 4: "4. 每月事假原则上不超过 3 天,全年累计不超过 15 天..."
4.2 SentenceSplitter:按句子切分
from llama_index.core.node_parser import SentenceSplitter
sentence_splitter = SentenceSplitter(
chunk_size=512, # 每块约 512 字符
chunk_overlap=50 # 块间重叠 50 字符
)
适用场景:中文文档(默认推荐),按句子边界切分,保持语义完整性。
4.3 SentenceWindowNodeParser:句子窗口切分
from llama_index.core.node_parser import SentenceWindowNodeParser
sentence_window_splitter = SentenceWindowNodeParser.from_defaults(
window_size=3, # 窗口大小(前后各 3 句)
window_metadata_key="window", # 窗口文本的元数据键
original_text_metadata_key="original_text" # 原文本的元数据键
)
原理:检索时用单句匹配(精准),生成时用窗口上下文(完整)。
检索时:匹配单句 "事假为无薪假"
生成时:使用窗口 "事假需提前申请...事假为无薪假...每月事假不超过 3 天"
配合后处理器使用:
from llama_index.core.node_parser import MetadataReplacementPostProcessor
query_engine = index.as_query_engine(
similarity_top_k=5,
streaming=True,
node_postprocessors=[
MetadataReplacementPostProcessor(target_metadata_key="window")
]
)
4.4 SemanticSplitterNodeParser:语义切分
from llama_index.core.node_parser import SemanticSplitterNodeParser
semantic_splitter = SemanticSplitterNodeParser(
buffer_size=1, # 缓冲句子数
breakpoint_percentile_threshold=95, # 语义差异阈值
embed_model=Settings.embed_model # 需要 Embedding 模型
)
原理:计算相邻句子的语义相似度,在相似度骤降处切分。
句子 A ←→ 句子 B:相似度 0.92(同一话题,不切分)
句子 B ←→ 句子 C:相似度 0.31(话题转换,在此切分)
适用场景:长文档、主题切换频繁的文档。
4.5 MarkdownNodeParser:Markdown 切分
from llama_index.core.node_parser import MarkdownNodeParser
markdown_splitter = MarkdownNodeParser()
原理:按 Markdown 标题层级(#、##、###)切分,保持文档结构。
4.6 切分器对比总结
| 切分器 | 切分依据 | 适用场景 | 是否需要 Embedding |
|---|---|---|---|
TokenTextSplitter |
Token 数量 | 英文/混合文本 | ❌ |
SentenceSplitter |
句子边界 | 中文文档(通用) | ❌ |
SentenceWindowNodeParser |
句子 + 窗口 | 需要上下文的问答 | ❌ |
SemanticSplitterNodeParser |
语义相似度 | 长文档、主题切换 | ✅ |
MarkdownNodeParser |
Markdown 标题 | Markdown 文档 | ❌ |
5. 向量索引与检索
5.1 构建索引
# 从文档构建
index = VectorStoreIndex.from_documents(documents)
# 从节点构建
index = VectorStoreIndex(nodes)
# 持久化保存
index.storage_context.persist(persist_dir="./storage")
# 加载已保存的索引
from llama_index.core import StorageContext, load_index_from_storage
storage_context = StorageContext.from_defaults(persist_dir="./storage")
index = load_index_from_storage(storage_context)
5.2 检索器(Retriever)
# 基础检索器
retriever = index.as_retriever(similarity_top_k=5)
# 检索
nodes = retriever.retrieve("怎么休事假?")
for i, node in enumerate(nodes):
print(f"Node {i+1} (相似度: {node.score:.4f}): {node.text[:50]}...")
检索结果示例:
5.3 查询引擎(Query Engine)
# 基础查询
query_engine = index.as_query_engine()
response = query_engine.query("怎么休事假?")
print(response)
# 流式查询
streaming_engine = index.as_query_engine(streaming=True)
response = streaming_engine.query("怎么休事假?")
response.print_response_stream()
# 对话引擎(多轮问答)
chat_engine = index.as_chat_engine()
response = chat_engine.chat("我想请事假,需要什么流程?")
6. 后处理器(PostProcessor)
后处理器在检索后、生成前对节点进行过滤或重排,提升答案质量。
6.1 SimilarityPostprocessor:相似度过滤
from llama_index.core.postprocessor import SimilarityPostprocessor
# 创建后处理器,设置相似度阈值
similarity_postprocessor = SimilarityPostprocessor(similarity_cutoff=0.71)
# 应用后处理器
filtered_nodes = similarity_postprocessor.postprocess_nodes(nodes)
print(f"原始 Node 数: {len(nodes)}, 过滤后 Node 数: {len(filtered_nodes)}")
效果:

6.2 其他常用后处理器
| 后处理器 | 作用 | 使用场景 |
|---|---|---|
SimilarityPostprocessor |
按相似度阈值过滤 | 去除低相关性结果 |
KeywordNodePostprocessor |
按关键词过滤 | 确保结果包含特定词 |
MetadataReplacementPostProcessor |
替换元数据 | 句子窗口切分后恢复上下文 |
LongContextReorder |
重排长上下文 | 避免"中间丢失"问题 |
SentenceEmbeddingOptimizer |
优化句子嵌入 | 提升检索精度 |
6.3 在查询引擎中使用后处理器
query_engine = index.as_query_engine(
similarity_top_k=10, # 先检索 10 个
node_postprocessors=[
SimilarityPostprocessor(similarity_cutoff=0.7), # 过滤到相似度 > 0.7
KeywordNodePostprocessor(required_keywords=["事假"]) # 确保包含关键词
]
)
处理流程:
用户提问 → 检索 Top-10 → 相似度过滤 → 关键词过滤 → LLM 生成答案
7. 完整实战:企业知识库问答系统
7.1 系统架构

7.2 完整代码
from dotenv import load_dotenv
load_dotenv()
import os
import dashscope
from llama_index.core import Settings
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.llms.openai_like import OpenAILike
from llama_index.embeddings.dashscope import DashScopeEmbedding, DashScopeTextEmbeddingModels
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.postprocessor import SimilarityPostprocessor
# 配置
dashscope.api_key = os.getenv("DASHCOPE_KEY")
Settings.llm = OpenAILike(
model="qwen-plus",
api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
api_key=os.getenv("DASHCOPE_KEY"),
is_chat_model=True
)
Settings.embed_model = DashScopeEmbedding(
model_name=DashScopeTextEmbeddingModels.TEXT_EMBEDDING_V3,
embed_batch_size=6,
embed_input_length=8192
)
Settings.transformations = [
SentenceSplitter(chunk_size=256, chunk_overlap=32)
]
# 加载文档
documents = SimpleDirectoryReader("data").load_data()
# 构建索引
index = VectorStoreIndex.from_documents(documents)
# 创建查询引擎(带后处理器)
query_engine = index.as_query_engine(
similarity_top_k=5,
node_postprocessors=[
SimilarityPostprocessor(similarity_cutoff=0.7)
]
)
# 查询
response = query_engine.query("怎么休事假?")
print(response)
7.3 运行效果
Q: 怎么休事假?
A: 员工因私事必须本人处理的,可申请事假。需提前向直属主管提出申请并获得批准;
如遇紧急情况,可事后补办手续。事假为无薪假,按日扣除相应工资。
每月事假原则上不超过 3 天,全年累计不得超过 15 天。
Q: 元旦休假几天?
A: 元旦休假 1 天。
Q: 春节休假几天?
A: 春节休假 3 天。
8. LlamaIndex 核心语法速查
8.1 导入路径
# 核心组件
from llama_index.core import Settings, Document, VectorStoreIndex, SimpleDirectoryReader
from llama_index.core import StorageContext, load_index_from_storage
# Schema
from llama_index.core.schema import TextNode, Document
# NodeParser(切分器)
from llama_index.core.node_parser import (
TokenTextSplitter,
SentenceSplitter,
SentenceWindowNodeParser,
SemanticSplitterNodeParser,
MarkdownNodeParser
)
# PostProcessor(后处理器)
from llama_index.core.postprocessor import (
SimilarityPostprocessor,
KeywordNodePostprocessor,
MetadataReplacementPostProcessor
)
# Reader(加载器)
from llama_index.readers.smart_pdf_loader import SmartPDFLoader
from llama_index.core.readers.base import BaseReader
# LLM & Embedding
from llama_index.llms.openai_like import OpenAILike
from llama_index.embeddings.dashscope import DashScopeEmbedding
8.2 核心 API
| 操作 | API | 说明 |
|---|---|---|
| 加载文档 | SimpleDirectoryReader(path).load_data() |
返回 List[Document] |
| 创建文档 | Document(text=..., metadata={...}) |
手动创建文档 |
| 切分文档 | splitter.get_nodes_from_documents(docs) |
返回 List[Node] |
| 构建索引 | VectorStoreIndex.from_documents(docs) |
自动向量化 |
| 持久化 | index.storage_context.persist(dir) |
保存到磁盘 |
| 加载索引 | load_index_from_storage(ctx) |
从磁盘加载 |
| 创建检索器 | index.as_retriever(top_k=5) |
返回 Retriever |
| 创建查询引擎 | index.as_query_engine() |
返回 QueryEngine |
| 创建对话引擎 | index.as_chat_engine() |
返回 ChatEngine |
| 检索 | retriever.retrieve(query) |
返回 List[NodeWithScore] |
| 查询 | query_engine.query(query) |
返回 Response |
| 后处理 | postprocessor.postprocess_nodes(nodes) |
过滤/重排节点 |
8.3 数据流
文件 → Reader → Document → NodeParser → Node → Index → Retriever → PostProcessor → LLM → Response
9. 常见问题与最佳实践
9.1 切分粒度选择
| 场景 | 推荐切分器 | chunk_size |
|---|---|---|
| 中文文档 | SentenceSplitter | 256-512 |
| 英文文档 | TokenTextSplitter | 128-256 |
| 长文档 | SemanticSplitterNodeParser | 自动 |
| Markdown | MarkdownNodeParser | 按标题 |
| 需要上下文 | SentenceWindowNodeParser | window_size=3 |
9.2 相似度阈值调优
# 阈值过高:过滤掉相关结果
SimilarityPostprocessor(similarity_cutoff=0.9) # 太严格
# 阈值过低:包含不相关结果
SimilarityPostprocessor(similarity_cutoff=0.5) # 太宽松
# 推荐范围
SimilarityPostprocessor(similarity_cutoff=0.7) # 平衡
9.3 性能优化
# 1. 持久化索引,避免重复构建
index.storage_context.persist(persist_dir="./storage")
# 2. 批量 Embedding,减少 API 调用
Settings.embed_model = DashScopeEmbedding(embed_batch_size=10)
# 3. 流式输出,提升用户体验
query_engine = index.as_query_engine(streaming=True)
10. 总结
核心概念
| 概念 | 作用 | 关键类 |
|---|---|---|
| Reader | 加载数据源 | SimpleDirectoryReader、BaseReader |
| Document | 完整文档 | Document |
| NodeParser | 切分文档 | SentenceSplitter、TokenTextSplitter |
| Node | 文档片段 | TextNode |
| Index | 向量索引 | VectorStoreIndex |
| Retriever | 语义检索 | as_retriever() |
| PostProcessor | 结果过滤 | SimilarityPostprocessor |
| QueryEngine | 检索 + 生成 | as_query_engine() |
学习路径
1. 环境配置 → Settings 全局设置
2. 文档加载 → SimpleDirectoryReader
3. 文档切分 → SentenceSplitter / TokenTextSplitter
4. 索引构建 → VectorStoreIndex
5. 检索查询 → Retriever / QueryEngine
6. 结果优化 → PostProcessor
7. 高级功能 → 自定义 Reader / 多模态 / Agent
更多推荐
所有评论(0)