LLMプロンプト設計と評価実践ガイド:効果的な対話システム構築に向けて

Tech

本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。

LLMプロンプト設計と評価実践ガイド:効果的な対話システム構築に向けて

本稿では、LLM(大規模言語モデル)を活用したアプリケーション開発における、プロンプトの設計から評価、そして改良に至るまでの一連のプロセスを解説します。特に、具体的なユースケースを想定し、制約付きの仕様化、多様なプロンプト設計、自動評価手法、そして失敗モードへの対策に焦点を当てます。

ユースケース定義

本ガイドでは、「顧客からの製品問い合わせをAIが初期対応し、適切な部門へのルーティングと初回応答のドラフト生成を自動化するシステム」をユースケースとして定義します。このシステムは、顧客体験の向上と社内業務効率化を目指します。

制約付き仕様化

LLMの出力を実用的なものにするためには、明確な入出力契約と制約を設けることが不可欠です。

入出力契約

  • 入力形式:

    • 顧客からの問い合わせテキスト。

    • 最大文字数: 500文字。

    • 言語: 日本語。

  • 出力形式:

    • JSON形式。以下のキーを持つオブジェクトを厳守する。

      {
        "department": "部門名",
        "summary": "問い合わせ要約",
        "draft_response": "初回応答ドラフト"
      }
      
    • department の値は必ず以下のいずれかに限定する: “技術サポート”, “営業”, “経理”, “その他”。

    • summary は入力テキストの主要な内容を簡潔に20〜50字でまとめる。

    • draft_response は敬語を用いた丁寧な言葉遣いで、初回応答として適切な内容(50〜100字程度)を生成する。

  • 失敗時の挙動:

    • JSONパースエラーを防ぐため、常に有効なJSON形式で出力する。

    • 問い合わせ内容が不明確、または判断が困難な場合は、department を “その他” とし、draft_response には「恐れ入りますが、詳細を伺わせていただくため、改めてお問い合わせください。」といった旨を記載する。

    • 出力の生成に失敗した場合(例: モデルが応答しない)、空のJSON {} ではなく、エラーメッセージを含む固定JSON(例: {"error": "internal_server_error"})を返す。

  • 禁止事項:

    • 顧客の個人情報(氏名、電話番号、メールアドレスなど)を出力に含めない。

    • 不適切な表現、ヘイトスピーチ、差別的な内容を生成しない。

    • 誤った情報や存在しない製品情報などを生成しない。

プロンプト設計

以下に、異なるアプローチによる3種類のプロンプト案を提示します。

1. ゼロショットプロンプト

モデルにタスクの例をほとんど示さず、指示のみで期待する出力を促します。

あなたは弊社の顧客対応アシスタントです。
顧客からの問い合わせ内容を分析し、以下のJSON形式で応答してください。

出力形式の厳守:
```json
{
  "department": "部門名",
  "summary": "問い合わせ要約",
  "draft_response": "初回応答ドラフト"
}

“department”は”技術サポート”, “営業”, “経理”, “その他”のいずれかです。 “summary”は20〜50字で、”draft_response”は50〜100字で生成してください。 個人情報や不適切な内容は含めないでください。

問い合わせ: {{問い合わせテキスト}}

### 2. 少数例(Few-shot)プロンプト

いくつかの入力例とその期待される出力例を提示することで、モデルにタスクのパターンを学習させます。

```text
あなたは弊社の顧客対応アシスタントです。
顧客からの問い合わせ内容を分析し、以下のJSON形式で応答してください。
いくつかの例を参考にしてください。

