モデルコンテキストプロトコル(MCP)を習得する:AIを外部ツールで強化する

AI tutorial - IT technology blog
AI tutorial - IT technology blog

おそらく、便利なチャットボットや自動コンテンツ生成ツールなど、AIアプリケーションを構築する楽しさを体験したことがあるでしょう。しかし、すぐに共通の課題に直面するかもしれません。それは、AIが閉鎖的な環境で動作しているという点です。

AIはリアルタイムのデータを取得したり、他のシステムでアクションを実行したり、私たちが日常的に使用する膨大な数の外部サービスと連携したりすることはできません。ここで、私がモデルコンテキストプロトコル(MCP)と呼ぶ概念が不可欠になります。これは、AIモデルが言語的なサンドボックスを超えて、現実世界と有意義に関わることを可能にするフレームワークです。

MCPを標準化された手法と考えてください。AIが外部の関数やAPIを理解し、いつ使用するかを決定し、その結果を解釈することを可能にします。異なるAIプロバイダーが「関数呼び出し」や「ツール利用」といった用語を使用するかもしれませんが、核となる原則は一貫しています。それは、利用可能なツールの構造化された説明と、それらを実行するためのオーケストレーションレイヤーをモデルに提供することです。

クイックスタート:AIをシンプルなツールと連携させる (5分)

説明のために、具体的な例を考えてみましょう。大規模言語モデル(LLM)だけではこれはできませんが、適切な「ツール」を提供すれば、この情報を要求することを学習できます。

シンプルなツールの定義

まず、外部ツールを定義します。これは通常、明確な説明とパラメータを持つ関数として表現され、JSON Schemaのようなスキーマを使用することがよくあります。以下に天気取得ツールの例を示します。


# tool_definitions.py

def get_current_weather(location: str, unit: str = "celsius") -> dict:
    """
    指定された場所の現在の天気を取得します。

    引数:
        location (str): 都市と州/国、例: 「San Francisco, CA」または「Paris, France」。
        unit (str, optional): 気温の単位。「celsius」または「fahrenheit」。デフォルトは「celsius」です。

    戻り値:
        dict: 天気情報(気温、状況など)を含む辞書。
    """
    # 実際のアプリケーションでは、これはOpenWeatherMapやAccuWeatherのような外部の天気APIに接続します。
    # このクイックスタートでは、モック応答を返します。
    if "london" in location.lower():
        return {"location": location, "temperature": "10 C", "conditions": "Cloudy"}
    elif "new york" in location.lower():
        return {"location": location, "temperature": "50 F", "conditions": "Partly Cloudy"}
    else:
        # 予測可能な動作を保証するために、未処理の場所に対するデフォルト応答。
        return {"location": location, "temperature": "N/A", "conditions": "Unknown"}

# これはLLMに送信する構造化された定義です。
# ツールの目的とその必須入力について記述しています。
weather_tool_schema = {
    "name": "get_current_weather",
    "description": "指定された場所の現在の天気を取得する",
    "parameters": {
        "type": "object",
        "properties": {
            "location": {
                "type": "string",
                "description": "都市と州、例: San Francisco, CA",
            },
            "unit": {
                "type": "string",
                "enum": ["celsius", "fahrenheit"],
                "description": "気温の単位",
            },
        },
        "required": ["location"],
    },
}

基本的なLLMとの統合

次に、LLMがこのツールを利用するように促す方法を見ていきましょう。この例では、ツール呼び出しをサポートする仮想的なLLM APIを想定しています。基本的な考え方は、ユーザーのプロンプトと利用可能なすべてのツールのスキーマをLLMに提供することです。モデルがツールが必要であると判断した場合、直接的なテキスト応答の代わりに「ツール呼び出し」オブジェクトを返します。


# ai_orchestrator.py
import json
from tool_definitions import get_current_weather, weather_tool_schema

