LlamaIndex 实战:用索引与检索器构建生产级 RAG 应用

做 RAG(检索增强生成)时,很多人第一反应是”把文档向量化丢进向量库”。但当文档变多、问答变复杂,你就会遇到切分策略、多路召回、重排序、可观测等一系列工程问题。LlamaIndex 正是为解决这个问题而生的数据框架:它把”文档加载→切分→建索引→检索→合成答案”整条链路标准化,让你用几十行代码就搭出可演进的生产级 RAG 应用。

一、为什么需要 LlamaIndex:它不只是向量库

向量数据库只负责”存向量、做相似度搜索”。而一个完整的 RAG 应用还需要:把 PDF/Word/网页读进来、按语义切分成节点、选择索引结构、在检索阶段做过滤与融合、把命中的上下文喂给大模型、最后把回答链路记录下来。LlamaIndex 把这些环节抽象成统一的 Document → Node → Index → Retriever → QueryEngine 管线,既能在本地用 Ollama 跑,也能接云端模型,还方便替换任意组件。

如果你已经在本地用 Ollama 部署了模型,LlamaIndex 可以无缝接管”知识检索”这一层,和你的私有化部署形成闭环。我们之前讲过用 Ollama + Open WebUI 搭建团队 AI 助手,本文则聚焦检索编排本身。

为什么不直接用 LangChain 或自己写一套?核心差异在于”数据抽象层级”:LlamaIndex 把”节点(Node)”作为一等公民,所有切分、索引、检索、重排都围绕节点展开,并内置了几十种数据源连接器与检索策略。对以”知识库问答”为目标的应用,它的开箱成本显著低于从零拼装;当你需要更复杂的工作流编排时,再叠加其他框架也不冲突。换句话说,LlamaIndex 解决的是”怎么把企业里散落的知识变成可被大模型精确调用的结构”,这正是 RAG 落地的主战场。

二、快速起步:一个最小可运行的 RAG

先安装核心包。LlamaIndex 0.10 之后拆分了核心与各集成,基础能力都在 llama-index 里。

pip install llama-index

下面 5 行就能构建一个基于本地文档的问答引擎:加载目录下的文件、建向量索引、转成查询引擎、提问。

from llama_index.core import VectorStoreIndex, SimpleDirectoryReader

# 1. 加载数据:支持 pdf / docx / md / txt 等
documents = SimpleDirectoryReader("./data").load_data()

# 2. 建索引(默认会把文档切分并对每个节点做嵌入)
index = VectorStoreIndex.from_documents(documents)

# 3. 转查询引擎并提问
query_engine = index.as_query_engine()
response = query_engine.query("退货政策里支持七天无理由吗?")
print(response.response)

注意:默认会调用 OpenAI 的嵌入与对话接口。生产环境我们通常换成自托管模型,避免把敏感文档外发,这正是下一节要解决的。

三、文档加载与切分:数据接入的关键

RAG 质量的 70% 取决于切分。切得太长会引入噪声、超出上下文;切得太碎会丢失语义。LlamaIndex 的 SentenceSplitter 按句子边界切分,比按固定字符数更自然。

from llama_index.core.node_parser import SentenceSplitter

splitter = SentenceSplitter(chunk_size=512, chunk_overlap=64)
nodes = splitter.get_nodes_from_documents(documents)

# 用预先切好的节点建索引,行为完全可控
index = VectorStoreIndex(nodes)

切分时给每个节点打上元数据(来源、章节、分类),后续就能做”按分类过滤”的精准检索。这对多租户知识库尤其重要。

四、接入本地大模型:用 Ollama 替换默认接口

通过全局 Settings 把对话模型和嵌入模型都指向本地 Ollama,文档无需离开内网。这与用 Dify + Ollama 本地部署私有 AI 知识库的思路一致,只是把编排层换成了 LlamaIndex。

from llama_index.core import Settings
from llama_index.llms.ollama import Ollama
from llama_index.embeddings.ollama import OllamaEmbedding

Settings.llm = Ollama(model="qwen2.5:7b", base_url="http://localhost:11434")
Settings.embed_model = OllamaEmbedding(
    model_name="bge-m3",
    base_url="http://localhost:11434",
)

设置好 Settings 后,前面所有 from_documents / as_query_engine 调用都会自动走本地模型,无需逐处传参。

五、检索器与查询引擎:不止”相似度 TopK”

查询引擎底层依赖检索器(Retriever)。拿到检索器后,你可以直接观察召回结果、分数与原文,便于调试。

retriever = index.as_retriever(similarity_top_k=5)
nodes = retriever.retrieve("如何申请退款?")
for n in nodes:
    print(round(n.score, 3), n.text[:80])

更进一步,用元数据过滤器把检索限定在特定分类或时间范围,避免”全局相似但答非所问”。

from llama_index.core.vector_stores import MetadataFilters, FilterCondition

filters = MetadataFilters.from_dicts(
    [{"key": "category", "value": "refund", "operator": "=="}],
    condition=FilterCondition.AND,
)
retriever = index.as_retriever(similarity_top_k=5, filters=filters)