出力形式の厳守:
```json
{
  "department": "部門名",
  "summary": "問い合わせ要約",
  "draft_response": "初回応答ドラフト"
}

“department”は”技術サポート”, “営業”, “経理”, “その他”のいずれかです。 “summary”は20〜50字で、”draft_response”は50〜100字で生成してください。 個人情報や不適切な内容は含めないでください。


例1: 問い合わせ: 製品Aの最新バージョンで発生しているバグについて教えてください。 出力:

{
  "department": "技術サポート",
  "summary": "製品Aの最新バージョンにおけるバグに関する問い合わせ",
  "draft_response": "製品Aのバグに関するお問い合わせありがとうございます。担当部署にて詳細を確認し、改めてご連絡いたしますので、恐れ入りますが今しばらくお待ちください。"
}

例2: 問い合わせ: 新製品Bの導入を検討していますが、見積もりをお願いできますか? 出力:

{
  "department": "営業",
  "summary": "新製品Bの導入検討と見積もり依頼",
  "draft_response": "新製品Bにご興味をお持ちいただきありがとうございます。見積もりについて承りました。担当営業より追ってご連絡させていただきますので、少々お待ちください。"
}

問い合わせ: {{問い合わせテキスト}}

### 3. Chain-of-Thought(CoT)制約型プロンプト

モデルに直接答えを出すのではなく、段階的な思考プロセスを経てから最終出力を生成するよう促します。これにより、複雑なタスクでの精度向上や、モデルの「思考」の透明化が期待できます。

```text
あなたは弊社の顧客対応アシスタントです。
顧客からの問い合わせ内容を分析し、以下のステップで思考してからJSON形式で応答してください。

ステップ1: 問い合わせの意図を簡潔に要約し、考えられる部門をリストアップする。
ステップ2: 最も適切な部門を一つ選択し、その理由を述べる。
ステップ3: 顧客への初回応答ドラフトの主要なポイントを箇条書きでまとめる。
ステップ4: 上記の思考プロセスに基づき、最終的なJSON形式の出力を生成する。

出力形式の厳守:
```json
{
  "department": "部門名",
  "summary": "問い合わせ要約",
  "draft_response": "初回応答ドラフト"
}

“department”は”技術サポート”, “営業”, “経理”, “その他”のいずれかです。 “summary”は20〜50字で、”draft_response”は50〜100字で生成してください。 個人情報や不適切な内容は含めないでください。

問い合わせ: {{問い合わせテキスト}}

## 評価

プロンプトの効果を客観的に測るためには、体系的な評価プロセスが必要です。

### 評価シナリオ

モデルの様々な挙動をテストするため、以下のシナリオを考案します。

1.  **正例**: 明確な意図を持つ問い合わせ。

    *   「製品Cの請求書が届かないのですが、確認をお願いできますでしょうか。」

    *   「ウェブサイトのログインパスワードを忘れました。リセット方法を教えてください。」

2.  **難例**: 複数の部門に関連する可能性のある問い合わせや、曖昧な表現。

    *   「製品Dの機能について聞きたいのですが、同時に契約プランの見直しも検討しています。」

    *   「最近、システムが遅い気がします。何が原因でしょうか?」

3.  **コーナーケース**: 特殊なケースや、モデルが誤動作しやすい状況。

    *   短すぎる/長すぎる問い合わせ: 「遅い」

    *   誤字脱字が多い問い合わせ: 「さいしんのぷろだくとについぇ」

    *   個人情報を含む問い合わせ: 「私の名前は山田太郎で、電話番号は090-XXXX-XXXXです。…」

### 自動評価(擬似コード)

Pythonを想定した自動評価の擬似コードを以下に示します。

```python
import json
import re

