本教學課程步驟示範如何搭配代理程式使用函式工具,其中代理程式是以 Azure OpenAI 聊天完成服務為基礎。
這很重要
並非所有代理程式類型都支援函數工具。 有些可能只支援自訂內建工具,而不允許呼叫端提供自己的函式。 此步驟使用 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.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using OpenAI;
AIAgent agent = new AzureOpenAIClient(
new Uri("https://<myresource>.openai.azure.com"),
new DefaultAzureCredential())
.GetChatClient("gpt-4o-mini")
.AsAIAgent(instructions: "You are a helpful assistant", tools: [AIFunctionFactory.Create(GetWeather)]);
警告
DefaultAzureCredential 開發方便,但在生產過程中需謹慎考量。 在生產環境中,建議使用特定的憑證(例如 ManagedIdentityCredential),以避免延遲問題、意外的憑證探測,以及備援機制帶來的安全風險。
現在您可以正常執行代理程式,且代理程式將能夠在需要時呼叫 GetWeather 函式工具。
Console.WriteLine(await agent.RunAsync("What is the weather like in Amsterdam?"));
小提示
完整可執行範例請參閱 .NET 範例 。
這很重要
並非所有代理程式類型都支援函數工具。 有些可能只支援自訂內建工具,而不允許呼叫端提供自己的函式。 此步驟使用透過聊天用戶端建立的代理程式,這些用戶端支援功能工具。
先決條件
如需必要條件和安裝 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參數,框架將自動使用函數的名稱和說明字串作為後備。
使用明確的結構模式 @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.",
建立僅供宣告使用的工具
如果工具是在框架外實作的(例如用戶端介面),你可以用 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
from agent_framework.azure import AzureOpenAIChatClient
from azure.identity import AzureCliCredential
agent = AzureOpenAIChatClient(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())
建立具有多個函數工具的類別
您也可以建立包含多個函式工具作為方法的類別。 這對於將相關函數組織在一起或當您想要在它們之間傳遞狀態時非常有用。
class WeatherTools:
def __init__(self):
self.last_location = None
def get_weather(
self,
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."
def get_weather_details(self) -> int:
"""Get the detailed weather for the last requested location."""
if self.last_location is None:
return "No location specified yet."
return f"The detailed weather in {self.last_location} is cloudy with a high of 15°C, low of 7°C, and 60% humidity."
建立代理程式時,您現在可以將類別的所有方法提供為函數:
tools = WeatherTools()
agent = AzureOpenAIChatClient(credential=AzureCliCredential()).as_agent(
instructions="You are a helpful assistant",
tools=[tools.get_weather, tools.get_weather_details]
)
您也可以使用與以前相同的 @tool 裝飾器來裝飾功能。
完整範例
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from typing import Annotated, Any
from agent_framework import tool
from agent_framework.openai import OpenAIResponsesClient
from pydantic import Field
"""
AI Function with kwargs Example
This example demonstrates how to inject custom keyword arguments (kwargs) into an AI function
from the agent's run method, without exposing them to the AI model.
This is useful for passing runtime information like access tokens, user IDs, or
request-specific context that the tool needs but the model shouldn't know about
or provide.
"""
# Define the function tool with **kwargs to accept injected arguments
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/02-agents/tools/function_tool_with_approval.py and samples/02-agents/tools/function_tool_with_approval_and_sessions.py.
@tool(approval_mode="never_require")
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
**kwargs: Any,
) -> str:
"""Get the weather for a given location."""
# Extract the injected argument from kwargs
user_id = 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 = OpenAIResponsesClient().as_agent(
name="WeatherAgent",
instructions="You are a helpful weather assistant.",
tools=[get_weather],
)
# Pass the injected argument when running the agent
# The 'user_id' kwarg will be passed down to the tool execution via **kwargs
response = await agent.run("What is the weather like in Amsterdam?", user_id="user_123")
print(f"Agent: {response.text}")
if __name__ == "__main__":
asyncio.run(main())
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from typing import Annotated, Any
from agent_framework import tool
from agent_framework.openai import OpenAIResponsesClient
from pydantic import Field
"""
AI Function with kwargs Example
This example demonstrates how to inject custom keyword arguments (kwargs) into an AI function
from the agent's run method, without exposing them to the AI model.
This is useful for passing runtime information like access tokens, user IDs, or
request-specific context that the tool needs but the model shouldn't know about
or provide.
"""
# Define the function tool with **kwargs to accept injected arguments
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/02-agents/tools/function_tool_with_approval.py and samples/02-agents/tools/function_tool_with_approval_and_sessions.py.
@tool(approval_mode="never_require")
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
**kwargs: Any,
) -> str:
"""Get the weather for a given location."""
# Extract the injected argument from kwargs
user_id = 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 = OpenAIResponsesClient().as_agent(
name="WeatherAgent",
instructions="You are a helpful weather assistant.",
tools=[get_weather],
)
# Pass the injected argument when running the agent
# The 'user_id' kwarg will be passed down to the tool execution via **kwargs
response = await agent.run("What is the weather like in Amsterdam?", user_id="user_123")
print(f"Agent: {response.text}")
if __name__ == "__main__":
asyncio.run(main())