このチュートリアルの手順では、エージェントで関数ツールを使用する方法について説明します。エージェントは 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)]);
Warning
DefaultAzureCredential は開発には便利ですが、運用環境では慎重に考慮する必要があります。 運用環境では、待機時間の問題、意図しない資格情報のプローブ、フォールバック メカニズムによる潜在的なセキュリティ リスクを回避するために、特定の資格情報 ( ManagedIdentityCredential など) を使用することを検討してください。
これで、通常どおりにエージェントを実行するだけで、エージェントは必要に応じて GetWeather 関数ツールを呼び出すことができます。
Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?"));
Tip
実行可能な完全な例については、 .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 スキーマ ディクショナリを指定できます。
from pydantic import BaseModel, Field
# 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.",
)
def get_weather(location: str, unit: str = "celsius") -> str:
"""Get the current weather for a location."""
return f"The weather in {location} is 22 degrees {unit}."
# Approach 2: JSON schema dictionary for a trusted, non-sensitive tool.
# This receives lightweight top-level checks only; use Pydantic for runtime enforcement.
get_current_time_schema = {
"type": "object",
"properties": {
"timezone": {"type": "string", "description": "The timezone to get the current time for", "default": "UTC"},
},
}
@tool(
変換されたPythonパラメーター型を使用する
Pydantic 入力モデルによってサポートされるツールの場合、Agent Framework はモデル指定の JSON 引数を検証し、変換されたPython値を関数に渡します。 サポートされる変換には、 date、 datetime、 time、 timedelta、 Decimal、 UUID、 tuple、 set、および frozenset の値が含まれます。 Pydantic モデル インスタンスも、呼び出し前に再検証されます。
生の JSON スキーマ ディクショナリで宣言されたツールでは、Pydantic 変換の代わりに JSON ネイティブ型の検証が引き続き使用されます。
ランタイムのみのコンテキストをツールに渡す
モデルで指定する必要がある値には、通常の関数パラメーターを使用します。
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"},
)
print(f"Agent: {response.text}")
ctx.kwargs、ctx.session、関数ミドルウェアの詳細については、「ランタイム コンテキスト」を参照してください。
宣言専用ツールを作成する
フレームワークの外部 (UI のクライアント側など) にツールが実装されている場合は、 FunctionTool(..., func=None)を使用して実装せずに宣言できます。
モデルは引き続きツールを推論して呼び出すことができます。アプリケーションは後で結果を提供できます。
# 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"},
},
"required": ["reason"],
},
)
カスタム ツールの結果を解析する
生の戻り値を文字列またはContent項目のリストに変換する必要がある場合は、FunctionToolまたは@toolにresult_parserを設定します。 カスタム パーサーで例外が発生した場合、直接呼び出しによって例外が反映されますが、関数の自動呼び出しでは通常のツール エラーの結果が返され、 include_detailed_errorsが適用されます。 生の戻り値はフォールバックとして使用されないため、カスタム パーサー内で回復可能な変換エラーを処理します。
エージェントを作成するときに、 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())
ツールの自動呼び出しを制限する
モデルとのラウンドトリップ回数、関数呼び出しの総数、および経過したウォールクロック時間に基づくツールの自動呼び出しを制御するために、チャット クライアントに制限を設定します。
max_function_calls
max_duration_secondsは既定で None に設定されます。これは無制限を意味します。 正の値に設定します。 クライアントは、制限に達すると、ツールの呼び出しを停止し、モデルに最終的なテキスト応答を要求します。
これらの制限はベストエフォートで適用され、モデルが要求したツール呼び出しの各バッチの後にチェックされるため、1つのバッチが呼び出し回数または実行時間の上限を超える可能性があります。 ツールの承認を待つ時間はmax_duration_secondsに含まれます。
from agent_framework.openai import OpenAIChatCompletionClient
client = OpenAIChatCompletionClient()
client.function_invocation_configuration.update(
{
"max_iterations": 5,
"max_function_calls": 20,
"max_duration_seconds": 30.0,
}
)
同時実行ツールの呼び出しを制御する
1 つのアシスタント メッセージで返される関数呼び出しは、既定で同時に実行されます。 ツールが変更可能な状態を共有する場合、またはモデルの順序で実行する必要がある場合に、 allow_concurrent_invocation を False に設定します。
client.function_invocation_configuration["allow_concurrent_invocation"] = False
この設定は、 allow_multiple_tool_callsなどのプロバイダー側オプションとは別に、クライアント側のツールの実行を制御します。 セッションに基づく承認バッチの場合、決定は個別に到着できますが、ツールは実行されません。モデルは、元のバッチのすべての承認が決定されるまで再開されません。 承認されたツールは、モデルの元の順序で実行されます。
コントロール ツールのエラーの詳細
既定では、include_detailed_errors は Falseです。 関数の実行と引数検証の失敗により、モデルおよびその他のシリアル化されたチャネルに汎用の結果が返されます。 元の診断は、 Content.exceptionの信頼されたホスト コードで引き続き使用できます。
Content.to_dict() は、そのフィールドを固定の非機密障害マーカーに置き換えます。プロトコル変換では診断が公開されません。
結果が信頼されたチャネルにとどまっている場合にのみ、 client.function_invocation_configuration["include_detailed_errors"] = True を設定します。 例外テキストには機密データを含めることができます。この設定により、そのテキストがチャネルに表示される結果に追加されます。
複数の関数ツールを使用してクラスを作成する
複数のツールが依存関係または変更可能な状態を共有する場合は、それらをクラスでラップし、バインドされたメソッドをエージェントに渡します。 サービス クライアント、機能フラグ、キャッシュされた状態など、モデルが提供すべきでない値にはクラス属性を使用します。
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 を使用してください。
関数ツール
関数ツールを使用すると、エージェントはカスタム Go 関数を呼び出すことができます。
functool パッケージは、スキーマの自動生成を使用してタイプ セーフなツールを定義する簡単な方法を提供します。
関数ツールを定義する
import (
"context"
"github.com/microsoft/agent-framework-go/tool"
"github.com/microsoft/agent-framework-go/tool/functool"
)
var weatherTool = functool.MustNew(functool.Config{
Name: "weather",
Description: "Get the current weather for a given location",
}, func(_ context.Context, location string) (string, error) {
return fmt.Sprintf("The weather in %s is cloudy with a high of 15°C.", location), nil
})
関数シグネチャによって、ツールの入力スキーマが決まります。
context.Context パラメーターはフレームワークによって挿入され、モデルには公開されません。
構造化された入力の種類
複数のパラメーターを持つツールの場合は、構造体を定義します。
type WeatherInput struct {
Location string `json:"location" jsonschema:"description=The city to check weather for"`
Unit string `json:"unit" jsonschema:"description=Temperature unit (celsius or fahrenheit),enum=celsius,enum=fahrenheit"`
}
var weatherTool = functool.MustNew(functool.Config{
Name: "weather",
Description: "Get weather for a location",
}, func(_ context.Context, input WeatherInput) (string, error) {
return fmt.Sprintf("Weather in %s: 15°%s", input.Location, input.Unit), nil
})
ツールを使用してエージェントを作成する
a := foundryprovider.NewAgent(endpoint, token, foundryprovider.ModelDeployment(model), foundryprovider.AgentConfig{
Instructions: "You are a helpful assistant.",
Config: agent.Config{
Tools: []tool.Tool{weatherTool},
},
})
resp, err := a.RunText(ctx, "What is the weather like in Amsterdam?").Collect()
エージェントを関数ツールとして使用する
任意のエージェントを、別のエージェントで使用するための関数ツールとしてラップできます。
import "github.com/microsoft/agent-framework-go/tool/agenttool"
weatherAgent := foundryprovider.NewAgent(endpoint, token, foundryprovider.ModelDeployment(model), foundryprovider.AgentConfig{
Instructions: "You answer questions about the weather.",
Config: agent.Config{
Name: "WeatherAgent",
Description: "An agent that answers weather questions.",
Tools: []tool.Tool{weatherTool},
},
})
mainAgent := foundryprovider.NewAgent(endpoint, token, foundryprovider.ModelDeployment(model), foundryprovider.AgentConfig{
Instructions: "You are a helpful assistant who responds in French.",
Config: agent.Config{
Tools: []tool.Tool{agenttool.New(weatherAgent, agenttool.Config{})},
},
})
ローカル シェル ツールを使用する
Go SDK には、ローカル シェルを実行するための tool/shelltool が含まれています。 このツールは既定で承認を必要とし、環境コンテキスト プロバイダーとペアにして、モデルが現在のシェル ファミリ、作業ディレクトリ、および一般的なツール バージョンを認識できるようにします。
import "github.com/microsoft/agent-framework-go/tool/shelltool"
shell, err := shelltool.NewLocal(shelltool.LocalConfig{
Mode: shelltool.ModeStateless,
})
if err != nil {
return err
}
defer shell.Close()
envProvider := shelltool.NewEnvironmentProvider(shell, shelltool.EnvironmentProviderConfig{})
a := foundryprovider.NewAgent(endpoint, token, foundryprovider.ModelDeployment(model), foundryprovider.AgentConfig{
Instructions: "Run shell commands only when needed and summarize the result.",
Config: agent.Config{
Tools: []tool.Tool{shell},
ContextProviders: []agent.ContextProvider{envProvider},
},
})
各呼び出しを新しいシェルで実行する必要がある場合は、 shelltool.ModeStateless を使用します。
shelltool.ModePersistentは、1 つのエージェント セッションで、変更されたディレクトリやエクスポートされた環境変数などのシェル状態が呼び出し間で保持される必要がある場合にのみ使用します。 独立した分離境界を指定し、組み込みの承認ゲートが不要な場合にのみ、 AcknowledgeUnsafe: true を設定します。
Tip
完全な例については、 関数ツールのサンプル、 ツールとしてのエージェントのサンプル、および 環境を含むシェルのサンプル を参照してください。
Harness Agent で関数ツールを使用する
プレーン エージェントは、エージェントの構築時に渡すツールを使用し、追加のプロバイダーまたはミドルウェアを自分で作成します。 Harness エージェントは同じ関数ツールを使用しますが、関数呼び出しパイプライン、サービスごとの呼び出し履歴の永続化、ツール承認サポート、およびその他のハーネス機能を事前に構成します。
HarnessAgentOptions.ChatOptions.Toolsを使用してHarnessAgentを作成するときに、関数ツールをAsHarnessAgentに渡します。
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
ChatOptions = new ChatOptions
{
Instructions = "You are a helpful assistant.",
Tools = [AIFunctionFactory.Create(GetWeather)],
},
});
AgentSession session = await agent.CreateSessionAsync();
AgentResponse response = await agent.RunAsync(
"What is the weather like in Amsterdam?",
session);
HarnessAgent では、 FunctionInvokingChatClient が自動的に構成されます。
HarnessAgentOptions.MaximumIterationsPerRequestを設定して関数呼び出しの制限をオーバーライドします。既定のnullでは、FunctionInvokingChatClientの既定値が使用されます。 また、ハーネスは既定でHostedWebSearchToolを追加するため、エージェントがDisableWebSearch = trueのツールのみを公開する必要がある場合は、ChatOptions.Toolsを設定します。
toolsの create_harness_agent パラメーターに、1 つのツールまたは一連のツールを渡します。
from agent_framework import create_harness_agent
agent = create_harness_agent(
client=client,
agent_instructions="You are a helpful assistant.",
tools=get_weather,
)
session = agent.create_session()
response = await agent.run(
"What is the weather like in Amsterdam?",
session=session,
)
print(response.text)
ファクトリは、関数の自動呼び出しとサービスごとの呼び出し履歴の永続化を構成します。
@toolで修飾された関数は、既定でapproval_mode="never_require"を使用します。
disable_web_search=False また、クライアントがサポートしている場合は、クライアントの Web 検索ツールも追加されます。 disable_web_search=True を省略するように設定します。
ハーネスは既定で ToolApprovalMiddleware (disable_tool_auto_approval=False) をインストールし、そのミドルウェアには実行ごとに AgentSession が必要です。 次に示すように session=agent.create_session() 渡すか、ハーネス承認ミドルウェアが不要な場合は disable_tool_auto_approval=True を明示的に設定します。
パッケージ化された Go ハーネスは現在使用できません。
agent.Config.Tools に関数ツールを追加し、必要なミドルウェアとコンテキスト プロバイダーを直接構成します。
次のステップ
実行時のツールの可用性の制御
FunctionInvocationContext.add_tools()
/
remove_tools()を使用してエージェントの実行中にツールを追加または削除したり、関数ミドルウェアを介して呼び出しをゲートしたり、tool_choiceを使用して特定の最初の呼び出しを強制したりできます。 完全なパターンについては、 ツールの可用性の制御 を参照してください。