# モックLLM連携: 本番システムでは、これはOpenAI、Anthropic、またはGoogle Geminiのようなプロバイダーへの実際のAPI呼び出しとなります。
class MockLLM:
    def predict_with_tools(self, user_prompt: str, tools: list):
        print(f"LLMがプロンプト「'{user_prompt}'」とツール「{json.dumps(tools)}」を受け取りました")
        # プロンプトに基づいてLLMの意思決定プロセスをシミュレートします。
        if "weather" in user_prompt.lower() and "london" in user_prompt.lower():
            return {
                "tool_calls": [
                    {
                        "name": "get_current_weather",
                        "arguments": {"location": "London, UK", "unit": "celsius"}
                    }
                ]
            }
        elif "weather" in user_prompt.lower() and "new york" in user_prompt.lower():
             return {
                "tool_calls": [
                    {
                        "name": "get_current_weather",
                        "arguments": {"location": "New York, USA", "unit": "fahrenheit"}
                    }
                ]
            }
        else:
            # ツールが不要な場合、LLMは直接的なテキスト応答を提供します。
            return {"text_response": "今はそれをお手伝いできません。"}

llm = MockLLM()

# メインのオーケストレーションループ: このコンポーネントは、ユーザー、LLM、および外部ツール間のやり取りを管理します。
def process_user_query(query: str):
    response = llm.predict_with_tools(query, [weather_tool_schema]) # クエリと利用可能なツールスキーマを送信します。

    if "tool_calls" in response:
        for tool_call in response["tool_calls"]:
            tool_name = tool_call["name"]
            tool_args = tool_call["arguments"]
            print(f"AIはツール「{tool_name}」を引数「{tool_args}」で呼び出そうとしています")

            # ツールを実行します。ここで「モデルコンテキストプロトコル」が起動します。
            if tool_name == "get_current_weather":
                tool_result = get_current_weather(**tool_args)
                print(f"ツール結果: {tool_result}")
                
                # ユーザーフレンドリーな応答を生成するために、ツール結果をLLMにフィードバックします。
                # 実際のAPIでは、これは通常、ツールの出力をコンテキストとして使用する別のLLM呼び出しを伴います。
                print(f"LLMは現在、「{tool_result['location']} の天気は {tool_result['temperature']} で {tool_result['conditions']} です。」と合成するでしょう。")
            else:
                print(f"不明なツール: {tool_name}") # 予期しないツール名が返された場合の処理。
    else:
        print(f"AI応答: {response['text_response']}")

# AIのツール利用能力を示すテストケース。
process_user_query("ロンドンの天気はどうですか?")
process_user_query("ニューヨークの天気について教えてください。")
process_user_query("冗談を教えてください。")

# 上記のPythonスクリプトを実行した場合の予想される出力:
LLMがプロンプト「'ロンドンの天気はどうですか?'」とツール「[{...}]」を受け取りました
AIはツール「get_current_weather」を引数「{'location': 'London, UK', 'unit': 'celsius'}」で呼び出そうとしています
ツール結果: {'location': 'London, UK', 'temperature': '10 C', 'conditions': 'Cloudy'}
LLMは現在、「ロンドン, UK の天気は 10 C で Cloudy です。」と合成するでしょう。
LLMがプロンプト「'ニューヨークの天気について教えてください。'」とツール「[{...}]」を受け取りました
AIはツール「get_current_weather」を引数「{'location': 'New York, USA', 'unit': 'fahrenheit'}」で呼び出そうとしています
ツール結果: {'location': 'New York, USA', 'temperature': '50 F', 'conditions': 'Partly Cloudy'}
LLMは現在、「ニューヨーク, USA の天気は 50 F で Partly Cloudy です。」と合成するでしょう。
LLMがプロンプト「'冗談を教えてください。'」とツール「[{...}]」を受け取りました
AI応答: 今はそれをお手伝いできません。

深掘り:モデルコンテキストプロトコルの理解

