ai-agents-for-beginners

優れたAIエージェントを設計する方法

(上の画像をクリックすると、このレッスンのビデオが表示されます)

ツール使用デザインパターン

ツールは、AIエージェントにより広範な能力を持たせることができるため、興味深いものです。エージェントが実行できるアクションが限られている代わりに、ツールを追加することで、エージェントは幅広いアクションを実行できるようになります。この章では、AIエージェントが特定のツールを利用して目標を達成する方法を説明するツール使用デザインパターンについて見ていきます。

はじめに

このレッスンでは以下の質問に答えることを目指します:

学習目標

このレッスンを修了すると、以下ができるようになります:

ツール使用デザインパターンとは何か?

ツール使用デザインパターンは、LLMに外部ツールと連携して特定の目標を達成する能力を与えることに焦点を当てています。ツールとは、エージェントが実行できるアクションを行うために用いるコードのことです。ツールは、計算機のような単純な関数であったり、株価照会や天気予報のようなサードパーティサービスのAPI呼び出しであったりします。AIエージェントの文脈では、ツールはモデルが生成した関数呼び出しに応じてエージェントにより実行されるよう設計されています。

適用できるユースケースは何か?

AIエージェントはツールを活用して複雑なタスクの完遂、情報の取得、意思決定を行うことができます。ツール使用デザインパターンは、データベース、ウェブサービス、コードインタプリタなどの外部システムと動的にやり取りする必要があるシナリオでよく使われます。この能力は、以下の多様なユースケースで有用です:

ツール使用デザインパターンを実装するために必要な要素/構成要素は何か?

これらの構成要素により、AIエージェントは幅広いタスクを実行できます。ツール使用デザインパターンを実装するために必要な主要な要素を見てみましょう:

次に、関数/ツール呼び出しについて詳しく見てみましょう。

関数/ツール呼び出し

関数呼び出しは、大規模言語モデル(LLM)がツールと連携するための主な方法です。『関数』と『ツール』はしばしば同義で使われます。なぜなら、『関数』は再利用可能なコードのブロックであり、エージェントがタスクに用いる『ツール』だからです。関数のコードを呼び出すには、LLMがユーザーのリクエストを関数の説明と照合する必要があります。このために、利用可能な全関数の説明を含むスキーマがLLMに送られます。LLMはタスクに最適な関数を選択して、その関数名と引数を返します。選ばれた関数が呼び出され、その応答がLLMに戻され、それをもとにユーザーへの応答が生成されます。

エージェントの関数呼び出しを実装する開発者に必要なものは以下の通りです:

  1. 関数呼び出しをサポートするLLMモデル
  2. 関数の説明を含むスキーマ
  3. 各関数のコード

例として都市の現在時刻を取得する方法を説明しましょう:

  1. 関数呼び出しをサポートするLLMを初期化する:

    すべてのモデルが関数呼び出しをサポートしているわけではないため、利用しているLLMが対応しているか確認が重要です。Azure OpenAIは関数呼び出しをサポートしています。Azure OpenAIのResponses API(安定版の/openai/v1/エンドポイント、api_versionは不要)に対してOpenAIクライアントを初期化して始めることができます。

     # Initialize the OpenAI client for Azure OpenAI (Responses API, v1 endpoint)
     client = OpenAI(
         base_url=f"{os.environ['AZURE_OPENAI_ENDPOINT'].rstrip('/')}/openai/v1/",
         api_key=os.environ["AZURE_OPENAI_API_KEY"],
     )
     deployment_name = os.environ["AZURE_OPENAI_DEPLOYMENT"]
    
  2. 関数スキーマを作成する:

    次に、関数名、関数の役割、関数パラメーターの名前と説明を含むJSONスキーマを定義します。 そのスキーマを先に作成したクライアントに、ユーザーの「サンフランシスコの時間を知りたい」というリクエストとともに渡します。重要なのは、ツール呼び出しが返されることであり、質問への最終回答ではないことです。先述の通り、LLMはタスクのために選択した関数名と引数を返します。

     # Function description for the model to read (Responses API flat tool format)
     tools = [
         {
             "type": "function",
             "name": "get_current_time",
             "description": "Get the current time in a given location",
             "parameters": {
                 "type": "object",
                 "properties": {
                     "location": {
                         "type": "string",
                         "description": "The city name, e.g. San Francisco",
                     },
                 },
                 "required": ["location"],
             },
         }
     ]
    
      
     # Initial user message
     messages = [{"role": "user", "content": "What's the current time in San Francisco"}]
    
     # First API call: Ask the model to use the function
     response = client.responses.create(
         model=deployment_name,
         input=messages,
         tools=tools,
         tool_choice="auto",
         store=False,
     )
    
     # The Responses API returns tool calls as function_call items in response.output.
     # Append them to the conversation so the model has full context on the next turn.
     messages += response.output
    
     print("Model's response:")
     print(response.output)
      
    
     Model's response:
     [ResponseFunctionToolCall(arguments='{"location":"San Francisco"}', call_id='call_pOsKdUlqvdyttYB67MOj434b', name='get_current_time', type='function_call')]
    
  3. タスクを実行するための関数コード:

    LLMがどの関数を実行するか決めたら、そのタスクを実行するためのコードを実装し呼び出す必要があります。 Pythonで現在時刻を取得するコードを実装します。また、レスポンスメッセージから関数名と引数を抽出して最終結果を得るコードも必要です。

       def get_current_time(location):
         """Get the current time for a given location"""
         print(f"get_current_time called with location: {location}")  
         location_lower = location.lower()
            
         for key, timezone in TIMEZONE_DATA.items():
             if key in location_lower:
                 print(f"Timezone found for {key}")  
                 current_time = datetime.now(ZoneInfo(timezone)).strftime("%I:%M %p")
                 return json.dumps({
                     "location": location,
                     "current_time": current_time
                 })
          
         print(f"No timezone data found for {location_lower}")  
         return json.dumps({"location": location, "current_time": "unknown"})
    
     # Handle function calls
     tool_calls = [item for item in response.output if item.type == "function_call"]
     if tool_calls:
         for tool_call in tool_calls:
             if tool_call.name == "get_current_time":
    
                 function_args = json.loads(tool_call.arguments)
    
                 time_response = get_current_time(
                     location=function_args.get("location")
                 )
    
                 # Return the tool result as a function_call_output item
                 messages.append({
                     "type": "function_call_output",
                     "call_id": tool_call.call_id,
                     "output": time_response,
                 })
     else:
         print("No tool calls were made by the model.")
    
     # Second API call: Get the final response from the model
     final_response = client.responses.create(
         model=deployment_name,
         input=messages,
         tools=tools,
         store=False,
     )
    
     return final_response.output_text
    
       get_current_time called with location: San Francisco
       Timezone found for san francisco
       The current time in San Francisco is 09:24 AM.
    