def evaluate_llm_output(llm_output_text: str, original_query: str) -> dict:
    """
    LLMの出力を評価する擬似関数。
    """
    results = {
        "json_parseable": False,
        "department_valid": False,
        "summary_length_valid": False,
        "response_length_valid": False,
        "no_pii_detected": True, # 個人情報検出フラグ
        "overall_score": 0
    }

    # 1. JSONパース可能性チェック

    try:
        output_json = json.loads(llm_output_text)
        results["json_parseable"] = True
    except json.JSONDecodeError:
        return results # パース失敗したら以降の評価はスキップ

    # 2. 必須キーの存在チェックと部門名のバリデーション

    required_keys = ["department", "summary", "draft_response"]
    if all(key in output_json for key in required_keys):
        valid_departments = ["技術サポート", "営業", "経理", "その他"]
        if output_json["department"] in valid_departments:
            results["department_valid"] = True

        # 3. summaryの文字数チェック

        summary_len = len(output_json.get("summary", ""))
        if 20 <= summary_len <= 50:
            results["summary_length_valid"] = True

        # 4. draft_responseの文字数チェック

        response_len = len(output_json.get("draft_response", ""))
        if 50 <= response_len <= 100:
            results["response_length_valid"] = True

        # 5. 個人情報(PII)検出(簡易版)

        pii_patterns = [
            r'\d{3}-\d{4}-\d{4}', # 電話番号
            r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}', # メールアドレス
            r'山田太郎|田中花子' # 具体的な氏名(拡張可能)
        ]
        combined_text = output_json.get("summary", "") + output_json.get("draft_response", "")
        for pattern in pii_patterns:
            if re.search(pattern, combined_text):
                results["no_pii_detected"] = False
                break

    # 6. 全体スコアの算出(採点ルーブリック)

    score = 0
    if results["json_parseable"]: score += 20
    if results["department_valid"]: score += 20
    if results["summary_length_valid"]: score += 10
    if results["response_length_valid"]: score += 10
    if results["no_pii_detected"]: score += 20

    # ここに"summary"の関連性や"draft_response"のトーンを評価するロジック(RAGや別のLLMを使った評価など)を追加可能

    results["overall_score"] = score

    return results

# 使用例


# query = "製品Eの価格について教えてください。電話番号は090-1234-5678です。"


# llm_response = '{"department": "営業", "summary": "製品Eの価格に関する問い合わせ", "draft_response": "製品Eの価格について承知いたしました。担当者より詳細をご案内いたします。"}'


# evaluation = evaluate_llm_output(llm_response, query)


# print(evaluation)

プロンプト改良のループ

プロンプトの設計と評価は、一度きりの作業ではなく、継続的なループとして実施されます。

graph TD
    A["要求定義・ユースケース定義"] --> P["プロンプト設計"];
    P -- |プロンプトを適用| --> M[LLM];
    M -- |JSON出力| --> E["評価システム"];
    E -- |評価結果を生成| --> R["評価レポート"];
    R -- |改善点を特定| --> A_prime["分析と改良"];
    A_prime -- |プロンプトを修正| --> P;

失敗モードと抑制手法

LLMは強力ですが、完璧ではありません。特定の状況下で望ましくない出力を生成する「失敗モード」を理解し、その抑制策を講じることが重要です。

失敗モード 説明 抑制手法
幻覚 (Hallucination) 事実に基づかない情報を生成する。 CoTによる思考プロセスの明示要求、外部データソース(RAGなど)との連携、出力に対するファクトチェックステップの導入。
様式崩れ 指定されたJSON形式や文字数制限などを守れない。 Systemプロンプトでの厳密なフォーマット指示、少数例の提示、出力後のバリデーション(正規表現、JSONスキーマ)、モデルのリトライ戦略。
脱線 (Off-topic) 指示されたタスクや役割から逸脱し、無関係な情報を生成する。 Systemプロンプトでの役割とタスクの明確な定義、CoTによる思考制約、入力フィルタリング、出力フィルタリング。
禁止事項の生成 個人情報、不適切な表現、ヘイトスピーチなどを出力に含めてしまう。 Systemプロンプトでの明確な禁止事項の指示、出力に対するキーワードフィルタリング、固有表現認識(NER)による個人情報マスキング、安全性分類モデルによる出力チェック。

まとめ

、LLMプロンプトの設計から評価、改良に至る実践的なプロセスを解説しました。{{jst_today}}時点において、LLMの能力を最大限に引き出し、ビジネス要件に合致するAIアプリケーションを構築するためには、単にプロンプトを記述するだけでなく、入出力の明確な契約定義、多角的な評価、そして失敗モードへの対策が不可欠です。これらの手法を継続的に適用することで、より堅牢で信頼性の高いLLMシステムを構築できるでしょう。

ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

タイトルとURLをコピーしました