🐤

PythonとOpenAI APIで実践!はじめてのMCP開発入門【第6回】コンテキスト情報」のJSON設計パターンとPythonでの実装

に公開

はじめに:プロンプトの限界を超える「構造化コンテキスト」という武器

皆さん、こんにちは!AI開発の冒険、第6回です。前回(第5回)は、プロンプトエンジニアリングの基本として、AIに「伝わる」指示の出し方や、ゼロショット・ワンショット・フューショットといったテクニックを学びました。効果的なプロンプトは、AIの応答品質を劇的に向上させる「魔法の呪文」のようでしたね。

しかし、私たちがAIに伝えたい「背景情報」や「状況」は、時としてプロンプト内に直接記述するには複雑すぎたり、長大すぎたりします。例えば、ユーザーの詳細なプロフィール、過去の長い会話履歴、参照すべき外部ドキュメントの内容などです。これらを毎回プロンプトに含めるのは非効率的ですし、トークン数の観点からも現実的ではありません。

ここで登場するのが、本シリーズの核となる**「モデルコンテキストプロトコル(MCP)」の考え方、すなわち「構造化されたコンテキスト情報を設計し、AIに効果的に提供する仕組み」**です。そして、その情報をAIとやり取りするための共通言語として、JSON (JavaScript Object Notation) が極めて強力なツールとなります。

今回は、AIの理解を飛躍的に深めるためのコンテキスト情報のJSON設計パターンと、それをPythonのデータ構造(辞書やリスト)を使ってどのように構築し、OpenAI APIコールに組み込むか、具体的なコード例を交えながら徹底解説します。この「構造化コンテキスト」という武器を手にすることで、あなたのAIアプリケーションは、より高度で、パーソナルで、そしてインテリジェントなものへと進化するでしょう。(ここ東京の活発なテックコミュニティでも、このようなデータ連携とAIの高度化は注目の的です!)

なぜ「構造化されたコンテキスト」が重要なのか?

シンプルなプロンプトだけでは、AIは以下のような限界に直面しがちです。

  • 一貫性の欠如
    • 長い対話や複雑なタスクにおいて、AIが以前の情報を忘れたり、矛盾した応答をしたりする。
  • パーソナライゼーションの限界
    • 個々のユーザーの特性や好みを深く反映した応答が難しい。
  • 外部知識の参照困難
    • プロンプトに含めきれない大量のドキュメントやリアルタイム情報を活用できない。
  • 指示の曖昧性
    • AIが状況を正確に把握できないため、こちらの意図を誤解しやすい。

構造化されたコンテキスト情報をMCPの考え方に沿って提供することで、これらの課題を克服し、AIに以下のような能力を与えることができます。

  • 状態の維持(ステートフル性)
    • 会話の履歴や作業の進捗を記憶し、一貫したインタラクションを実現。
  • 深いパーソナライズ
    • ユーザーの属性、過去の行動、明示的な好みを理解し、応答を最適化。
  • 外部知識の活用
    • 関連ドキュメントの要約や、特定のデータセットの内容を応答に反映(Retrieval Augmented Generation - RAG のコンセプトにも繋がります)。
  • 明確なタスク遂行
    • より複雑な指示や制約条件を正確に理解し、タスクを実行。

JSON: AIとのコンテキストコミュニケーションにおける「世界共通語」

前回までの記事でも触れましたが、JSONはAIとのデータ交換において「デファクトスタンダード(事実上の標準)」と言える形式です。

構造

  • キー: 値 のペアを基本とし、{}(オブジェクト、Pythonの辞書に相当)と [](配列、Pythonのリストに相当)を組み合わせて階層的なデータ構造を表現できます。

利点

  • 人間にとって可読性が高い (Human-readable)
    • 構造がシンプルで理解しやすい。
  • 機械にとって解析しやすい (Machine-parseable)
    • 多くのプログラミング言語で簡単に扱える。
  • 軽量 (Lightweight)
    • データ転送量が比較的少なく、API通信に適している。
  • 柔軟性 (Flexible)
    • 様々な種類のデータを表現できる。
      このJSON形式を使い、AIに提供するコンテキスト情報をどのように「設計」するかが、MCP実践の鍵となります。

コンテキストJSON設計パターン:AIの「理解」を助ける4つの型

