Hallucination and Factual Accuracy¶
What You'll Learn¶
| Objective | Time | Difficulty |
|---|---|---|
| Understand the root causes of hallucination | 40 min | Intermediate |
| Detect hallucinations programmatically | ||
| Apply grounding techniques to reduce fabrication | ||
| Build verification pipelines |
Why LLMs Hallucinate¶
LLMs are trained to predict the most likely next token, not to be truthful. This leads to confident-sounding but incorrect outputs.
Types of Hallucination¶
| Type | Description | Example |
|---|---|---|
| Factual fabrication | Inventing facts that sound plausible | "The Eiffel Tower was built in 1892" (actual: 1889) |
| Entity confusion | Mixing attributes of different entities | Attributing one author's work to another |
| Unfaithful reasoning | Correct premises leading to wrong conclusions | Math errors in multi-step problems |
| Source fabrication | Inventing citations and references | "According to a 2023 study in Nature..." (doesn't exist) |
| Temporal confusion | Mixing up dates or timelines | Describing events in the wrong chronological order |
Root Causes¶
- Statistical pattern matching: Models generate likely text, not verified text
- Training data contamination: Errors in training data become model knowledge
- Knowledge cutoff: Models don't know about events after training
- Ambiguity in prompts: Vague questions invite guessing
- Confidence calibration: Models don't know what they don't know
Detecting Hallucinations¶
Approach 1: Self-Consistency Checking¶
Generate multiple responses and check for agreement:
from openai import OpenAI
import json
client = OpenAI()
def self_consistency_check(prompt, model="gpt-4.1-mini", num_samples=5):
"""Generate multiple responses and check for consistency."""
responses = []
for _ in range(num_samples):
response = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
temperature=0.8 # Higher temp for diversity
)
responses.append(response.choices[0].message.content)
# Use GPT-4.1 to check consistency
consistency_check = client.chat.completions.create(
model="gpt-4.1",
messages=[{
"role": "user",
"content": f"""Compare these {num_samples} responses to the same question.
Question: {prompt}
Responses:
{chr(10).join(f'{i+1}. {r}' for i, r in enumerate(responses))}
Identify:
1. Claims that appear in ALL responses (likely reliable)
2. Claims that appear in only SOME responses (potentially unreliable)
3. Claims that contradict across responses (likely hallucinated)
Return JSON: {{
"consistent_claims": [...],
"inconsistent_claims": [...],
"contradictions": [...],
"reliability_score": <0-1>
}}"""
}],
temperature=0,
response_format={"type": "json_object"}
)
return json.loads(consistency_check.choices[0].message.content)
result = self_consistency_check("When was the Python programming language first released?")
print(f"Reliability: {result['reliability_score']}")
Approach 2: Claim Extraction and Verification¶
def extract_and_verify_claims(text, model="gpt-4.1"):
"""Extract factual claims from text and flag those that need verification."""
response = client.chat.completions.create(
model=model,
messages=[{
"role": "user",
"content": f"""Extract all factual claims from this text. For each claim, rate your confidence (high/medium/low).
Text: {text}
Return JSON array: [
{{"claim": "...", "confidence": "high|medium|low", "verifiable": true|false}},
...
]"""
}],
temperature=0,
response_format={"type": "json_object"}
)
claims = json.loads(response.choices[0].message.content)
# Flag claims that need human verification
flagged = [c for c in claims if isinstance(c, dict) and c.get("confidence") != "high"]
return {"all_claims": claims, "flagged_for_review": flagged}
Reducing Hallucinations¶
Strategy 1: RAG (Retrieval-Augmented Generation)¶
Ground model responses in retrieved documents:
def grounded_response(query, retrieved_docs, model="gpt-4.1-mini"):
"""Generate a response grounded in retrieved documents."""
context = "
".join(
f"[Source {i+1}]: {doc}" for i, doc in enumerate(retrieved_docs)
)
response = client.chat.completions.create(
model=model,
messages=[{
"role": "system",
"content": """Answer the user's question based ONLY on the provided sources.
If the sources don't contain enough information, say "I don't have enough information to answer that."
Always cite your sources using [Source N] notation.
Never make claims that aren't supported by the sources."""
}, {
"role": "user",
"content": f"Sources:
{context}
Question: {query}"
}],
temperature=0.3 # Lower temperature for factual tasks
)
return response.choices[0].message.content
Strategy 2: Ask the Model to Express Uncertainty¶
system_prompt = """You are a helpful assistant. When answering questions:
- If you are confident in your answer, state it clearly
- If you are uncertain, say "I'm not fully certain, but..."
- If you don't know, say "I don't have reliable information about that"
- Never fabricate sources, citations, or specific statistics
- Distinguish between facts and your interpretation"""
Strategy 3: Structured Output with Citations¶
def response_with_citations(query, context_docs, model="gpt-4.1-mini"):
"""Generate a response with inline citations."""
response = client.chat.completions.create(
model=model,
messages=[{
"role": "system",
"content": "Respond in JSON format with 'answer' and 'citations' fields. Each citation should reference a specific source document."
}, {
"role": "user",
"content": f"Context: {context_docs}
Question: {query}"
}],
temperature=0.3,
response_format={"type": "json_object"}
)
return json.loads(response.choices[0].message.content)
Strategy 4: Chain-of-Thought Verification¶
def verified_response(query, model="gpt-4.1"):
"""Two-pass approach: generate then verify."""
# Pass 1: Generate response
initial = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": query}],
temperature=0.3
).choices[0].message.content
# Pass 2: Verify claims
verification = client.chat.completions.create(
model=model,
messages=[{
"role": "user",
"content": f"""Review this response for factual accuracy:
Question: {query}
Response: {initial}
For each factual claim, assess whether it is:
- CORRECT (you are highly confident)
- UNCERTAIN (you are not sure)
- LIKELY INCORRECT (conflicts with your knowledge)
Return the corrected response with uncertain claims flagged."""
}],
temperature=0
).choices[0].message.content
return {"initial": initial, "verified": verification}
Hallucination Metrics¶
| Metric | How to Measure | Good Threshold |
|---|---|---|
| Factual accuracy | Human evaluation of claims | >95% for high-stakes |
| Faithfulness | Does response stay within given context? | >90% |
| Citation accuracy | Are cited sources real and relevant? | 100% |
| Abstention rate | How often does model say "I don't know"? | 5-15% (too high = unhelpful) |
Resources¶
- Paper: "A Survey on Hallucination in LLMs" — Comprehensive overview of the problem
- FActScore: Automated framework for measuring factual precision
- RAGAS: Evaluation framework for RAG-based systems
- Video: Why AI Lies — Understanding hallucination at a fundamental level
Key Takeaways¶
- Hallucination is inherent to how LLMs work — they predict likely text, not truthful text
- Use self-consistency checking to identify unreliable claims
- RAG (grounding in documents) is the most effective hallucination reduction technique
- Encourage uncertainty expression: models that say "I don't know" are more trustworthy
- For high-stakes applications, always include human verification in the pipeline
Next Lesson¶
Lesson 4: Prompt Injection and LLM Security — Learn how attackers exploit LLMs through prompt injection and how to defend your applications.