関数呼び出しはほとんど、あるいはすべてのエージェントのツール使用設計の根幹ですが、ゼロから実装するのは時に難しい場合があります。 レッスン 2で学んだように、エージェントフレームワークはツール使用の実装に役立つ既成の構成要素を提供します。

エージェントフレームワークを使ったツール使用の例

以下は、さまざまなエージェントフレームワークを利用してツール使用デザインパターンを実装する例です:

Microsoft Agent Framework

Microsoft Agent FrameworkはAIエージェントを構築するためのオープンソースフレームワークです。@toolデコレーターを使ってツールをPython関数として定義することで、関数呼び出しのプロセスを簡素化します。このフレームワークはモデルとコード間の双方向通信を処理し、FoundryChatClientを通じてファイル検索やコードインタプリタなどの既成ツールにもアクセス可能です。

以下の図はMicrosoft Agent Frameworkでの関数呼び出しの流れを示しています:

function calling

Microsoft Agent Frameworkでは、ツールはデコレートされた関数として定義されます。先に示したget_current_time関数を@toolデコレーターでラップしてツールに変換できます。フレームワークは関数とパラメーターを自動的にシリアライズし、LLMに送るスキーマを生成します。

import os
from agent_framework import tool
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential

@tool(approval_mode="never_require")
def get_current_time(location: str) -> str:
    """Get the current time for a given location"""
    ...

# Create the client
provider = FoundryChatClient(
    project_endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
    model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"],
    credential=AzureCliCredential(),
)

# Create an agent and run with the tool
agent = provider.as_agent(name="TimeAgent", instructions="Use available tools to answer questions.", tools=get_current_time)
response = await agent.run("What time is it?")

MCPサーバーからのツール

ツールは自分のコードの関数である必要はありません。モデルコンテキストプロトコル(MCP、レッスン11で解説)対応のサーバーはツールとスキーマを公開し、フレームワークはデコレート関数のようにシリアライズして読み込めます。MCPStreamableHTTPToolはHTTP経由でそのようなサーバーに接続し、ツールスキーマを読み取り、ローカルツールと共にモデルに提供します。

以下の例はKeenableがホストするウェブ検索サーバーを使用しています。アカウントやAPIキー不要で無料で利用でき(IPごとにリクエスト制限あり)、モデルの認証情報のみ必要です。リモートツールのため、モデルが生成するクエリはサーバーへ送られます。

from agent_framework import MCPStreamableHTTPTool

web_search = MCPStreamableHTTPTool(
    name="Keenable web search",
    url="https://api.keenable.ai/mcp",
)

# Connecting loads the server's tool schemas; the agent decides when to call them
async with web_search:
    agent = provider.as_agent(
        name="ResearchAgent",
        instructions="Use available tools to answer questions.",
        tools=[web_search],
    )
    response = await agent.run("What is new in the latest Python release?")
    print(response.text)

