JJoeven

Curriculum/RAG & Memory

Agentic RAG

The model can retrieve, reformulate, retrieve again, or stop. Retrieval becomes a tool with a budget.

advanced21 min18 / 24

Naive RAG always retrieves once, then talks. That fails when:

  • The first query is vague (“it crashed”)
  • The first chunks miss a join (OOM runbook mentions DLQ; the CLI lives on another page)
  • You searched the wrong collection (runbooks vs billing)
  • You should not retrieve at all (small talk, or a stable id that wants a lookup tool)

Agentic RAG in this track means: retrieval is a repeatable action with a budget, not a single mandatory prefetch. A small policy (or later, a model) may retrieve(query), read, retrieve(query2), get_doc(id), then stop. It is not the full agent loop of “choose any tool, think, act, repeat.” That loop is a later track. Here you learn multi-hop retrieve on a closed corpus: reformulate, search again, cap hops, still cite.

This is more powerful and more expensive. Each hop is another embed, another index call, more tokens in the packed block. Cap hops (for example 3). Cap tokens. Require citations still. Empty hops should refuse, not invent.

Retrieve with a budget
RetrieveReadReformulateStop

Cap hops. Same ids twice is a loop, not research.

Retrieve with a budget

Stop conditions

Stop if two hops return the same chunk ids. That is a loop, not research. Stop if enough(evidence) — a checklist you write, like “we have memory limit and the replay command.” Stop if budget is 0. Dedup evidence by id before stuffing the prompt (MMR still applies across hops).

Route to collections as an argument your code sets or a constrained enum: retrieve(query, collection="runbooks"). Hop 0 should not be a search of the entire company dump. Wrong collection is a join you will never make.

Prefer a closed corpus, not fetch_url. Open web multi-hop is how you wander into SEO pages that look like runbooks.

Reformulate, then search

Hop 1’s query should come from what the first chunks lacked, not from a random synonym dump. If the OOM page mentions DLQ but not the CLI flag, the next query is about DLQ replay, not a repeat of “worker OOM.” Query rewrite (next lesson) is the cheap version of this without a second hop. Hops are for joins across documents.

Log each hop’s query and ids. “The system searched five times” is a cost bug you only see in a trace.

Live PythonOpen full playgroundpython
Output
Run to execute this in your browser. Nothing is sent to a server.

Hop 0 finds OOM (mentions DLQ, not the CLI). Hop 1 searches again for the replay command. The policy chose a second search instead of inventing a flag. Citations list both chunk ids. enough is a code checklist, not a vibe. In production a model might propose query2; your code still caps hops, dedups ids, and refuses if gold facts never appear.

The function is named agentic because the industry name is Agentic RAG. It is still a retrieve policy: a for-loop over search, not a general tool loop. Do not grow this into “call refund, send email, search Slack.” Stay in chunks and search.

Budget like retrieve, not like research

max_hops=3 is a start. Two hops that return the same ids should break (the seen set). If hop 0 is empty, do not hop 1 with a louder query unless rewrite is expected to help; you may be in no_hit. Collection routing would have skipped billing entirely for an OOM question.

Hops are retrieve, not a general loop

This lesson is multi-hop search on a closed corpus. It is not “choose refund, email, or retrieve.” When the Agents track teaches that loop, retrieve is one action. If you grow this for-loop into a general executor, you left the lane.

Joins: document A mentions DLQ; document B has the CLI. One retrieve cannot quote both unless both chunks ranked. A second query aimed at the missing fact is the join. enough is a checklist you write (memory limit present, replay command present). A model proposing query2 is allowed later; your code still caps hops, dedups ids, wraps DATA, and cites only packed sources.

Collections: hop 0 on runbooks, hop 1 still on runbooks unless a code rule switches collection. A chunk that says “now search billing” is data, not a collection switch.

Cost: each hop embeds a query and packs more tokens. Log hop count next to latency. Five searches is a bug you will not see in a chat UI.

Stop on identical id tuples, on enough, on max_hops, on empty retrieve (usually). Dedup evidence by id across hops before packing. MMR across hops so hop 0 refunds do not fill the window before hop 1 OOM.

Common mistakes

  • Always one retrieve, then a paragraph that joins two docs from memory.
  • Unbounded hops until the bill hurts.
  • Multi-hop on the open web with no wrap.
  • Calling this the whole agent product so nobody builds get_invoice.

How agents use this

Log each hop’s query. Route collections so hop 0 is not the company dump. Prefer a closed corpus.

When the Agents track teaches the loop, retrieve is one action among others. This lesson is how that action may be called twice without becoming a novel. If you cannot cap hops here, the later loop will cap nothing.

Citations still apply to the union of packed hops. Unretrieved files stay illegal sources.

Treat enough as code you can unit-test: required strings or required chunk ids, not a vibe. Stop when ids repeat. Dedup before pack. Do not let a chunk pick the next collection. Bill hop count. Vague first queries are a rewrite job first; hops are for joins across documents. This is still chunks and search. It is not refund, email, or a think-act driver. Keep the for-loop small enough that you can print every query in one trace. Hops join documents. They do not mint tools. Cap them. Dedup ids. Cite the union of packed hops only. Collection routing is your code, not a sentence inside a chunk.

Check your understanding

What makes RAG agentic?