LlamaIndex入门:从零搭建RAG文档问答系统

发布时间:2026/10/21 19:56:03
LlamaIndex入门:从零搭建RAG文档问答系统 我前阵子帮朋友做一个内部文档问答的小工具需求不复杂把一堆PDF和Markdown丢进一个文件夹用户能像聊天一样问“报销流程有哪几步”“合同审批找谁”这类问题。我第一次动手时满脑子都是“这不得先做文本切分、再调嵌入模型、写检索逻辑、还要拼接Prompt”结果发现LlamaIndex已经把这条路走通了大半。今天这篇是基于我实际踩坑整理出来的入门笔记重点说清楚LlamaIndex是什么、为什么RAG场景要选它、核心概念怎么理解然后直接带你把一个能用的文档问答跑起来。如果你刚接触RAG或者被LangChain里各种概念绕晕过这篇就是为你写的。你不需要懂很深的机器学习原理只要会Python基础语法能执行pip install就能跟着走完全程。文章前半部分是概念铺垫后半部分是实操和排错看完你能从头写一个带本地索引持久化的问答工具也能知道后续往哪个方向深入。1. 5分钟理解LlamaIndex到底解决了什么问题1.1 大模型与私有数据之间的“最后一公里”先说一个很现实的问题大语言模型虽然能聊会写但它的知识截止到训练数据那一天而且它没见过你的企业文档、个人笔记、数据库里的记录。你可以把Prompt写到天花乱坠模型也不会“记得”你上个月那份合同里写了什么条款。所以当我们需要大模型去回答基于私有数据的问题时常见思路就一条——把相关资料找出来拼进Prompt里让模型基于这些材料作答。这个思路就是检索增强生成英文简称RAG。道理听着简单真做起来全是细节。数据格式五花八门有PDF、Word、纯文本文档长度从天差地别短的几十个字长的几百页。怎么把长文档拆成适合喂给模型的小片段怎么把“找相关片段”这件事做得又快又准Embedding用什么模型向量存在哪里检索出来之后怎么组织Prompt如果这些全部手写项目还没开始光基建就够忙一个月。LlamaIndex就是把这些环节封装成一套统一接口的工具让“私有数据大模型”这条路从手工作坊变成标准化流水线。打个比方如果没有这类框架你要自己种小麦、磨面、揉馒头有了LlamaIndex你只需要把面粉和水交给它它按固定流程帮你把馒头蒸出来至于面粉粗细切块大小、蒸多久检索的策略都可以按口味调节。1.2 它到底做了什么从文档到可检索的知识库LlamaIndex本质上是个数据框架核心职责是连接你的数据和你的大模型。你提供一份文档它帮你完成这条链路读取文件 → 分成若干片段 → 为每个片段生成向量表示 → 把向量和原文一起存入索引 → 当用户提问时把问题转成向量 → 在索引里找最相似的片段 → 把这些片段连同问题交给大模型 → 得到回答。这一条链路上的每一步都有可替换的组件。读文件时可以用不同的Reader分块可以用内置的多种Parser向量化可以用OpenAI的Embedding接口也可以用开源的本地嵌入模型存储可以只用内存也可以落到磁盘大模型可以用OpenAI也可以换其他兼容接口的模型。这正是LlamaIndex值得学的地方——不是逼你用一套死方案而是把RAG流程拆成积木你想换哪块就换哪块。实际操作中我见过很多朋友拿RAG当黑盒文档丢进去能回答就万事大吉效果差了不知道怎么调。这也是我写这篇笔记的核心目的先带你建立全链路的心智模型知道每个环节在干什么后面排查和优化才有方向感。只停留在“会用”层面一旦遇到文档读不完、回答乱引用、重启就丢索引这些问题很容易卡死。2. 第一次接触LlamaIndex核心概念逐个拆解2.1 Document、Node 与 MetadataLlamaIndex里最常碰到的两个对象是Document和Node。你把一个文件丢进去框架会把它包装成一个Document。Document可以是一份PDF、一个网页、一段JSON甚至是一张表格。但Document在LlamaIndex里不是最小处理单元因为它往往太大、内容太杂直接拿去做向量化和检索相关性会非常差。于是框架会做切分把Document按某种策略拆成Node。Node可以理解为“语义上相对完整的一小块文本”比如一两段话或者一个标题下的小节。每个Node除了正文内容之外还能挂Metadata元数据比如来源文件名、页码、章节号。这一步的价值在回答问题时特别明显——模型能根据元数据告诉你“这个结论来自第12页”这是RAG系统里非常实用的能力。第一次自己切分时我犯过把整章内容作为Node的毛病检索效果惨不忍睹。后来换成了更小粒度每个Node保持在几百个token以内准确率立刻就上来了。就像查字典如果你把整本字典作为一个词条翻到哪页都得从头读到尾那效率根本没救把每个字拆成独立词条查一个字才快。2.2 索引类型怎么选LlamaIndex里有几类索引最常用的是VectorStoreIndex向量索引也是最典型的RAG索引。它的逻辑是把每个Node生成一个向量用户提问时也生成一个向量然后计算向量之间的相似度把最接近的一批Node捞出来。除了向量索引还有SummaryIndex列表索引/摘要索引、TreeIndex树索引、KeywordIndex关键词索引。我分别说一下它们的适用场景列表索引会把所有Node挨个塞进上下文适合文本量不大、需要全局理解的情况树索引会先把Node构建成树状结构再逐层递归摘要适合处理超大文档但需要“先总后分”的问答关键词索引通过关键词匹配定位Node适合检索词非常明确的场景。新手不要贪多先把VectorStoreIndex玩明白它覆盖了绝大多数使用需求。等以后遇到“文档很长答案分散在多个章节”“需要先给个全局总结再深入到细节”这类需求再回头研究TreeIndex和SummaryIndex。我见过不少人一开始就堆了五六个索引最后发现实际项目里90%场景一个向量索引就解决了。2.3 Retriever 与 QueryEngine 的分工理解了Document和Node再看Retriever就简单了。Retriever负责“从索引里找出相关内容”它接收查询返回一组相关的Node。QueryEngine则是更高层的封装把“检索”和“生成”合在一起先调用Retriever再把检索到的Node和用户问题组装成Prompt最后调用大模型生成回答。在这个架构里大多数初学者其实想用的就是QueryEngine。一句话“把索引变成查询接口”就是as_query_engine()做的事。但理解Retriever这个层级的价值在于你可以中途拦截、替换它的行为。比如默认Retriever返回前2个Node你可以把similarity_top_k调到5让它返回更多候选你也可以把Retriever替换成自定义实现比如先做关键词过滤、再向量检索这种组合玩法在复杂项目里非常实用。我个人是建议你把query_engine.query()返回的Response对象里的source_nodes字段打开看看。它告诉你这个问题最终命中了哪几个Node每个Node来自哪个文件。这个字段是排查“为什么这么答”的一手证据也是后面调优的重要依据。3. 装包与配密钥动手前的关键准备3.1 安装命令与版本选择安装这一步看似简单其实坑不少。现在LlamaIndex已经从原本一个包拆成了多个子包比如llama-index-core、llama-index-llms-openai、llama-index-embeddings-openai。如果你只装最上层的llama-index它会自动把核心包和常见依赖一起拉下来适合新手。我建议你直接用这条命令pip install llama-index如果想确认装好没有可以跑一个短脚本import llama_index print(llama_index.__version__)由于子包拆分的关系不同模块的导入路径也有讲究。现在的主流写法是下面的方式from llama_index.core import VectorStoreIndex, SimpleDirectoryReader from llama_index.core.node_parser import SentenceSplitter from llama_index.llms.openai import OpenAI from llama_index.embeddings.openai import OpenAIEmbedding这里llama_index.core是所有核心抽象所在的地方llama_index.llms.openai这类是具体的模型适配器。如果你在旧教程里看到from llama_index import VectorStoreIndex这种写法多半是早期版本建议直接用当前版本的新写法逻辑更清晰也不容易踩导入坑。3.2 配置模型密钥与基础验证LlamaIndex默认用的是OpenAI的模型Embedding和文本生成都需要API Key。动手前先把环境变量设置好。Linux和macOS下执行export OPENAI_API_KEY你的keyWindows PowerShell下执行$env:OPENAI_API_KEY你的key配置好之后建议先做一个最小验证确认密钥没问题再往下走。你可以写一行代码试试调用Embedding模型from llama_index.embeddings.openai import OpenAIEmbedding embed_model OpenAIEmbedding() result embed_model.get_text_embedding(你好测试一下) print(len(result))如果能打印出一个几百维的向量说明接口通了。我第一次配置时漏了环境变量结果框架报错提示网络鉴权失败排查了半天才发现是Key没设置上。所以这块建议提前验证别等代码写完了才回头查环境问题。另外提醒一下如果你的网络环境访问OpenAI接口不稳定可以按框架的说明配置自定义API地址。LlamaIndex的模型对象支持传入api_base这类参数指向你实际可用的网关这个在团队协作和大陆网络环境里几乎是刚需。4. 30行代码跑通你的第一个LlamaIndex问答系统4.1 准备一份“能跑起来”的测试数据正式动手之前先准备一个干净的测试目录别一上来就用几百页的大文档。我在测试阶段用的是一个十来页的Markdown文件内容是一份“模拟项目X的运维手册”里面有节点清单、故障恢复流程、告警规则三部分。选Markdown有好处文本结构清楚、没有PDF解析的编码问题、切分后的Node语义相对完整。等你把整个流程跑通以后再换成PDF或Word测试。目录结构大致像这样data/ 运维手册.md里面写上几段内容比如# 节点清单 系统共包含3个节点分别为主节点、备节点与监控节点。主节点负责请求分发备节点负责状态同步监控节点只读采集指标。 # 故障恢复流程 当主节点不可用时备节点在30秒后自动接管。接管期间请勿手动重启主节点恢复由调度系统自动完成。 # 告警规则 当任何节点连续3次心跳丢失会触发告警。告警等级分为警告、严重、紧急三级。这种内容的好处是后面测问题时你能马上判断回答到底对不对——你对材料了如指掌才能快速验证系统的检索是否准确。4.2 完整代码与逐步拆解现在写核心代码。新建一个demo.pyfrom llama_index.core import SimpleDirectoryReader, VectorStoreIndex # 第一步读取目录下的所有文档 documents SimpleDirectoryReader(data).load_data() # 第二步构建向量索引 index VectorStoreIndex.from_documents(documents) # 第三步创建查询引擎并提问 query_engine index.as_query_engine() response query_engine.query(主节点不可用时应该怎么处理) print(response)这份代码是LlamaIndex最经典的骨架三步走读文档、建索引、做问答。不要嫌它简单RAG所有复杂配置最终都是围绕这三步在做文章。SimpleDirectoryReader(data)会扫描目录下支持的文本类文件常见格式有txt、md、pdf、docx、csv等。load_data()把所有文件转成Document列表。这里有一点值得记住加载PDF时它内部用的是解析库面对扫描版PDF会白费力气因为里面没有文字层。如果你手里的PDF是图片扫描件得先做OCR否则检索结果一定是空的。VectorStoreIndex.from_documents(documents)是整个流程中最重的一步。它内部会做几件事把Document按默认策略切成Node、调用Embedding模型把每个Node变成向量、把向量和原文放进索引。默认切分策略对中文文本效果还行但如果你的文档里有很多表格、代码块可能需要自定义切分规则这一点后面单独说。as_query_engine()返回QueryEngine对象调用query()传入用户问题它会在索引里找相关Node再组织Prompt交给大模型。我在测试时问了一句“主节点不可用怎么办”返回的结果引用了“故障恢复流程”这一段回答内容基本正确。首次跑通看到这样的结果整条链路就算通了。4.3 参数调整chunk_size、相似度TopK与温度系数跑通之后再往下走就是调参。最容易影响效果的是三个参数切分大小、检索数量、模型温度。切分大小对应SentenceSplitter的chunk_size和chunk_overlap。chunk_size决定每个Node包含多少字符太大则单个Node信息太杂太小则语义被切碎。我的经验是中文场景下500到800比较稳妥。chunk_overlap是相邻Node之间重复的字符数设多一点点可以避免句子的后半部分被截断。你可以用下面的方式覆盖默认切分from llama_index.core.node_parser import SentenceSplitter splitter SentenceSplitter(chunk_size512, chunk_overlap64) index VectorStoreIndex.from_documents(documents, transformations[splitter])检索数量对应similarity_top_k它决定每次拿几个Node喂给模型。默认值通常只有2如果你的文档信息很分散答案凑不齐就调大一点。但也不是越大越好Node太多会让Prompt变长、成本上涨还会让模型被无关信息干扰。我一般先从3开始调逐步加了看效果query_engine index.as_query_engine(similarity_top_k3)温度系数temperature影响回答的创造性。做事实型问答时我习惯调低比如0.1让模型更忠实于检索到的文本而不是自由发挥。LlamaIndex里可以通过Settings统一配置from llama_index.core import Settings from llama_index.llms.openai import OpenAI Settings.llm OpenAI(modelgpt-4o-mini, temperature0.1)这里用gpt-4o-mini是为了演示你可以按自己的账号模型情况换名字。设置之后后续问答都使用这个模型配置不用每次单独传参。5. 保存索引让程序重启后不用重新embedding5.1 持久化原理跑通上面的代码你已经拥有一个能问答的小系统。但有个问题很明显每次运行都要重新读文件、重新生成向量。如果你的文档不多比如就几页倒是无感如果文档一大每次启动都重新embedding既烧钱又费时间而且数据根本不支持超大批量。LlamaIndex对此提供了一套持久化方案核心逻辑是把索引数据和每个Node的文本、向量、元数据一起存到本地目录下次启动时从目录恢复跳过重新解析和embedding。5.2 存储到磁盘的两种正确姿势第一种方式构建完索引后直接持久化。index.storage_context.persist(persist_dir./storage)这样会在./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) query_engine index.as_query_engine()第二种方式构建索引时显式指定存储上下文。这在你想同时自定义存储位置和向量库时更常用from llama_index.core import StorageContext, VectorStoreIndex storage_context StorageContext.from_defaults(persist_dir./storage) index VectorStoreIndex.from_documents( documents, storage_contextstorage_context, )两种方式本质一样我个人习惯用第一种构建和保存分开逻辑更直白。有一点一定要记住持久化之后不要随便改文档内容因为索引里的向量对应的是旧文本如果你改了原文但没重新构建索引检索到的东西会和实际文件对不上。每次内容更新后老老实实重新构建一次索引。6. LlamaIndex常见报错与检索效果排查6.1 环境类问题模块找不到、网络和Key报错我实操中遇到最多的问题就是ModuleNotFoundError比如from llama_index.core import VectorStoreIndex时提示找不到llama_index.core。这种情况基本是安装的包版本太老或只装了一个子包。解决方法是更新主包pip install -U llama-index另外Python版本也是一个因素。LlamaIndex对Python 3.9以上支持较好我建议你在3.10或3.11环境里运行能规避不少依赖编译问题。其次是接口调用报错。看到“AuthenticationError”时先查环境变量是否真的配置了注意别把Key放在脚本里写死或者写在会提交到仓库的配置文件中。如果确认Key没问题再看网络是否稳定。我遇到过类似情况换成可用的API网关地址后一切正常。还有一类问题出现在读取文件环节。SimpleDirectoryReader能自动识别扩展名但如果你文件本身是坏的、加密的、或者版权保护状态它读不出来。遇到这种情况先用简单文本文件测试确认是文件问题再考虑对应解析方案。6.2 回答质量问题检索不到、答案拼接失效如果系统能跑但回答总是“一本正经地胡说八道”那大概率是检索环节出了问题而不是模型的问题。我建议你严格按下面几步排查。第一步看Response对象的source_nodes。打印出检索到的Node内容response query_engine.query(主节点不可用怎么办) for node in response.source_nodes: print(node.node.get_content())如果你发现检索到的Node内容里根本没有“主节点”这几个字说明召回环节出问题了。这时候先调大similarity_top_k从2改成5如果还不行把chunk_size改小让Node粒度更细提高命中率。第二步检查文档切分是否破坏了语义。代码块、表格、列表这类结构化内容被默认切分器拦腰截断是家常便饭。我处理代码类文档时会专门用CodeSplitter或MarkdownNodeParser这类结构感知的解析器这样Node边界能贴合内容结构检索质量和回答质量都会好很多。第三步确认Embedding模型和文本生成模型没有配置混乱。如果你的文档有一部分是英文、一部分是中文混合内容的检索效果可能因为Embedding模型的语言能力而变差。这时候要么做好语言归一化要么考虑更换对中文支持更好的嵌入模型。我把常见问题整理成一张速查表方便你对照查找现象常见原因处置建议ModuleNotFoundError子包拆分不完整或者版本过老pip install -U llama-indexAPI鉴权失败未设置或错设了密钥检查环境变量确认Key可访问接口文档读出来是空的扫描PDF无文字层、文件损坏先换纯文本测试扫描PDF走OCR回答明明没有引用到相关内容similarity_top_k过小或切块太大调大TopK、调小chunk_size每次重启都要重新embedding没有做索引持久化用persist和load_index_from_storage中文效果差嵌入模型对中文支持偏弱更换适合中文的Embedding模型7. 从Demo到实际项目还能怎么折腾7.1 面向真实场景的进阶方向一旦跑通了最小闭环你会发现LlamaIndex的门道远不止基础问答。我盘一下自己用下来觉得最值得深入的方向。第一个是多文档路由。不同文档回答问题的角度完全不同比如一个目录里既有“产品操作手册”又有“财务报销制度”用户问“发票怎么开”和“设备怎么开机”不应该从所有文档里捞内容。LlamaIndex里有RouterQueryEngine和SummaryIndex、KeywordIndex这类不同索引之间的路由机制能够把问题分发给专门的索引。第二个是对话记忆。基础QueryEngine每次问答都是独立的不记得你上一句说了什么。要做出真正“聊起来”的效果需要引入ChatMemoryBuffer这类记忆能力让多轮对话的上下文能累积。第三个是数据增量更新。真实文档是每天变化的不可能每次都全量重建索引。LlamaIndex支持对已有索引做insert和delete操作能够以Node为粒度增量更新这才满足生产系统的要求。如果你想做大项目还有一个重要方向是换向量数据库。默认的内存索引适合教学但在数据量超过几百万Node时内存扛不住。你可以把VectorStoreIndex的底层存储换成专业的向量数据库代码改动量很小因为你只需要传入一个已有的collection对象。这也是LlamaIndex设计得好的一点——很多组件都是可替换的。另外有一个在中文场景里非常普遍的问题嵌入模型选型。如果你完全依赖OpenAI的嵌入接口一旦网络不稳定整个系统就瘫痪而且部分场景下私有数据根本不允许出内网。LlamaIndex支持接入本地部署的嵌入模型把它作为Settings.embed_model传入即可。本地模型的好处是离线可用、隐私可控坏处是需要一定显存首次加载也要等模型下载。我个人建议数据量不大、无强隐私要求时可以先不做本地化优先把业务逻辑跑通。7.2 一些值得记住的实操体会从我这边实际使用的感受来说LlamaIndex并不是一个“装完就什么都会”的魔法框架它的价值在于把RAG里的脏活累活标准化了。你真正要花时间去琢磨的不是怎么调用API而是数据怎么组织、切分怎么定、评估怎么做。刚开始做的时候我犯过一个典型错误一上来就去研究各种高阶检索策略结果基础链路通了之后发现90%的失败都是因为没查source_nodes、没看Node边界长什么样。后来我调整了节奏每次改动一个变量跑一遍测试盯着看检索出的Node到底变了没有再决定下一步怎么走。这种“一个变量一个变量地动”的土办法反而比盲目堆高级配置靠谱得多。另一个心得是开始就建好评估集。十来个覆盖典型问题的问答对就够了每次改动后拿同一批问题跑一遍把回答质量对比一下。手动跑测试虽然土但带来的效果认知非常直接。等你积累多了再去接触RAG评估相关的工具会有种“这说的不就是我踩过的坑吗”的感觉。如果你打算把这个Demo继续推进我建议下一步给文档内容做精细的清洗和结构化比如给Markdown加更明确的标题层级、规范段落标签。你给数据增加的结构化信息越多LlamaIndex能利用的Metadata就越丰富检索效果自然跟着上去。框架本身是其次数据质量往往才是RAG系统的天花板。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询