ここでは、代表的なコンテキスト情報の種類と、それぞれのJSON設計パターン、そして対応するPythonの辞書/リスト構造の例を見ていきましょう。

A. ユーザープロファイル・コンテキスト (User Profile Context)

目的

AIにユーザーの基本的な属性、好み、スキルレベルなどを伝え、応答をパーソナライズする。

JSON設計例


{
  "user_profile": {
    "user_id": "qiita_dev_001",
    "name_display": "TechExplorer2025",
    "language_preference": "ja-JP",
    "technical_expertise": {
      "python": "intermediate",
      "javascript": "beginner",
      "cloud_infra": "intermediate",
      "ai_ml_concepts": "beginner"
    },
    "preferred_topics": ["LLM", "API開発", "サーバーレス", "セキュリティ"],
    "learning_goal": "OpenAI APIを使った実用的なアプリケーション開発スキルの習得",
    "response_style_preference": "technical_yet_easy_to_understand"
  }
}

Pythonでの表現 (辞書)


user_profile_context_py = {
    "user_profile": {
        "user_id": "qiita_dev_001",
        "name_display": "TechExplorer2025",
        "language_preference": "ja-JP",
        "technical_expertise": {
            "python": "intermediate",
            "javascript": "beginner",
            "cloud_infra": "intermediate",
            "ai_ml_concepts": "beginner"
        },
        "preferred_topics": ["LLM", "API開発", "サーバーレス", "セキュリティ"],
        "learning_goal": "OpenAI APIを使った実用的なアプリケーション開発スキルの習得",
        "response_style_preference": "technical_yet_easy_to_understand"
    }
}

B. 会話履歴コンテキスト (Conversation History Context)

目的

AIに過去の対話の流れを記憶させ、文脈に沿った自然な応答を継続させる。OpenAIのChat Completions APIのmessagesパラメータの形式そのものです。

JSON設計例 (OpenAI messages 形式)


{
  "conversation_history": [
    {"role": "user", "content": "OpenAI APIでチャットボットを作りたいんだけど、何から始めるべき?"},
    {"role": "assistant", "content": "素晴らしいですね!まずは、どのような機能を持つチャットボットを想定していますか?例えば、特定のトピックに詳しい専門家風ですか、それともフレンドリーな相談相手風ですか?"},
    {"role": "user", "content": "Pythonの技術的な質問に答えてくれる専門家風がいいな。"}
  ]
}

Pythonでの表現 (リストと辞書の組み合わせ)


conversation_history_context_py = { # 外側のキーは任意
    "conversation_history": [
        {"role": "user", "content": "OpenAI APIでチャットボットを作りたいんだけど、何から始めるべき?"},
        {"role": "assistant", "content": "素晴らしいですね!まずは、どのような機能を持つチャットボットを想定していますか?例えば、特定のトピックに詳しい専門家風ですか、それともフレンドリーな相談相手風ですか?"},
        {"role": "user", "content": "Pythonの技術的な質問に答えてくれる専門家風がいいな。"}
    ]
}

C. 外部ドキュメント/データ・コンテキスト (External Document/Data Context)

目的

AIに特定の文書、記事、データベースの抜粋などを参照させ、それに基づいて質問に答えさせたり、要約させたりする(Retrieval Augmented Generation - RAG の基礎)。

JSON設計例


{
  "external_document_context": {
    "document_id": "MCP_Spec_v1.2",
    "document_title": "モデルコンテキストプロトコル仕様書 Ver.1.2",
    "source_url": "https://example.com/mcp_spec_v1.2.pdf",
    "relevant_sections": [
      {
        "section_id": "3.1.2",
        "section_title": "コンテキストデータのJSONスキーマ定義",
        "content_snippet": "コンテキストデータは、user_profile、session_state、external_knowledgeの3つの主要キーを持つJSONオブジェクトとして定義される..."
      },
      {
        "section_id": "4.2",
        "section_title": "APIエンドポイント仕様",
        "content_snippet": "/v1/mcp/process エンドポイントは、HTTP POSTリクエストを受け付け、コンテキストを含むJSONペイロードを必須とする..."
      }
    ],
    "query_relevance_score": 0.85 // このドキュメントが現在の質問にどれだけ関連性が高いかのスコア (例)
  }
}

Pythonでの表現 (辞書とリストの組み合わせ)


# (上記JSONに対応するPython辞書構造は同様に記述可能)

