はじめに:ハルシネーションは「バグ」ではなく「設計の歪み」である

LLMアプリケーションの開発現場で「モデルが嘘をついた」という報告が上がったとき、多くのチームが最初に取る対応はプロンプトの文言を微調整することである。しかし、このアプローチは問題の構造を見誤っている可能性がある。存在しないAPI名を生成する、パラメータの型を勝手に変形する、利用可能なライブラリにない関数を呼び出す——これらの現象は、モデルの「能力不足」ではなく、トレーニングプロセスやプロンプト設計に起因する構造的な課題として捉える必要がある。

本記事では、プロンプト設計を「魔法」や「経験則の蓄積」ではなく、確固たる設計原則に基づくエンジニアリング課題として扱う。具体的には、ハルシネーションがなぜ起きるのかを対話理論とRLHFのメカニズムから解明し、それを是正するプロンプトパターンを実装レベルまで落とし込む。

読者には、単に「こう書けば直る」というTipsではなく、なぜその設計が必要なのかという理論的背景と、実装におけるトレードオフの判断基準を得てもらうことを目的とする。これにより、モデルのアップデートやユースケースの変更があったときにも、設計の妥当性を再評価できる判断力が身につく。

ハルシネーションの根本原因:RLHFによる「反射的応答」の誘導

現代のLLMがリクエストに対して即座にコード例や回答を提示する傾向は、偶然ではなく、RLHF(Reinforcement Learning from Human Feedback)プロセスにおける評価バイアスの直接的な結果であるという見方がある。

RLHFでは、人間のアノテーターがモデルの出力を比較評価し、そのフィードバックを報酬信号として学習に反映させる。GDEによる解説記事が指摘するように、この評価プロセスには明確な歪みがある。アノテーターは、即座に有用な出力(コード例や回答)を示すモデルを高く評価し、一方で「まず確認すべきです」と事実確認を求める回答を低く評価する傾向がある。つまり、「素直に答える」モデルが「慎重に答える」モデルよりも高評価を得るというインセンティブ構造が、学習データに反映されている可能性がある。

このバイアスが学習に反映されると、モデルの挙動は構造的に変わる。リクエストに対して反論や代替案を出す確率が低くなり、最初のトークンで即座に「はい、承知しました」と約束する反射的な応答(Reflexive Response)をすることが最適解となる場合がある。モデルは「このAPIは存在するか?」という検証ステップを挟まず、生成を開始してしまうことがある。

この「即座の正解提示」への最適化が、ハルシネーションを助長する要因となっている可能性がある。モデルは「答えること」自体が報酬になるため、存在しないAPIを推測で生成しても、それが「有用に見える出力」であれば報酬を得られる。検証を挟むコスト(トークンの消費、ユーザー体験の遅延)はペナルティとして学習されていない場合がある。結果として、モデルは「確信がなくても答える」ことを最適行動として学習してしまうことがあるのである。

ここで重要なのは、この問題がプロンプトの「文言」だけで解決できるかという点である。RLHFで形成された挙動は、モデルの内部表現に深く埋め込まれている。システムメッセージで「確認してください」と書いただけでは、学習された反射的な応答パターンを完全に上書きできない可能性がある。次のセクションで見るように、この問題はさらに歴史的な文脈まで遡る。

歴史的文脈:SHRDLUから現代までの「現実世界」の欠如

1971年に発表されたSHRDLUは、ブロックワールドと呼ばれる閉じた環境で指示を実行する自然言語理解システムとして知られる。このシステムが前提としていたのは、すべての指示が実行可能な環境であった。ブロックが何個あるか、テーブルのサイズがどれくらいか——それらはすべて既知であり、指示が実行不能になる状況は設計上存在しなかった。

GDEの解説記事が指摘するように、SHRDLU以降のAIシステムは、この「閉じた環境での指示実行」を前提として設計されてきた。現実世界の物理的制約、利用可能なAPIの欠如、リソースの不足——こうした「不可能な状況」に対応する仕組みが、長らく設計から欠落していたのである。

現代のLLMは、依然としてこのパターンを強く引きずっている。モデルが学習したタスクの多くは「入力を与えれば出力を返す」形式であり、「この要求は実行できません」と答えること自体が学習データにほとんど含まれていない。外部環境との健全なインタラクション——確認の要求、拒否、代替案の提示——は、モデルの出力空間の端に追いやられている。

