# Advanced Retrieval Patterns

Course: [Retrieval-Augmented Generation](https://www.devtutorials.ai/learn/rag) · Section: Advanced RAG
Source: https://www.devtutorials.ai/learn/rag/advanced-rag/advanced-retrieval-patterns

> Handling questions that need several steps: decomposition, stepwise and multi-hop retrieval, agentic RAG with tool loops and stop rules, Self-RAG reflection, and GraphRAG entity graphs and community summaries.

Some questions can be answered with one search. Other questions require information from several sources.

For example, imagine investigating who had access to a room where a letter was found. First, you need to identify the room. Next, you need to find who had access to it. Finally, you need to compare the available information before reaching a conclusion.

Each answer creates the next question that must be searched.

Basic RAG usually performs one search and uses the retrieved information to generate an answer. Advanced retrieval patterns allow the system to perform several searches, divide a complex question into smaller questions, check the retrieved information, and combine related facts before answering.

**The main idea:** **decompose** complex questions into steps, and **retrieve step by step**, using each answer to shape the next search (**multi-hop**). **Agentic RAG** lets the model decide when and what to search, in a loop with a step limit. **Self-RAG**-style **reflection** grades retrieval and answers and retries when they're weak. **GraphRAG** builds a graph of entities and relationships, so you can answer "big picture" and connection questions that no single chunk answers.

## A Real-World Example

Brightgrid found that some customer questions could not be answered with a single search.

For example:

“How many lights can the device used with Apple Home connect?”
 The system must first find that Apple Home requires the Brightgrid Hub. It must then search again to learn that the Hub supports 50 lights.

“How many more schedules does the Plus plan provide than the free plan?”
 The system must retrieve the schedule limits for both plans and compare the two values.

“Which Brightgrid products depend on the Hub?”
 The answer requires information about several products and their connections to the Hub.

Brightgrid added an agent loop for these complex questions. An agent loop allows the system to search, review the results, decide what information is still missing, and search again. Before answering, the system checks that each fact is supported by the retrieved sources.

For questions about relationships between products, features, and integrations, Brightgrid also uses an entity graph. An entity graph stores important items and the connections between them. For example, it can record that Apple Home connects through the Brightgrid Hub and that certain lamps depend on that Hub.

Simple questions still use a single search. The agent loop and entity graph are used only when the question requires several steps or connected facts.

**In simple words:** some questions need a plan, several searches, and a check at each step.

## What You Will Learn

In this lesson, you will learn:

1. Query decomposition: question decomposition, stepwise retrieval, multi-hop retrieval and complex question splitting.
2. Agentic RAG: letting the model decide when and what to search, tool loops, and stop rules.
3. Self-RAG-style reflection: grading retrieval and answers, and retrying.
4. GraphRAG: entity graphs, community summaries, and when graphs beat vectors.

The RAG Lab answers in one pass. You can still act like an agent: ask the first sub-question, read the answer, and use it to write the next search. The cells below show the same loops in code, with a scripted "model" so the results are repeatable.

## Query Decomposition

### Question Decomposition

Question decomposition means dividing a complex question into smaller questions that are easier to search and answer.

The smaller questions may be handled in two ways:

Parallel questions can be answered independently. For example, “How much is Brightgrid Plus, and how do I set up the Glow Strip?” can be divided into one question about pricing and another about setup. The system can search for both answers separately (Lesson 14's sub-queries).

Sequential questions must be answered in order because a later search depends on an earlier answer. For example, the system must first identify which device works with Apple Home. It can then search for how many lights that device supports.

A language model can help create the smaller questions and decide their order. The RAG system then searches for each answer and combines the supported information into a final response.

### Stepwise Retrieval

In **stepwise retrieval**, you search for step 1, read the answer, then **use it** in the step 2 search, and so on:

```js runnable title="Step 2 depends on step 1"
// Stepwise retrieval: the answer to step 1 decides what to search in step 2.
const kb = {
  "which hub works with apple home":
    "Apple Home is supported through the Brightgrid Hub.",
  "brightgrid hub how many lights": "The Brightgrid Hub connects up to 50 lights.",
};
const search = (q) =>
  kb[
    Object.keys(kb).find(
      (k) => k.split(" ").filter((w) => q.toLowerCase().includes(w)).length >= 3,
    )
  ];

const question = "How many lights can the device I need for Apple Home connect?";
const step1 = search("which hub works with apple home");
console.log("step 1:", step1);
const device = step1.match(/through the (.+?)\./)[1]; // "Brightgrid Hub"
const step2 = search(`${device} how many lights`);
console.log("step 2:", step2);
console.log(`answer: The ${device} connects up to ${step2.match(/(\d+)/)[1]} lights.`);
```

Expected output:

```text
step 1: Apple Home is supported through the Brightgrid Hub.
step 2: The Brightgrid Hub connects up to 50 lights.
answer: The Brightgrid Hub connects up to 50 lights.
```

### Multi-Hop Retrieval

Multi-hop retrieval finds an answer through several connected searches.

An important item mentioned in the information, such as a product, person, place, or code, is called an entity. The system can use an entity found in one search to decide what to search for next. Each search step is called a hop.

For example, the first search may find that the Glow Strip connects through the Brightgrid Hub. The next search uses Brightgrid Hub to find the required pairing instructions.

```js runnable title="Follow entities across chunks"
// Multi-hop retrieval: follow a link (an entity) from one chunk to the next.
const chunks = [
  {
    id: "glow-strip-setup#2",
    text: "The Glow Strip pairs through the Brightgrid Hub.",
    entities: ["Glow Strip", "Brightgrid Hub"],
  },
  {
    id: "hub-pairing#2",
    text: "To pair, hold the device's button for 5 seconds.",
    entities: ["Brightgrid Hub"],
  },
  { id: "energy-use#1", text: "The Lumo Lamp uses 9 watts.", entities: ["Lumo Lamp"] },
];

function hops(startEntity, maxHops) {
  const seen = new Set();
  let frontier = [startEntity];
  const path = [];
  for (let h = 0; h < maxHops && frontier.length; h++) {
    const next = [];
    for (const c of chunks) {
      if (!seen.has(c.id) && c.entities.some((e) => frontier.includes(e))) {
        seen.add(c.id);
        path.push(`hop ${h + 1}: ${c.id}`);
        next.push(...c.entities.filter((e) => !frontier.includes(e)));
      }
    }
    frontier = next;
  }
  return path;
}
console.log('Question: "How do I pair the Glow Strip?"');
console.log(hops("Glow Strip", 2).join("\n"));
```

Expected output:

```text
Question: "How do I pair the Glow Strip?"
hop 1: glow-strip-setup#2
hop 2: hub-pairing#2
```

### Complex Question Splitting

Practical rules for splitting:

- Split only when needed. Most questions are single-hop, and splitting adds latency.
- Keep **2–4 steps**. Longer chains compound errors.
- Carry forward **only the facts** found, with their citations, not whole chunks.
- Answer the **original** question at the end, citing every step.

## Agentic RAG

### The Model Decides When and What to Search

In **agentic RAG**, retrieval becomes a **tool** the model can call. Instead of "search once, then answer", the model works in a loop:

1. **Think:** what do I still need to know?
2. **Act:** call `search("…")`, or another tool like a calculator or an order lookup.
3. **Observe:** read the results.
4. Repeat, or **answer** when the evidence is enough.

This is the same tool-calling pattern taught in the MCP course: the model picks a tool and arguments, and your code runs it and returns the result.

```js runnable title="An agent loop with a step limit"
// Agentic RAG: the model decides each step (search again, or answer), within a step limit.
// Here a scripted "policy" stands in for the model's decisions.
const kb = {
  plus: "Brightgrid Plus costs $3.99 a month and adds unlimited schedules.",
  schedules: "Free accounts can create up to 5 schedules.",
};
function policy(question, notes) {
  if (!notes.length) {
    return { action: "search", query: "plus" };
  }
  if (question.includes("free") && !notes.some((n) => n.includes("Free"))) {
    return { action: "search", query: "schedules" };
  }
  return { action: "answer" };
}

const question = "How many more schedules do I get with Plus than on a free account?";
const notes = [];
const MAX_STEPS = 4;
for (let step = 1; step <= MAX_STEPS; step++) {
  const d = policy(question, notes);
  console.log(`step ${step}: ${d.action}${d.query ? ` "${d.query}"` : ""}`);
  if (d.action === "answer") {
    console.log("answer: Plus has unlimited schedules; free accounts have 5.");
    break;
  }
  notes.push(kb[d.query]);
  if (step === MAX_STEPS) {
    console.log("stopped: step limit reached");
  }
}
```

Expected output:

```text
step 1: search "plus"
step 2: search "schedules"
step 3: answer
answer: Plus has unlimited schedules; free accounts have 5.
```

### Stop Rules

Agents need firm limits, or they wander, repeat searches and run up costs:

- A **maximum number of steps** (for example 4) and a **time or token budget**.
- **Stop when the same search repeats** or when no new chunks appear.
- **Answer with what you have**, and say what's missing, when the limit is reached.
- **Read-only tools by default.** Any tool that changes something, like issuing a refund, needs explicit user approval.

Agentic RAG multiplies everything: model calls, latency, cost and **injection risk**. Each retrieved chunk can try to steer the next step. Use agents only for questions that need them, route simple questions to one-shot RAG (Lesson 14), and keep tools limited and permissioned.

## Self-RAG and Reflection

**Self-RAG** (self-reflective RAG) and **corrective RAG** add **checks** inside the loop:

- **Is retrieval needed?** Some questions, like greetings, need no search.
- **Is each retrieved chunk relevant?** Drop or re-search if not (Lesson 11's corrective retrieval).
- **Is the draft answer supported?** Check each claim against its source (Lesson 18).
- **Is the answer useful?** Does it actually answer the question?

The original Self-RAG research trained a model to emit special "reflection" tokens. In practice, most teams build the same idea with **grader prompts** or small classifiers at each step.

```js runnable title="Grade retrieval and the draft, retry if weak"
// Self-RAG style reflection: grade the retrieval, then grade the draft answer.
function gradeRetrieval(question, chunk) {
  const q = question
    .toLowerCase()
    .split(" ")
    .filter((w) => w.length > 3);
  return q.filter((w) => chunk.toLowerCase().includes(w)).length / q.length;
}
function gradeAnswer(answer, chunk) {
  const nums = answer.match(/\d+/g) ?? [];
  return nums.every((n) => chunk.includes(n)) ? "supported" : "not supported";
}

const question = "how long does standard shipping take";
const attempts = [
  {
    chunk: "Express shipping takes 1 to 2 business days.",
    draft: "Standard shipping takes 1 to 2 days.",
  },
  {
    chunk: "Standard shipping takes 3 to 5 business days.",
    draft: "Standard shipping takes 3 to 5 business days.",
  },
];
for (const [i, a] of attempts.entries()) {
  const r = gradeRetrieval(question, a.chunk);
  const g = gradeAnswer(a.draft, a.chunk);
  const sameTopic = a.chunk.toLowerCase().includes("standard");
  console.log(
    `attempt ${i + 1}: relevance ${r.toFixed(2)}, mentions "standard": ${sameTopic}, draft ${g}`,
  );
  if (sameTopic && g === "supported") {
    console.log("accepted:", a.draft);
    break;
  }
  console.log("  -> rejected, retrieve again with a sharper query");
}
```

Expected output:

```text
attempt 1: relevance 0.40, mentions "standard": false, draft supported
  -> rejected, retrieve again with a sharper query
attempt 2: relevance 0.60, mentions "standard": true, draft supported
accepted: Standard shipping takes 3 to 5 business days.
```

The first attempt found a chunk about **express** shipping. Its draft was "supported" by that chunk, but it answered the wrong question. The relevance check caught it.

## GraphRAG

### Entity Graphs

**GraphRAG** builds a **knowledge graph** from your documents:

1. **Extract entities** (products, features, integrations, people) and **relationships** ("pairs through", "requires", "supports") from each chunk, usually with a language model.
2. Store them as **nodes** and **edges**, with links back to the source chunks.
3. At question time, find the entities in the question and **walk the graph** to related entities and their chunks.

```js runnable title="Build a small entity graph and walk it"
// GraphRAG in miniature: build an entity graph from chunks, then answer a "big picture"
// question by walking the graph instead of matching one chunk.
const chunks = [
  ["Lumo Lamp", "Wi-Fi"],
  ["Lumo Lamp", "Brightgrid App"],
  ["Glow Strip", "Brightgrid Hub"],
  ["Brightgrid Hub", "Apple Home"],
  ["Brightgrid Hub", "Brightgrid App"],
  ["Brightgrid Plus", "Brightgrid App"],
];

const graph = new Map();
for (const [a, b] of chunks) {
  for (const [x, y] of [
    [a, b],
    [b, a],
  ]) {
    if (!graph.has(x)) {
      graph.set(x, new Set());
    }
    graph.get(x).add(y);
  }
}

// Which entity connects the most things? (A simple "hub" measure.)
const ranked = [...graph]
  .map(([e, n]) => [e, n.size])
  .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
console.log(
  "most connected:",
  ranked
    .slice(0, 3)
    .map(([e, n]) => `${e} (${n})`)
    .join(", "),
);
console.log("Brightgrid Hub links:", [...graph.get("Brightgrid Hub")].join(", "));
console.log(
  "Summary: the app ties products together; the hub links the Glow Strip and Apple Home.",
);
```

Expected output:

```text
most connected: Brightgrid App (3), Brightgrid Hub (3), Lumo Lamp (2)
Brightgrid Hub links: Glow Strip, Apple Home, Brightgrid App
Summary: the app ties products together; the hub links the Glow Strip and Apple Home.
```

### Community Summaries

For "big picture" questions, like "What are the main themes in our support tickets?" or "How do our products fit together?", GraphRAG groups densely connected nodes into **communities**, and writes a **summary** of each one ahead of time. A global question is answered from these summaries, not from a handful of top-k chunks.

### When Graphs Beat Vectors

| Question type | Better approach |
| --- | --- |
| A single fact ("How long is the warranty?") | Vector or hybrid search |
| Connections ("Which products depend on the Hub?") | Graph traversal |
| Multi-hop chains through named entities | Graph, or an agent loop with search |
| Whole-corpus themes and summaries | GraphRAG community summaries |
| Rapidly changing content | Vectors (graphs cost more to keep updated) |

GraphRAG is powerful but expensive: building the graph needs many model calls, and the graph must be updated when documents change. Try it when you have many connection or summary questions that your gold set shows plain RAG can't answer.

## Common mistakes

- **Over-engineering:** agents and graphs for questions one search answers.
- **No stop rule**, which leads to repeated searches, high cost and slow answers.
- **Losing citations** between steps, so the final answer can't be checked.
- **Reflection that only checks support, not relevance**, so answers are "supported" by the wrong chunk.
- **Stale graphs** that no longer match the documents.

## Exercises

Each exercise ends with a value on the last line. Press **Check** to compare it with the expected answer.

## What to Remember

- **Decompose** complex questions into **parallel parts** or **sequential steps**. **Stepwise** and **multi-hop** retrieval use each answer to shape the next search.
- **Agentic RAG** makes search a tool the model calls in a loop. Set **step, time and token limits**, detect repeats, and keep tools **read-only** unless approved.
- **Self-RAG-style reflection** checks whether retrieval is needed, whether chunks are **relevant**, and whether the answer is **supported** and useful, then retries.
- **GraphRAG** builds an **entity graph** and **community summaries** for connection and big-picture questions. It's costly to build and keep fresh.
- Route **simple** questions to one-shot RAG, and use advanced patterns only where they pay off.

Next: **RAG Security**, permissions, injection defense and privacy.
