Home / Articles

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.

Publisher: REACTAPP.TOP6 min read1383 words

This article is published in English.

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

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.