How Retrieval Augmented Generation (RAG) Powers AI Agent Systems: A Deep Dive into AI-Agent-Book

Retrieval Augmented Generation (RAG) in AI agent systems fuses external knowledge retrieval with LLM reasoning through a modular ReAct architecture that supports multiple backends including local vector stores, Dify, RAPTOR, and GraphRAG.

The bojieli/ai-agent-book repository demonstrates a production-ready implementation of Retrieval Augmented Generation (RAG) within AI agents. This codebase showcases how to build extensible retrieval pipelines that separate orchestration logic from storage backends, enabling agents to dynamically decide when and how to retrieve external information.

The Modular RAG Architecture

The repository implements RAG through a clean, three-layer architecture following the ReAct pattern (reason-act-observe-think). This design allows developers to swap retrieval strategies without touching the agent's reasoning logic.

The architecture consists of:

Core Components of the Agentic RAG System

AgenticRAG: The ReAct Orchestrator

The AgenticRAG class (lines 73-115 in agent.py) serves as the central coordinator. It holds the LLM client, manages conversation state, and implements the decision loop that determines when to retrieve information versus when to generate answers.

When processing a query, the agent wraps user input with a system prompt (lines 73-99) that instructs the LLM to use the knowledge base exclusively. The query() method runs the iterative ReAct loop where the model may output tool calls like knowledge_base_search to fetch relevant context before synthesizing a response.

KnowledgeBaseTools: Abstracting Retrieval Operations

The KnowledgeBaseTools class (lines 58-147 in tools.py) implements the concrete retrieval interface. It exposes two primary operations:

  • knowledge_base_search: Performs semantic search across the configured backend
  • get_document: Retrieves full documents by ID when the LLM needs additional context

The tool layer inspects self.config.type and routes requests to the appropriate private method: _search_local(), _search_dify(), _search_raptor(), or the GraphRAG equivalent.

Configuration-Driven Backend Selection

The KnowledgeBaseConfig dataclass and KnowledgeBaseType enum (in chapter3/contextual-retrieval/config.py) enable one-line backend swaps. Changing the KNOWLEDGE_BASE_TYPE environment variable switches from a local vector store to GraphRAG without modifying agent code.

Supported Retrieval Backends

Local Hybrid Retrieval

The default backend (KnowledgeBaseType.LOCAL) runs a self-hosted FastAPI service that performs hybrid dense and sparse retrieval on a local vector store. The _search_local() method (lines 70-84 in tools.py) sends POST requests to the /search endpoint and normalizes responses into a standard SearchResult format containing doc_id, chunk_id, text, and score.

Dify API Integration

For cloud-hosted retrieval, the _search_dify() method (lines 58-79 in tools.py) calls the Dify /datasets/search endpoint using the configured API key. This backend suits teams wanting managed vector storage without infrastructure overhead.

RAPTOR: Hierarchical Tree Retrieval

The RAPTOR (Recursive Abstractive Processing for Tree-Organized Retrieval) backend enables multi-step retrieval through a tree-structured index. While the specific implementation follows the same pattern as other backends in KnowledgeBaseTools, it supports hierarchical document traversal that standard flat retrieval cannot achieve.

GraphRAG: Structured Knowledge Graphs

GraphRAG (chapter3/structured-index/graphrag_indexer.py) moves beyond semantic similarity to structured graph traversal. The GraphRAGIndexer class builds knowledge graphs by:

  1. Chunking documents (using chunk_text with configurable chunk_size and chunk_overlap)
  2. Extracting entities and relationships via LLM calls (extract_entities_relationships)
  3. Constructing traversable graphs using community detection algorithms like Leiden

This backend supports multi-hop reasoning essential for complex, compositional queries where relationships between entities matter more than keyword overlap.

The ReAct Loop: How Retrieval Augmented Generation Works in Practice

The RAG workflow executes through seven distinct steps:

  1. Query Processing: The user question enters AgenticRAG.query() and gets wrapped with the system prompt enforcing knowledge base usage.

  2. Tool Decision: In the ReAct loop (lines 94-115 in agent.py), the LLM decides whether to answer directly or call knowledge_base_search.

  3. Backend Routing: AgenticRAG._execute_tool forwards the call to KnowledgeBaseTools, which dispatches to the appropriate _search_* method based on config.type.

  4. Data Retrieval: The selected backend queries vector stores, Dify APIs, or traverses knowledge graphs to fetch relevant chunks.

  5. Result Formatting: Search results return as standardized dictionaries containing document metadata and relevance scores.

  6. Iterative Refinement: If needed, the LLM issues secondary calls (get_document) to fetch complete documents rather than chunks.

  7. Answer Synthesis: The LLM generates final responses with inline citations (e.g., [Doc: 123]) based on retrieved evidence.

Contextual Chunking for Improved Accuracy

