首页 / 文章

使用 LlamaIndex 和 ReAct 构建一个维基百科 RAG 聊天助手

按需检索维基百科页面,通过小型编码器嵌入相关内容,并让ReAct智能体调用查询引擎工具,从而使Chainlit的回答始终基于检索到的证据。

发布者:REACTAPP.TOP阅读约 6 分钟1383 词

来源: **I Built a ChatGPT-Like Wikipedia Assistant Using RAG, ReAct Agents, and LlamaIndex in Under 300 Lines of Python**

简介

大型语言模型虽然表达流畅,但流畅度并不等同于能够判断答案的准确性或时效性。当问题超出训练数据范围,或是模型的自信度超过现有证据时,它们可能会编造看似合理的谎言。

一个实用的维基百科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文件夹内,以便完整体验整套架构。