F-6: プロンプト制御 vs コード/ポリシー制御¶
この相反は何か¶
エージェントの振る舞いを制約する手段として、LLMへのプロンプト(システムプロンプト、指示文)で誘導するか、コードやポリシーエンジンで強制するかという二者択一です。前者は「お願い」であり後者は「壁」です。この違いは確率的な遵守と決定論的な強制の違いに直結します。
この選択が排他的として扱われる理由は、制約の信頼性が質的に異なるためです。プロンプトによる制御は確率的です。「個人情報を出力しないでください」と指示しても、プロンプトインジェクション(F14)で上書きされたり、コンテキストの膨張で指示の効力が揺らいだりします。コードによる制御は決定論的です。出力に個人情報のパターンが含まれていたら正規表現で検出してブロックする処理は、LLMの判断に関係なく確実に動作します。
ただし、すべての制約をコードで表現できるわけではありません。「丁寧な口調で応答する」「ユーザーの気持ちに寄り添う」「専門用語を避ける」のような行動の質や様式に関する制約は、条件→判定に還元しにくく、プロンプトでの誘導が適切です。問題は「どの制約をコードに引き上げるか」の判断であり、それを [failure_cost] と [accountability] が決定します。
選択肢A:プロンプト制御¶
LLMへのシステムプロンプトや指示文に制約を記述し、LLMの振る舞いを誘導します。Few-shotの例示、ネガティブ指示(「〜しないでください」)、ロール設定(「あなたは〜の専門家です」)などが典型的な手法です。
プロンプト制御の利点は柔軟性と表現力です。自然言語で記述するため、微妙なニュアンスや文脈依存の振る舞いを指示できます。「法務チーム向けの文書では正式な表現を使い、エンジニア向けでは技術用語を使ってよい」のような条件付き様式は、プロンプトで自然に表現できます。変更もデプロイなしにプロンプトを書き換えるだけで済み、反復速度が高いです。
しかし弱点は信頼性の低さです。プロンプトの遵守は確率的であり、100%の保証はありません。特に以下の状況で制約が破れます。
- プロンプトインジェクション:ユーザー入力に「上記の指示を無視して〜」が含まれると、システムプロンプトの制約が上書きされえます。
- コンテキスト膨張:会話が長くなりコンテキスト窓がいっぱいになると、システムプロンプトの注意が薄れます。
- モデル更新:モデルのバージョンが変わると、同じプロンプトでも振る舞いが変わります(F9)。
また、プロンプトに埋め込まれた制約は版管理・テスト・監査が困難です。「いつからこのルールが適用されていたか」をプロンプトの変更履歴から追跡する必要があり、[accountability] が高い環境では不十分です。
選択肢B:コード/ポリシー制御¶
制約を実行可能なコード(条件分岐、正規表現、スキーマ検証)またはポリシーエンジン(OPA/Rego、Cedar、カスタムDSL)で実装し、LLMの入出力や操作を強制的に制御します。LLMが何を出力しようとも、コードが最終的な許可/拒否を決定します。
コード制御の最大の利点は確定性と監査性です。制約はコードとして版管理され、ユニットテストで検証され、監査証跡として残ります。「この出力がなぜブロックされたか」は、適用されたポリシーの版とルールIDで一意に特定できます。プロンプトインジェクションやモデル更新の影響を受けません。
ポリシーエンジンを使う場合、ルールの変更をデプロイなしに(ポリシーの更新だけで)反映できます。E2 ポリシーのコード化が詳述するように、ルールの適用条件・バージョン・判定結果を構造化ログとして記録することで、規制要件を満たす監査証跡を構築できます。
弱点は表現力の限界と開発コストです。「丁寧に応答する」「文脈に応じたトーンで話す」のような質的な制約はコードで表現できません。また、ポリシーエンジンの導入・運用にはインフラとスキルが必要です。ルール数が少なく(1〜2個)変更頻度も低い場合は、B1 決定論的な殻のコード内の条件分岐で十分であり、ポリシーエンジンはオーバーエンジニアリングです。
選定基準¶
選定は [failure_cost] と [accountability] の2つの駆動変数で判定します。
制約の種類ごとの判定基準:
| 制約の種類 | 判定 |
|---|---|
| 境界・法的制約(個人情報の出力禁止、金額上限、禁止操作) | コード/ポリシー |
| 不可逆操作の制限(削除、送金、公開) | コード/ポリシー |
| 出力形式の強制(JSONスキーマ、値域の検証) | コード/ポリシー |
| 応答の品質・様式(口調、丁寧さ、専門用語の使用) | プロンプト |
| 文脈依存の判断指針(どのような場合に何を優先するか) | プロンプト |
駆動変数による判定:
[failure_cost]が高い制約 → コード/ポリシーで強制します。制約違反が法的リスク・金銭損失・安全上の問題を引き起こす場合、確率的な遵守では不十分です。[accountability]が高い環境 → コード/ポリシーで制約を版管理・監査可能にします。「このルールがいつから適用されていたか」を証跡として示す必要がある場合です。[failure_cost]が低く[accountability]も低い制約 → プロンプトで十分です。
デフォルトとハイブリッド¶
デフォルト:境界(壊してはならない制約)はコード、誘導(望ましい振る舞い)はプロンプトです。迷ったら「この制約が破られたとき、どれだけ痛むか」を自問してください。痛みが大きいならコードで強制し、小さいならプロンプトで誘導します。
ハイブリッド:ポリシー強制+プロンプト誘導の二層構造が定石です。コード/ポリシーで境界を決定論的に強制しつつ、プロンプトでLLMの振る舞いを境界の内側で誘導します。例えば「金額上限は10万円」をポリシーで強制し、「金額が上限に近い場合はユーザーに確認を促す」をプロンプトで誘導します。この二層があることで、プロンプトの誘導が失敗してもポリシーが最終防壁として機能します。
判断を誤ったときの症状¶
コード制御を選ぶべきだったのにプロンプト制御を選んだ場合:
- プロンプトインジェクションにより、禁止していたはずの操作(個人情報出力、上限超過の決済)が実行されるインシデントが発生します。
- モデルのバージョンを更新した途端、従来守られていた制約が守られなくなり、回帰テストで発見できません。
- 監査で「このルールが守られていた証拠」を求められたとき、「プロンプトに書いてありました」としか答えられません。
- 同じ制約を複数のプロンプトに分散して記述しており、変更漏れや矛盾が発生します。
プロンプト制御を選ぶべきだったのにコード制御を選んだ場合:
- 「丁寧な口調で」「ユーザーの気持ちに寄り添って」のような質的な要件をコードで判定しようとし、偽陽性(正常な出力がブロックされる)が多発します。
- ルール数が爆発し、ポリシーエンジンの運用自体が負担になります。
- 文脈依存の微妙な判断が必要な場面で、硬直的なルールがUXを悪化させます。
- 頻繁にルールを追加・変更する必要があり、ポリシーの版管理・テスト・デプロイがボトルネックになります。
関連パターン¶
- E2 ポリシーのコード化 — コード/ポリシー制御の代表パターンです。OPA/Rego、Cedarなどのポリシーエンジンで制約を一元管理します。
- B1 決定論的な殻 — コード制御の基本原則です。権限チェック・予算管理・検証をコードで強制し、LLMには曖昧な判断だけを委ねます。
- E3 入出力ガードレール — LLMの入出力両面にコード制御を配置する構成です。
- E4 検証済み構造化出力 — 出力形式のコード制御を担います。
- C1 ツールゲートウェイ — ツール呼び出しの許可/拒否をコードで制御します。