Standard RAG often loses contextual meaning when splitting documents. The repository addresses this through ContextualChunker (in chapter3/contextual-retrieval/contextual_chunking.py).

This class enriches each retrieved chunk with surrounding context:

from chapter3.contextual_retrieval.contextual_chunking import ContextualChunker

chunker = ContextualChunker(window_size=2)  # keep 2 preceding/following chunks

chunks = chunker.chunk_document(full_document_text)

# Each chunk contains a context field for disambiguation

for ch in chunks:
    print(f"{ch.context} {ch.text}")

By preserving surrounding sentences, the system resolves ambiguous references like "the company" to specific entities (e.g., "ACME Corp"), significantly improving answer fidelity.

Implementation Examples

Instantiating the Agentic RAG System

from chapter3.contextual_retrieval.agent import AgenticRAG
from chapter3.contextual_retrieval.config import Config

# Load from environment variables (.env)

cfg = Config.from_env()

# Initialize with configured backend

rag = AgenticRAG(cfg)

# Query triggers automatic retrieval and ReAct reasoning

answer = rag.query("What are the revenue growth figures for ACME Corp in 2023?")
print(answer)

The Config.from_env() method reads KNOWLEDGE_BASE_TYPE, LOCAL_BASE_URL, and related parameters to instantiate the correct pipeline.

Building a GraphRAG Index

from chapter3.structured_index.graphrag_indexer import GraphRAGIndexer
from chapter3.structured_index.config import GraphRAGConfig

graphrag_cfg = GraphRAGConfig(
    llm_api_key="YOUR_API_KEY",
    base_url="https://api.openrouter.ai/v1",
    llm_model="gpt-5.6-luna",
    index_dir="data/graph_index",
    cache_dir="data/graph_cache",
    chunk_size=1200,
    chunk_overlap=100,
    max_knowledge_triples=10,
    community_detection_algorithm="leiden",
)

graphrag = GraphRAGIndexer(graphrag_cfg)

# Build graph from technical documentation

with open("docs/technical_spec.md") as f:
    sample_text = f.read()

graphrag.build_knowledge_graph(sample_text)

# Query supports multi-hop reasoning

results = graphrag.query("Which x86 instruction modifies the FLAGS register?")

Adding Context to Retrieved Chunks

from chapter3.contextual_retrieval.contextual_chunking import ContextualChunker

chunker = ContextualChunker(window_size=2)
contextualized = chunker.chunk_document(technical_manual)

# Results include surrounding sentences for LLM context

for chunk in contextualized[:3]:
    print(f"Context: {chunk.context}\nContent: {chunk.text}\n")

Summary

  • Modular Architecture: The AgenticRAG class separates reasoning logic from retrieval implementation, enabling backend swaps via configuration changes.
  • Multiple Backends: The system supports local hybrid retrieval, Dify cloud APIs, RAPTOR hierarchical indexes, and GraphRAG structured graphs through a unified KnowledgeBaseTools interface.
  • ReAct Pattern: The implementation uses a ReAct loop where the LLM decides when to retrieve information, enabling iterative, multi-step reasoning workflows.
  • Context Preservation: The ContextualChunker class adds surrounding context to retrieved segments, solving ambiguity problems common in naive RAG implementations.
  • File Locations: Core logic resides in chapter3/contextual-retrieval/agent.py (orchestration), tools.py (retrieval abstraction), and chapter3/structured-index/graphrag_indexer.py (graph construction).

Frequently Asked Questions

What makes Agentic RAG different from standard RAG pipelines?

Standard RAG retrieves documents once before generation begins. Agentic RAG, as implemented in AgenticRAG.query(), uses the ReAct pattern to let the LLM decide iteratively when to search, what to search for, and whether to fetch additional documents based on intermediate reasoning. This enables multi-hop queries and self-correction workflows impossible in single-pass retrieval.

How does GraphRAG improve upon vector-based retrieval?

According to the graphrag_indexer.py implementation, GraphRAG builds structured knowledge graphs of entities and relationships rather than relying solely on semantic similarity. This supports multi-hop reasoning queries like "Which company founded by the CEO of ACME's competitor filed for bankruptcy?" where the answer requires traversing relationships across documents, not just keyword matching.

Can I switch between retrieval backends without code changes?

Yes. The KnowledgeBaseType enum in chapter3/contextual-retrieval/config.py enables backend selection through environment variables. Changing KNOWLEDGE_BASE_TYPE from LOCAL to GRAPHRAG or DIFY switches the retrieval mechanism while the agent logic in AgenticRAG remains identical.

Why is contextual chunking necessary for production RAG systems?

As demonstrated in contextual_chunking.py, standard chunking often creates segments with ambiguous references (e.g., "it", "the company", "this approach"). The ContextualChunker adds surrounding sentences to each chunk, providing the LLM with disambiguating context that improves citation accuracy and answer quality when processing technical documentation or long-form content.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →