大语言模型(LLM)面临知识时效性滞后、私有数据盲区与事实幻觉三大工程瓶颈。
检索增强生成(RAG, Retrieval-Augmented Generation) 是解决私有数据问答成本最低、准确率最高的工程路径。
在 RAG 生态中,与侧重通用 Agent 链式调度的 LangChain 不同,LlamaIndex 专注数据接入、索引构建与结构化检索。
框架已全面转向模块化解构(llama-index-core)与以 Settings 对象为核心的配置范式,弃用了旧版冗重的 ServiceContext。
本文基于现代 LlamaIndex 范式,系统拆解架构、运行原理,并提供本地多源数据与 ChromaDB 向量库落地的工程实践。
一、 技术选型:RAG vs 模型微调 (SFT)
| 评估维度 | RAG (检索增强生成) | SFT (监督微调) |
|---|---|---|
| 核心机制 | 外挂向量数据库,动态检索上下文注入 Prompt | 通过梯度下降更新神经网络模型参数 |
| 数据更新成本 | 极低(仅需新增/更新向量数据库节点) | 高(需重新整理数据集并消耗 GPU 重新训练) |
| 幻觉抑制 | 高(输出基于检索到的真实文档,可提供出处溯源) | 中/低(无法保证不生成似是而非的虚假事实) |
| 适用场景 | 企业私有文档库、实时新闻、法规政策、动态知识库 | 改变模型交互风格、指定特定输出格式、特定领域语言习惯 |
二、 架构演进:LlamaIndex 五大核心组件
LlamaIndex 拆分为独立的轻量化核心库 llama-index-core 与各功能扩展包。其数据流动闭环由以下五大组件构成:
[原始数据] ──> 1. Data Connectors (Readers) ──> Document
│ (Node Parser)
▼
[向量存储] <── 2. Data Index <─────────────── Node (Chunks)
│
└──> 3. Query/Chat Engine & 4. Workflows ──> 5. Application
- Data Connectors (数据连接器) :通过
SimpleDirectoryReader或专用 Reader 加载 TXT、PDF、Markdown、CSV、HTML 及数据库数据,统一解析为标准Document对象。 - Data Index (数据索引与切片) :使用
NodeParser将Document切分为Node(数据块),生成 Embedding 向量并存储在向量数据库(如 ChromaDB)中。 - Engines (交互引擎) :提供
QueryEngine(单轮检索问答) 与ChatEngine(带记忆的连续对话)。 - Workflows (事件驱动工作流) :自 0.11+ 版本起推出的新一代编排引擎,基于事件驱动机制取代旧版
QueryPipeline,用于构建多步骤复杂 Agent 与循环 RAG 任务。 - Application Integration (应用集成层):轻松对接 FastAPI、Flask 或 Streamlit 等生产环境框架。
数据存储三层结构
- Element:顶层数据源容器。
- Document:带有元数据(Metadata)的文本实体。
- Node:Document 切分后的最小检索单元,包含文本、向量及前后 Node 的关联指针。
三、 配置范式革新:从 ServiceContext 到 Settings
早期版本的 ServiceContext 存在强制同步加载全量组件、内存开销大的问题。
在新版本中,全局 Settings 对象全面接管了 LLM 与 Embedding 的延迟加载(Lazy Instantiation)。
python
# 旧版写法(已弃用)
# service_context = ServiceContext.from_defaults(llm=llm, chunk_size=500)
# 现代范式(基于 Settings 全局配置)
from llama_index.core import Settings
from llama_index.llms.ollama import Ollama
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.core.node_parser import SentenceSplitter
# 1. 配置全局 LLM (以本地 Ollama 为例)
Settings.llm = Ollama(model="llama3.2", request_timeout=120.0)
# 2. 配置全局 Embedding 模型
Settings.embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-zh-v1.5")
# 3. 配置全局文本切片器
Settings.node_parser = SentenceSplitter(chunk_size=512, chunk_overlap=50)
四、 本地私有知识库全流程实战
以本地文档加载、ChromaDB 向量持久化与高级检索问答为例,演示完整落地代码。
1. 环境依赖安装
bash
pip install llama-index-core \
llama-index-readers-file \
llama-index-vector-stores-chroma \
llama-index-embeddings-huggingface \
llama-index-llms-ollama \
chromadb
2. 多格式文档加载与向量索引持久化
python
import chromadb
from llama_index.core import SimpleDirectoryReader, VectorStoreIndex, StorageContext, Settings
from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.llms.ollama import Ollama
# 1. 全局模型与环境初始化
Settings.llm = Ollama(model="llama3.2", request_timeout=120.0)
Settings.embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-zh-v1.5")
# 2. 从本地目录读取多格式文档 (支持 .pdf, .md, .txt, .csv 等)
reader = SimpleDirectoryReader(
input_dir="./data",
recursive=True,
required_exts=[".pdf", ".md", ".txt"]
)
documents = reader.load_data()
print(f"成功加载 {len(documents)} 个文档片段。")
# 3. 初始化本地持久化 ChromaDB 数据库
db = chromadb.PersistentClient(path="./chroma_db")
chroma_collection = db.get_or_create_collection("private_knowledge")
# 4. 构建向量存储与存储上下文
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
# 5. 生成向量索引并写入 ChromaDB
index = VectorStoreIndex.from_documents(
documents,
storage_context=storage_context,
show_progress=True
)
print("向量索引构建成功并已持久化。")
3. 加载已有索引并进行语义问答
python
# 若需要从已有 ChromaDB 加载索引(无需重新 Embedding)
db_client = chromadb.PersistentClient(path="./chroma_db")
chroma_collection = db_client.get_collection("private_knowledge")
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
index = VectorStoreIndex.from_vector_store(
vector_store=vector_store
)
# 构建 QueryEngine,设置检索 Top-k 结果数
query_engine = index.as_query_engine(
similarity_top_k=3,
response_mode="compact" # 将上下文压缩后送入 LLM 生成回答
)
# 执行私有知识库查询
response = query_engine.query("请总结知识库中关于业务架构的核心内容")
print("\n--- 回答结果 ---")
print(response)
# 查看答案引用的溯源节点
print("\n--- 检索溯源信息 ---")
for node in response.source_nodes:
print(f"[Score: {node.score:.4f}] {node.node.get_text()[:100]}...\n")
五、 高级检索进阶:基于 Workflows 的事件驱动 RAG
当基础的 as_query_engine() 无法满足"重排序(Reranking)"、"检索结果过滤"或"人机交互校验"等复杂场景时,可采用事件驱动的 Workflows 范式。
python
from llama_index.core.workflow import Workflow, step, Event, StartEvent, StopEvent
from llama_index.core.schema import NodeWithScore
# 定义自定义检索与重排序事件
class RetrievedNodesEvent(Event):
nodes: list[NodeWithScore]
class AdvancedRAGWorkflow(Workflow):
@step
async def retrieve(self, ev: StartEvent) -> RetrievedNodesEvent:
"""步骤 1:检索节点"""
query = ev.get("query")
retriever = index.as_retriever(similarity_top_k=5)
nodes = await retriever.aretrieve(query)
return RetrievedNodesEvent(nodes=nodes)
@step
async def generate(self, ev: RetrievedNodesEvent) -> StopEvent:
"""步骤 2:使用 LLM 根据上下文生成回答"""
# 可在此处插入 Reranker 节点或过滤逻辑
context_str = "\n\n".join([n.node.get_text() for n in ev.nodes])
prompt = f"上下文:\n{context_str}\n\n问题:生成总结回答。"
response = await Settings.llm.acomplete(prompt)
return StopEvent(result=str(response))
# 运行工作流
rag_workflow = AdvancedRAGWorkflow(timeout=60)
# result = await rag_workflow.run(query="核心业务流程是什么?")
六、 调优与工程避坑指南
- 块大小(Chunk Size)平衡 :
- 推荐配置
chunk_size=256 ~ 512,chunk_overlap=50。块太小会导致上下文语义断裂,块太大会降低向量匹配精准度并浪费 Context Token。
- 推荐配置
- 混合检索 (Hybrid Search) :
- 纯向量检索(余弦相似度)对专有名词、产品型号不敏感。生产环境中建议结合 BM25 文本检索,配置
VectorIndexRetriever的 sparse/dense 混合检索。
- 纯向量检索(余弦相似度)对专有名词、产品型号不敏感。生产环境中建议结合 BM25 文本检索,配置
- 复杂文档解析(PDF/表格) :
- 标准文本提取对多栏 PDF、扫描件与复杂表格效果有限。高精度文档解析建议引入
LlamaParse(官方解析服务)将文档转化为结构化 Markdown 后再进行分块。
- 标准文本提取对多栏 PDF、扫描件与复杂表格效果有限。高精度文档解析建议引入
掌握以 Settings 全局配置 与 Workflows 动态编排 为核心的现代范式,即可兼顾开发效率与工程扩展性,快速搭建高精度的私有 RAG 知识库系统。