QRefAI
Contents
Advanced RAG

Part 7 — Agentic Patterns and the Accuracy Flywheel

How does an agent self-correct after retrieval and generation, and how does the system get more accurate the longer it runs in production?

13 min · Updated July 2026

7.1 “R1–R9 got the right context in hand — so why do I still need an agent?”

R1–R9 are feed-forward optimizations: they maximize the probability of having the right context beforegenerating. Necessary, but not sufficient — because retrieval quality isn’t fully knowable before retrieving, and answer quality isn’t fully knowable before generating.

The residual failures are exactly the cases that need an agent: a component that observes the current state, grades the quality, and decides the next action. The cost (extra LLM calls, latency, spend) is real, so the operating discipline is to apply it selectively — cheap guardrails run on every query, expensive patterns fire only when a cheaper gate says they must. That cost ladder is the mental model for the whole page:

The seven patterns, cheapest first. A1 router and A6 budget are near-free and always on. A2 CRAG and A3 Self-RAG add a grading/verification call, but only when retrieval or generation actually happened. A5 citation is a prompt constraint (near-free). A4 fan-out and A7 tool-use are the expensive ones — and they fire only for confirmed multi-hop or when the corpus falls short. See the cost-ladder diagram below.

7.2 “How do I catch the seven ways the answer still goes wrong?” — the agentic patterns

A1

The heavy retrieval tool is dragging down my simple lookups.

Adaptive RAG router · kills F8 — wrong-tool routing

The graph’s entry node classifies the query before any retrieval, routing to: no-retrieval (parametric memory suffices); single-hop; multi-hop iterative; GraphRAG (only confirmed multi-hop or narrative); SQL (numeric); or real-time web search (temporal). It runs on a cheap model. The critical benefit: GraphRAG is gated behind it — applied to simple factoids, GraphRAG degrades accuracy by ~13% (empirically established).

A2

A wrong-but-plausible chunk keeps sneaking into the answer.

CRAG: Corrective Retrieval Augmented Generation · kills F6 — distractor poisoning; F7 — no-answer / stale

After retrieval and before generation, a grade_documents node scores each chunk for relevance to this specific query. Irrelevant chunks are filtered. If the filtered set is empty, the conditional edge routes to query transformation or web-search fallback rather than generating.

crag_core.py
# CRAG core: grade_documents node + conditional edge
workflow.add_node("retrieve", retrieve_node)
workflow.add_node("grade_documents", grade_documents_node)

workflow.add_edge("retrieve", "grade_documents")
workflow.add_conditional_edges(
    "grade_documents",
    decide_to_generate,
    {
        "transform_query": "transform_query",
        "web_search": "web_search",
        "generate": "generate",
    },
)
A3

The model wrote a confident answer the retrieved text doesn't actually support.

Self-RAG: generation verification loop · kills F5 — lost-in-the-middle; F6 — distractor; F7 — no-answer

After synthesis, a grade_generation node checks the answer on two axes: is it supported by the retrieved context (faithfulness), and is it useful— does it answer the question (relevancy)?

self_rag_loop.py
workflow.add_conditional_edges(
    "generate",
    grade_generation_v_documents_and_question,
    {
        "not supported": "generate",      # Hallucination: regenerate
        "not useful": "transform_query",  # Correct but irrelevant: re-retrieve
        "useful": END,                    # Quality gate passed
    },
)

The key insight: the system can now detect and label its own hallucinations in production. Every “not supported” edge traversal is a detected hallucination event — a trace that should enter the human-review queue.

A4

The question needs facts chained across documents, and one lookup can't do it.

Query decomposition and parallel fan-out · kills F4 — multi-hop failure

A planner node splits a compound question into atomic sub-queries, each independently retrievable. LangGraph’s Send API distributes sub-queries to parallel retriever nodes. A join node fuses results with RRF before reranking.

parallel_fanout.py
def parallel_retrieve(state: GraphState) -> list[Send]:
    return [
        Send("retrieve_subquery", {"query": q, "tenant_id": state["tenant_id"]})
        for q in state["sub_queries"]
    ]

workflow.add_conditional_edges("planner", parallel_retrieve, ["retrieve_subquery"])
workflow.add_node("fuse_results", rrf_fusion_node)
A5

When an answer is wrong, I can't tell which source misled it.

Mandatory grounded citation · kills F6, F7; makes F5 auditable

Synthesis is constrained to attach a citation (chunk_id, source_uri, page) to every factual claim. Making the constraint explicit in the prompt raises faithfulness structurally and makes every failure traceable — a wrong cited claim is auditable; an uncited claim is not.

# A5: force a citation on every claim. The constraint lives in the synthesis prompt.
CITED_SYNTHESIS = """Answer using ONLY the numbered context chunks below.
After every factual sentence, cite the chunk it came from as [chunk_id].
A sentence with no citation is not allowed — if you can't cite it, don't say it.

{numbered_context}

Question: {question}"""
A6

