使用 LlamaIndex 和 ReAct 构建一个维基百科 RAG 聊天助手
按需检索维基百科页面,通过小型编码器嵌入相关内容,并让ReAct智能体调用查询引擎工具,从而使Chainlit的回答始终基于检索到的证据。
简介
大型语言模型虽然表达流畅,但流畅度并不等同于能够判断答案的准确性或时效性。当问题超出训练数据范围,或是模型的自信度超过现有证据时,它们可能会编造看似合理的谎言。
一个实用的维基百科RAG聊天助手通过结合按需页面索引、语义检索、证据整合、能够决定搜索时机的ReAct智能体,以及基于检索文本进行回复的对话式用户界面,来弥补这一缺陷。该系统的源代码存储在data-etl-ml GitHub仓库中的agentic-ai wiki RAG示例中。
为何选择RAG?
如果没有检索功能,路径会非常短且脆弱:
User Question
↓
LLM
↓
Answer
一切结果都取决于模型在训练过程中记住了什么。
采用RAG后,证据会在内容生成之前被纳入考量:
User Question
↓
Retriever
↓
Relevant Documents
↓
LLM
↓
Grounded Answer
模型会从检索到的段落中获取答案。这意味着幻觉现象会更少,信息更及时,无需通过微调来更新知识,同时也能轻松更换或扩展语料库。
项目目标
构建一个聊天助手,能够从用户选择索引的维基百科页面中获取答案。示例请求:
Please index:
2023 United States banking crisis
然后提问:
Which bank experienced the fastest bank run
in US history during 2023?
回复时应引用检索到的页面内容,而非模型自身的记忆。
架构概览
从高层次来看,该系统结构如下:
Wikipedia Pages
│
▼
WikipediaReader
│
▼
SentenceSplitter
│
▼
Embeddings
BAAI/bge-small-en
│
▼
VectorStoreIndex
│
▼
Query Engine
│
▼
QueryEngineTool
│
▼
ReAct Agent
│
▼
Chainlit UI
有两个关键流程:用于构建可搜索向量存储的索引流程,以及用于检索内容片段并生成答案的查询流程。
步骤1:读取OpenAI API密钥
凭证信息不应出现在源代码中,避免直接硬编码:
api_key = "xxxxx"
改从配置文件加载:
def get_apikey():
with open("apikeys.yml") as file:
config = yaml.safe_load(file)
return config["OPENAI_API_KEY"]
密钥与应用程序逻辑保持分离。
步骤2:理解代码结构
该项目由三个Python模块组成:
utils.py
index_wikipages.py
chat_agent.py
utils.py用于加载API密钥和配置信息。index_wikipages.py负责解析页面请求、下载维基百科文本、嵌入相关内容并构建索引。chat_agent.py则负责整合ReAct智能体、查询引擎工具以及Chainlit用户界面。
步骤3:利用结构化输出提取维基百科页面
用户可以输入自由格式的列表:
Please index:
London, Birmingham, New York
索引器需要的是结构清晰的数组:
[
"London",
"Birmingham",
"New York"
]
相比易出错的字符串分割方式,Pydantic模型能够更好地捕获结构化输出:
class WikiPageList(BaseModel):
pages: List[str]
该模型能自动将自然语言转换为类型化的页面列表。
第4步:加载维基百科页面
LlamaIndex提供了便捷的加载工具:
WikipediaReader
导入操作只需几行代码即可完成:
loader = WikipediaReader()
documents = loader.load_data(
pages=["2023 United States banking crisis"]
)
此时,处理流程中已包含原始页面文档。
第5步:分割文档
模型难以直接处理作为整体的大页面内容,需将其分割成重叠的片段:
SentenceSplitter(
chunk_size=150,
chunk_overlap=45
)
通过设置重叠部分,可以保留跨越边界的内容:
Chunk A:
---------------------
Paragraph 1
Paragraph 2
Chunk B:
---------------------
Paragraph 2
Paragraph 3
第6步:生成嵌入向量
仅靠原始文本无法实现语义搜索,需将每个片段转换为向量。此处使用的嵌入模型为:
BAAI/bge-small-en
诸如以下的查询:
"What is Databricks?"
会被映射为一个密集向量:
[0.21, -0.44, 0.87 ...]
在语义上相关的片段会聚集在该向量空间的相近位置。
第7步:构建向量索引
LlamaIndex负责处理索引的构建工作:
index = VectorStoreIndex(nodes)
最终得到的是一个内存中的向量索引,可用于执行类似以下的相似度查询:
Find chunks similar to:
"fastest bank run in history"
第8步:创建搜索引擎
将该索引作为查询引擎对外提供:
query_engine = index.as_query_engine(
response_mode="compact",
similarity_top_k=10,
verbose=True
)
similarity_top_k = 10
Return 10 most relevant chunks
similarity_top_k用于控制用于生成响应的邻近数据块的数量。
第9步:构建ReAct智能体
ReAct的含义是:
Reason + Act
简单的机器人会直接从问题跳到答案:
Question
→ Answer
而ReAct智能体则会加入推理过程和工具使用:
Question
↓
Reason
↓
Choose Tool
↓
Observe Output
↓
Answer
这样一来,信息检索就会是动态的,而非始终处于开启或关闭状态。
工具创建
需要将查询引擎封装成智能体可以调用的工具:
QueryEngineTool(
query_engine=query_engine,
metadata=ToolMetadata(
name="Wikipedia"
)
)
这样智能体就知道存在维基百科搜索功能。
ReAct Loop
The agent internally performs:
在内部,该循环的结构如下:
Thought:
Need information about the banking crisis.
Action:
Wikipedia Tool
Observation:
Found relevant passages.
Answer:
Grounded response.
那种模式比没有工具的普通聊天补全功能更可靠。
第10步:使用Chainlit构建用户界面
Chainlit提供了结构极为简化的聊天框架,其设置面板可展示:
Model
Wikipedia Pages
示例配置:
MODEL:
gpt-5-nano
Wikipedia Pages:
Please index:
2023 United States banking crisis
在对话开始前,这些设置会先初始化向量索引。
演示
完成索引构建后:
2023 United States banking crisis
提出如下问题:
Which bank experienced the fastest
bank run in US history?
便会得到类似这样的有依据的回复:
Silicon Valley Bank (SVB)
Approximately $42 billion
was withdrawn in 10 hours.
这些数字来自检索到的证据,而非凭空编造。
经验总结
哪些做法效果良好
LlamaIndex让索引构建流程更为简洁:
Documents
→ Nodes
→ Index
ReAct智能体能够优化信息检索的时机判断,从而仅在需要时才进行搜索。Chainlit依然是构建可用AI聊天界面的最快途径之一。
面临的挑战
Python 3.13兼容性给多个技术栈带来了问题——Chainlit、Pydantic与LlamaIndex的组合存在不稳定现象。通过固定版本:
Python 3.11
能显著提升稳定性。
信息检索质量直接决定答案的质量。低质量的检索片段会带来低质量的关联内容,进而生成劣质答案。一个重要的经验是:通常信息检索的质量比所使用的聊天模型更为重要。
未来的改进方向
接下来的自然发展路径包括混合搜索:
Keyword Search
+
Semantic Search
在初步检索之后进行重新排序——即再次检索:
Top 50
然后将其压缩为最精简的版本。为每个论点对应的段落标注引用来源,并使用持久向量数据库而非临时性存储:
VectorStoreIndex
可选方案包括Chroma、Pinecone、Weaviate或Milvus。
总结
该项目简要展示了现代智能体架构中的各类技术:RAG、嵌入模型、向量搜索、ReAct工具框架、Chainlit用户界面以及LlamaIndex编排机制。其核心价值在于各组件的协同,而非单一库的功能。可将6_wiki_rag_react_project示例克隆到data-etl-ml GitHub项目中的agentic-ai文件夹内,以便完整体验整套架构。