← All posts

What Is Agentic RAG? How It Differs From Classic RAG

Quick answer: Agentic RAG is retrieval-augmented generation where an agent, not a fixed pipeline, decides what to retrieve, when to retrieve it, and whether to retrieve again. Classic RAG runs one retrieval step before generation. Agentic RAG puts retrieval inside a reasoning loop, so the model can search, read a result, realize it needs something else, and search again until it has what it needs.

Classic RAG solved a real problem: give a language model access to information it was not trained on. But it solved it with a rigid shape. You embed a query, pull the top chunks, stuff them into the prompt, and generate. That works when the question maps cleanly to one retrieval. It falls apart when the question needs several lookups, when the first result reveals what you actually should have asked, or when the answer lives on a page that is not in your vector store at all.

If you are new to the underlying idea, start with what is RAG. This post assumes you know the basics and focuses on what changes when you make it agentic.

Classic RAG vs agentic RAG

The difference is who controls retrieval. In classic RAG, your code controls it: one query, one fetch, done. In agentic RAG, the model controls it through tool calls, and it can call as many times as the task needs.

Dimension Classic RAG Agentic RAG
Retrieval trigger Always, once, before generation When the model decides it needs data
Number of retrievals Exactly one Zero to many, model-driven
Query source The user's raw question Queries the model writes, refined per step
Data source A fixed vector store Vector store, live web search, direct URL fetch, APIs
Handling gaps Answers with what it got Notices the gap, retrieves again
Failure mode Wrong chunks, confident wrong answer Slower, more tokens, occasional over-searching

The trade is straightforward. Classic RAG is cheaper, faster, and simpler. Agentic RAG is more capable on hard, multi-hop questions and can reach data that was never indexed. You pay for that with latency, token cost, and a loop that needs guardrails.

Why the agent deciding matters

Consider a question like "Did the company that acquired Figma in 2022 raise prices this year?" Classic RAG embeds the whole sentence and retrieves chunks. But the useful chunks for "who acquired Figma" and "did that company raise prices" are different documents, and the second lookup depends on the answer to the first. One retrieval cannot express that dependency.

An agent can. It searches for the acquirer, reads that it was Adobe, then searches for Adobe's recent pricing changes, then fetches the pricing page to confirm. Each retrieval is informed by the last. That is multi-hop reasoning, and it is the thing classic RAG structurally cannot do.

The other unlock is the live web. A vector store only contains what you indexed. An agent with a web search tool can answer questions about things that happened after your last index run, or about pages you never thought to include.

Architecture

An agentic RAG system has three parts:

  1. A reasoning model that plans and decides when to retrieve.
  2. Retrieval tools the model can call: a vector-store lookup for your private docs, plus live web search and fetch for everything else.
  3. A loop that runs the model, executes its tool calls, feeds results back, and repeats until the model answers.

The web-facing tools are where most teams underinvest. Building a reliable fetcher and search layer means handling rendering, proxies, parsing, and anti-bot friction. link.sc provides both as one API: search returns structured results with the real target URLs, and fetch turns any of those URLs into clean markdown, which is exactly what a model needs to reason over.

Code sketch

Here is the retrieval half wired to link.sc. The two functions become tools the agent calls when it decides it needs external data:

import os
import requests

HEADERS = {"x-api-key": os.environ["LINKSC_API_KEY"]}

def web_search(query: str) -> str:
    r = requests.post(
        "https://api.link.sc/v1/search",
        headers=HEADERS,
        json={"q": query, "engine": "google"},
        timeout=60,
    )
    r.raise_for_status()
    results = r.json()["serpData"]["results"][:5]
    return "\n\n".join(
        f"{item['title']}\n{item['targetUrl']}\n{item['description']}"
        for item in results
    )

def web_fetch(url: str) -> str:
    r = requests.post(
        "https://api.link.sc/v1/fetch",
        headers=HEADERS,
        json={"url": url, "format": "markdown"},
        timeout=60,
    )
    r.raise_for_status()
    return r.json()["content"][:8000]

And the loop that makes it agentic. The model plans, calls a retrieval tool, observes, and decides whether to retrieve again:

import anthropic

client = anthropic.Anthropic()

TOOLS = [
    {
        "name": "web_search",
        "description": "Search the live web for current information. Use when the private knowledge base is unlikely to contain the answer, or when the question involves recent events.",
        "input_schema": {
            "type": "object",
            "properties": {"query": {"type": "string"}},
            "required": ["query"],
        },
    },
    {
        "name": "web_fetch",
        "description": "Fetch a specific URL as clean markdown. Use to read a page you found via search.",
        "input_schema": {
            "type": "object",
            "properties": {"url": {"type": "string"}},
            "required": ["url"],
        },
    },
]
DISPATCH = {"web_search": web_search, "web_fetch": web_fetch}

def agentic_rag(question: str, max_steps: int = 6) -> str:
    messages = [{"role": "user", "content": question}]
    for _ in range(max_steps):
        resp = client.messages.create(
            model="claude-opus-4-8",
            max_tokens=4096,
            thinking={"type": "adaptive"},
            tools=TOOLS,
            messages=messages,
        )
        if resp.stop_reason == "end_turn":
            return next(b.text for b in resp.content if b.type == "text")
        messages.append({"role": "assistant", "content": resp.content})
        results = []
        for block in resp.content:
            if block.type == "tool_use":
                out = DISPATCH[block.name](**block.input)
                results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": out,
                })
        messages.append({"role": "user", "content": results})
    return "Stopped without a final answer."

Notice there is no forced retrieval step. The model may answer directly if it already knows, search once for a simple lookup, or search several times for a multi-hop question. That is the whole point.

When agentic RAG is worth the complexity

It is not always the right call. Use this rough guide:

  • Stick with classic RAG when questions map to a single lookup, latency matters, and your data is fully indexed. A support bot answering from a fixed FAQ does not need an agent.
  • Go agentic when questions are multi-hop, when answers need current web data outside your index, or when the first retrieval often turns out to be the wrong one. Research assistants, competitive-intelligence tools, and anything that reasons across sources benefit.

A useful middle path: default to classic RAG and let the model escalate to web search only when its indexed context comes up short. You get the speed of one-shot retrieval most of the time and the depth of an agent when you need it.

Common pitfalls

  • Over-searching. Without a step limit, an agent can search repeatedly for marginal gains. Cap the loop and truncate results.
  • Dumping raw HTML into context. Feeding messy pages wastes tokens and confuses the model. Retrieve clean markdown so the model reasons over content, not markup.
  • No source tracking. If the agent cites nothing, you cannot verify it. Ask it to return the URLs it read alongside the answer.
  • Treating every question as agentic. The complexity is real. Reserve it for questions that actually need it.

Agentic RAG is classic RAG with the retrieval decision handed to the model. That single change turns a fixed pipeline into something that can chase an answer across the live web, one informed step at a time.


Give your agentic RAG system clean, reliable retrieval. Start free with link.sc and get web search and fetch behind one API.