MCPの核は、大規模言語モデルの根本的な制約に対処することです。これらのモデルは膨大なデータセットで訓練され、人間のようなテキストを生成するのに優れています。しかし、インターネット、データベースへのリアルタイムアクセスや、物理的なアクションを実行する能力は本質的に欠いています。彼らは現在の天気「を知らず」、フライトを予約したり、顧客関係管理(CRM)レコードを更新したりすることはできません。MCPはこのギャップを効果的に埋めます。

このプロトコルは通常、3つの主要な段階を含み、継続的な対話ループを形成します。

  1. ツール記述とモデルコンテキスト: AIモデルに利用可能なツールのリストを提供します。各ツールには、その機能に関する明確で簡潔な自然言語の説明と、その入力パラメータを定義する構造化されたスキーマ(JSON Schemaなど)が含まれます。モデルはこれらの記述をその運用コンテキストに統合します。
  2. モデルの決定とツール呼び出しの生成: ユーザーがクエリを送信すると、AIモデルはそれを処理します。要求を満たすために外部アクションが必要であると判断した場合、直接的なテキスト応答を生成しません。代わりに、「ツール呼び出し」オブジェクトを生成します。このオブジェクトは、ユーザーの意図とツールの機能に対するモデルの理解に基づいて、どのツールを使用するか、およびそのツールに渡す正確な引数を正確に指定します。
  3. オーケストレーションと結果の統合: アプリケーションは「オーケストレーター」として機能し、このツール呼び出しを傍受します。その後、指定されたツールを提供された引数で実行します。ツールが結果を返すと(多くの場合、テキスト形式の表現にフォーマットされます)、この新しい情報はAIモデルにフィードバックされます。モデルはこのコンテキストを活用して、ユーザーに対して最終的で首尾一貫した役立つ応答を生成します。

この動的なサイクルにより、AIアプリケーションは真に強力で適応性の高いものになります。私の経験では、このプロトコルを習得することは、理論的なAIコンセプトから実用的で本番環境に対応したシステムを構築する上で非常に重要です。MCPがなければ、AIは主に静的な知識ベースのままですが、MCPがあれば、AIはデジタルエコシステム全体と相互作用できるアクティブなエージェントへと変貌します。

高度な利用法:複雑なワークフローのオーケストレーション

基本を把握すれば、MCPはさらに複雑なシナリオを管理するために拡張でき、AIエージェントが多段階の問題を効果的に解決することを可能にします。

ツールの連携と条件ロジック

多くの場合、1つのユーザーリクエストに複数のツール呼び出しが必要です。たとえば、複数区間の旅行を計画するために、AIはまず利用可能なフライトを見つけるツールを必要とするかもしれません。次に、それらのフライト日に基づいてホテルオプションを検索する別のツールを使用するかもしれません。最後に、全体的な旅行費用を推定するために3番目のツールを利用する可能性があります。このプロセスには、あるツールの出力が次のツールの入力として機能する連続的なツール呼び出しが含まれます。


# フライト情報を取得するためのツールの例(説明のために簡略化)
flight_search_tool_schema = {
    "name": "find_flights",
    "description": "特定の日に出発地と目的地間の利用可能なフライトを検索します。",
    "parameters": {
        "type": "object",
        "properties": {
            "origin": {"type": "string", "description": "出発空港コード、例: 'NYC'"},
            "destination": {"type": "string", "description": "到着空港コード、例: 'LAX'"},
            "date": {"type": "string", "format": "date", "description": "YYYY-MM-DD形式の旅行日"}
        },
        "required": ["origin", "destination", "date"]
    }
}

def find_flights(origin: str, destination: str, date: str) -> dict:
    # モックのフライトデータ。実際のシナリオでは、これはSkyscannerやGoogle Flightsのようなフライト予約APIをクエリします。
    if origin == "NYC" and destination == "LAX" and date == "2024-07-20":
        return {"flights": [{"flight_number": "AA123", "price": "$300"}]}
    return {"flights": []}