この歴史的経緯を理解すると、プロンプト設計における「実行前の検証」の位置づけが明確になる。検証ステップは「おまけ」ではなく、SHRDLU以降失われていた「環境との対話」を再導入する行為である。次のセクションでは、この再導入を理論的に支える1986年の対話理論について詳しく見る。

解決策の理論的基盤:1986年の対話理論と自己帰納的文法トラップ

自己帰納的文法トラップとは

LLMがAPIを捏造する現象は、一見すると「モデルが覚えている知識が不十分だから」のように見える。しかし、この説明は根本的な誤りである可能性がある。GDEによる解説記事 で述べられているように、APIの捏造は記憶の欠損ではなく、1986年にFernando FloresとTerry Winogradが解明した自己帰納的文法トラップに起因する可能性がある。 この理論の核心は、言語行為が自己言及的に真偽判定される際に生じる論理的矛盾にある。具体的には、モデルが「このAPIを呼び出す」という出力を生成する瞬間、その出力自体が「このAPIは存在する」という主張になる。つまり、モデルは出力を生成する行為そのもので、出力の真偽を証明しようとしている。これは循環論法であり、外部の検証がない限り、この循環を断ち切る手段を持たない。 プロンプト設計の文脈で言えば、モデルに「このAPIを呼び出しなさい」と指示したとき、モデルは「呼び出す」という行為を即座に開始する。その過程で「本当に存在するか」を確認するステップが構造的に欠落している。これが自己帰納的トラップの実体であり、RLHFのバイアス(前節で見た反射的応答)と相まって、捏造を助長している可能性がある。

Floresの「行動のための対話」理論

同じ1986年にFloresが提唱した「行動のための対話」理論は、このトラップを回避する正規の手順を定義している。GDEによる解説記事 に従えば、この理論では以下の手順が正規とされる:
  1. リクエストを受信する
  2. 実行前に物理的現実を検証する(対象が存在するか、条件を満たしているか)
  3. 検証に合格した場合のみ実行する
  4. 検証に不合格な場合は、カウンターオファーや拒否を行う
この4つのステップのうち、現代のプロンプト設計が欠落させているのは2番目の検証ステップである。モデルに「呼び出しなさい」とだけ指示すると、1から3がスキップされ、4の対話も発生しない。プロンプト設計の課題は、この検証ステップをモデルの出力プロセスに構造的に埋め込むことである。

プロンプト設計の原則:「検証」を最優先するシステムメッセージ

Guardian Roleの設計

上記の理論をプロンプトに適用する第一の原則は、モデルに対して「即座の実行者」ではなく「事前検証者(Guardian Role)」の役割を割り当てることである。システムメッセージの中で、モデルの最初のタスクを「リクエストの実行」ではなく「リクエストの実行可能性の判定」に置き換える。 具体的には、システムメッセージの先頭に以下の構造を置く:
あなたはAPI呼び出しの検証者である。
ユーザーのリクエストを受け取ったら、以下の順序で処理せよ:

1. 指定されたAPIが利用可能か確認する
2. パラメータが仕様書に準拠しているか確認する
3. 上記のいずれかが不備であれば、実行せず、
   不備の内容と代替手段を報告する
4. すべて合格した場合のみ、API呼び出しを生成する
この構造の設計判断の根拠は、自己帰納的トラップを断ち切るためである。モデルが「確認する」行為を最初に実行することで、出力が自己言及的な循環に陥る前に外部基準(利用可能なAPIリスト、パラメータ仕様)との照合が挿入される。

検証ステップのフロー

以下のフローは、リクエスト受信からAPI実行までの処理順序を示す。検証ステップを挟むことで、不合格のリクエストが実行フェーズに到達するのを構造的に防ぐ。
flowchart TD
    A["リクエスト受信"] --> B{"指定APIが存在するか?"}
    B -->|"Yes"| C{"パラメータが仕様準拠か?"}
    B -->|"No"| D["拒否 / カウンターオファー"]
    C -->|"Yes"| E["API呼び出しを生成"]
    C -->|"No"| D
