このチュートリアルの手順では、エージェントで関数ツールを使用する方法について説明します。エージェントは Azure OpenAI チャット補完サービス上に構築されています。
Important
すべてのエージェントの種類で関数ツールがサポートされているわけではありません。 呼び出し元が独自の関数を提供することを許可せずに、カスタム組み込みツールのみをサポートする場合があります。 この手順では、関数ツールをサポートする ChatClientAgentを使用します。
[前提条件]
前提条件と NuGet パッケージのインストールについては、このチュートリアルの 「エージェントの作成と実行 」の手順を参照してください。
関数ツールを使用してエージェントを作成する
関数ツールは、エージェントが必要なときに呼び出せるようにするカスタム コードにすぎません。
AIFunctionFactory.Create メソッドを使用してメソッドから AIFunction インスタンスを作成することで、任意の C# メソッドを関数ツールに変換できます。
関数またはそのパラメーターに関する追加の説明をエージェントに提供する必要がある場合は、異なる関数の中からより正確に選択できるように、メソッドとそのパラメーターに System.ComponentModel.DescriptionAttribute 属性を使用できます。
特定の場所の天気を偽装する単純な関数ツールの例を次に示します。 説明属性で修飾され、エージェントに自身とその場所パラメーターに関する追加の説明を提供します。
using System.ComponentModel;
[Description("Get the weather for a given location.")]
static string GetWeather([Description("The location to get the weather for.")] string location)
=> $"The weather in {location} is cloudy with a high of 15°C.";
エージェントを作成するときに、 AsAIAgent メソッドにツールの一覧を渡すことで、関数ツールをエージェントに提供できるようになりました。
using System;
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
AIAgent agent = new AIProjectClient(
new Uri("<your-foundry-project-endpoint>"),
new DefaultAzureCredential())
.AsAIAgent(
model: "gpt-4o-mini",
instructions: "You are a helpful assistant",
tools: [AIFunctionFactory.Create(GetWeather)]);
Warnung
DefaultAzureCredential は開発には便利ですが、運用環境では慎重に考慮する必要があります。 運用環境では、待機時間の問題、意図しない資格情報のプローブ、フォールバック メカニズムによる潜在的なセキュリティ リスクを回避するために、特定の資格情報 ( ManagedIdentityCredential など) を使用することを検討してください。
これで、通常どおりにエージェントを実行するだけで、エージェントは必要に応じて GetWeather 関数ツールを呼び出すことができます。
Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?"));
ヒント
実行可能な完全な例については、 .NET サンプル を参照してください。
Important
すべてのエージェントの種類で関数ツールがサポートされているわけではありません。 呼び出し元が独自の関数を提供することを許可せずに、カスタム組み込みツールのみをサポートする場合があります。 この手順では、関数ツールをサポートするチャット クライアントを介して作成されたエージェントを使用します。
[前提条件]
前提条件と Python パッケージのインストールについては、このチュートリアルの 「エージェントの作成と実行 」の手順を参照してください。
関数ツールを使用してエージェントを作成する
関数ツールは、エージェントが必要なときに呼び出せるようにするカスタム コードにすぎません。
任意の Python 関数を関数ツールに変換するには、エージェントの作成時にエージェントの tools パラメーターに渡します。
関数またはそのパラメーターに関する追加の説明をエージェントに提供する必要がある場合は、異なる関数の中からより正確に選択できるように、Python の型注釈と Annotated と Pydantic の Field を使用して説明を提供できます。
特定の場所の天気を偽装する単純な関数ツールの例を次に示します。 型注釈を使用して、関数とその場所パラメーターに関する追加の説明をエージェントに提供します。
from typing import Annotated
from pydantic import Field
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
) -> str:
"""Get the weather for a given location."""
return f"The weather in {location} is cloudy with a high of 15°C."
@toolデコレーターを使用して、関数の名前と説明を明示的に指定することもできます。
from typing import Annotated
from pydantic import Field
from agent_framework import tool
@tool(name="weather_tool", description="Retrieves weather information for any location")
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
) -> str:
return f"The weather in {location} is cloudy with a high of 15°C."
name デコレーターでdescriptionパラメーターと@tool パラメーターを指定しない場合、フレームワークは関数の名前と docstring をフォールバックとして自動的に使用します。
明示的なスキーマを使用する @tool
モデルに公開されているスキーマを完全に制御する必要がある場合は、 schema パラメーターを @toolに渡します。
Pydantic モデルまたは生の JSON スキーマ ディクショナリを指定できます。
# Load environment variables from .env file
load_dotenv()
# Approach 1: Pydantic model as explicit schema
class WeatherInput(BaseModel):
"""Input schema for the weather tool."""
location: Annotated[str, Field(description="The city name to get weather for")]
unit: Annotated[str, Field(description="Temperature unit: celsius or fahrenheit")] = "celsius"
@tool(
name="get_weather",
description="Get the current weather for a given location.",
schema=WeatherInput,
approval_mode="never_require",
"""Get the current weather for a location."""
return f"The weather in {location} is 22 degrees {unit}."
# Approach 2: JSON schema dictionary as explicit schema
get_current_time_schema = {
"type": "object",
"properties": {
"timezone": {"type": "string", "description": "The timezone to get the current time for", "default": "UTC"},
},
}
@tool(
name="get_current_time",
description="Get the current time in a given timezone.",
ランタイムのみのコンテキストをツールに渡す
モデルで指定する必要がある値には、通常の関数パラメーターを使用します。
FunctionInvocationContextや現在のセッションなどのランタイムのみの値には、function_invocation_kwargsを使用します。 挿入されたコンテキスト パラメーターは、モデルに公開されているスキーマから非表示になります。
import asyncio
from typing import Annotated
from agent_framework import Agent, FunctionInvocationContext, tool
from agent_framework.openai import OpenAIChatClient
from dotenv import load_dotenv
from pydantic import Field
# Define the function tool with explicit invocation context.
# The context parameter can also be declared as an untyped ``ctx`` parameter.
@tool(approval_mode="never_require")
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
ctx: FunctionInvocationContext,
) -> str:
"""Get the weather for a given location."""
# Extract the injected argument from the explicit context
user_id = ctx.kwargs.get("user_id", "unknown")
# Simulate using the user_id for logging or personalization
print(f"Getting weather for user: {user_id}")
return f"The weather in {location} is cloudy with a high of 15°C."
async def main() -> None:
agent = Agent(
client=OpenAIChatClient(),
name="WeatherAgent",
instructions="You are a helpful weather assistant.",
tools=[get_weather],
)
# Pass the runtime context explicitly when running the agent.
response = await agent.run(
"What is the weather like in Amsterdam?",
function_invocation_kwargs={"user_id": "user_123"},
ctx.kwargs、ctx.session、関数ミドルウェアの詳細については、「ランタイム コンテキスト」を参照してください。
宣言専用ツールを作成する
フレームワークの外部 (UI のクライアント側など) にツールが実装されている場合は、 FunctionTool(..., func=None)を使用して実装せずに宣言できます。
モデルは引き続きツールを推論して呼び出すことができます。アプリケーションは後で結果を提供できます。
# Load environment variables from .env file
load_dotenv()
# A declaration-only tool: the schema is sent to the LLM, but the framework
# has no implementation to execute. The caller must supply the result.
get_user_location = FunctionTool(
name="get_user_location",
func=None,
description="Get the user's current city. Only the client application can resolve this.",
input_model={
"type": "object",
"properties": {
"reason": {"type": "string", "description": "Why the location is needed"},
エージェントを作成するときに、 tools パラメーターに渡すことで、関数ツールをエージェントに提供できるようになりました。
import asyncio
import os
from agent_framework.openai import OpenAIChatCompletionClient
from azure.identity import AzureCliCredential
agent = OpenAIChatCompletionClient(
model=os.environ["AZURE_OPENAI_CHAT_COMPLETION_MODEL"],
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
api_version=os.getenv("AZURE_OPENAI_API_VERSION"),
credential=AzureCliCredential(),
).as_agent(
instructions="You are a helpful assistant",
tools=get_weather
)
これで、通常どおりにエージェントを実行するだけで、エージェントは必要に応じて get_weather 関数ツールを呼び出すことができます。
async def main():
result = await agent.run("What is the weather like in Amsterdam?")
print(result.text)
asyncio.run(main())
複数の関数ツールを使用してクラスを作成する
複数のツールが依存関係または変更可能な状態を共有する場合は、それらをクラスでラップし、バインドされたメソッドをエージェントに渡します。 サービス クライアント、機能フラグ、キャッシュされた状態など、モデルが提供すべきでない値にはクラス属性を使用します。
import asyncio
from typing import Annotated
from agent_framework import Agent, tool
from agent_framework.openai import OpenAIChatClient
from dotenv import load_dotenv
class MyFunctionClass:
def __init__(self, safe: bool = False) -> None:
"""Simple class with two tools: divide and add.
The safe parameter controls whether divide raises on division by zero or returns `infinity` for divide by zero.
"""
self.safe = safe
def divide(
self,
a: Annotated[int, "Numerator"],
b: Annotated[int, "Denominator"],
) -> str:
"""Divide two numbers, safe to use also with 0 as denominator."""
result = "∞" if b == 0 and self.safe else a / b
return f"{a} / {b} = {result}"
def add(
self,
x: Annotated[int, "First number"],
y: Annotated[int, "Second number"],
) -> str:
return f"{x} + {y} = {x + y}"
async def main():
# Creating my function class with safe division enabled
tools = MyFunctionClass(safe=True)
# Applying the tool decorator to one of the methods of the class
add_function = tool(description="Add two numbers.")(tools.add)
agent = Agent(
client=OpenAIChatClient(),
name="ToolAgent",
instructions="Use the provided tools.",
)
print("=" * 60)
print("Step 1: Call divide(10, 0) - tool returns infinity")
query = "Divide 10 by 0"
response = await agent.run(
query,
tools=[add_function, tools.divide],
)
print(f"Response: {response.text}")
print("=" * 60)
print("Step 2: Call set safe to False and call again")
# Disabling safe mode to allow exceptions
tools.safe = False
このパターンは、有効期間の長いツールの状態に適しています。 呼び出しごとに値が変更される場合は、代わりに FunctionInvocationContext を使用してください。