Negation Promptingの実践:LLMによる否定指定の精密制御

Tech

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

Negation Promptingの実践:LLMによる否定指定の精密制御

LLMは自然言語を理解し生成する強力なツールですが、「〜しない」といった否定指定(Negation Prompting)の解釈と遵守は、しばしばその能力の限界を試す領域となります。本稿では、プロンプト設計と評価の専門家として、このNegation Promptingを効果的に実践するための具体的な手法とフレームワークを提示します。

1. 入出力契約の定義

Negation Promptingを適用するにあたり、まずはモデルとの明確な契約を定義します。

入力契約:

  • フォーマット: JSONオブジェクト。

    {
      "document": "string",            // 処理対象のテキスト
      "negation_keywords": ["string"], // 除外すべきキーワードの配列(大文字小文字を区別しない)
      "output_format": "string"        // 出力形式 ("paragraph" または "bullet_points")
    }
    
  • 必須項目: document, negation_keywords

  • 最小要件: negation_keywordsは1つ以上の文字列を含む。

  • 禁止事項: 不適切な内容、個人情報(PII)をdocumentに含めない。

出力契約:

  • フォーマット: JSONオブジェクト。

    {
      "summary": "string",             // 生成された要約テキスト
      "violations": ["string"]         // 検出された否定キーワード違反の配列。違反がない場合は空配列。
    }
    
  • 成功時の挙動: summaryフィールドにNegationキーワードを含まない要約を生成し、violationsは空配列。

  • 失敗時の挙動:

    • 入力契約違反の場合: { "error": "Invalid input format or missing required fields." } を返却。

    • モデルがNegationキーワードを検出した場合: violations配列に該当キーワードを格納。

    • モデルが要約生成に失敗した場合: { "error": "Failed to generate summary.", "details": "..." } を返却。

  • 禁止事項: 暴力、ヘイトスピーチ、不法行為を助長する内容の生成。

2. ユースケース定義

今回のテーマは「会議議事録の要約からの特定話題の除外」とします。 ビジネス会議の議事録は多岐にわたるトピックを含みますが、特定のステークホルダーには関心のない、または公開すべきでない情報(例: 予算詳細、特定の個人に関する評価)が含まれる場合があります。このような場合、Negation Promptingを用いて、指定されたトピックを除外した要約を生成します。

3. 制約付き仕様化

上記ユースケースに基づき、具体的な仕様と制約を定義します。

機能: 会議議事録から、指定されたキーワードに関連する内容を除外した要約を生成する。

入力:

  • document: 会議議事録のテキスト。

  • negation_keywords: 除外したいトピックを示すキーワードの配列。例: ["予算", "コスト", "人事", "Aさんの評価"]

  • output_format: "paragraph" (段落形式)

出力:

  • summary: negation_keywordsに含まれる内容を除外した、簡潔な会議の要約。

  • violations: 生成されたsummaryに除外キーワードが含まれていた場合に、そのキーワードをリストアップ。

制約:

  1. 否定遵守: summaryにはnegation_keywords内のいずれのキーワードも(大文字小文字を区別せず、部分一致も考慮して)直接的、間接的に含まれてはならない。

  2. 情報保持: 否定対象外の主要な論点は、原文の意味を損なうことなく要約に含めること。

  3. 文字数: summaryは最大500文字程度とする。

  4. 出力形式: output_formatの指定に従うこと。

4. プロンプト設計

Negation Promptingの精度を高めるため、異なるアプローチのプロンプトを設計します。

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

基本的な指示のみで否定条件を伝えます。

You are an expert summarizer. Your task is to summarize the provided meeting transcript.
Crucially, you must exclude any mention of the following topics: {negation_keywords}.
Ensure the summary is concise and maintains the core message, without exceeding 500 characters.
The output format should be a single paragraph.

Meeting Transcript:
---
{document}
---

Summary:

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