このフローで重要なのは、分岐の判定がモデルの内部的な「確信」ではなく、外部基準との照合に基づいて行われるべき点である。後述する実装パターン3(外部インデックス連携)でこの外部基準をどう供給するかを詳しく見るが、ここでは設計上の原則として「検証の基準はプロンプト内で明示的に定義する」ことを強調しておく。

トレードオフ:レイテンシーと精度

検証ステップの追加は、モデルが生成するトークン数と、場合によっては外部参照の往復回数を増やす。これはレイテンシーの増加に直結する可能性がある。しかし、ハルシネーションによる後続処理の失敗(存在しないAPIへの呼び出し、不正なパラメータによるエラー)が発生した場合の復旧コスト——手動修正、デプロイの巻き戻し、ユーザーへの影響——を考慮すると、このレイテンシー増は許容可能な投資であると考えられる。

実装パターン1:カウンターオファーによる健全な拒否

「できません」だけではない拒否

検証ステップで不合格と判定された場合、最も単純な対応は「指定されたAPIは存在しません」と返すことである。しかし、この対応はFloresの理論における「拒否」の段階に留まり、「カウンターオファー」の段階に到達していない可能性がある。 カウンターオファーとは、不可能なリクエストに対して、代替の実行可能性のある提案を提示する行為である。GDEによる解説記事 で定義されるように、実行前に物理的現実を検証し、不可能な場合はカウンターオファーや拒否を行うことが正規の手順とされる。つまり、「できません」で終わらせず、「できませんが、代わりにこれならできます」という構造をプロンプト内で強制する。

プロンプトへの組み込み方

システムメッセージに以下の指示を追加する:
検証に不合格の場合、以下の形式で応答せよ:

- 不合格の理由(どのAPIが存在しないか、どのパラメータが不正か)
- 代替案:利用可能な類似API、または手動での対応手順
- 代替案が実行可能な場合、その呼び出しを具体的に提示する

「できません」だけで終わる応答は禁止する。
この指示の効果は、モデルの出力空間が「拒否」の1点に収束するのを防ぎ、「拒否 + 代替案」の2要素を含む出力を強制することにある。自己帰納的トラップの文脈で言えば、モデルが「呼び出せない」という出力を生成した瞬間、その出力自体が「代替手段を提示せよ」という次の言語行為を誘発する。つまり、カウンターオファーの指示は、トラップの循環を建設的な方向に転換する仕組みとして機能する可能性がある。

従来の拒否パターンとの比較

パターン出力内容ユーザー体験後続処理への影響
単純拒否「指定APIは存在しません」次の一手が不明、再試行が必要ユーザーが代替案を自力で探す
カウンターオファー「存在しません。代わりにX APIが同様の機能を持ちます」代替案が即座に提示され、意思決定が早い代替APIへの切り替えが自動的に行える
の比較が示すように、カウンターオファーパターンの価値は「拒否の質」にある。単に失敗を伝えるだけでなく、次の行動を提示することで、ユーザー(または上流のエージェント)が追加の判断コストを負わずに済む。エージェントチェーンの文脈では、この差が特に重要になる。上流のエージェントが「拒否」を受け取った場合、再試行や人手介入が必要になるが、「カウンターオファー」を受け取れば、代替APIへの切り替えを自動的に行える可能性がある。

実装上の注意点

カウンターオファーの指示には1つの落とし穴がある。モデルが「代替案」を捏造する可能性である。検証ステップで「指定APIが存在しない」と判定したモデルが、次のターンで「代わりにX APIがあります」と主張する際、そのX APIの存在もまた未検証である。この問題を回避するためには、後述する実装パターン3(外部インデックス連携)で、代替案の提示にも外部基準を参照させる設計が必要になる。検証ステップとカウンターオファーは独立した機能ではなく、同じ外部基準に依存する一連のプロセスとして設計すべきである。

実装パターン2:有限状態機械としての文法制約

検証ステップとカウンターオファーが「何をすべきか」を指示するのに対し、文法制約は「どのように出力するか」を構造的に制限する。この2つは直交する次元であり、両方を適用することで出力空間を2方向から狭められる。

BNF文法や正規表現は有限状態機械として振る舞う性質を持つ。歴史的には、文法を用いてランダムな文を生成するプログラムが作成され、この構造的特性が実証されてきた(Linux Magazine Column 04)。この性質をプロンプト設計に転用すると、モデルの出力を「文法が許容する文字列の集合」に限定できる可能性がある。モデルは文法に従ってトークンを生成する必要があるため、定義された形式外のAPI名やパラメータ名を出力することが構造的に困難になる。

実装としては、システムメッセージ内にJSON Schemaを明示的に提示し、出力がそのスキーマに準拠することを強制するのが一般的である。

## 出力形式(厳守)
以下のJSON Schemaに完全に準拠して出力せよ。
スキーマに定義されていないキーの追加は禁止。

{
  "action": "execute" | "reject" | "counter_offer",
  "api_name": string,
  "parameters": object,
  "reason": string
}

- action が "execute" の場合、api_name と parameters は
  提供されたAPIカタログに実在する値のみ使用すること。
- 実在しない値を生成した場合は action を "reject" にすること。

ここで設計判断が分かれるのは、スキーマの粒度である。スキーマを細かくすると(各パラメータの型・範囲まで指定する)、捏造の余地は狭まるが、モデルが表現したいニュアンスをスキーマに収容できなくなる。一方、粗いスキーマ(actionとreasonのみ)では柔軟性は得られるが、api_nameの捏造を防ぐ効果が薄れる。判断基準は、後続のパーサーがどの程度の構造を前提にしているかに従う。自動実行パイプラインに組み込む場合は細粒度を、人間が最終確認するフローでは粗粒度で十分である。

実装パターン3:外部インデックスとの連携による事実確認

文法制約が「出力の形」を制限するのに対し、外部インデックス連携は「出力の内容」の根拠をモデルの内部知識から切り離す。モデルが「知っているつもり」でAPIを生成するのではなく、検索インデックスから抽出した事実のみを出力に含めるように役割を限定する。

Algoliaのような検索インフラは、AIアシスタントやエージェントが既存のインデックスに基づいて回答を行う機能を備えている。この機能をプロンプト内で明示的に参照させることで、モデルの役割を「インデックスから該当情報を抽出・要約する」に制限できる。モデルが推測で答える構造上の動機を低減できる可能性がある。

sequenceDiagram
    participant U as ユーザー
    participant A as エージェント
    participant S as 検索インデックス
    participant L as LLM

    U->>A: API呼び出しのリクエスト
    A->>S: インデックス照会(API名・パラメータ)
    S-->>A: 該当レコード(または空)
    alt 該当レコードあり
        A->>L: 照会結果をコンテキストに含めて要約を指示
        L-->>A: 構造化出力(JSON Schema準拠)
        A-->>U: 実行結果
    else 該当レコードなし
        A->>L: 拒否+カウンターオファーを指示
        L-->>A: 代替案(インデックス内の類似APIのみ)
        A-->>U: 拒否+代替案
    end

このアーキテクチャの利点は、LLMがAPIカタログを「暗記」している必要がなくなる点にある。カタログの更新はインデックスの再構築で済み、プロンプトやモデルの再学習は不要である。また、インデックスのスキーマは文法制約(実装パターン2)と整合させることで、抽出結果がそのまま出力スキーマにマッピングでき、中間変換ロジックを削減できる。

トレードオフと設計判断:柔軟性 vs 堅牢性

検証ステップ、文法制約、外部インデックス連携をすべて適用すると、プロンプトの長さが倍増し、1リクエストあたりのトークン消費とレイテンシーが増加する。また、外部インデックスへの依存はネットワーク遅延とインデックス鮮度の問題を新たに生む。これらのコストを正当化できるかは、ユースケースの性質に依存する。

設計判断のフレームワークとしては、「失敗時の回復コスト」と「検証ステップの実装・運用コスト」を比較する。回復コストとは、ハルシネーションが検出された後に人間が修正し、再実行し、影響範囲を特定するために費やされる工数である。一方、検証コストはプロンプト設計の複雑化、インデックスの保守、追加レイテンシーの総和である。

ユースケース推奨される検証の厳格度主な理由
自動実行パイプライン(コード生成・デプロイ)高(全パターン適用)失敗時の手動修正コストが極大。インデックス依存も許容
社内ナレッジ検索・要約中(インデックス連携+スキーマ)誤情報の拡散リスクがあるが、人間が最終確認
クリエイティブ支援・ brainstorming低(スキーマのみ or なし)捏造が「創造性」として許容される場面