基于 LlamaIndex 的 RAG 文档问答实现
基于 LlamaIndex 的 RAG 文档问答实现
本文使用 LlamaIndex 构建一个面向本地 Markdown 文档的 RAG 问答应用。最小可行系统采用初始化设置、文档加载、索引构建、查询引擎创建和查询验证五步。下面就采用这个最小系统实现基于 LlamaIndex 的 RAG 应用。
一、初始化设置
这一步要完成环境变量加载、LLM 配置和嵌入模型配置。RAG 应用同时依赖生成模型与向量模型,因此需要先通过 LlamaIndex 的 Settings 设置全局默认组件,后续索引构建和查询引擎会自动复用这些配置。
import os
# os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'
from dotenv import load_dotenv
from llama_index.core import Settings
from llama_index.llms.openai_like import OpenAILike
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
load_dotenv()
Settings.llm = OpenAILike(
model=os.getenv("DEEPSEEK_API_MODEL"),
api_key=os.getenv("DEEPSEEK_API_KEY"),
api_base=os.getenv("DEEPSEEK_API_BASE"),
is_chat_model=True
)
Settings.embed_model = HuggingFaceEmbedding("BAAI/bge-small-zh-v1.5")
这里使用 load_dotenv() 从 .env 文件加载环境变量,避免在代码中硬编码密钥。OpenAILike 用于兼容 OpenAI 接口的模型服务,其中 model 指定模型名称,api_key 从环境变量读取,api_base 指定服务地址,is_chat_model=True 表示按聊天模型方式调用。
嵌入模型使用 BAAI/bge-small-zh-v1.5。该模型体积较小,适合中文检索场景,并且可以在 CPU 环境中运行。这里使用其默认参数进行文本嵌入。如果模型下载受限,可以取消 HF_ENDPOINT 的注释,使用镜像地址。
使用
Settings而不是在每个组件中重复传入模型,主要是为了让索引构建和查询引擎共享同一套 LLM 与嵌入配置。
二、加载本地文档
这一步需要将本地 Markdown 文档读取为 LlamaIndex 的 Document 对象。RAG 的第一步是数据准备,只有先将原始文档加载进内存,后续才能进行切分、嵌入和检索。
from llama_index.core import SimpleDirectoryReader
documents = SimpleDirectoryReader(
input_files=["./text.md"]
).load_data()
SimpleDirectoryReader 是 LlamaIndex 中常用的文档加载器。这里通过 input_files 指定单个 Markdown 文件,load_data() 会返回一个 Document 列表。相对于直接读取目录,指定 input_files 可以精确控制输入文档,避免加载无关文件。
当不指定参数初始化
SimpleDirectoryReader时,其默认行为旨在读取目录中的支持文件;这里使用input_files是为了让示例更聚焦于单篇文档。
三、构建向量索引
这一步要将加载后的文档转换为可检索的向量索引。LlamaIndex 会先对文档进行切分,再调用嵌入模型生成向量,最后写入默认的向量存储中。
from llama_index.core import VectorStoreIndex
index = VectorStoreIndex.from_documents(documents)
VectorStoreIndex.from_documents() 是高层 API,内部会完成节点切分、嵌入计算和索引构建。它默认使用 Settings.embed_model,也就是上一步配置的 HuggingFaceEmbedding。在不指定向量数据库时,索引通常保存在内存中,适合最小可行示例。
使用
VectorStoreIndex.from_documents()而不是手动切分和写入,主要是为了以最小代码完成索引构建;后续需要优化时,可以替换切分器、向量存储或嵌入模型。
四、创建查询引擎
这一步要将索引封装为查询引擎。查询引擎负责接收用户问题,先检索相关文档片段,再将这些片段与问题一起交给 LLM,生成最终答案。
query_engine = index.as_query_engine()
print(query_engine.get_prompts())
index.as_query_engine() 会返回一个查询引擎。在不指定参数时,它会采用默认的检索与响应合成策略,例如默认检索一定数量的相关节点,并使用默认响应模式组织上下文。get_prompts() 用于查看当前查询引擎使用的提示词模板,便于调试检索增强生成中的上下文注入方式。
使用查询引擎而不是手动调用检索器和 LLM,主要是为了复用 LlamaIndex 的响应合成逻辑,包括上下文拼接、提示词模板和答案生成。
五、执行查询与验证
最后一步是输入问题并验证端到端流程。查询引擎会自动完成检索、上下文构造和大模型生成,因此这里只需要调用 query() 并打印结果。
question = "文中举了哪些例子?"
answer = query_engine.query(question)
print(answer)
query() 返回的是 Response 对象,直接 print 可以查看生成的答案文本。这里的问题与参考文档内容相关,用于验证文档是否被正确加载、索引是否建立成功,以及 LLM 是否能基于检索到的上下文作答。