Negationが適用された例を事前に与えることで、モデルに期待する挙動を具体的に示します。

You are an expert summarizer. Your task is to summarize meeting transcripts, strictly excluding specified topics.

---
Example 1:
Meeting Transcript: "本日、新製品Xの販売戦略について議論しました。特にマーケティング予算の配分と広告費用が主要な議題でした。しかし、技術的な課題や開発スケジュールも簡単に触れられました。来週までに予算案を固めます。"
Negation Keywords: ["予算", "費用"]
Summary: "新製品Xの販売戦略について議論が行われました。主な議題は広告戦略と技術的な課題、開発スケジュールでした。"
---
Example 2:
Meeting Transcript: "今日の会議では、採用プロセスとチームAの人員配置について話し合いました。市場動向分析の結果と、競合他社の戦略も共有されました。特に、新規プロジェクトの担当者配置が急務です。品質改善策も重要なテーマでした。"
Negation Keywords: ["人事", "人員配置"]
Summary: "本日の会議では、市場動向分析の結果、競合他社の戦略、新規プロジェクトの担当者選定、および品質改善策が主要な議題となりました。"
---

Now, your turn:
Meeting Transcript:
---
{document}
---
Negation Keywords: {negation_keywords}
Summary:

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

思考プロセスを段階的に指示し、特に否定条件の自己検証を組み込みます。

You are an advanced summarization AI. Your goal is to provide a concise summary of the meeting transcript, while strictly adhering to negation constraints. Follow these steps:

1.  **Extract Key Points:** Identify all major discussion points and decisions from the transcript.

2.  **Filter Negations:** Review the extracted key points. Identify and remove any points that directly or indirectly relate to the provided negation keywords: {negation_keywords}.

3.  **Synthesize Summary:** Construct a concise summary (max 500 characters, paragraph format) using only the filtered key points. Ensure the summary flows naturally and accurately reflects the remaining content.

4.  **Self-Correction/Verification:** Double-check the final summary. Does it contain *any* of the negation keywords or topics? If so, revise the summary until all negation conditions are met.

Meeting Transcript:
---
{document}
---

Think step-by-step:

1. Key Points:

2. Filtered Points (after negation removal for {negation_keywords}):

3. Final Summary:

5. 評価

設計したプロンプトの有効性を評価するためのシナリオと自動評価の擬似コードを定義します。

5.1. 評価シナリオ

