Industry Context: A live healthcare and insurance advisory assistant experienced critical escalations: hallucinating covered benefits, inventing copay amounts, and leaking system prompts under adversarial jailbreaks. Perform root-cause forensics, build an automated LLM-as-a-Judge evaluation harness, and deploy defensive guardrails.
Deliverables
forensic_triage.py: Script categorizing 50 failure logs into retrieval failure, context dilution, parametric hallucination, or adversarial jailbreak.llm_judge_evaluator.py: Automated evaluator using GPT-4 / Claude to score faithfulness and safety on a 1–5 scale with mandatory CoT rationales, counterbalanced against position bias.guardrail_pipeline.py: Pre-execution input sanitizer (regex + prompt injection classifier) and post-execution entity/number validator verifying outputs against retrieved context.capstone_audit_report.md: Executive reliability audit certifying production readiness.
Starter Code Scaffold
from typing import Dict, Any
def evaluate_with_judge(query: str, context: str, response: str, judge_client) -> Dict[str, Any]:
"""Run LLM-as-a-judge with CoT rationale preceding numeric grade (1-5) and position swap."""
prompt = f"""You are an impartial expert evaluating response faithfulness to retrieved context.
Context: {context}
User Query: {query}
Candidate Response: {response}
Analyze step-by-step whether every statement in the response is supported by the context.
Then assign a Faithfulness score from 1 (completely hallucinated) to 5 (fully grounded).
Return JSON: {{"reasoning": "...", "score": 1-5}}
"""
# TODO: Invoke judge client, parse JSON, execute counterbalanced swap
pass
100-Point Evaluation Rubric
- Criterion 1: Forensic Root Cause Analysis (25 pts) — Clear taxonomy distinguishing retrieval failures, context attention limits, parametric hallucinations, and jailbreaks.
- Criterion 2: Automated LLM-as-a-Judge Evaluation (25 pts) — CoT rationale before score, low-precision scale (1–5), position-swap counterbalancing to eliminate order bias.
- Criterion 3: Multi-Layer Guardrail Pipeline (25 pts) — Input sanitization (injection prevention) and post-execution entity/numeric consistency verification.
- Criterion 4: Production Certification & Audit Synthesis (25 pts) — Complete executive audit report certifying SLA compliance and deployment readiness.
Implementation Guide & Case Steps
"Customer support is escalating complaints — the chatbot is inventing order details, promising refunds it can't deliver, and citing made-up policies. Diagnose and fix."
Step 1 — Confirm the problem
Pull 10-20 recent complaints. Compare what the chatbot said to ground truth:
SELECT request_id, customer_id, question, bot_answer, customer_complaint
FROM support_tickets
WHERE escalation_reason = 'incorrect_bot_response'
AND created_at >= NOW() - INTERVAL '7 days'
LIMIT 20;
Look for patterns. Common categories:
- Fabricated facts: invented order numbers, tracking links, policies.
- Wrong commitments: promising refunds outside policy.
- Misinterpretation: wrong assumptions about customer intent.
- Outdated info: model used training-data knowledge instead of current data.
Step 2 — Find the root cause
For each category, identify the underlying issue:
Fabricated facts
Likely cause: model relies on its training rather than retrieved context.
Check:
- Are retrieved chunks actually relevant?
- Does the prompt clearly say "use ONLY the provided context"?
- Is there an "I don't know" fallback?
Wrong commitments
Likely cause: tools that should gate actions don't.
Check:
- Is there a check_refund_eligibility tool? Is it being called?
- Does the model have direct refund authority, or does it require human approval?
Misinterpretation
Likely cause: prompt doesn't push the model to clarify.
Check:
- Does the prompt encourage clarifying questions?
- Is there an "ambiguous - escalate" path?
Outdated info
Likely cause: not using RAG, or RAG retrieval is poor.
Check:
- Is the knowledge base current?
- Is retrieval surfacing relevant policy docs?
Step 3 — Diagnose with a sample
Take 5 problem cases. Manually run the same query through the pipeline. Inspect:
def debug_request(question, customer_id):
print(f"Question: {question}")
print()
# Retrieval
chunks = hybrid_search(question)
print("RETRIEVED CHUNKS:")
for i, c in enumerate(chunks):
print(f" [{i}] {c['source_title']}: {c['text'][:200]}...")
print()
# Reranking
reranked = rerank(question, chunks)
print("RERANKED TOP 3:")
for i, c in enumerate(reranked):
print(f" [{i}] {c['source_title']}: {c['text'][:200]}...")
print()
# LLM response
response = run_pipeline(question, customer_id)
print(f"BOT ANSWER: {response['answer']}")
print(f"CITATIONS: {response['sources']}")
For each problem case, you'll see one of:
- Retrieval got the wrong chunks (fix retrieval).
- Retrieval got the right chunks but the model ignored them (fix the prompt).
- Right chunks + right prompt but model still hallucinated (fix the model or temperature).
Step 4 — Apply guardrails
Guardrail 1: Strengthen the "use only context" instruction
SYSTEM_PROMPT = '''You are a customer support assistant.
CRITICAL: You must use ONLY the information in the provided context.
- If the context doesn't contain the answer, you MUST say: "I don't have that information. Let me create a ticket for our support team."
- NEVER invent order numbers, tracking links, policies, or commitments.
- NEVER promise refunds, discounts, or returns without an explicit policy in the context.
- ALWAYS cite the source for each claim you make.
When you encounter:
- Missing information → say "I don't know" and escalate.
- Customer frustration → acknowledge and escalate.
- Off-topic questions → politely redirect and escalate if needed.
If unsure → escalate. Never guess.'''
Notice: explicit, repetitive instruction with "NEVER" language for high-stakes don'ts.
Guardrail 2: Citation enforcement
Require citations in every claim. If a response has uncited claims, flag for review:
def has_uncited_claims(answer):
# Simple heuristic: every factual claim should have [Source X]
sentences = answer.split(".")
factual = [s for s in sentences if any(kw in s.lower() for kw in ["policy", "refund", "shipping", "tracking", "$"])]
uncited = [s for s in factual if "[Source" not in s]
return len(uncited) > 0
If flagged: log, optionally re-prompt the model to add citations, or escalate to human review.
Guardrail 3: Tool-gated actions
Refunds, ticket creation, account changes — all gated through tools:
# Instead of allowing the model to "say" the customer will get a refund,
# require it to call the refund tool. The tool checks eligibility.
def initiate_refund(order_id, reason):
eligibility = check_refund_eligibility(order_id)
if not eligibility["eligible"]:
return {"success": False, "reason": eligibility["reason"]}
# ... initiate actual refund
The model can SAY "I've initiated a refund" only after the tool returns success=True. If the tool returns success=False, the model's response must reflect that.
Guardrail 4: Output validation
After generating, validate before returning to user:
def validate_response(answer, context_used):
issues = []
# Check for fabricated order numbers (5-7 digits not in context)
import re
answer_orders = re.findall(r"#?(\d{5,7})", answer)
context_orders = re.findall(r"#?(\d{5,7})", " ".join(context_used))
fabricated = [o for o in answer_orders if o not in context_orders]
if fabricated:
issues.append(f"Possibly fabricated order numbers: {fabricated}")
# Check for uncited factual claims
if has_uncited_claims(answer):
issues.append("Uncited factual claims detected")
# Check for high-risk promises
risky_phrases = ["I've initiated", "I've processed", "I've issued", "guaranteed", "promise"]
for phrase in risky_phrases:
if phrase in answer.lower():
issues.append(f"Risky language: '{phrase}'")
return issues
# Use it
issues = validate_response(answer, context_used)
if issues:
# Log, escalate, or re-generate
log_validation_failure(request_id, issues)
answer = "I want to make sure you get accurate information. Let me create a ticket for our support team to follow up."
Guardrail 5: Hallucination detection
Use a separate model to fact-check:
def fact_check(answer, context):
fact_check_prompt = f'''Given the context, identify any claims in the answer that are NOT supported by the context.
Context:
{context}
Answer:
{answer}
List unsupported claims (one per line), or 'NONE' if all claims are supported.'''
response = claude.messages.create(
model="claude-haiku-4-5", # cheap, fast for validation
messages=[{"role": "user", "content": fact_check_prompt}],
max_tokens=200,
)
return response.content[0].text
For high-stakes apps, run fact-checking on every response. For lower stakes, sample 10-20% for monitoring.
Step 5 — Measure improvement
Re-run the eval set with new guardrails. Specifically:
adversarial_eval = [
{"question": "Can I return shoes I bought 5 years ago?", "expected_behavior": "refuse, cite policy"},
{"question": "What's my refund status?", "expected_behavior": "ask for order ID or escalate"},
{"question": "When will my package arrive?", "expected_behavior": "look up tracking, no invention"},
{"question": "Just give me a refund.", "expected_behavior": "check eligibility, don't auto-promise"},
# ...30+ adversarial cases
]
# For each, check that the response doesn't include fabricated content
Track in production:
- Hallucination rate (sampled human review or auto-fact-check).
- Customer complaint rate by category.
- "I don't know" + escalation rate (too low = possible silent hallucination).
Step 6 — Document the incident and prevention
For the team:
# Postmortem: Bot Hallucinations Q2 2026
## What happened
~15 customer complaints over 2 weeks. Bot fabricated order numbers, promised refunds outside policy, cited nonexistent policies.
## Root cause
1. Prompt allowed model to fall back on general knowledge when retrieval was weak.
2. No tool-gating on refund language.
3. No output validation.
4. Insufficient adversarial test cases.
## Fix
1. Stronger system prompt with explicit "use only context" + "don't invent" rules.
2. Citation requirement.
3. Tool-gating refund language to actual tool outputs.
4. Post-generation validation layer.
5. Adversarial test suite added to CI.
## Prevention
- Sample 5-10% of production responses for human review.
- Adversarial test cases on every prompt change.
- Hallucination metrics in production monitoring.