LLMのハルシネーション対策:実装可能な5つの有効な方法

本記事では、LLM(大規模言語モデル)が生成する根拠のない情報(ハルシネーション)を削減する実践的な対策方法を解説します。RAG、プロンプト工学、出力検証など、すぐに仕事で活用できる具体的な実装例とベストプラクティスを紹介します。

LLMハルシネーションが起こる理由

LLMのハルシネーションは、モデルが訓練データに存在しない情報や、統計的確率に基づいて「もっともらしく聞こえる」が実際には誤った情報を生成する現象です。これは機械学習モデルの性質上、完全には排除できませんが、適切な対策により大幅に削減できます。

対策1:Retrieval-Augmented Generation(RAG)の導入

RAGは、LLMが回答を生成する前に、外部の信頼できる知識ベースから関連情報を取得する手法です。これにより、モデルが訓練データに依存せず、最新かつ正確な情報に基づいた回答を生成できます。

RAG実装例:Python + LangChain + OpenAI API


from langchain.chat_models import ChatOpenAI
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain.text_splitter import CharacterTextSplitter
from langchain.chains import RetrievalQA
from langchain.document_loaders import TextLoader

# ステップ1:信頼できるドキュメントを読み込む
loader = TextLoader("company_knowledge_base.txt")
documents = loader.load()

# ステップ2:ドキュメントをベクトル化してインデックス化
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
docs = text_splitter.split_documents(documents)

embeddings = OpenAIEmbeddings()
vector_store = FAISS.from_documents(docs, embeddings)

# ステップ3:RAGチェーンを構築
llm = ChatOpenAI(model_name="gpt-4", temperature=0.3)
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=vector_store.as_retriever(search_kwargs={"k": 3})
)

# ステップ4:ユーザーの質問に回答
response = qa_chain.run("会社の返金ポリシーは?")
print(response)
  

RAG導入時のハマりポイント

問題1:チャンク分割が不適切
ドキュメントをテキスト分割する際、重要な文脈が失われることがあります。chunk_sizechunk_overlapを調整し、テスト用データセットで精度を検証してください。

問題2:ベクトル埋め込みモデルの相性
クエリのエンコーディングと、ベクトルストアのエンコーディングに異なるモデルを使用すると、検索精度が大幅に低下します。必ず同一のEmbeddingsモデルを使用してください。

問題3:検索結果が過度に冗長
search_kwargs={"k": 3}で取得する関連ドキュメント数を増やしすぎると、かえってLLMが混乱し、ハルシネーションが増加します。3〜5件程度が目安です。

対策2:プロンプト工学による制約強化

LLMの振る舞いは、与えられるプロンプト(指示文)で大きく変わります。曖昧な指示ではなく、明確に「わからない場合は『わかりません』と答える」という制約を加えることが効果的です。

効果的なプロンプト設計例


import anthropic

client = anthropic.Anthropic()

# 非効果的なプロンプト(ハルシネーション誘発)
bad_prompt = "弊社の製品の特徴について説明してください。"

# 効果的なプロンプト(制約あり)
good_prompt = """
あなたは顧客サポート担当者です。以下のルールを必ず守ってください:

1. 提供されたドキュメント内の情報のみを使用してください
2. ドキュメントに記載されていない情報は、必ず「その情報は確認できません」と答えてください
3. 推測や一般知識で補足しないでください
4. 回答に不確実性がある場合は、信頼度を明示してください

ユーザーの質問:弊社の製品の特徴について説明してください。
提供されたドキュメント:[ここにドキュメント内容を挿入]
"""

message = client.messages.create(
    model="claude-3-5-sonnet-20241022",
    max_tokens=1024,
    messages=[
        {
            "role": "user",
            "content": good_prompt
        }
    ]
)

print(message.content[0].text)
  

Chain-of-Thought プロンプティングでの透明性向上

LLMに「考え方のプロセスを段階的に説明してから答える」よう指示すると、ハルシネーション発生時に根拠が欠落していることに気づきやすくなります。


cot_prompt = """
以下の質問に答える際は、必ず以下の形式で答えてください:

1. 与えられた情報の確認:提供されたドキュメントに関連情報があるか確認
2. 推論プロセス:確認した情報から論理的に導き出す
3. 確実性の評価:この答えがどの程度確実かを評価
4. 最終回答:上記に基づいた回答

質問:[ユーザーの質問]
  """
  

対策3:出力検証とファクトチェック機構の実装

LLMの出力を、別のLLMやルールベース検証システムで検証することで、ハルシネーションを事後的に検出できます。

検証ロジック実装例


import anthropic
import re

client = anthropic.Anthropic()

