“The bot hallucinated” is not a root cause. Senior RAG debugging separates wrong API (invented symbols/paths), wrong advice (real APIs, bad guidance), and wrong chunk (retrieval miss) because each class has a different fix. Blindly stuffing more context into the prompt wastes money and often makes overreach worse.
⚡ TL;DR: Tag every bad answer as
missing_chunk|bad_rerank|stale_index|model_overreach|prompt_leak; map each to a concrete remediation; require citations so wrong-API cases fail closed. Use with Citation-Required RAG and RAG Evaluation.
The triage taxonomy
| Class | What you see | Primary fix |
|---|---|---|
missing_chunk |
Correct answer exists in repo; not in top-k | Chunking, rewrite, filters, index coverage |
bad_rerank |
Right chunk in top-20, not top-5 | Reranker / hybrid weights |
stale_index |
Answer matched deleted/renamed code | Sync freshness, tombstones |
model_overreach |
Citations OK but advice extrapolates | Tighter prompts, refuse gaps, smaller temp |
wrong_api |
Invented method/path | Citation validator refuse + dictionary |
prompt_leak |
Answer follows issue/tracker injection | Sanitize inputs, guardrails |
type HallucinationClass =
| "missing_chunk"
| "bad_rerank"
| "stale_index"
| "model_overreach"
| "wrong_api"
| "prompt_leak";
function classify(bug: BugReport, retrieved: Chunk[], answer: GroundedAnswer): HallucinationClass {
const expect = bug.expect_paths;
const inTopK = retrieved.some((r) => expect.includes(r.path));
const inTop5 = retrieved.slice(0, 5).some((r) => expect.includes(r.path));
if (answer.claims.some((c) => !retrieved.find((r) => r.path === c.path))) return "wrong_api";
if (!inTopK && bug.still_in_git) return "missing_chunk";
if (!inTopK && !bug.still_in_git) return "stale_index";
if (inTopK && !inTop5) return "bad_rerank";
if (bug.injection_markers) return "prompt_leak";
return "model_overreach";
}
Wrong API vs wrong advice
Wrong API is a grounding failure: the model named PaymentClient.chargeAll that never existed. Fix with structured citations + validation (citation-required), not with a bigger context window.
Wrong advice cites real files but recommends async where the module is sync-only, or skips a mandatory auth check. Fix with:
- Stronger system rules / negative constraints
- ADR retrieval (RAG over ADRs)
- Faithfulness judges in eval, not only hit@k
# Separate eval slices
slices = {
"wrong_api": golden_where(expect_reject_invented_symbols=True),
"wrong_advice": golden_where(expect_policy_constraints=True),
"retrieval": golden_where(expect_paths=True),
}
Playbook per class
missing_chunk → rewrite + metadata filters + chunk boundaries; re-embed
bad_rerank → cross-encoder / weight sweep; don't raise k blindly past 30
stale_index → webhook sync SLA; show index git_sha in UI
model_overreach→ lower temperature; require open_questions; refuse
wrong_api → JSON schema + citation ⊆ retrieved; hard refuse
prompt_leak → sanitize Jira/Slack; Bedrock Guardrails
Avoid the “add top-20 chunks” reflex for every bug class — you increase overreach and cost for problems that are really sync or citation bugs.
Incident template
## RAG bad answer
- Question:
- Class: (taxonomy)
- Retrieved paths (k=10):
- Cited paths:
- Index git_sha / KB sync time:
- Fix PR / prompt version:
- Eval case ID added: yes/no
Every production complaint should graduate into a golden eval item—otherwise you only fix folklore.
Closing checklist
Dos
– Classify before changing prompts or k
– Split wrong-API vs wrong-advice eval slices
– Enforce citations to kill wrong-API classes
– Show index freshness in the UI for stale_index
– Add a golden case for every prod miss
Donts
– Do not treat all hallucinations as “model too small”
– Do not raise k as a universal remedy
– Do not skip triage in favor of anecdote-driven prompt edits
– Do not ignore prompt injection from trackers
– Do not close bugs without an eval fixture
Related reading
Last updated on September 11, 2026
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
