次の方法で共有


エージェントを用いた関数ツールの利用

このチュートリアルの手順では、エージェントで関数ツールを使用する方法について説明します。エージェントは 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.kwargsctx.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 を使用してください。

次のステップ