D. タスク固有パラメータ/制約コンテキスト (Task-Specific Parameters/Constraints Context)

目的

AIが生成する応答の形式、長さ、スタイル、遵守すべきルールなどを細かく指示する。

JSON設計例


{
  "task_directives": {
    "output_language": "ja",
    "output_format_requested": "markdown_table", // table, json, bullet_pointsなど
    "max_output_sentences": 5,
    "tone_and_style": "formal_and_concise", // formal, casual, humorousなど
    "constraints_to_follow": [
      "個人情報は一切含めないこと。",
      "専門用語には簡単な注釈を付けること。",
      "結論を最初に述べ、その後に理由を3点挙げること。"
    ],
    "target_audience_profile": "python_developer_intermediate"
  }
}

Pythonでの表現 (辞書とリストの組み合わせ)


# (上記JSONに対応するPython辞書構造は同様に記述可能)

PythonでJSONコンテキストをOpenAI APIコールに組み込む実践

設計したコンテキスト情報を、実際にOpenAI API(今回はgpt-3.5-turboまたはgpt-4oのようなチャットモデルを想定)に渡す方法を見ていきましょう。


# (第3回で作成した .env ファイルと APIキー設定、第5回の get_ai_response 関数の基礎が前提)
import os
import json # JSON文字列の操作に使う
from dotenv import load_dotenv
from openai import OpenAI

# .envとクライアント初期化 (エラー処理は簡略化)
load_dotenv()
client = OpenAI()

def execute_mcp_call(user_prompt, context_data_py, model="gpt-3.5-turbo", max_tokens=500, temperature=0.5):
    """MCPに基づいたコンテキストデータをシステムメッセージに含めてAPIコールを実行する関数"""
    
    # Pythonの辞書であるコンテキストデータをJSON文字列に変換 (整形して可読性UP)
    # ensure_ascii=False で日本語がエスケープされないようにする
    context_json_string = json.dumps(context_data_py, ensure_ascii=False, indent=2)
    
    system_message_content = f"""
あなたは高度なAIアシスタントです。以下のコンテキスト情報を正確に理解し、ユーザーのリクエストに応答してください。
コンテキスト情報はJSON形式で提供されます。

<context_information>
{context_json_string}
</context_information>

上記のコンテキストを踏まえ、ユーザーの指示に従ってください。
"""
    
    messages = [
        {"role": "system", "content": system_message_content},
        {"role": "user", "content": user_prompt}
    ]
    
    try:
        print(f"\n--- システムメッセージ (一部抜粋) ---\n{system_message_content[:300]}...\n--- ユーザープロンプト ---\nUser: {user_prompt}")
        
        completion = client.chat.completions.create(
            model=model,
            messages=messages,
            max_tokens=max_tokens,
            temperature=temperature
        )
        ai_response = completion.choices[0].message.content
        print(f"--- AIからの応答 ---\n{ai_response}")
        print(f"(使用トークン数 - Prompt: {completion.usage.prompt_tokens}, Completion: {completion.usage.completion_tokens}, Total: {completion.usage.total_tokens})")
        return ai_response
    except Exception as e:
        print(f"APIリクエストエラー: {e}")
        return None

# --- MCPコール実行例 ---
# 例1: ユーザープロファイルとタスク指示をコンテキストとして渡す
user_profile_for_code_gen = {
    "user_profile": {
        "user_id": "coder_qiita_007",
        "programming_languages": ["Python", "JavaScript"],
        "experience_level": "3 years",
        "current_project_type": "Web API development"
    }
}
task_directives_for_code_gen = {
    "task_directives": {
        "target_language": "Python",
        "framework_preference": "FastAPI",
        "request_type": "simple data validation function",
        "output_format": "complete python code block with type hints"
    }
}

# 複数のコンテキスト情報を一つのJSONにまとめる
combined_context = {
    "user_info": user_profile_for_code_gen,
    "task_details": task_directives_for_code_gen
}

user_query = "ユーザー名(username)とメールアドレス(email)を受け取り、usernameが英数字のみで6文字以上、emailが正しい形式かバリデーションするFastAPI用のPydanticモデルを使ったPython関数を生成してください。"

print("\n=== MCPコール実行例:コード生成 ===")
execute_mcp_call(user_query, combined_context, model="gpt-4o", max_tokens=1000) # GPT-4oなど高性能モデル推奨