# 天気とフライト検索を組み合わせる、連携ロジックを示すオーケストレーター。
def process_travel_query(query: str):
    # まず、LLMはフライト検索が必要かどうかを判断します。
    response1 = llm.predict_with_tools(query, [flight_search_tool_schema, weather_tool_schema]) # 関連するすべてのツールを渡します。

    if "tool_calls" in response1:
        flight_call = response1["tool_calls"][0] # このステップでは1つのツール呼び出しのみを想定。
        if flight_call["name"] == "find_flights":
            flight_result = find_flights(**flight_call["arguments"])
            print(f"フライトツール結果: {flight_result}")

            if flight_result["flights"]:
                # フライトが見つかった場合、システムは目的地の天気についてLLMに問い合わせます。
                destination_city = flight_call['arguments']['destination'] # フライト引数から目的地を抽出。
                travel_date = flight_call['arguments']['date']
                weather_query = f"{destination_city} の {travel_date} の天気はどうですか?"
                
                response2 = llm.predict_with_tools(weather_query, [weather_tool_schema]) # 天気のための2回目のLLM呼び出し。
                if "tool_calls" in response2:
                    weather_call = response2["tool_calls"][0]
                    if weather_call["name"] == "get_current_weather":
                        weather_result = get_current_weather(**weather_call["arguments"])
                        print(f"天気ツール結果: {weather_result}")
                        print(f"LLMはその後、「{flight_call['arguments']['origin']} から {flight_call['arguments']['destination']} への {travel_date} のフライト: {flight_result['flights'][0]['flight_number']} で {flight_result['flights'][0]['price']}。天気は {weather_result['conditions']} でしょう。」と組み合わせるでしょう。")
            else:
                print("クエリに一致するフライトは見つかりませんでした。")
    else:
        print("LLMはこのクエリに対してフライト検索ツールを識別しませんでした。")

# 使用例: この特定の高度なクエリをテストするにはコメントを解除してください。
# process_travel_query("2024年7月20日にNYCからLAXへのフライトを見つけて、そこの天気を教えてください。")

条件ロジックも不可欠です。たとえば、フライトが利用可能であれば、システムはホテルの価格を確認に進むかもしれませんが、利用できない場合は、利用できないことをユーザーに通知します。これは、オーケストレーションレイヤー内での細心の状態管理を必要とします。

エラーハンドリングとレジリエンス

外部ツールは、他のソフトウェアコンポーネントと同様に問題に遭遇する可能性があります。APIが一時的にオフラインになったり、レート制限を超過したり、誤ったパラメータが提供されたりするかもしれません。堅牢なMCPの実装には、以下を組み込む必要があります。

  • 入力検証: ツールを呼び出す前に、LLMによって提供された引数をツールの定義されたスキーマと照合して厳密に検証します。これにより、外部システムに不正な形式のリクエストが届くのを防ぎます。
  • 指数バックオフによるリトライ: 一時的なAPIエラーを再試行するためのメカニズムを実装し、試行間に徐々に長い間隔を置きます。これは、一時的なネットワークの不具合やサービスの中断を克服するのに役立ちます。
  • フォールバックメカニズム: 主要なツールが永続的に失敗した場合、代替ツールに gracefully 切り替えることはできますか?または、少なくとも、AIはユーザーに役立つ丁寧なフォールバックメッセージを提供できますか?
  • タイムアウト: 外部呼び出しに適切なタイムアウトを設定します。これにより、外部サービスが応答しなくなった場合に、アプリケーションが無期限にハングアップするのを防ぎます。

非同期ツール呼び出し

長時間のデータ処理ジョブや複雑なAPI呼び出しなど、一部のツールは完了までにかなりの時間を要する場合があります。これらのシナリオでは、非同期実行を検討してください。オーケストレーターはツール呼び出しを開始し、プロセスが開始されたことをユーザーに即座に通知し、結果が利用可能になったときに(Webhooks、メッセージキュー、ポーリングメカニズムなどを介して)処理できます。