LlamaIndex 提供多种索引结构,选型直接决定检索行为与成本:

索引类型适用场景检索方式
VectorStoreIndex绝大多数 RAG 默认选择向量相似度 TopK
ListIndex文档少、需要全量顺序或摘要逐节点或 LLM 择优
TreeIndex长文档分层总结自底向上聚合
KeywordTableIndex关键词特征明显、术语表关键词映射表

六、生产级优化:混合检索、重排序与评估

单一路向量检索在术语密集或长尾问题上容易漏召。生产环境普遍采用混合检索(向量 + 关键词)与重排序优化:先用多路检索器召回候选,再用交叉编码器重排,把最相关的几条顶到前面。

from llama_index.core.retrievers import QueryFusionRetriever
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core.postprocessor import SentenceTransformerRerank

retriever = QueryFusionRetriever(
    [index.as_retriever(similarity_top_k=5)],
    mode="reciprocal_rerank",
    similarity_top_k=5,
)
rerank = SentenceTransformerRerank(model="BAAI/bge-reranker-base", top_n=3)
query_engine = RetrieverQueryEngine.from_args(
    retriever, node_postprocessors=[rerank]
)

上线前务必做量化评估,而不是凭感觉调参。可以参考向量检索评测:用 Recall@K 与 RAG 质量度量建立自动评测集,把每次改动都跑一遍回归。

七、可观测性:把链路接到 Langfuse

RAG 出问题往往不在模型,而在”哪段上下文被召回了”。接入Langfuse 做 LLM 应用链路与成本可观测,能看到每次提问实际检索了哪些节点、token 消耗与延迟,排障效率提升一个量级。

import llama_index.core.settings as settings
from llama_index.callbacks import LlamaDebugHandler, CallbackManager

debug = LlamaDebugHandler()
settings.callback_manager = CallbackManager([debug])
# 此后所有 query 的事件都会进入 debug 处理器,便于打印与上报

八、索引持久化:别让每次重启都重建

上面的示例每次运行都会重新切分并嵌入文档,文档量大时既慢又浪费算力。生产环境应当把索引持久化到磁盘或向量库,重启后直接加载。LlamaIndex 用 StorageContext 管理索引的存储位置,支持本地文件、Redis、PostgreSQL(pgvector)等多种后端。

from llama_index.core import StorageContext, load_index_from_storage
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader

# 首次:构建并持久化
documents = SimpleDirectoryReader("./data").load_data()
index = VectorStoreIndex.from_documents(documents)
index.storage_context.persist(persist_dir="./storage")

# 后续:直接加载,跳过重新嵌入
ctx = StorageContext.from_defaults(persist_dir="./storage")
index = load_index_from_storage(ctx)

如果团队已经在使用 PostgreSQL,建议直接把向量存进 pgvector,这样索引和业务的元数据可以共用一套备份与权限体系,扩容也更顺滑。持久化之后,文档更新只需增量重建变动节点,而不是全量重跑。

九、小结与落地清单

LlamaIndex 的价值在于把 RAG 从”调几个 API”升级成”可装配的数据应用”:切分、索引、检索、重排、观测各层解耦,任何一环都能单独替换优化。先用默认管线跑通,再按下面清单逐项加固。

环节生产建议
切分chunk 256–512 字符,overlap 10–20%
嵌入本地 bge-m3 或云端 text-embedding-3-small
检索top_k 4–8 + 元数据过滤缩小范围
重排bge-reranker 显著提升答案精度
评估Recall@K + 答案忠实度自动回归
可观测Langfuse 追踪检索链路与成本

当你把这套结构搭好,后续无论是接企业 Wiki、客服知识库还是研报分析,都只是”换数据源 + 调检索参数”的事。RAG 的真正门槛从来不是模型,而是这条被 LlamaIndex 理顺的数据管线。

上一篇 Bun 实战:更快的 JavaScript 运行时与工具链
下一篇 Tekton 实战:K8s 原生 CI/CD 流水线
最新评论
Docker 网络模式详解:bridge/host 生产选型 - FSData
Docker 网络模式详解:bridge/host 生产选型 - FSData
8月20日
[…] 注意最后那条:把端口绑定到 127.0.0.1,再由宿主机上的 Nginx 反代进来,比直接 -p 80:80 暴露安全得多,也方便统一做
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
8月20日
[…] Pinia 的工程化用法,可以参考我们之前的实战文章 《Vue3 组合式 API + Pinia:状态管理从入门到实战》,那里覆盖了 Sto
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
8月19日
[…] 状态,点一下就能起停容器,配合我们讲过的 Docker Compose 编排 […]
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
8月19日
[…] 缓存三大坑的本质都是”缓存失效导致请求穿透到数据库”。记住口诀:穿透用布隆、击穿用锁、雪崩用错峰。配合合理的过期策略
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
8月19日
[…] Docker 镜像部署。结合我们之前写的 Docker Compose 一键编排实战,可以在 CI […]