def validate_factual_claims(response_text, source_documents):
    """
    LLMの回答に含まれる具体的な主張を検証する
    """
    validation_prompt = f"""
以下のLLMの回答に含まれる具体的な主張(数値、日付、名前など)が、
提供されたドキュメント内に根拠があるか確認してください。

LLMの回答:
{response_text}

根拠とすべきドキュメント:
{source_documents}

検証結果をJSON形式で以下の構造で返してください:
{{
  "verified_claims": ["根拠のある主張1", "根拠のある主張2"],
  "unverified_claims": ["根拠のない主張1"],
  "risk_level": "low/medium/high",
  "recommendation": "このLLM出力は使用可能か?"
}}
    """
    
    validation_result = client.messages.create(
        model="claude-3-5-sonnet-20241022",
        max_tokens=1000,
        messages=[
            {"role": "user", "content": validation_prompt}
        ]
    )
    
    return validation_result.content[0].text

# 使用例
llm_response = "弊社の製品は市場シェア45%で、業界第2位です。"
source_doc = "2024年の調査によると、当社は市場シェア32%です。"

validation = validate_factual_claims(llm_response, source_doc)
print(validation)
  

対策4:温度パラメータ(Temperature)の適切な調整

temperatureパラメータは、LLMの出力の多様性を制御します。値が低いほどモデルは確率的に最も可能性の高い回答を選択するため、ハルシネーション削減に効果的です。

Temperature値 特性 推奨用途
0.0 - 0.3 決定的、一貫性が高い 事実ベースの回答、ハルシネーション削減が優先
0.3 - 0.7 バランス型 顧客サポート、一般的なQ&A
0.7 - 1.0 創造的、多様性が高い ブレインストーミング、クリエイティブ業務

import anthropic

client = anthropic.Anthropic()

# ハルシネーション削減が重要な用途:低いTemperature
factual_message = client.messages.create(
    model="claude-3-5-sonnet-20241022",
    max_tokens=1024,
    temperature=0.2,  # 低く設定
    messages=[
        {
            "role": "user",
            "content": "当社の営業実績について、正確な数字で説明してください。"
        }
    ]
)

# クリエイティブな用途:高いTemperature
creative_message = client.messages.create(
    model="claude-3-5-sonnet-20241022",
    max_tokens=1024,
    temperature=0.8,  # 高く設定
    messages=[
        {
            "role": "user",
            "content": "新製品のマーケティングキャンペーン案をブレインストーミングしてください。"
        }
    ]
)
  

対策5:トークン制限と出力スキーマの強制

LLMに過度な冗長性や推測の余地を与えないよう、max_tokensを適切に制限し、JSONスキーマなど構造化出力を強制することでハルシネーションを減らせます。


import anthropic
import json

client = anthropic.Anthropic()

# JSON出力スキーマを強制する
schema_prompt = """
以下の製品情報をJSON形式で返してください。
スキーマに該当する情報がない場合は、必ず null を使用し、推測値は入力しないでください。

必須スキーマ:
{
  "product_name": string,
  "price": number or null,
  "availability": string or null,
  "specifications": array or null
}

製品情報:[提供されたドキュメント]

重要:スキーマ外の推測情報を追加しないでください。
  """

response = client.messages.create(
    model="claude-3-5-sonnet-20241022",
    max_tokens=500,  # トークン制限で冗長性を削減
    messages=[
        {
            "role": "user",
            "content": schema_prompt
        }
    ]
)

try:
    # JSON形式の検証
    result = json.loads(response.content[0].text)
    print("検証済みの構造化データ:", result)
except json.JSONDecodeError:
    print("エラー:LLMがJSON形式で返さなかった場合の処理")
  

これらの対策を組み合わせるベストプラクティス

システム構成例


def hallucination_resistant_pipeline(user_query, knowledge_base):
    """
    複数の対策を組み合わせたハルシネーション耐性パイプライン
    """
    
    # ステップ1:RAGで信頼できる情報を取得
    relevant_docs = retrieve_documents(user_query, knowledge_base)
    
    if not relevant_docs:
        return "申し訳ございません。その情報は当社の知識ベースに存在しません。"
    
    # ステップ2:制約強化プロンプトを構築
    system_prompt = """
あなたは正確性を最優先とするアシスタントです。
以下のルールを絶対に守ってください:
1. 提供ドキュメント以外の情報は使用禁止
2. 不確実な場合は「確認できません」と答える
3. 回答は簡潔に、構造化形式で返す
    """
    
    # ステップ3:低いTemperatureと制限されたトークンで生成
    response = generate_response(
        query=user_query,
        context=relevant_docs,
        system_prompt=system_prompt,
        temperature=0.2,
        max_tokens=500
    )
    
    # ステップ4:出力を検証
    validation_result = validate_factual_claims(response, relevant_docs)
    
    if validation_result["risk_level"] == "high":
        return f"生成結果に不確実性があります。{response}"
    
K
AWS・Python・生成AIを専門とするソフトウェアエンジニア。AI・クラウド・開発ワークフローの実践ガイドを執筆しています。詳しく見る →