# 会話履歴は、上記のmessagesリストに直接過去のやり取りを追加していくのがOpenAIの標準的な方法です。
# その場合、system_message_contentに他の固定的なコンテキスト(ユーザープロファイルなど)を入れ、
# messagesリストに {"role": "user", ...}, {"role": "assistant", ...} の履歴と最後のユーザープロンプトを追加します。

この例では、システムメッセージ内に、設計したコンテキスト情報(Pythonの辞書をjson.dumps()でJSON文字列に変換したもの)を埋め込んでいます。ensure_ascii=Falseは日本語が\uXXXXのようにエスケープされるのを防ぎ、indent=2はJSON文字列を人間が読みやすいように整形(インデント付け)します。
AIは、このシステムメッセージで提供された広範なコンテキスト情報と、ユーザーメッセージで与えられた具体的な指示の両方を考慮して応答を生成します。

注意ポイント

  • APIコールにたいs以下のようなエラーが返る場合は、gpt-3.5-turbo または gpt-4o が利用できない場合があるので、OpenAI側のBillingの設定を確認してください。
  • gpt-3.5-turbogpt-4o を想定していますが、 gpt-4o-mini に変更してレスポンスが返ってくることをご確認いただくことは可能です。
APIリクエストエラー: Error code: 429 - {'error': {'message': 'You exceeded your current quota, please check your plan and billing details. For more information on this error, read the docs: https://platform.openai.com/docs/guides/error-codes/api-errors.', 'type': 'insufficient_quota', 'param': None, 'code': 'insufficient_quota'}}

コンテキストの量とトークン数、そしてコスト:賢いバランス感覚

重要な注意点として、APIに送信するコンテキスト情報もすべてトークンとしてカウントされ、API利用料金に影響します。 特に、長い会話履歴や大きなドキュメントをコンテキストとして含めると、プロンプトトークン数が大幅に増加する可能性があります。

戦略

  1. 情報の取捨選択
    本当に必要なコンテキスト情報だけを選別する。
  2. 要約
    長いドキュメントや会話履歴は、AIを使って事前に要約し、その要約をコンテキストとして利用する。
  3. チャンキング
    非常に大きな情報は、いくつかの塊(チャンク)に分割し、段階的に処理する。
  4. ベクトルデータベースとRAG(概念)
    大量の外部知識を参照する場合、全ての情報をプロンプトに含めるのではなく、ユーザーの質問に関連性の高い部分だけをベクトル検索で動的に抽出し、それをコンテキストとしてAIに与えるRetrieval Augmented Generation (RAG) という高度な手法があります。これは今後のAIアプリケーション開発の主流の一つとなるでしょう。(詳細は専門的なトピックになりますが、概念として知っておくと良いでしょう。)
    常に、提供するコンテキストの「質」と「量(トークン数)」のバランスを意識することが、効果的かつ経済的なAIアプリケーション開発の鍵となります。

おわりに:「構造化コンテキスト」でAIとの対話をネクストレベルへ

今回は、MCPの核心技術として、AIの理解を深めるための「コンテキスト情報」をJSON形式で設計する具体的なパターンと、それをPythonコードでOpenAI APIコールに組み込む方法を、技術的な詳細と共に解説しました。

ユーザープロファイル、会話履歴、外部ドキュメント、タスク指示といった多様な情報を、構造化されたJSONとして設計し、システムメッセージなどを通じてAIに提供することで、AIはより深い文脈理解に基づいた、的確でパーソナルな応答を返すことが可能になります。

この「構造化コンテキスト」というアプローチは、あなたのAIアプリケーションを、単なる「おもちゃ」から、真に「使えるツール」へと進化させるための強力な武器となるはずです。Qiitaコミュニティの皆さんも、ぜひ独自のコンテキスト設計パターンを考案し、共有してみてください。

次回予告

コンテキスト情報の設計とAPIへの渡し方が分かりました。では、これらを組み合わせて、より実践的なアプリケーションを構築するステップを見ていきましょう!
次回、第7回「Pythonコードで実践MCP - 構造化コンテキストを付与してOpenAI APIを呼び出すテクニック(総合演習)」では、これまで学んだプロンプト技術とコンテキスト設計を組み合わせ、具体的なシナリオに基づいたPythonプログラムを作成し、MCPの考え方を実際にコードで体現する総合的な演習を行います。お楽しみに!

Discussion