生成AI入門 2026.04.19
ハルシネーションとは?AIが嘘をつく5つの原因と検出・防止コード【2026年版】
ハルシネーションとは何か
「ハルシネーション(幻覚)」とは、生成AIが実際には存在しない情報を、本当のことのように自信を持って答える現象です。
例1: 「2025年ノーベル医学賞の受賞者は?」
AI回答:「田中一郎博士(東京大学)が受賞しました」← 架空の人物
例2: 「X社のAPIドキュメントURLは?」
AI回答:「https://docs.example.com/api/v2」← 存在しないURL
最近注目されているのが**「検証ギャップ」**です。答え自体は正しいのに、その根拠や出典がウソという現象で、正しい結論を信じてその根拠まで鵜呑みにするのは危険です。
ハルシネーションの5つの原因
| 原因 | 説明 |
|---|---|
| ① 知識カットオフ | 学習データより後の情報は推測で埋める |
| ② 統計的生成 | 「もっともらしい続き」を確率で生成する |
| ③ 訓練データの偏り | 偏った情報が多ければ偏った回答が生成される |
| ④ 文脈の理解限界 | 長い文書では前の内容を取り違える |
| ⑤ 自信スコアの欠如 | 確信がない場合でも「わかりません」と言わない |
ハルシネーション検出スクリプト
# hallucination_detector.py
# 同じ質問を複数回して回答の一致率を確認する
import anthropic
from difflib import SequenceMatcher
client = anthropic.Anthropic()
def detect_hallucination(question: str, trials: int = 3) -> dict:
"""同じ質問を複数回して一致率を測定する"""
responses = []
for i in range(trials):
response = client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=512,
messages=[{"role": "user", "content": question}]
)
responses.append(response.content[0].text)
# 回答間の類似度を計算
similarities = []
for i in range(len(responses)):
for j in range(i + 1, len(responses)):
sim = SequenceMatcher(None, responses[i], responses[j]).ratio()
similarities.append(sim)
avg_similarity = sum(similarities) / len(similarities) if similarities else 1.0
return {
"question": question,
"responses": responses,
"avg_similarity": round(avg_similarity, 2),
"risk_level": "低" if avg_similarity > 0.8 else "中" if avg_similarity > 0.5 else "高",
"recommendation": "信頼性あり" if avg_similarity > 0.8 else "要確認 - 回答が毎回変わっています"
}
# テスト
result = detect_hallucination("日本の消費税率は何パーセントですか?")
print(f"一致率: {result['avg_similarity']} / リスク: {result['risk_level']}")
print(f"判定: {result['recommendation']}")
RAGでハルシネーションを防ぐ
# rag_anti_hallucination.py
# 信頼できる文書ベースに回答を限定する
import anthropic
import chromadb
client = anthropic.Anthropic()
chroma = chromadb.Client()
collection = chroma.create_collection("knowledge_base")
def add_document(doc_id: str, text: str) -> None:
"""知識ベースに文書を追加"""
collection.add(documents=[text], ids=[doc_id])
def ask_with_rag(question: str, top_k: int = 3) -> str:
"""知識ベースから関連文書を取得してClaudeに渡す"""
results = collection.query(query_texts=[question], n_results=top_k)
context_docs = results["documents"][0] if results["documents"] else []
if not context_docs:
return "関連する文書が見つかりませんでした。情報を提供できません。"
context = "\n\n".join(context_docs)
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
system="""あなたは提供された文書のみを根拠として回答するアシスタントです。
文書に記載されていない情報は「文書に記載がないためお答えできません」と答えてください。
推測や一般知識での補足は禁止です。""",
messages=[{"role": "user", "content": f"""
文書:
{context}
質問: {question}
文書の内容のみを根拠に答えてください。"""}]
)
return response.content[0].text
# 使用例
add_document("policy_001", "当社の返品ポリシー: 購入から30日以内、未使用品のみ返品可能。送料はお客様負担。")
add_document("policy_002", "営業時間: 月〜金 9:00〜18:00。土日祝は休業。")
answer = ask_with_rag("返品できますか?")
print(answer) # 文書に基づいた正確な回答が得られる
モデル別ハルシネーション対策
| アプローチ | コスト | 有効性 | 実装難度 |
|---|---|---|---|
| RAG(検索拡張) | Haiku: $0.80/1M | 高い | 中 |
| 複数回質問して比較 | 3倍のコスト | 中程度 | 低 |
| Grounding(根拠引用を必須化) | 追加なし | 中程度 | 低 |
| 専用Fine-tuning | 高い | 高い | 高い |