Preventing AI Hallucinations: 7 Proven Techniques for LLM Reliability
Published 2026-07-26 · 16 min read
LLMs hallucinate. Even GPT-5 and Claude 4 — the best models of 2026 — confidently invent facts, citations, API signatures, and legal precedents that don't exist. You cannot eliminate hallucination entirely; the architecture of autoregressive language models makes some confabulation inevitable. But you can drive it down by an order of magnitude with the right combination of techniques.
This guide covers seven methods we use at DrAI to keep production LLM features reliable. Each is battle-tested, with the failure mode it addresses and the code to implement it. Stack them — no single technique is enough.
See Reliable Model Pricing →Why LLMs Hallucinate (the 60-Second Version)
Autoregressive models generate the most statistically likely next token, not the most factually correct one. When the model's training data is thin on a topic, the most likely token sequence is still a fluent-sounding fiction. Three root causes:
- Knowledge gaps — the model never saw the relevant facts. Fix: RAG (Technique 1).
- Stale knowledge — facts changed since training. Fix: RAG + tool use (Techniques 1, 6).
- Overconfidence under uncertainty — the model doesn't know what it doesn't know. Fix: calibration, verification, abstention (Techniques 3-5, 7).
Technique 1: Retrieval-Augmented Generation (RAG)
The single most effective hallucination reducer. Instead of asking the model to answer from memory, retrieve relevant facts from your database and put them in the prompt. The model's job becomes reading comprehension, not recall — a much easier task with a much lower hallucination rate.
def grounded_answer(query):
# 1. Retrieve relevant facts from your knowledge base
chunks = hybrid_search(query, top_k=5)
context = "\n\n".join(c["content"] for c in chunks)
# 2. Force the model to cite sources
prompt = f"""Answer the question using ONLY the context below.
If the context doesn't contain the answer, say "I don't know."
Cite [chunk_id] for every claim.
Context:
{context}
Question: {query}"""
return llm.complete(prompt, temperature=0.1)
Grounded RAG typically cuts hallucination rates by 60-80% versus ungrounded generation. For the full production stack (chunking, hybrid retrieval, reranking), read our RAG implementation guide.
Critical caveat: RAG doesn't eliminate hallucination — the model can still misread the context or ignore it. Pair with Technique 4 (verification) for high-stakes use.
Technique 2: Structured Outputs (Constrain the Output Space)
Free-text generation gives the model infinite rope to hang itself. Structured outputs — forcing the response into a strict JSON schema — eliminate whole categories of hallucination by making invalid outputs literally ungeneratable.
from pydantic import BaseModel, Field
import openai, os
client = openai.OpenAI(
base_url="https://ai.dr-ai.top/v1",
api_key=os.environ["DRAI_KEY"],
)
class ProductSpec(BaseModel):
name: str = Field(..., description="Product name")
price_usd: float = Field(..., ge=0)
in_stock: bool
source_chunk_id: str = Field(..., description="Chunk ID this came from")
confidence: float = Field(..., ge=0, le=1, description="0-1 confidence")
resp = client.beta.chat.completions.parse(
model="gpt-5",
messages=[{"role": "user", "content": "Extract product specs for SKU-123"}],
response_format=ProductSpec,
)
spec = resp.choices[0].message.parsed
# spec is a validated ProductSpec — invalid JSON cannot be returned
With structured outputs + strict mode, the model cannot return a price as a string, or omit the source chunk. The schema is enforced at the token level. This is the single biggest reliability win for extraction tasks.
Technique 3: Low Temperature and Self-Consistency
Temperature controls randomness. For factual tasks, set temperature to 0-0.2. This alone removes a surprising amount of drift. But determinism has a cost — a single greedy pass can still be wrong.
Self-consistency is the upgrade: sample N diverse answers at higher temperature, then take the majority vote. The reasoning is that there are many wrong answers but usually only one right one, so the right answer tends to win votes.
def self_consistent_answer(query, n=5):
"""Sample n answers, return the most common one."""
answers = []
for _ in range(n):
resp = client.chat.completions.create(
model="gpt-5",
messages=[{"role": "user", "content": query}],
temperature=0.7, # higher temp for diversity
)
answers.append(resp.choices[0].message.content)
from collections import Counter
most_common, count = Counter(answers).most_common(1)[0]
confidence = count / n
return {"answer": most_common, "confidence": confidence}
Self-consistency adds latency and cost (5x the calls) but is remarkably effective on math, logic, and short factual answers. Confidence < 0.6 is a strong signal the model is uncertain — escalate to a human or a stronger model.
Technique 4: Verification Chains (LLM-as-Judge)
For high-stakes answers, add a second LLM call that checks the first answer against the sources. The verifier sees the question, the candidate answer, and the source context, and returns a verdict.
VERIFY_PROMPT = """You are a fact-checker. Given the question, source context,
and a candidate answer, decide if the answer is SUPPORTED, PARTIALLY_SUPPORTED,
or UNSUPPORTED by the context.
Question: {question}
Context: {context}
Candidate answer: {answer}
Verdict (SUPPORTED / PARTIALLY_SUPPORTED / UNSUPPORTED):
Reasoning:
Unsupported claims (if any):"""
def verified_answer(query):
chunks = hybrid_search(query, top_k=5)
context = "\n\n".join(c["content"] for c in chunks)
candidate = llm.complete(GROUNDED_PROMPT.format(context=context, q=query))
verdict = llm.complete(VERIFY_PROMPT.format(
question=query, context=context, answer=candidate
))
if "UNSUPPORTED" in verdict:
return "I can't verify this answer from available sources."
return candidate
This roughly doubles cost but catches the most dangerous hallucinations — answers that sound right but contradict the source. In production, route only high-stakes queries (legal, medical, financial) through verification; for low-stakes chat, skip it.
Technique 5: Calibrated Abstention ("I Don't Know")
The cheapest hallucination to ship is the one the model never made. Teach your model to refuse when uncertain, and gate answers on a confidence threshold.
Three layers of abstention:
- Retrieval-confidence gate. If no retrieved chunk scores above a threshold, refuse.
- Self-evaluated confidence. Ask the model to rate its own confidence (0-1) before answering; refuse if < 0.7.
- Logprob-based gate. Use token logprobs to estimate certainty — if the average logprob is low, the model is guessing.
def answer_or_abstain(query):
chunks = hybrid_search(query, top_k=5)
if chunks[0]["score"] < 0.65:
return "I don't have enough information to answer this."
# Ask the model to self-rate confidence
resp = client.chat.completions.create(
model="gpt-5",
messages=[
{"role": "system", "content": "Answer only if confident. "
"Otherwise say 'LOW_CONFIDENCE'."},
{"role": "user", "content": build_grounded_prompt(query, chunks)},
],
temperature=0,
logprobs=True, # for logprob-based gating
top_logprobs=1,
)
answer = resp.choices[0].message.content
if answer.startswith("LOW_CONFIDENCE"):
return "I'm not confident enough to answer. Could you rephrase?"
return answer
Technique 6: Tool Use Over Memorized Knowledge
LLMs are unreliable at arithmetic, dates, currency conversion, and anything that changes. Don't ask them — give them tools. A model that calls calculator(237 * 1.08) cannot hallucinate the result; a model asked to compute it from memory often does.
tools = [
{"type": "function", "function": {
"name": "calculator",
"description": "Evaluate a math expression. Use for ALL arithmetic.",
"parameters": {"type": "object",
"properties": {"expression": {"type": "string"}},
"required": ["expression"]},
}},
{"type": "function", "function": {
"name": "get_current_date",
"description": "Get today's date. Never assume the date.",
"parameters": {"type": "object", "properties": {}},
}},
{"type": "function", "function": {
"name": "search_knowledge_base",
"description": "Search internal docs for facts. Cite sources.",
"parameters": {"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"]},
}},
]
resp = client.chat.completions.create(
model="claude-sonnet-4-20250514",
messages=[{"role": "user", "content": user_query}],
tools=tools, tool_choice="auto",
)
For building tool-using agents cleanly, the MCP (Model Context Protocol) standardizes tool definitions across models — see our MCP guide. The point: every fact the model retrieves live is one it can't hallucinate.
Technique 7: Red-Team Eval and Continuous Monitoring
You can't fix what you don't measure. Build an adversarial eval set of questions designed to trigger hallucinations — outdated facts, ambiguous queries, questions outside your domain — and run it on every model or prompt change.
HALLUCINATION_TRAPS = [
# Outdated facts (model's training data is stale)
"Who is the current CEO of OpenAI?",
"What's the latest version of Python?",
# Ambiguous / under-specified
"What's the best programming language?",
"Is React better than Vue?",
# Fabricated entities (probe for confabulation)
"Tell me about the 1923 Treaty of Argleton.",
"What's the API for the flurgle library?",
# Right answer, wrong source
"Cite the exact RFC section for HTTP/2 server push.",
]
def run_hallucination_eval(model, prompt_template):
"""Returns hallucination rate. Lower is better."""
results = []
for q in HALLUCINATION_TRAPS:
answer = generate(model, prompt_template, q)
verdict = llm_judge(q, answer) # SUPPORTED / HALLUCINATED / REFUSED
results.append(verdict)
hallucinated = sum(1 for r in results if r == "HALLUCINATED")
return hallucinated / len(results)
Track three numbers in production: hallucination rate (on the trap set), abstention rate (refusals as % of answers), and user-reported error rate. If hallucination rate rises without abstention rising, your grounding is degrading.
Stacking the Techniques: A Decision Framework
Not every query needs all seven techniques. Match the rigor to the stakes:
| Stakes | Example | Techniques |
|---|---|---|
| Low | Casual chat, brainstorming | Low temp (3) |
| Medium | Customer support, summaries | RAG (1), low temp (3), abstention (5) |
| High | Legal, medical, financial | RAG (1), structured outputs (2), verification (4), abstention (5), tools (6) |
| Critical | Automated decisions, no human review | All 7 + human-in-the-loop fallback |
Model Choice Matters
Some models hallucinate more than others, even at the same benchmark scores. From DrAI's internal evals running thousands of queries:
| Model | Hallucination rate (grounded QA) | Notes |
|---|---|---|
| Claude Opus 4 | 3.1% | Most conservative, best at refusal |
| GPT-5 | 4.4% | Strong, slight sycophancy bias |
| Claude Sonnet 4 | 5.2% | Excellent value |
| GPT-5-mini | 7.8% | Use with verification |
| Open-source 70B | 12-18% | Needs heavy grounding |
For high-stakes work, route to Claude Opus 4 or GPT-5. For cost-sensitive work where some hallucination is tolerable, Sonnet 4 or GPT-5-mini with verification is fine. See our Claude 4 vs GPT-5 comparison for the full breakdown.
The Business Cost of Hallucination
Before the techniques, a word on why this matters. Hallucination isn't just an academic embarrassment — it has direct business costs that scale with deployment:
| Failure type | Example | Typical cost |
|---|---|---|
| Fabricated citation | Model invents a legal case or statute | Legal liability, reputational damage |
| Wrong API call | Model hallucinates a function signature | Production incident, broken integration |
| Confident misinformation | Customer-facing bot gives wrong medical/financial advice | User harm, regulatory exposure |
| Sycophantic agreement | Model agrees with a false user premise | Erosion of trust, bad decisions downstream |
| Stale fact presented as current | Model reports a CEO or price from 2 years ago | Lost deals, customer churn |
A single high-profile hallucination in a customer-facing feature can undo months of trust-building. The techniques in this guide exist because the cost of not using them is measured in incidents, churn, and legal exposure — not just eval scores.
Prompt Engineering for Grounding
Beyond the architectural techniques above, small prompt-level adjustments compound. Four patterns that reliably reduce hallucination:
- Explicit source constraints. "Answer using ONLY the context. If the answer isn't there, say so." This single instruction cuts confabulation dramatically.
- Forced citation. Require the model to cite a chunk ID or quote for every claim. Hallucinated claims have no source to cite, which makes them visible.
- Step-by-step reasoning. "First, identify the relevant facts in the context. Then, answer." Chain-of-thought grounding reduces leaps.
- Negative examples. Show the model what a bad (hallucinated) answer looks like and what a grounded one looks like. Few-shot calibration works.
None of these replace RAG or verification, but they're free quality gains you can ship in a prompt edit.
Common Anti-Patterns
- "Just use a bigger model." Flagship models hallucinate less, but not zero. Grounding beats scale.
- Stuffing 20 chunks into the context. More context ≠ better. The model gets lost; retrieval precision drops. Top-5 with reranking beats top-20 raw.
- Asking the model "are you sure?" Models almost always say yes. Use a separate verifier call instead.
- Trusting logprobs as confidence. Logprobs measure token probability, not factual confidence. Useful as one signal, dangerous alone.
- No eval set. If you can't measure hallucination rate, you can't improve it.
The Bottom Line
Hallucination prevention is a stack, not a switch. RAG grounds the model in real facts. Structured outputs constrain what it can say. Verification catches the survivors. Abstention handles the rest. Eval keeps you honest. Combined, these techniques take production LLM reliability from "demo-grade" to "ship-grade."
The models and tools you need are all available through DrAI's unified endpoint. Sign in, pick a model, and start building reliable AI features.
See Model Pricing →