(上の画像をクリックすると、このレッスンのビデオが表示されます)
ツールは、AIエージェントにより広範な能力を持たせることができるため興味深いものです。エージェントが実行できるアクションが限定されている代わりに、ツールを追加することで、エージェントはいろいろな種類のアクションを実行できるようになります。この章では、AIエージェントが目標を達成するために特定のツールを使用する方法を説明する、ツール使用デザインパターンについて見ていきます。
このレッスンでは、次の質問に答えることを目指します:
このレッスンを終えた後、次のことができるようになります:
ツール使用デザインパターンは、LLMに外部ツールとやり取りする機能を持たせ、特定の目標を達成させることに焦点を当てています。ツールとは、エージェントがアクションを実行するために呼び出せるコードのことです。ツールは電卓のような単純な関数であったり、株価や天気予報を取得するサードパーティーサービスへのAPI呼び出しであったりします。AIエージェントの文脈では、ツールはモデルが生成した関数呼び出しに応じてエージェントによって実行されるよう設計されています。
AIエージェントはツールを活用して複雑なタスクを完遂したり、情報を取得したり、意思決定を行ったりできます。ツール使用デザインパターンは、データベース、ウェブサービス、コードインタプリタなど外部システムとの動的なやり取りを必要とするシナリオでよく使われます。この能力は、以下のような多様なユースケースに役立ちます:
これらの構成要素はAIエージェントが多彩なタスクを実行することを可能にします。ツール使用デザインパターンを実装するために必要な主要な要素は以下の通りです:
関数/ツールスキーマ:利用可能なツールの詳細定義(関数名、目的、必要パラメータ、期待される出力など)。これらのスキーマはLLMがどのツールが使えるかを理解し、有効なリクエストを構築するために必要です。
関数実行ロジック:ユーザーの意図や会話コンテキストに基づき、ツールがいつどのように呼び出されるかを制御します。プランナーモジュール、ルーティングメカニズム、条件フローなどが含まれる場合があります。
メッセージハンドリングシステム:ユーザー入力、LLM応答、ツール呼び出し、ツール出力の会話フローを管理するコンポーネント。
ツール統合フレームワーク:単純な関数から複雑な外部サービスまで、エージェントとさまざまなツールとの接続基盤。
エラーハンドリング&検証:ツール実行の失敗処理、パラメータ検証、不意の応答管理のためのメカニズム。
状態管理:会話コンテキスト、過去のツールとのやり取り、永続データを追跡し、多段会話にわたる一貫性を確保。
次に、関数/ツールの呼び出しについて詳しく見ていきましょう。
関数呼び出しは、大規模言語モデル(LLM)がツールとやり取りするための主な方法です。『関数』と『ツール』はしばしば同義に使われますが、『関数』(再利用可能なコードのブロック)がエージェントがタスクを実行するために使う『ツール』だからです。関数のコードを呼び出すためには、LLMがユーザーのリクエストと関数の説明を比較する必要があります。そのため、利用可能なすべての関数の説明を含むスキーマをLLMに送信します。LLMはタスクに最適な関数を選択し、その名前と引数を返します。選択された関数が呼び出され、その応答がLLMに返され、LLMはこの情報を使ってユーザーのリクエストに応答します。
関数呼び出しをエージェント用に実装するには以下が必要です:
都市の現在時刻を取得する例で説明しましょう:
関数呼び出し対応のLLMを初期化する:
すべてのモデルが関数呼び出しをサポートしているわけではないので、使用しているLLMが対応しているか確認することが重要です。Azure OpenAIは関数呼び出しに対応しています。まずAzure OpenAIクライアントを起動します。
# Azure OpenAI クライアントを初期化する
client = AzureOpenAI(
azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT"),
api_key=os.getenv("AZURE_OPENAI_API_KEY"),
api_version="2024-05-01-preview"
)
関数スキーマの作成:
次に、関数名、関数の説明、関数パラメータの名前と説明を含むJSONスキーマを定義します。 このスキーマを先ほど作成したクライアントに渡し、ユーザーのサンフランシスコの時刻を取得したいというリクエストとともに送ります。重要なのは、ツール呼び出しが返されることであって、質問の最終回答ではないという点です。前述したように、LLMはタスクのために選択した関数の名前と、その関数に渡される引数を返します。
# モデルが読むための関数の説明
tools = [
{
"type": "function",
"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"],
},
}
}
]
# 初期ユーザーメッセージ
messages = [{"role": "user", "content": "What's the current time in San Francisco"}]
# 最初のAPI呼び出し: モデルに関数を使用するよう依頼する
response = client.chat.completions.create(
model=deployment_name,
messages=messages,
tools=tools,
tool_choice="auto",
)
# モデルの応答を処理する
response_message = response.choices[0].message
messages.append(response_message)
print("Model's response:")
print(response_message)
Model's response:
ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_pOsKdUlqvdyttYB67MOj434b', function=Function(arguments='{"location":"San Francisco"}', name='get_current_time'), type='function')])
タスクを実行する関数コード:
LLMが実行すべき関数を選択したので、そのタスクを実行するコードを実装・実行します。 Pythonで現在時刻を取得するコードを実装できます。また、最終結果を得るためにresponse_messageから関数名と引数を抽出するコードも必要です。
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"})
# 関数呼び出しを処理する
if response_message.tool_calls:
for tool_call in response_message.tool_calls:
if tool_call.function.name == "get_current_time":
function_args = json.loads(tool_call.function.arguments)
time_response = get_current_time(
location=function_args.get("location")
)
messages.append({
"tool_call_id": tool_call.id,
"role": "tool",
"name": "get_current_time",
"content": time_response,
})
else:
print("No tool calls were made by the model.")
# 2番目のAPI呼び出し:モデルから最終応答を取得する
final_response = client.chat.completions.create(
model=deployment_name,
messages=messages,
)
return final_response.choices[0].message.content
get_current_time called with location: San Francisco
Timezone found for san francisco
The current time in San Francisco is 09:24 AM.
関数呼び出しはほとんどすべてのエージェントのツール使用デザインの中心ですが、ゼロから実装するのは時に難しいこともあります。 レッスン2で学んだように、agenticフレームワークを使うことでツール使用を実装するための構成要素があらかじめ用意されています。
以下は、さまざまなagenticフレームワークを活用してツール使用デザインパターンを実装する例です:
Semantic Kernelは、.NET、Python、Javaの開発者向けのオープンソースAIフレームワークで、大規模言語モデル(LLM)利用を簡単にします。関数をモデルに自動で説明し、シリアライズすることで関数呼び出しのプロセスを簡素化します。また、モデルとコード間の通信も管理します。Semantic Kernelのようなagenticフレームワークを使う利点のひとつは、ファイル検索やコードインタプリタなどの事前構築済みツールにアクセス可能な点です。
以下の図は、Semantic Kernelにおける関数呼び出しプロセスを示しています:

Semantic Kernelでは関数やツールはプラグインと呼ばれます。前述のget_current_time関数をクラス化してプラグインに変換できます。また、関数の説明を受け取るkernel_functionデコレータを使用できます。GetCurrentTimePluginでカーネルを作成すると、カーネルが自動的に関数とパラメータをシリアライズし、LLMに送るためのスキーマを作成します。
from semantic_kernel.functions import kernel_function
class GetCurrentTimePlugin:
async def __init__(self, location):
self.location = location
@kernel_function(
description="Get the current time for a given location"
)
def get_current_time(location: str = ""):
...
from semantic_kernel import Kernel
# カーネルを作成する
kernel = Kernel()
# プラグインを作成する
get_current_time_plugin = GetCurrentTimePlugin(location)
# プラグインをカーネルに追加する
kernel.add_plugin(get_current_time_plugin)
Azure AI Agent Serviceは、基盤となる計算リソースやストレージ管理不要で高品質かつ拡張可能なAIエージェントを安全に構築・展開・スケールできる新しいagenticフレームワークです。特に企業向けアプリケーションに適しており、エンタープライズグレードのセキュリティを備えたフルマネージドサービスです。
LLM APIを直接使って開発するのと比べ、Azure AI Agent Serviceは以下の利点があります:
Azure AI Agent Serviceで利用できるツールは、次の2つのカテゴリーに分けられます:
Agent Serviceは、これらのツールをtoolsetとして一緒に使うことを可能にします。また、特定の会話のメッセージ履歴を追跡するthreadsも活用しています。
例えば、Contosoという会社の営業担当者だとします。営業データに関する質問に応答できる対話型エージェントを開発したいとします。
以下の画像は、Azure AI Agent Serviceを使って営業データを分析する様子を表しています:

これらのツールをサービスで使うには、クライアントを作成し、ツールまたはツールセットを定義します。実際に実装するには次の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_functions.py ファイル内にある fetch_sales_data_using_sqlite_query 関数。
from azure.ai.projects.models import ToolSet, FunctionTool, CodeInterpreterTool
project_client = AIProjectClient.from_connection_string(
credential=DefaultAzureCredential(),
conn_str=os.environ["PROJECT_CONNECTION_STRING"],
)
# ツールセットの初期化
toolset = ToolSet()
# fetch_sales_data_using_sqlite_query 関数を使用して関数呼び出しエージェントを初期化し、ツールセットに追加する
fetch_data_function = FunctionTool(fetch_sales_data_using_sqlite_query)
toolset.add(fetch_data_function)
# Code Interpreter ツールを初期化し、ツールセットに追加する。
code_interpreter = code_interpreter = CodeInterpreterTool()
toolset.add(code_interpreter)
agent = project_client.agents.create_agent(
model="gpt-4o-mini", name="my-agent", instructions="You are helpful agent",
toolset=toolset
)
LLMにより動的に生成されるSQLでの一般的な懸念はセキュリティです。特にSQLインジェクションやデータベースの削除・改ざんなどの悪意ある行動のリスクがあります。これらの懸念は妥当ですが、適切にデータベースアクセス権限を設定することで効果的に軽減できます。ほとんどのデータベースでは、データベースを読み取り専用に構成します。PostgreSQLやAzure SQLのようなデータベースサービスの場合は、アプリに読み取り専用(SELECT)ロールを割り当てるべきです。 アプリをセキュアな環境で実行することは、保護をさらに強化します。エンタープライズのシナリオでは、データは通常、運用システムから抽出および変換され、ユーザーフレンドリーなスキーマを持つ読み取り専用のデータベースまたはデータウェアハウスに格納されます。このアプローチにより、データは安全で、パフォーマンスやアクセス性が最適化され、アプリは制限された読み取り専用のアクセス権を持つことが保証されます。
Azure AI Foundry Discord に参加して、他の学習者と交流したり、オフィスアワーに参加したり、AIエージェントに関する質問に答えてもらいましょう。
免責事項:
本書類はAI翻訳サービス「Co-op Translator」(https://github.com/Azure/co-op-translator)を使用して翻訳されています。正確性を期しておりますが、自動翻訳には誤りや不正確な部分が含まれる可能性があります。原文の言語によるオリジナル文書が正式な情報源とみなされるべきです。重要な情報については、専門の人間による翻訳を推奨いたします。本翻訳の利用により生じるいかなる誤解や誤訳についても、当方は責任を負いかねます。