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
“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).
“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: 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",
},
)“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)?
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.
“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.
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)“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}"""“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)”“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

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.
7.5 Failure mode → metric → action
| Failure mode | Primary metric | Human action when triggered |
|---|---|---|
| F1 Semantic gap | Context recall@k | Tune RRF weights; add query-expansion rules |
| F2 Lexical miss | BM25-recall vs. dense-recall split | Confirm BM25 indexing for that term class |
| F3 Top-k cliff | MRR, NDCG@10 | Tune reranker top-k and score threshold |
| F4 Multi-hop | Multi-hop answer accuracy | Tune decomposer prompt; expand graph KG for that domain |
| F5 Lost-in-middle | Faithfulness vs. chunk position | Tune reordering in R7 |
| F6 Distractor poisoning | Faithfulness, relevance of top chunk | Raise reranker score floor in R9 |
| F7 No-answer | No-answer accuracy on unanswerable set | Review R9 threshold; expand web-search fallback triggers |
| F8 Wrong-tool routing | Routing accuracy | Augment classifier training set; re-run DSPy |
| Hallucination (A3 fires) | not_supported edge traversal rate | Human reviews trace in Argilla; adds to golden set |
| Ingestion degradation | extraction F1 vs. golden | Block the pipeline change; investigate parser issue |
7.6 “Does it actually get better on its own?” — the compound accuracy flywheel

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.