My correction loops sometimes spin forever and torch the budget.

Bounded reflection budget · prevents runaway loops and cost blow-up

The Self-RAG and CRAG correction loops are recursive. LangGraph’s recursion_limit bounds the maximum correction iterations; once exhausted, the system returns a hedged answer rather than spinning:

# A6: cap the loops. Past the budget, hedge instead of spinning.
config = {"recursion_limit": 6}          # e.g. ≤6 total node steps of correction
try:
    result = app.invoke(initial_state, config=config)
except GraphRecursionError:
    result = hedged_answer(initial_state)  # “Based on available sources, likely… (unverified)”
A7

The answer just isn't in our corpus — I'd rather fetch it live than hallucinate.

Tool-use fallback · kills F7 — stale / missing content

When CRAG (A2) finds the corpus insufficient, it invokes a real-time tool instead of hallucinating. The MCP tool registry provides web_search (current events), sql_query (exact numeric lookups), http_fetch (live APIs), and asr_transcribe (on-demand audio). The knowledge base isn’t the only source of truth; the agent hands off cleanly when the corpus reaches its limits.

7.3 “What does the whole thing look like assembled?” — the complete LangGraph structure

Diagram of the complete LangGraph graph structure for agentic RAG: routing, parallel retrieval fan-out, CRAG grading, query transform loops, synthesis, Self-RAG verification, and citation nodes with conditional edges

This graph is not a pipeline. A pipeline has one path. This graph has multiple conditional paths, loops with termination conditions, and parallel branches. Its accuracy comes precisely from being able to observe intermediate results and change course. The operating sequence across the patterns is: route (A1) → retrieve/fan-out (A4) → grade (A2) → generate + cite (A5) → verify (A3) → fall back to tools if needed (A7), all bounded by the budget (A6).

By this point the two failure planes are fully covered: the retrieval patterns R1–R9 from Part 6 handle F1–F8 feed-forward, and the agentic patterns here catch the residual F5/F6/F7 at generation time and F8 at routing time. Nothing in the F-list is left without a fix.

7.4 “How do I even know it’s improving?” — the three observability functions

Patterns are only as good as their ability to improve over time. A static system decays as the corpus changes, query distributions shift, and information needs evolve. The observability and human-review architecture creates a compound accuracy flywheel: the system produces traces → traces are evaluated → failures are reviewed → reviews become golden data → golden data improves the system → the improved system produces better traces.

Function 1 — Real-time correction triggers.
Every CRAG grading decision, Self-RAG faithfulness failure, and routing decision is emitted as an OTel span. When faithfulness drops below threshold, an alert fires; when CRAG fires repeatedly on one query class, a human-review job is queued.
Function 2 — Systematic pattern detection.
Individual failures are noise; patterns of failures are signal. Langfuse surfaces the aggregate — which query patterns consistently fail faithfulness — and Phoenix visualizes embedding-space clusters that retrieve poorly, making the topology of the failure space visible rather than just individual instances.
Function 3 — CI regression enforcement.
Every model upgrade, embedding-model change, or prompt modification runs against the golden dataset. DeepEval and RAGAS provide quantitative evaluation against the baseline. Accuracy is maintained automatically rather than hoped for manually.

7.5 Failure mode → metric → action

Failure modePrimary metricHuman action when triggered
F1 Semantic gapContext recall@kTune RRF weights; add query-expansion rules
F2 Lexical missBM25-recall vs. dense-recall splitConfirm BM25 indexing for that term class
F3 Top-k cliffMRR, NDCG@10Tune reranker top-k and score threshold
F4 Multi-hopMulti-hop answer accuracyTune decomposer prompt; expand graph KG for that domain
F5 Lost-in-middleFaithfulness vs. chunk positionTune reordering in R7
F6 Distractor poisoningFaithfulness, relevance of top chunkRaise reranker score floor in R9
F7 No-answerNo-answer accuracy on unanswerable setReview R9 threshold; expand web-search fallback triggers
F8 Wrong-tool routingRouting accuracyAugment classifier training set; re-run DSPy
Hallucination (A3 fires)not_supported edge traversal rateHuman reviews trace in Argilla; adds to golden set
Ingestion degradationextraction F1 vs. goldenBlock the pipeline change; investigate parser issue

7.6 “Does it actually get better on its own?” — the compound accuracy flywheel

Diagram of the compound accuracy flywheel: production queries feed traces into Langfuse, alerts trigger human review, golden datasets expand, DSPy recompiles prompts, and DeepEval CI gates validate improvements back into production

Yes — and not from engineering effort at deployment time, but from the structural loop between the system’s own uncertainty signals and the human knowledge that corrects them. Every hallucination the system detects (A3) becomes a golden example; every golden example tightens the next evaluation; every evaluation gates the next change. The longer it runs, the more it has corrected.

Found this useful?