効果的なMCP実装のための実践的なヒント

MCPを慎重に実装することで、AIアプリケーションの機能は劇的に向上します。以下に、実績のある戦略をいくつか紹介します。

明確なツール定義が最も重要

AIのツール利用の有効性は、ツール記述の品質に直接関係します。以下を確実に提供してください。

  • 正確な名前: 分かりやすい名前を選択してください。たとえば、get_weather_forecastweather_functionよりも情報量が多いです。
  • 詳細な説明: ツールが何をするかどのように機能するか、そして最も重要ないつ呼び出されるべきかを明確に説明してください。この明確さは、LLMが正確な決定を下すために不可欠です。
  • 正確なスキーマ: パラメータ名、データ型、および説明が正確で曖昧でないことを保証してください。パラメータが限られた値のセット(enum)を受け入れる場合は、スキーマ内でこれらのオプションを明示的に指定してください。

包括的な監視とロギング

AIの意思決定プロセスを真に理解しデバッグするためには、堅牢な可観測性が不可欠です。次の主要なやり取りをログに記録してください。

  • 受信したユーザーのクエリ。
  • LLMによって生成されたツール呼び出し(特定のツール名とその引数を含む)。
  • 外部ツールによって返された結果。
  • ユーザーに配信された最終的なAI応答。

この詳細なロギングは、デバッグ、微妙なユーザーの意図の理解、およびツール定義とオーケストレーションロジックの継続的な改善にとって非常に貴重な洞察を提供します。

セキュリティの考慮事項:最優先事項

AIを外部システムに接続する場合、セキュリティは決して後回しにしてはなりません。これらのベストプラクティスを実装してください。

  • APIキー管理: APIキーをコードに直接埋め込まないでください。代わりに、安全な環境変数、専用のシークレット管理サービス(例: AWS Secrets Manager、HashiCorp Vault)、またはクラウドキー管理ソリューションを使用してください。
  • 最小権限の原則: ツールおよびそれらがアクセスするサービスが、意図された機能を実行するために絶対的に必要な最小限の権限のみを持つことを確認してください。
  • 入力サニタイズ: LLMから受け取った入力を外部システムに渡す前に、常に厳密にサニタイズし、検証してください。これはインジェクション攻撃に対する重要な防御であり、予期しないまたは悪意のある動作を防ぎます。
  • レート制限: 外部サービス呼び出しに堅牢なレート制限を実装してください。これにより、偶発的な(バグによる)または悪意のある(サービス拒否攻撃)過剰なリクエストによってAPIが圧倒されるのを防ぎます。

反復的な開発:小さく始め、賢く成長する

反復的なアプローチを採用してください。まず1つまたは2つのツールを実装し、それらを徹底的にテストし、AIがそれらとどのように相互作用するかを注意深く観察してください。自信と理解が深まるにつれて、徐々に複雑さと追加のツールを導入してください。最初から何十ものツールを持つ汎用エージェントを構築しようとする落とし穴を避けてください。

適切なLLMプロバイダーの選択

異なるLLMプロバイダー(例: OpenAI、Anthropic、Google)は、異なるレベルの洗練度と、ツール利用(多くの場合「関数呼び出し」と呼ばれる)のためのわずかに異なるインターフェースを提供します。彼らの特定のドキュメントと機能をよく理解してください。MCPの核となる概念は一貫していますが、正確な実装の詳細はプラットフォーム間で自然に異なります。

モデルコンテキストプロトコルを習得することで、AIは単なる会話パートナーから、現実世界のタスクを実行できるアクティブで有能な参加者へと変革されます。これは、顧客サービスの強化からDevOpsワークフローの最適化、さらにはそれ beyond まで、あらゆる場面でAI自動化とインテリジェントエージェントの真の可能性を解き放つメカニズムです。

Share: