Agentic RAG: Letting the Agent Decide When and What to Retrieve

The previous four posts covered Agent fundamentals (the tool calling loop), framework abstraction layers, design patterns, and three tool-use patterns. These gave Agents the ability to "call tools to fetch information."

But calling tools ≠ intelligent retrieval. A truly intelligent Agent should autonomously decide: Do I need to look something up? What keywords should I search for? Is the retrieved information sufficient? Should I re-search from a different angle?

That's exactly what Agentic RAG addresses.


📦 Links


🎯 Traditional RAG vs Agentic RAG: What's the Difference

flowchart LR
    subgraph "Traditional RAG (Fixed Pipeline)"
        A1["User Query"] --> A2["Vector Retrieval<br/>(fixed top-k)"]
        A2 --> A3["Concatenate Context"]
        A3 --> A4["LLM Generates Answer"]
    end
 
    subgraph "Agentic RAG (Autonomous Decisions)"
        B1["User Query"] --> B2{"LLM decides:<br/>need retrieval?"}
        B2 -->|No| B6["Answer directly"]
        B2 -->|Yes| B3["LLM generates search keywords"]
        B3 --> B4["Execute retrieval tool"]
        B4 --> B5{"Results sufficient?"}
        B5 -->|No| B3
        B5 -->|Yes| B6
    end

Traditional RAG's problem isn't quality — it's the lack of autonomy:

Dimension Traditional RAG Agentic RAG
Retrieval timing Every time (fixed pipeline) LLM decides autonomously
Search keywords Raw user query (no rewriting) LLM generates/optimizes keywords
Retrieval rounds Once Can iterate multiple times
Verification None Self-check, second-pass verification
Best for Simple Q&A Complex queries needing multi-step reasoning, cross-source comparison

Core difference: retrieval is no longer a fixed pre-processing step — it's just another tool the Agent can call whenever needed.


🔬 Core Mechanism: Wrapping the Knowledge Base as a Parameterized Search Tool

In Agentic RAG, the knowledge base is not a "retrieval step" — it's a tool:

Tool Definition (Raw SDK)

TRAVEL_KNOWLEDGE_BASE = {
    "Barcelona": "Barcelona is Spain's cosmopolitan capital of Catalonia...",
    "Tokyo": "Tokyo is Japan's capital, mixing ultramodern with traditional...",
    "Paris": "Paris is France's capital and a global center for art...",
    "Cape Town": "Cape Town sits on South Africa's southwest tip...",
}
 
TOOLS_SCHEMA = [{
    "type": "function",
    "function": {
        "name": "search_travel_knowledge",
        "description": "Search the travel knowledge base for destination info...",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "Search query — destination name or keywords",
                },
            },
            "required": ["query"],
        },
    },
}]

Key design points:

  • Tool name search_travel_knowledge — The LLM sees this name and immediately knows it's for looking up information
  • Parameter query — The LLM autonomously decides what keyword to pass. It might send "architecture Gaudí" instead of the user's raw question
  • Return value — The tool returns matching knowledge base entries; the LLM generates its answer based on these entries

This is indistinguishable from a regular "call an API for data" tool — and that's precisely the insight: retrieval is just another tool call.

Tool Definition (qwen-agent Framework Version)

Comparing the framework version — same semantics, different syntax:

@register_tool("search_travel_knowledge")
class SearchTravelKnowledge(BaseTool):
    description = "Search the travel knowledge base for destination info..."
    parameters = [
        {"name": "query", "type": "string",
         "description": "Search query about a travel destination",
         "required": True},
    ]
 
    def call(self, params: str, **kwargs) -> str:
        args = json.loads(params)
        query = args.get("query", "")
        # Search TRAVEL_KNOWLEDGE_BASE for matching entries
        ...

sequenceDiagram
    participant User
    participant Agent
    participant SearchTool
    participant KnowledgeBase
 
    User->>Agent: I'm interested in places with great architecture. Recommendations?
    Agent->>Agent: Decision: need to search knowledge base
    Agent->>SearchTool: search_travel_knowledge(query="architecture Gaudí")
    SearchTool->>KnowledgeBase: Match against knowledge base
    KnowledgeBase-->>SearchTool: Barcelona, Paris (matching entries)
    SearchTool-->>Agent: Barcelona: Gaudí architecture...\nParis: Louvre...
    Agent->>Agent: Generate answer from retrieved results
    Agent-->>User: Recommend Barcelona and Paris. Their architectural...

This flow is identical to Lesson 04's "multi-tool composition." The Agent doesn't know whether search_travel_knowledge is backed by a vector database or a hardcoded dictionary — it only knows "there's a tool to look things up, and I call it when needed."


🔄 The Producer-Checker Pattern: The Essence of Iterative Retrieval

Basic Agentic RAG solves "autonomous retrieval." But there's a more advanced need: how do you ensure the retrieved information is complete and accurate?

The answer is the Producer-Checker pattern — guiding the Agent through a two-pass retrieval via the system prompt:

System message (Checker mode):
1. Search for relevant destinations first
2. For each destination found, search again with the destination name for full details
3. Compare the options using verified information
4. Present a final recommendation with specific costs, best travel times, and highlights
5. If any detail seems incomplete, search once more to confirm before responding
flowchart TD
    A["User: $175/day budget, traveling in April. Where fits?"] --> B["Round 1:<br/>search(query='April budget 175')"]
    B --> C["Results: Barcelona($150-200), Cape Town($100-150)"]
    C --> D{"Results complete?"}
    D -->|Not detailed enough| E["Round 2:<br/>search(query='Barcelona')<br/>search(query='Cape Town')"]
    E --> F["Full details for each destination"]
    F --> G["Compare: Cape Town $100-150 (fits), Barcelona $150-200 (borderline)"]
    G --> H["Final recommendation: Cape Town, Nov-Mar best; Barcelona as backup"]

Producer = first retrieval, getting a candidate list Checker = second retrieval, using candidate names for deep verification

The keywords differ between rounds: the first is "fuzzy search" (by budget and month), the second is "precise search" (by destination name). This "broad-to-narrow, fuzzy-to-precise" retrieval strategy is Agentic RAG's core advantage over traditional RAG.


📊 Comparing the Two Agent Configurations

The two Agents in this lesson differ only in their system prompt strategy instructions:

Dimension Basic RAG Agent Producer-Checker Agent
system prompt strategy "Always search before answering" "Search → re-search by name → compare → confirm"
Retrieval rounds Typically 1 At least 2 (broad + deep)
Best for Simple fact lookup Comparative analysis, budget matching
Code difference Only system_prompt differs Same tools, same loop

Key insight: an Agent's "intelligence" comes from the strategy instructions in its system prompt, not the number of tools. The tools are identical, but different instructions produce completely different behavioral patterns.


🔑 Framework Comparison Cheat Sheet

Concept Raw SDK qwen-agent
Knowledge base Hardcoded TRAVEL_KNOWLEDGE_BASE dict Same
Search tool JSON Schema + Python function + TOOL_MAP @register_tool + BaseTool class
Parameterized tool json.loads(tc.function.arguments) extract args params auto-passed, json.loads internally
Agent creation run_agent(system_prompt, user_message) Assistant(llm=..., system_message=..., function_list=[...])
Strategy switching Change system_prompt parameter Change system_message parameter
Tool calling loop Manual while True Framework handles internally

💡 Three Key Insights

1. Retrieval is just another tool call

The Agent neither knows nor cares what backs search_travel_knowledge — vector DB, SQL query, web scraper, hardcoded dict — it's all the same to the Agent. This means you can swap the retrieval implementation without changing the Agent's behavior.

2. The system prompt determines the Agent's retrieval strategy

Same tools + same knowledge base, different system prompts produce completely different retrieval behaviors. The basic prompt tells the Agent "search once, that's enough." The Checker prompt tells it "search twice, then compare." The Agent's intelligence isn't in the tools — it's in the prompt's strategy.

3. Iterative retrieval depends on parameter design

Why can the Producer-Checker pattern "search again with a more precise keyword"? Because the search tool accepts a query parameter. If the tool were a parameterless get_all_documents(), the Agent would have no room to optimize search terms. The tool's parameter design determines the upper bound of the Agent's intelligence.


🚀 Running

cd 05-agentic-rag/code_samples
 
# Raw SDK version (basic RAG + Producer-Checker demos)
python 05-python-agent-framework.py
 
# Framework version (same two demos)
python 05-qwen-agent-framework.py

🔮 Next Post

Next up: System Message Framework — how to use an LLM to automatically generate high-quality system prompts. What is a meta-prompt? And how does this relate to building trustworthy AI Agents?


✍️ Closing

Agentic RAG boils down to one sentence: don't let the pipeline decide when to retrieve; let the Agent decide.

Traditional RAG's "retrieve then generate" is a fixed assembly line. Agentic RAG turns retrieval into a tool — the Agent autonomously decides whether to call it, how many times, and with what keywords. This isn't a technical architecture change — it's a design philosophy change: from "pipeline-driven" to "Agent-driven."

The moment you wrap your knowledge base as a tool, your RAG system ceases to be a "retrieval + generation" pipe and becomes an intelligent agent that can think, verify, and iterate.