Build a Wikipedia RAG chat assistant with LlamaIndex and ReAct
Index Wikipedia pages on demand, embed chunks with a small encoder, and let a ReAct agent call a query-engine tool so Chainlit answers stay grounded in retrieved evidence.
This article is published in English.
Introduction
Large language models are fluent, but fluency is not the same as knowing whether an answer is accurate or current. They can invent plausible falsehoods when a question falls outside training cutoffs or when confidence outruns evidence.
A practical Wikipedia RAG chat assistant closes that gap by combining on-demand page indexing, semantic retrieval, evidence packing, a ReAct agent that decides when to search, and a conversational UI that answers from retrieved text. Source materials for the build live under the agentic-ai wiki RAG sample in the data-etl-ml GitHub repository.
Why RAG?
Without retrieval, the path is short and brittle:
User Question
↓
LLM
↓
Answer
Everything depends on what the model memorized in training.
With RAG, evidence enters before generation:
User Question
↓
Retriever
↓
Relevant Documents
↓
LLM
↓
Grounded Answer
The model answers from retrieved passages. That usually means fewer hallucinations, more timely facts, no fine-tune cycle to refresh knowledge, and an easy path to swap or grow the corpus.
Project Goal
Build a chat assistant that answers from Wikipedia pages the user chooses to index. Example request:
Please index:
2023 United States banking crisis
Then ask:
Which bank experienced the fastest bank run
in US history during 2023?
The reply should cite retrieved page content rather than unaided model memory.
Architecture Overview
At a high level the system looks like this:
Wikipedia Pages
│
▼
WikipediaReader
│
▼
SentenceSplitter
│
▼
Embeddings
BAAI/bge-small-en
│
▼
VectorStoreIndex
│
▼
Query Engine
│
▼
QueryEngineTool
│
▼
ReAct Agent
│
▼
Chainlit UI
Two flows matter: an indexing pipeline that builds the searchable vector store, and a query pipeline that retrieves chunks and generates answers.
Step 1: Reading the OpenAI API Key
Credentials should stay out of source. Avoid hardcoding:
api_key = "xxxxx"
Load from a config file instead:
def get_apikey():
with open("apikeys.yml") as file:
config = yaml.safe_load(file)
return config["OPENAI_API_KEY"]
Secrets remain separate from application logic.
Step 2: Understanding the Code Structure
The project is three Python modules:
utils.py
index_wikipages.py
chat_agent.py
utils.py loads API keys and configuration. index_wikipages.py parses page requests, downloads Wikipedia text, embeds chunks, and builds indexes. chat_agent.py wires the ReAct agent, query-engine tool, and Chainlit UI.
Step 3: Extracting Wikipedia Pages using Structured Output
Users may type free-form lists:
Please index:
London, Birmingham, New York
The indexer needs a clean array:
[
"London",
"Birmingham",
"New York"
]
Rather than brittle string splitting, a Pydantic model captures structured output:
class WikiPageList(BaseModel):
pages: List[str]
The model turns natural language into a typed page list automatically.
Step 4: Loading Wikipedia Pages
LlamaIndex ships a convenience loader:
WikipediaReader
Ingestion is a few lines:
loader = WikipediaReader()
documents = loader.load_data(
pages=["2023 United States banking crisis"]
)
At this point the pipeline holds raw page documents.
Step 5: Chunking Documents
Models handle large pages poorly as single blobs. Split into overlapping pieces:
SentenceSplitter(
chunk_size=150,
chunk_overlap=45
)
Overlap keeps ideas that straddle boundaries:
Chunk A:
---------------------
Paragraph 1
Paragraph 2
Chunk B:
---------------------
Paragraph 2
Paragraph 3
Step 6: Creating Embeddings
Raw text is not enough for semantic search. Each chunk becomes a vector. The embedding model used here is:
BAAI/bge-small-en
A query such as:
"What is Databricks?"
maps to a dense vector:
[0.21, -0.44, 0.87 ...]
Semantically related chunks cluster nearby in that space.
Step 7: Creating the Vector Index
LlamaIndex wraps the store construction:
index = VectorStoreIndex(nodes)
The result is an in-memory vector index that can answer similarity lookups like:
Find chunks similar to:
"fastest bank run in history"
Step 8: Creating the Search Engine
Expose the index as a query engine:
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 controls how many neighbor chunks feed the response.
Step 9: Building the ReAct Agent
ReAct means:
Reason + Act
A naive bot jumps straight from question to answer:
Question
→ Answer
A ReAct agent inserts reasoning and tool use:
Question
↓
Reason
↓
Choose Tool
↓
Observe Output
↓
Answer
Retrieval becomes dynamic instead of always-on or never-on.
Tool Creation
Wrap the query engine as a tool the agent can call:
QueryEngineTool(
query_engine=query_engine,
metadata=ToolMetadata(
name="Wikipedia"
)
)
The agent then knows a Wikipedia search capability exists.
ReAct Loop
The agent internally performs:
Internally the loop looks like:
Thought:
Need information about the banking crisis.
Action:
Wikipedia Tool
Observation:
Found relevant passages.
Answer:
Grounded response.
That pattern is more reliable than a plain chat completion with no tools.
Step 10: Building the UI using Chainlit
Chainlit provides a chat shell with minimal boilerplate. A settings panel exposes:
Model
Wikipedia Pages
Example configuration:
MODEL:
gpt-5-nano
Wikipedia Pages:
Please index:
2023 United States banking crisis
Settings initialize the vector index before conversation starts.
Demo
After indexing:
2023 United States banking crisis
a question such as:
Which bank experienced the fastest
bank run in US history?
yields a grounded reply like:
Silicon Valley Bank (SVB)
Approximately $42 billion
was withdrawn in 10 hours.
The numbers come from retrieved evidence, not free invention.
Lessons Learned
What Worked Well
LlamaIndex keeps the indexing path short:
Documents
→ Nodes
→ Index
ReAct agents improve when-to-retrieve decisions so search runs only when needed. Chainlit remains one of the fastest routes to a usable AI chat UI.
Challenges
Python 3.13 compatibility bit several stacks—Chainlit, Pydantic, and LlamaIndex combinations were unstable. Pinning:
Python 3.11
was far more reliable.
Retrieval quality dominates answer quality. Poor chunks produce poor neighbors produce poor answers. The durable lesson: retrieval quality often matters more than which chat model sits on top.
Future Improvements
Natural next steps include hybrid search:
Keyword Search
+
Semantic Search
Reranking after a broad recall pass—retrieve:
Top 50
then compress to the best few. Citations that surface the paragraphs behind each claim. And a persistent vector database instead of an ephemeral:
VectorStoreIndex
with options such as Chroma, Pinecone, Weaviate, or Milvus.
Final Thoughts
The project is a compact tour of modern agentic patterns: RAG, embeddings, vector search, ReAct tooling, Chainlit UI, and LlamaIndex orchestration. The strategic point is larger than any one library. Reliable systems come from combining models with retrieval, tools, and reasoning loops—not from chasing ever-larger parameter counts alone. Clone the 6_wiki_rag_react_project sample under the agentic-ai folder of the data-etl-ml GitHub project to experiment with the full tree.