カテゴリ シナリオ名 会議議事録(document 除外キーワード(negation_keywords 期待される要約のポイント
正例 基本的な除外 “本日、四半期レビュー会議が行われ、売上目標達成度と新規顧客獲得数が報告されました。来期のマーケティング戦略と予算配分が主な議題となりましたが、市場動向分析と競合調査の結果も共有されました。次のアクションとして、戦略の細部を詰めることになりました。” ["予算"] 売上目標と新規顧客獲得、市場動向分析、競合調査、マーケティング戦略、次のアクション(戦略の詳細化)が含まれ、「予算」に関する言及がないこと。
難例 間接的な除外 “来月の新製品ローンチについて議論しました。特に販売価格の設定が重要で、競合他社の動向を綿密に調査する必要があります。広報キャンペーンの計画も進んでおり、メディア露出の最大化を図ります。製品の機能改善案もいくつか提示されました。次の会議で最終的な費用対効果を検討します。” ["価格", "費用"] 新製品ローンチ、競合他社の動向調査、広報キャンペーン計画、製品機能改善案が含まれ、「価格」「費用」という直接的なキーワードだけでなく、それらを示唆する「販売価格」「費用対効果」などの間接的な表現も除外されていること。
コーナーケース 除外キーワードが必須 “本日の議題は、来年の予算策定です。各部署からの要望を聞き、全体最適な予算計画を立てることが目標です。特に、新規プロジェクトの予算が重要となります。この予算案は来週、取締役会に提出されます。この会議は、予算なしには成立しません。” ["予算"] 要約生成が困難であることを示す、または「予算」という言葉を一切使わずに「資金計画」「財源配分」のような代替表現を工夫して要約を試みる。ただし、このケースでは主要なテーマである「予算」を完全に除外すると意味が失われるため、モデルの挙動を注意深く観察する。理想的には、「資金計画の策定が議論され、各部署からの要望や新規プロジェクトへの配分が焦点となった。」のように迂回表現を使う。
コーナーケース 否定の否定を含む “このソフトウェアはバグがないことを保証します。ただし、既知の不具合リストは公開しており、それらは今後のアップデートで修正されます。システムのセキュリティ強化が急務であり、そのための新しいプロトコル導入を検討しています。ユーザビリティの改善も重要な課題です。” ["バグ", "不具合"] ソフトウェアの保証、既知の問題修正、システムセキュリティ強化、プロトコル導入、ユーザビリティ改善が含まれる。「バグがない」という肯定的な表現は維持しつつ、「不具合」というキーワードは除外されていること。

5.2. 自動評価の擬似コード

import re
import json

def evaluate_summary(generated_output_json: str, expected_negations: list, original_document: str, max_length: int = 500) -> dict:
    """
    LLMが生成した要約を自動評価する擬似コード。
    """
    try:
        output = json.loads(generated_output_json)
        summary = output.get("summary", "")

        # 自動評価ではviolationsはモデルが出力したものを検証する

        model_reported_violations = output.get("violations", []) 
    except json.JSONDecodeError:
        return {"score": 0, "feedback": "JSONフォーマットが不正です。", "details": {}}

    score = 100 # 初期スコア
    feedback = []
    details = {}

    # 1. 否定条件の遵守 (正規表現/キーワードチェック)

    detected_violations = []
    for keyword in expected_negations:

        # 大文字小文字を区別せず、単語全体または部分一致をチェック

        if re.search(r'\b' + re.escape(keyword) + r'\b', summary, re.IGNORECASE) or \
           re.search(re.escape(keyword), summary, re.IGNORECASE):
            detected_violations.append(keyword)
            score -= 40 # 重大な違反
            feedback.append(f"要約に禁止キーワード '{keyword}' が含まれています。")

    details["detected_violations_by_evaluator"] = detected_violations
    details["model_reported_violations"] = model_reported_violations

    # モデルが報告した違反と評価者が検出した違反の整合性チェック


    # 理想的にはこれらは一致すべきだが、モデルが自身の違反をすべて検出できない場合もある

    if set(detected_violations) != set(model_reported_violations):
        if detected_violations and not model_reported_violations:
            feedback.append("モデルは違反を検出したが、自身では報告していません。")
            score -= 10
        elif model_reported_violations and not detected_violations:
            feedback.append("モデルは違反を報告しましたが、評価者側では検出されませんでした。(誤検知の可能性)")
            score -= 5 # 誤検知も望ましくない

    # 2. 要約の品質 (簡潔さ/文字数)

    if not summary:
        score = 0
        feedback.append("要約が空です。")
    elif len(summary) > max_length:
        score -= 15
        feedback.append(f"要約が長すぎます ({len(summary)}文字、最大{max_length}文字)。")

    details["summary_length"] = len(summary)

    # 3. 関連性の維持 (関数評価 - 擬似的な意味的類似度チェック)


    # 実際の評価では、Embedding比較や別のLLMによる評価が必要


    # ここでは簡略化のため、元のドキュメントから主要な非否定キーワードが残っているかをチェック

    relevant_keywords = [kw for kw in original_document.split() if kw not in expected_negations] # 簡易的な例
    summary_contains_relevant = any(re.search(r'\b' + re.escape(kw) + r'\b', summary, re.IGNORECASE) for kw in relevant_keywords[:5]) # 上位5件のみ
    if not summary_contains_relevant and len(summary) > 50: # 要約が短い場合は例外
        score -= 20
        feedback.append("要約が元のドキュメントの主要な関連情報を十分に含んでいない可能性があります。")

    details["relevance_check"] = summary_contains_relevant

    # 最終スコア調整

    score = max(0, score) # スコアは0未満にならない

    return {
        "score": score,
        "feedback": feedback if feedback else ["良好な要約です。"],
        "details": details
    }

# 使用例


# generated_output = '{"summary": "会議では新製品Xの販売戦略が議論され、広告戦略や技術的課題が触れられました。","violations":[]}'


# original_doc = "本日、新製品Xの販売戦略について議論しました。特にマーケティング予算の配分と広告費用が主要な議題でした。しかし、技術的な課題や開発スケジュールも簡単に触れられました。来週までに予算案を固めます。"


# neg_keywords = ["予算", "費用"]


# result = evaluate_summary(generated_output, neg_keywords, original_doc)


# print(result)

6. 誤り分析

上記評価に基づき、一般的な失敗モードとその原因を分析します。

  1. 否定条件の未遵守(幻覚/脱線):

    • 現象: 生成された要約に、除外すべきキーワードやその関連情報が含まれている。

    • 原因:

      • モデルが否定指示を十分に理解できていない。

      • 否定キーワードが文脈中に深く埋め込まれており、モデルがそれを切り離せない。

      • 「否定の否定」や「間接的な否定」のような複雑な指示を処理しきれない。

      • 要約の簡潔さを優先するあまり、否定条件を見落とす。

    • 例: 「予算」を除外指示したのに、「予算案が承認された」と生成してしまう。

  2. 情報欠落(脱線/様式崩れ):

    • 現象: 否定キーワード以外の重要な情報まで要約から抜け落ちてしまう、または要約が極端に短くなる。

    • 原因:

      • 否定条件を過剰に適用し、関連性のない情報まで除去してしまう。

      • 要約生成の指示よりも否定条件の優先度がモデル内で高くなりすぎる。

    • 例: 「予算」を除外した結果、会議の他の主要な決定事項も抜け落ちてしまう。

  3. 出力フォーマットの不遵守(様式崩れ):

    • 現象: JSON形式が崩れる、指定された段落形式や箇条書き形式ではない出力がされる。

    • 原因:

      • プロンプトの指示が曖昧、または長すぎて見落とされる。

      • モデルの出力安定性が低い。

7. 改良

誤り分析に基づき、プロンプトと評価プロセスを改良します。

  1. プロンプトの明確化と重み付け:

    • 否定指示をプロンプトの冒頭や末尾に繰り返し強調する。

    • STRICTLY, ABSOLUTELY NO, CRUCIALLY などの強調語を使用。

    • CoTプロンプトで否定条件の自己検証ステップを具体的に記述させる。

    • 否定条件と要約品質のトレードオフを明示的に指示(例: 「否定条件を最優先し、その上で要約の品質を維持してください」)。

  2. 少数例の拡充:

    • 難例やコーナーケースに対応するFew-shot例を追加し、モデルに複雑な否定の解釈方法を示す。

    • 間接的な否定や、否定の否定に対応する例を組み込む。

  3. モデルチューニング:

    • 特定の否定パターンに特化したデータセットでファインチューニングを検討する。
  4. ガードレールLLMの導入:

    • 要約生成後、別の小型LLMやルールベースのシステムで否定キーワードの有無をチェックし、違反があればリトライ指示または修正指示をメインLLMに送る。

8. 再評価

改良されたプロンプトと抑制手法を用いて、再度評価シナリオを実行し、改善度合いを測定します。特に、難例とコーナーケースでのスコア上昇とviolationsの減少を重視します。

9. まとめ

Negation Promptingは、LLMの出力制御において極めて重要な技術です。本稿では、明確な入出力契約の定義、多様なプロンプト設計、厳格な評価プロセス、そして体系的な誤り分析と改良を通じて、その実践方法を詳細に示しました。特にChain-of-Thought制約型プロンプトや少数例の活用、そして最終的な出力検証ステップの導入が、LLMによる否定指定の遵守精度を大幅に向上させる鍵となります。今後も、より複雑な否定表現や文脈に応じたNegation Promptingの最適化が求められるでしょう。

プロンプト→モデル→評価→改良のループ

graph TD
    A["要求定義・ユースケース定義"] --> B{"入出力契約定義"};
    B --> C["制約付き仕様化"];
    C --> D("プロンプト設計: ゼロショット/少数例/CoT");
    D --> E["LLMモデル"];
    E --|出力生成| F["出力"];
    F --> G["評価シナリオ実行"];
    G --> H{"自動評価"};
    H --|評価結果| I{"誤り分析"};
    I --|失敗モード/原因| J["改良プロンプト・抑制手法"];
    J --> D;
    H --|合格| K["デプロイ"];

    style A fill:#f9f,stroke:#333,stroke-width:2px;
    style B fill:#f9f,stroke:#333,stroke-width:2px;
    style C fill:#f9f,stroke:#333,stroke-width:2px;
    style D fill:#bbf,stroke:#333,stroke-width:2px;
    style E fill:#bfb,stroke:#333,stroke-width:2px;
    style F fill:#ccc,stroke:#333,stroke-width:2px;
    style G fill:#fbf,stroke:#333,stroke-width:2px;
    style H fill:#fbb,stroke:#333,stroke-width:2px;
    style I fill:#f9f,stroke:#333,stroke-width:2px;
    style J fill:#bbf,stroke:#333,stroke-width:2px;
    style K fill:#afa,stroke:#333,stroke-width:2px;

失敗モードと抑制手法

失敗モード 説明 抑制手法
幻覚 (Hallucination) 除外すべき内容が、モデルの解釈ミスで要約に紛れ込む。 System指示の強化: 「絶対にいかなる状況でもXを含めるな」と強調。
CoT: 否定条件検証ステップを明示的に組み込む。
Few-shot: 否定条件を厳守した具体的な成功例を示す。
後処理検証: 生成後に正規表現やセマンティックチェックで否定キーワードを検出・リトライ。
様式崩れ (Format Break) 出力JSON構造が不正、または要約形式(段落/箇条書き)が守られない。 System指示の強化: 「出力は厳密にこのJSONフォーマットに従うこと」と明記。
Few-shot: 正しいJSONフォーマットの入出力例を示す。
スキーマ検証: 出力JSONをjson.loads()でパースし、スキーマバリデーションを実施。失敗時はリトライまたはエラー通知。
脱線 (Off-topic) 要約が本来の会議内容から逸脱したり、無関係な情報を生成する。 System指示の明確化: 「会議議事録の範囲内でのみ要約を生成せよ」と指示。
CoT: 主要論点抽出ステップを明示的に設け、その範囲で要約を構成させる。
リトライ戦略: 評価ステップで元のドキュメントとの関連性が低いと判断された場合、リトライを促す。
禁止事項の生成 倫理的・法的に不適切な内容、個人情報(PII)を生成する。 System指示の強制: 「不適切・違法・差別的な内容、個人特定情報は一切生成しないこと」と強く指示。
フィルタリング層: LLMの前にPII検出や安全フィルターを配置。出力後にも同様のフィルタリングを実施。
ガードレールLLM: 不適切な出力を検知・修正する。
過剰な情報削除 否定キーワードだけでなく、関連するが除外すべきでない情報まで削除される。 System指示のバランス: 「否定条件を厳守しつつ、他の重要な情報を維持すること」と指示。
Few-shot: 適切な情報が残されたNegationの成功例を示す。
CoT: フィルタリングステップで削除理由を記述させ、過剰削除を自己認識させる。
ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

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