サーバーはsearch_web_pagesとfetch_page_contentという2つのツールを公開しています。最新情報が必要な質問にはモデルがsearch_web_pagesを呼び出し、タイトル、URL、テキストスニペットを受け取り、回答を作成します。先述のget_current_timeの例と同様のループです。

Microsoft Foundry Agent Service

Microsoft Foundry Agent Serviceは、開発者が基盤の計算資源・ストレージ管理を気にせずに、安全に質の高い拡張可能なAIエージェントを構築・展開・スケールできるよう設計された最新のエージェントフレームワークです。特にエンタープライズアプリケーションに適し、企業グレードのセキュリティを備えた完全管理サービスです。

LLM APIを直接使った開発に比べ、Microsoft Foundry Agent Serviceは以下の利点があります:

Microsoft Foundry Agent Serviceで利用可能なツールは2つのカテゴリに分かれます:

  1. 知識ツール:
  2. アクションツール:

Agent Serviceはこれらのツールをtoolsetとしてまとめて利用可能にします。また、特定の会話履歴を追跡するthreadsも活用します。

あなたがContoso社の営業担当者で、販売データに関して質問に答えられる会話型エージェントを開発するとしましょう。

以下の図は、Microsoft Foundry Agent Serviceを使って販売データを分析する方法を示しています:

Agentic Service In Action

サービスでこれらのツールを使うには、クライアントの作成とツールまたはツールセットの定義を行います。実装には以下のPythonコードを参考にできます。LLMはツールセットを参照して、ユーザーのリクエストに応じてユーザー作成関数fetch_sales_data_using_sqlite_queryか、組み込みのコードインタプリタを利用するかを決定します。

import os
from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential
from fetch_sales_data_functions import fetch_sales_data_using_sqlite_query # fetch_sales_data_using_sqlite_query function which can be found in a fetch_sales_data_functions.py file.
from azure.ai.projects.models import ToolSet, FunctionTool, CodeInterpreterTool

project_client = AIProjectClient.from_connection_string(
    credential=DefaultAzureCredential(),
    conn_str=os.environ["PROJECT_CONNECTION_STRING"],
)

# Initialize toolset
toolset = ToolSet()

# Initialize function calling agent with the fetch_sales_data_using_sqlite_query function and adding it to the toolset
fetch_data_function = FunctionTool(fetch_sales_data_using_sqlite_query)
toolset.add(fetch_data_function)

# Initialize Code Interpreter tool and adding it to the toolset. 
code_interpreter = CodeInterpreterTool()toolset.add(code_interpreter)

agent = project_client.agents.create_agent(
    model="gpt-5-mini", name="my-agent", instructions="You are helpful agent", 
    toolset=toolset
)

ツール使用デザインパターンを利用して信頼できるAIエージェントを構築する際の特別な考慮事項は?

LLMによって動的に生成されるSQLに対する一般的な懸念はセキュリティであり、特にSQLインジェクションやデータベースの破壊・改ざんのリスクです。これらの懸念は、データベースアクセス権限を適切に設定することで効果的に軽減できます。多くのデータベースでは読み取り専用に設定することが含まれます。PostgreSQLやAzure SQLなどのデータベースサービスでは、アプリに読み取り専用(SELECT)ロールを割り当てるべきです。

アプリを安全な環境で実行することも保護を強化します。エンタープライズ環境では、通常は運用システムからデータを抽出・変換して、ユーザーフレンドリーなスキーマの読み取り専用データベースまたはデータウェアハウスに格納します。この方法により、データの安全性、性能やアクセス性の最適化、アプリへの制限された読み取り専用アクセスが保証されます。

サンプルコード

ツール使用デザインパターンについてさらに質問がありますか?

Microsoft Foundry Discordに参加して、他の学習者と交流し、オフィスアワーに参加してAIエージェントの質問を解決しましょう。

追加リソース

このエージェントのスモークテスト(任意)

Lesson 16でエージェントのデプロイ方法を学習した後、このレッスンのTravelToolAgent(まだツールを呼び出し、応答するか?)を tests/lesson-04-smoke-tests.jsonでスモークテストできます。実行方法は tests/README.mdを参照してください。

前のレッスン

エージェントデザインパターンの理解

次のレッスン

Agentic RAG


免責事項: 本書類は AI 翻訳サービス Co-op Translator を使用して翻訳されています。正確性を期していますが、自動翻訳には誤りや不正確な部分が含まれる可能性があることをご承知おきください。原文の原語版が正式な情報源とみなされるべきです。重要な情報については、専門の人間による翻訳を推奨します。本翻訳の利用により生じたいかなる誤解や解釈違いについても、当方は責任を負いかねます。