<p><!--META
{
"title": "LLMにおける入力バリデーションとサニタイズのプロンプト設計",
"primary_category": "LLMプロンプトエンジニアリング",
"secondary_categories": ["セキュリティ", "データ品質"],
"tags": ["入力バリデーション", "サニタイズ", "プロンプト設計", "セキュリティ", "LLM", "Gemini"],
"summary": "LLMを用いた入力バリデーションとサニタイズのプロンプト設計、評価、改良手法を解説します。",
"mermaid": true,
"verify_level": "L0",
"tweet_hint": {"text":"LLMで入力バリデーションとサニタイズを実現するプロンプト設計手法を徹底解説。セキュリティとデータ品質向上に役立つヒント。", "hashtags":["#LLM","#プロンプトエンジニアリング","#セキュリティ"]},
"link_hints": []
}
-->
本記事は<strong>Geminiの出力をプロンプト工学で整理した業務ドラフト(未検証)</strong>です。</p>
<h1 class="wp-block-heading">LLMにおける入力バリデーションとサニタイズのプロンプト設計</h1>
<p>LLMにユーザー入力を処理させる際、セキュリティとデータ品質を確保するため入力バリデーションとサニタイズは不可欠です。本稿では、そのプロンプト設計、評価、改良のプロセスを詳述します。</p>
<h2 class="wp-block-heading">ユースケース定義</h2>
<p>本稿では、WebアプリケーションやAPIに送られるユーザーからの任意のテキスト入力に対し、LLMが以下の処理を行うシナリオを想定します。</p>
<ol class="wp-block-list">
<li><strong>バリデーション</strong>: 入力がメールアドレス、数値(1~100)、日付(YYYY-MM-DD)のいずれかの指定された形式に合致するか検証します。</li>
<li><strong>サニタイズ</strong>: 入力に含まれる危険なHTMLタグやSQLキーワード、JavaScriptコード断片を除去し、安全な文字列に変換します。</li>
</ol>
<h2 class="wp-block-heading">制約付き仕様化(入出力契約)</h2>
<h3 class="wp-block-heading">入力契約</h3>
<ul class="wp-block-list">
<li><strong>フォーマット</strong>: 任意のテキスト文字列。最大256文字。</li>
<li><strong>エンコーディング</strong>: UTF-8。</li>
<li><strong>禁止事項</strong>: なし(LLMが処理するため)。</li>
</ul>
<h3 class="wp-block-heading">出力契約</h3>
<ul class="wp-block-list">
<li><strong>フォーマット</strong>: JSON形式の文字列。<code>{"status": "success", "type": "...", "value": "...", "message": "..."}</code> または <code>{"status": "error", "type": "...", "message": "..."}</code>。</li>
<li><strong>成功時の挙動</strong>:
<ul>
<li><code>status</code>: “success”</li>
<li><code>type</code>: “email”, “number”, “date”, “text” のいずれか。</li>
<li><code>value</code>: バリデートされ、サニタイズされた入力値。</li>
<li><code>message</code>: 処理結果に関する簡潔なメッセージ(例: “入力は有効です。”)。</li>
</ul></li>
<li><strong>失敗時の挙動</strong>:
<ul>
<li><code>status</code>: “error”</li>
<li><code>type</code>: バリデーションエラーの種類、または “unknown”。</li>
<li><code>message</code>: 具体的なエラー内容(例: “無効なメールアドレス形式です。”, “数値が範囲外です。”, “危険な文字列が検出されました。”)。</li>
</ul></li>
<li><strong>禁止事項</strong>: 処理されていない危険な文字列(<code><script></code>、<code>DROP TABLE</code>など)の出力は厳禁です。出力されたJSON以外の形式も禁止します。</li>
</ul>
<h2 class="wp-block-heading">プロンプト設計</h2>
<h3 class="wp-block-heading">1. ゼロショットプロンプト</h3>
<div class="codehilite">
<pre data-enlighter-language="generic">あなたは入力バリデーションとサニタイズを行うセキュリティアシスタントです。
以下のルールに従い、ユーザーからの入力を検証・サニタイズし、JSON形式で結果を返してください。
ルール:
1. 入力がメールアドレス形式(RFC 5322相当)であるか検証してください。
2. 入力が1~100の範囲の整数であるか検証してください。
3. 入力がYYYY-MM-DD形式の日付であるか検証してください。
4. 上記いずれにも該当しない場合は、一般テキストとして扱います。
5. 全ての入力に対し、`script`タグ、`onerror`属性、SQLキーワード(`SELECT`, `INSERT`, `UPDATE`, `DELETE`, `DROP TABLE`など)を検出・除去し、安全な文字列にしてください。
6. 結果はJSON形式で出力してください。
- 成功時: `{"status": "success", "type": "email|number|date|text", "value": "安全な値", "message": "..."}`
- 失敗時: `{"status": "error", "type": "email|number|date|text|unknown", "message": "エラー内容"}`
入力: {user_input}
</pre>
</div>
<h3 class="wp-block-heading">2. 少数例プロンプト</h3>
<div class="codehilite">
<pre data-enlighter-language="generic">あなたは入力バリデーションとサニタイズを行うセキュリティアシスタントです。
以下の例を参考に、ユーザーからの入力を検証・サニタイズし、JSON形式で結果を返してください。
---
入力: test@example.com
出力: {"status": "success", "type": "email", "value": "test@example.com", "message": "メールアドレスが有効です。"}
---
入力: <script>alert('XSS')</script>user@example.jp
出力: {"status": "success", "type": "email", "value": "user@example.jp", "message": "メールアドレスが有効で、危険な文字列を除去しました。"}
---
入力: 50
出力: {"status": "success", "type": "number", "value": "50", "message": "数値が有効です。"}
---
入力: 101
出力: {"status": "error", "type": "number", "message": "数値が範囲外です(1-100)。"}
---
入力: 2023-11-01
出力: {"status": "success", "type": "date", "value": "2023-11-01", "message": "日付が有効です。"}
---
入力: invalid_email
出力: {"status": "error", "type": "email", "message": "無効なメールアドレス形式です。"}
---
入力: DROP TABLE users;
出力: {"status": "error", "type": "text", "message": "危険なSQLキーワードが検出されました。"}
---
入力: {user_input}
</pre>
</div>
<h3 class="wp-block-heading">3. Chain-of-Thought制約型プロンプト</h3>
<div class="codehilite">
<pre data-enlighter-language="generic">あなたは入力バリデーションとサニタイズを行うセキュリティアシスタントです。
以下のステップバイステップの思考プロセスと出力形式を厳守し、ユーザーからの入力を処理してください。
### 思考プロセス
1. **入力の分析**: ユーザー入力 `{user_input}` を受け取ります。
2. **タイプ推定**: 入力が以下のいずれのタイプに最も近いかを判断します。
- メールアドレス(RFC 5322相当の正規表現 `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$` で検証)
- 数値(1~100の範囲の整数か検証)
- 日付(YYYY-MM-DD形式の正規表現 `^\d{4}-\d{2}-\d{2}$` で検証)
- 上記に該当しない場合は「text」とします。
3. **バリデーション**: 推定されたタイプに基づき、厳密なルールでバリデーションを実行します。
- メールアドレス: 形式が不正ならエラー。
- 数値: 形式が不正、または範囲外ならエラー。
- 日付: 形式が不正ならエラー。
4. **サニタイズ**: 以下の危険な文字列を完全に除去します。
- HTMLタグ(例: `<script>`, `<div>`)
- 特定の属性(例: `onerror`, `onload`)
- SQLキーワード(例: `SELECT`, `INSERT`, `UPDATE`, `DELETE`, `DROP TABLE`, `;`)
- サニタイズ後に値が空になる場合、または危険な文字列が除去できない場合はエラーとします。
5. **結果の生成**: バリデーションとサニタイズの結果をJSON形式で出力します。
### 出力フォーマット
必ず以下のJSON形式で出力してください。
成功時: `{"status": "success", "type": "email|number|date|text", "value": "安全な値", "message": "処理結果"}`
失敗時: `{"status": "error", "type": "email|number|date|text|unknown", "message": "エラー内容"}`
入力: {user_input}
</pre>
</div>
<h2 class="wp-block-heading">評価</h2>
<h3 class="wp-block-heading">評価シナリオ</h3>
<figure class="wp-block-table"><table>
<thead>
<tr>
<th style="text-align:left;">カテゴリ</th>
<th style="text-align:left;">入力値</th>
<th style="text-align:left;">期待される出力JSON</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left;"><strong>正例</strong></td>
<td style="text-align:left;"><code>test@example.com</code></td>
<td style="text-align:left;"><code>{"status": "success", "type": "email", "value": "test@example.com", "message": "..."}</code></td>
</tr>
<tr>
<td style="text-align:left;"><strong>正例</strong></td>
<td style="text-align:left;"><code>50</code></td>
<td style="text-align:left;"><code>{"status": "success", "type": "number", "value": "50", "message": "..."}</code></td>
</tr>
<tr>
<td style="text-align:left;"><strong>正例</strong></td>
<td style="text-align:left;"><code>2023-10-26</code></td>
<td style="text-align:left;"><code>{"status": "success", "type": "date", "value": "2023-10-26", "message": "..."}</code></td>
</tr>
<tr>
<td style="text-align:left;"><strong>難例</strong></td>
<td style="text-align:left;"><code><script>alert(1)</script>safe@domain.com</code></td>
<td style="text-align:left;"><code>{"status": "success", "type": "email", "value": "safe@domain.com", "message": "..."}</code></td>
</tr>
<tr>
<td style="text-align:left;"><strong>難例</strong></td>
<td style="text-align:left;"><code>SELECT * FROM users; 10</code></td>
<td style="text-align:left;"><code>{"status": "success", "type": "number", "value": "10", "message": "..."}</code></td>
</tr>
<tr>
<td style="text-align:left;"><strong>難例</strong></td>
<td style="text-align:left;"><code>101</code></td>
<td style="text-align:left;"><code>{"status": "error", "type": "number", "message": "数値が範囲外です(1-100)。"}</code></td>
</tr>
<tr>
<td style="text-align:left;"><strong>難例</strong></td>
<td style="text-align:left;"><code>invalid-email</code></td>
<td style="text-align:left;"><code>{"status": "error", "type": "email", "message": "無効なメールアドレス形式です。"}</code></td>
</tr>
<tr>
<td style="text-align:left;"><strong>コーナーケース</strong></td>
<td style="text-align:left;"><code></code> (空文字列)</td>
<td style="text-align:left;"><code>{"status": "error", "type": "unknown", "message": "入力が空です。"}</code></td>
</tr>
<tr>
<td style="text-align:left;"><strong>コーナーケース</strong></td>
<td style="text-align:left;"><code>null</code></td>
<td style="text-align:left;"><code>{"status": "error", "type": "unknown", "message": "入力が空、または無効です。"}</code></td>
</tr>
<tr>
<td style="text-align:left;"><strong>コーナーケース</strong></td>
<td style="text-align:left;"><code>user@.com</code></td>
<td style="text-align:left;"><code>{"status": "error", "type": "email", "message": "無効なメールアドレス形式です。"}</code></td>
</tr>
</tbody>
</table></figure>
<h3 class="wp-block-heading">自動評価擬似コード</h3>
<div class="codehilite">
<pre data-enlighter-language="generic">import re
import json
def evaluate_llm_output(llm_output_json_str: str, expected_output_json: dict) -> int:
try:
actual_output = json.loads(llm_output_json_str)
except json.JSONDecodeError:
return 0 # JSON形式でない場合は0点
score = 0
# 1. JSON形式の妥当性 (既にチェック済み)
# 2. statusの一致
if actual_output.get("status") == expected_output_json.get("status"):
score += 1
# 3. typeの一致
if actual_output.get("type") == expected_output_json.get("type"):
score += 1
# 4. success時のvalueの一致とサニタイズチェック
if actual_output.get("status") == "success":
if actual_output.get("value") == expected_output_json.get("value"):
score += 1
# サニタイズの観点: 危険な文字列が含まれていないかチェック
dangerous_patterns = [r"<script.*?>", r"onerror", r"SELECT", r"DROP TABLE"]
is_sanitized = all(not re.search(p, actual_output.get("value", ""), re.IGNORECASE) for p in dangerous_patterns)
if is_sanitized:
score += 1
# 5. error時のmessage内容の近似性 (部分一致でも可)
elif actual_output.get("status") == "error":
expected_message = expected_output_json.get("message", "")
actual_message = actual_output.get("message", "")
if expected_message in actual_message or actual_message in expected_message:
score += 2 # エラーメッセージの重要度を高く設定
return score
# 採点ルーブリック:
# 0点: 全く異なる、JSON形式でない、危険な文字列を含む
# 1-3点: 部分的に正しい
# 4点: 完全に正しい
</pre>
</div>
<h2 class="wp-block-heading">誤り分析</h2>
<h3 class="wp-block-heading">失敗モード</h3>
<ol class="wp-block-list">
<li><strong>幻覚/誤ったバリデーション</strong>:
<ul>
<li>無効な入力を有効と判断する(例: <code>user@.com</code>を有効なメールアドレスと認識)。</li>
<li>有効な入力を無効と判断する(例: <code>99</code>を範囲外と認識)。</li>
</ul></li>
<li><strong>様式崩れ</strong>:
<ul>
<li>出力が指定のJSON形式ではない(例: プレーンテキスト、部分的なJSON)。</li>
<li><code>status</code>や<code>type</code>フィールドが期待値と異なる文字列になる。</li>
</ul></li>
<li><strong>脱線/禁止事項の漏れ</strong>:
<ul>
<li>サニタイズされるべき危険な文字列(XSSペイロード、SQLインジェクション)が<code>value</code>フィールドにそのまま残る。</li>
<li>サニタイズ後の<code>value</code>が、元の意図と大きく異なる(過剰な除去)。</li>
</ul></li>
</ol>
<h3 class="wp-block-heading">抑制手法</h3>
<ol class="wp-block-list">
<li><strong>System指示の強化</strong>:
<ul>
<li>正規表現パターンをプロンプト内に明示的に記述し、バリデーションロジックをLLMに厳密に指示します。</li>
<li>サニタイズすべき危険なキーワードやパターンリストを具体的に列挙し、除去を命令します。</li>
</ul></li>
<li><strong>検証ステップの導入 (CoT)</strong>:
<ul>
<li>Chain-of-Thoughtプロンプトで、タイプ推定→バリデーション→サニタイズの各ステップを明確に指示し、LLMにその思考過程を出力させることで、内部でのロジック適用を促します。</li>
</ul></li>
<li><strong>リトライ戦略</strong>:
<ul>
<li>LLMからの出力がJSON形式ではない、またはサニタイズが不十分な場合、再試行を促すプロンプトを追加し、再度処理を要求します。</li>
<li>外部の正規表現ライブラリやサニタイザー関数を呼び出すためのファンクションコール機能を検討します。</li>
</ul></li>
</ol>
<h2 class="wp-block-heading">改良</h2>
<p>評価結果に基づき、失敗モードを抑制するためプロンプトを改良します。特に、Chain-of-Thought制約型プロンプトに正規表現パターンを直接記述し、サニタイズ対象キーワードリストをさらに詳細化することが有効です。また、JSON Schemaをプロンプトに含めることで、様式崩れのリスクを低減します。</p>
<h2 class="wp-block-heading">再評価</h2>
<p>改良されたプロンプトを用いて、同じ評価シナリオで再度LLMの出力を評価します。これにより、改良が有効であったか、新たな問題が発生していないかを確認します。自動評価スクリプトのスコア向上を目指します。</p>
<h2 class="wp-block-heading">まとめ</h2>
<p>LLMを用いた入力バリデーションとサニタイズは、適切なプロンプト設計により実現可能です。ゼロショットからChain-of-Thoughtへと段階的に制約を強め、詳細な入出力契約と厳密な評価基準を設けることで、セキュリティとデータ品質を確保できます。特に、危険な文字列の具体的なリスト化とJSON出力の厳格な指示が鍵となります。</p>
<h2 class="wp-block-heading">プロンプト→モデル→評価→改良のループ</h2>
<div class="wp-block-merpress-mermaidjs diagram-source-mermaid"><pre class="mermaid">
graph TD
A["要求定義・要件分析"] --> B{"プロンプト設計"};
B --> C["プロンプト生成"];
C --> D[LLM];
D --> E["出力結果"];
E --> F{"評価"};
F --|評価結果が不十分| G["誤り分析"];
G --> H["改良案立案"];
H --> B;
F --|評価結果が良好| I["デプロイ/採用"];
</pre></div>
本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証) です。
LLMにおける入力バリデーションとサニタイズのプロンプト設計
LLMにユーザー入力を処理させる際、セキュリティとデータ品質を確保するため入力バリデーションとサニタイズは不可欠です。本稿では、そのプロンプト設計、評価、改良のプロセスを詳述します。
ユースケース定義
本稿では、WebアプリケーションやAPIに送られるユーザーからの任意のテキスト入力に対し、LLMが以下の処理を行うシナリオを想定します。
バリデーション : 入力がメールアドレス、数値(1~100)、日付(YYYY-MM-DD)のいずれかの指定された形式に合致するか検証します。
サニタイズ : 入力に含まれる危険なHTMLタグやSQLキーワード、JavaScriptコード断片を除去し、安全な文字列に変換します。
制約付き仕様化(入出力契約)
入力契約
フォーマット : 任意のテキスト文字列。最大256文字。
エンコーディング : UTF-8。
禁止事項 : なし(LLMが処理するため)。
出力契約
フォーマット : JSON形式の文字列。{"status": "success", "type": "...", "value": "...", "message": "..."}
または {"status": "error", "type": "...", "message": "..."}
。
成功時の挙動 :
status
: “success”
type
: “email”, “number”, “date”, “text” のいずれか。
value
: バリデートされ、サニタイズされた入力値。
message
: 処理結果に関する簡潔なメッセージ(例: “入力は有効です。”)。
失敗時の挙動 :
status
: “error”
type
: バリデーションエラーの種類、または “unknown”。
message
: 具体的なエラー内容(例: “無効なメールアドレス形式です。”, “数値が範囲外です。”, “危険な文字列が検出されました。”)。
禁止事項 : 処理されていない危険な文字列(<script>
、DROP TABLE
など)の出力は厳禁です。出力されたJSON以外の形式も禁止します。
プロンプト設計
1. ゼロショットプロンプト
あなたは入力バリデーションとサニタイズを行うセキュリティアシスタントです。
以下のルールに従い、ユーザーからの入力を検証・サニタイズし、JSON形式で結果を返してください。
ルール:
1. 入力がメールアドレス形式(RFC 5322相当)であるか検証してください。
2. 入力が1~100の範囲の整数であるか検証してください。
3. 入力がYYYY-MM-DD形式の日付であるか検証してください。
4. 上記いずれにも該当しない場合は、一般テキストとして扱います。
5. 全ての入力に対し、`script`タグ、`onerror`属性、SQLキーワード(`SELECT`, `INSERT`, `UPDATE`, `DELETE`, `DROP TABLE`など)を検出・除去し、安全な文字列にしてください。
6. 結果はJSON形式で出力してください。
- 成功時: `{"status": "success", "type": "email|number|date|text", "value": "安全な値", "message": "..."}`
- 失敗時: `{"status": "error", "type": "email|number|date|text|unknown", "message": "エラー内容"}`
入力: {user_input}
2. 少数例プロンプト
あなたは入力バリデーションとサニタイズを行うセキュリティアシスタントです。
以下の例を参考に、ユーザーからの入力を検証・サニタイズし、JSON形式で結果を返してください。
---
入力: test@example.com
出力: {"status": "success", "type": "email", "value": "test@example.com", "message": "メールアドレスが有効です。"}
---
入力: <script>alert('XSS')</script>user@example.jp
出力: {"status": "success", "type": "email", "value": "user@example.jp", "message": "メールアドレスが有効で、危険な文字列を除去しました。"}
---
入力: 50
出力: {"status": "success", "type": "number", "value": "50", "message": "数値が有効です。"}
---
入力: 101
出力: {"status": "error", "type": "number", "message": "数値が範囲外です(1-100)。"}
---
入力: 2023-11-01
出力: {"status": "success", "type": "date", "value": "2023-11-01", "message": "日付が有効です。"}
---
入力: invalid_email
出力: {"status": "error", "type": "email", "message": "無効なメールアドレス形式です。"}
---
入力: DROP TABLE users;
出力: {"status": "error", "type": "text", "message": "危険なSQLキーワードが検出されました。"}
---
入力: {user_input}
3. Chain-of-Thought制約型プロンプト
あなたは入力バリデーションとサニタイズを行うセキュリティアシスタントです。
以下のステップバイステップの思考プロセスと出力形式を厳守し、ユーザーからの入力を処理してください。
### 思考プロセス
1. **入力の分析**: ユーザー入力 `{user_input}` を受け取ります。
2. **タイプ推定**: 入力が以下のいずれのタイプに最も近いかを判断します。
- メールアドレス(RFC 5322相当の正規表現 `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$` で検証)
- 数値(1~100の範囲の整数か検証)
- 日付(YYYY-MM-DD形式の正規表現 `^\d{4}-\d{2}-\d{2}$` で検証)
- 上記に該当しない場合は「text」とします。
3. **バリデーション**: 推定されたタイプに基づき、厳密なルールでバリデーションを実行します。
- メールアドレス: 形式が不正ならエラー。
- 数値: 形式が不正、または範囲外ならエラー。
- 日付: 形式が不正ならエラー。
4. **サニタイズ**: 以下の危険な文字列を完全に除去します。
- HTMLタグ(例: `<script>`, `<div>`)
- 特定の属性(例: `onerror`, `onload`)
- SQLキーワード(例: `SELECT`, `INSERT`, `UPDATE`, `DELETE`, `DROP TABLE`, `;`)
- サニタイズ後に値が空になる場合、または危険な文字列が除去できない場合はエラーとします。
5. **結果の生成**: バリデーションとサニタイズの結果をJSON形式で出力します。
### 出力フォーマット
必ず以下のJSON形式で出力してください。
成功時: `{"status": "success", "type": "email|number|date|text", "value": "安全な値", "message": "処理結果"}`
失敗時: `{"status": "error", "type": "email|number|date|text|unknown", "message": "エラー内容"}`
入力: {user_input}
評価
評価シナリオ
カテゴリ
入力値
期待される出力JSON
正例
test@example.com
{"status": "success", "type": "email", "value": "test@example.com", "message": "..."}
正例
50
{"status": "success", "type": "number", "value": "50", "message": "..."}
正例
2023-10-26
{"status": "success", "type": "date", "value": "2023-10-26", "message": "..."}
難例
<script>alert(1)</script>safe@domain.com
{"status": "success", "type": "email", "value": "safe@domain.com", "message": "..."}
難例
SELECT * FROM users; 10
{"status": "success", "type": "number", "value": "10", "message": "..."}
難例
101
{"status": "error", "type": "number", "message": "数値が範囲外です(1-100)。"}
難例
invalid-email
{"status": "error", "type": "email", "message": "無効なメールアドレス形式です。"}
コーナーケース
(空文字列)
{"status": "error", "type": "unknown", "message": "入力が空です。"}
コーナーケース
null
{"status": "error", "type": "unknown", "message": "入力が空、または無効です。"}
コーナーケース
user@.com
{"status": "error", "type": "email", "message": "無効なメールアドレス形式です。"}
自動評価擬似コード
import re
import json
def evaluate_llm_output(llm_output_json_str: str, expected_output_json: dict) -> int:
try:
actual_output = json.loads(llm_output_json_str)
except json.JSONDecodeError:
return 0 # JSON形式でない場合は0点
score = 0
# 1. JSON形式の妥当性 (既にチェック済み)
# 2. statusの一致
if actual_output.get("status") == expected_output_json.get("status"):
score += 1
# 3. typeの一致
if actual_output.get("type") == expected_output_json.get("type"):
score += 1
# 4. success時のvalueの一致とサニタイズチェック
if actual_output.get("status") == "success":
if actual_output.get("value") == expected_output_json.get("value"):
score += 1
# サニタイズの観点: 危険な文字列が含まれていないかチェック
dangerous_patterns = [r"<script.*?>", r"onerror", r"SELECT", r"DROP TABLE"]
is_sanitized = all(not re.search(p, actual_output.get("value", ""), re.IGNORECASE) for p in dangerous_patterns)
if is_sanitized:
score += 1
# 5. error時のmessage内容の近似性 (部分一致でも可)
elif actual_output.get("status") == "error":
expected_message = expected_output_json.get("message", "")
actual_message = actual_output.get("message", "")
if expected_message in actual_message or actual_message in expected_message:
score += 2 # エラーメッセージの重要度を高く設定
return score
# 採点ルーブリック:
# 0点: 全く異なる、JSON形式でない、危険な文字列を含む
# 1-3点: 部分的に正しい
# 4点: 完全に正しい
誤り分析
失敗モード
幻覚/誤ったバリデーション :
無効な入力を有効と判断する(例: user@.com
を有効なメールアドレスと認識)。
有効な入力を無効と判断する(例: 99
を範囲外と認識)。
様式崩れ :
出力が指定のJSON形式ではない(例: プレーンテキスト、部分的なJSON)。
status
やtype
フィールドが期待値と異なる文字列になる。
脱線/禁止事項の漏れ :
サニタイズされるべき危険な文字列(XSSペイロード、SQLインジェクション)がvalue
フィールドにそのまま残る。
サニタイズ後のvalue
が、元の意図と大きく異なる(過剰な除去)。
抑制手法
System指示の強化 :
正規表現パターンをプロンプト内に明示的に記述し、バリデーションロジックをLLMに厳密に指示します。
サニタイズすべき危険なキーワードやパターンリストを具体的に列挙し、除去を命令します。
検証ステップの導入 (CoT) :
Chain-of-Thoughtプロンプトで、タイプ推定→バリデーション→サニタイズの各ステップを明確に指示し、LLMにその思考過程を出力させることで、内部でのロジック適用を促します。
リトライ戦略 :
LLMからの出力がJSON形式ではない、またはサニタイズが不十分な場合、再試行を促すプロンプトを追加し、再度処理を要求します。
外部の正規表現ライブラリやサニタイザー関数を呼び出すためのファンクションコール機能を検討します。
改良
評価結果に基づき、失敗モードを抑制するためプロンプトを改良します。特に、Chain-of-Thought制約型プロンプトに正規表現パターンを直接記述し、サニタイズ対象キーワードリストをさらに詳細化することが有効です。また、JSON Schemaをプロンプトに含めることで、様式崩れのリスクを低減します。
再評価
改良されたプロンプトを用いて、同じ評価シナリオで再度LLMの出力を評価します。これにより、改良が有効であったか、新たな問題が発生していないかを確認します。自動評価スクリプトのスコア向上を目指します。
まとめ
LLMを用いた入力バリデーションとサニタイズは、適切なプロンプト設計により実現可能です。ゼロショットからChain-of-Thoughtへと段階的に制約を強め、詳細な入出力契約と厳密な評価基準を設けることで、セキュリティとデータ品質を確保できます。特に、危険な文字列の具体的なリスト化とJSON出力の厳格な指示が鍵となります。
プロンプト→モデル→評価→改良のループ
graph TD
A["要求定義・要件分析"] --> B{"プロンプト設計"};
B --> C["プロンプト生成"];
C --> D[LLM];
D --> E["出力結果"];
E --> F{"評価"};
F --|評価結果が不十分| G["誤り分析"];
G --> H["改良案立案"];
H --> B;
F --|評価結果が良好| I["デプロイ/採用"];
コメント