你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
Azure AI 代理支持函数调用,这使你可以将函数的结构描述为代理,然后返回需要调用的函数及其参数。
注意
创建 10 分钟后运行将过期。 请务必在过期之前提交工具输出。
使用支持
Azure AI Foundry 支持 | Python SDK | C# SDK | JavaScript SDK | REST API | 基本代理设置 | 标准代理设置 |
---|---|---|---|---|---|---|
✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
定义代理要调用的函数
首先定义代理要调用的函数。 为代理调用创建函数时,可以使用 docstring 中的任何必需参数来描述其结构。
import json
import datetime
from typing import Any, Callable, Set, Dict, List, Optional
def fetch_weather(___location: str) -> str:
"""
Fetches the weather information for the specified ___location.
:param ___location: The ___location to fetch weather for.
:return: Weather information as a JSON string.
"""
# Mock weather data for demonstration purposes
mock_weather_data = {"New York": "Sunny, 25°C", "London": "Cloudy, 18°C", "Tokyo": "Rainy, 22°C"}
weather = mock_weather_data.get(___location, "Weather data not available for this ___location.")
return json.dumps({"weather": weather})
# Define user functions
user_functions = {fetch_weather}
创建客户端和代理
在下面的示例中,我们将创建一个客户端并定义一个 toolset
,它将用于处理在 user_functions
中定义的函数。
toolset
:使用工具集参数时,不仅提供函数定义和说明,而且还提供其实现。 SDK 将在 create_and_run_process
或流式处理中执行这些函数。 将根据这些函数的定义调用这些函数。
import os, time
from azure.identity import DefaultAzureCredential
from azure.ai.projects import AIProjectClient
from azure.ai.agents.models import FunctionTool
# Retrieve the project endpoint from environment variables
project_endpoint = os.environ["PROJECT_ENDPOINT"]
# Initialize the AIProjectClient
project_client = AIProjectClient(
endpoint=project_endpoint,
credential=DefaultAzureCredential(),
api_version="latest",
)
# Initialize the FunctionTool with user-defined functions
functions = FunctionTool(functions=user_functions)
with project_client:
# Create an agent with custom functions
agent = project_client.agents.create_agent(
model=os.environ["MODEL_DEPLOYMENT_NAME"],
name="my-agent",
instructions="You are a helpful agent",
tools=functions.definitions,
)
print(f"Created agent, ID: {agent.id}")
创建线程
# Create a thread for communication
thread = project_client.agents.threads.create()
print(f"Created thread, ID: {thread.id}")
# Send a message to the thread
message = project_client.agents.messages.create(
thread_id=thread.id,
role="user",
content="Hello, send an email with the datetime and weather information in New York?",
)
print(f"Created message, ID: {message['id']}")
创建运行并检查输出
# Create and process a run for the agent to handle the message
run = project_client.agents.runs.create_and_process(thread_id=thread.id, agent_id=agent.id)
print(f"Created run, ID: {run.id}")
# Poll the run status until it is completed or requires action
while run.status in ["queued", "in_progress", "requires_action"]:
time.sleep(1)
run = project_client.agents.runs.get(thread_id=thread.id, run_id=run.id)
if run.status == "requires_action":
tool_calls = run.required_action.submit_tool_outputs.tool_calls
tool_outputs = []
for tool_call in tool_calls:
if tool_call.name == "fetch_weather":
output = fetch_weather("New York")
tool_outputs.append({"tool_call_id": tool_call.id, "output": output})
project_client.agents.runs.submit_tool_outputs(thread_id=thread.id, run_id=run.id, tool_outputs=tool_outputs)
print(f"Run completed with status: {run.status}")
# Fetch and log all messages from the thread
messages = project_client.agents.messages.list(thread_id=thread.id)
for message in messages:
print(f"Role: {message['role']}, Content: {message['content']}")
# Delete the agent after use
project_client.agents.delete_agent(agent.id)
print("Deleted agent")
配置客户端并定义函数
首先,使用 appsettings.json
设置配置并创建 PersistentAgentsClient
。
using Azure;
using Azure.AI.Agents.Persistent;
using Azure.Identity;
using Microsoft.Extensions.Configuration;
using System.Text.Json;
// Load configuration from appsettings.json file
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(AppContext.BaseDirectory)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.Build();
// Read necessary configuration values (Project Endpoint and Model Deployment Name)
var projectEndpoint = configuration["ProjectEndpoint"];
var modelDeploymentName = configuration["ModelDeploymentName"];
// Initialize the client to interact with the Azure AI Agents Persistent Client using default credentials
PersistentAgentsClient client = new(projectEndpoint, new DefaultAzureCredential());
定义函数
定义代理可以调用的本地 C# 函数,并用 FunctionToolDefinition
描述函数的用途及参数。
// Function to get the user's favorite city (hardcoded for example)
string GetUserFavoriteCity() => "Seattle, WA";
// Definition for the GetUserFavoriteCity function, describing its purpose to the agent
FunctionToolDefinition getUserFavoriteCityTool = new("getUserFavoriteCity", "Gets the user's favorite city.");
// Function to get a city's nickname based on its ___location
string GetCityNickname(string ___location) => ___location switch
{
"Seattle, WA" => "The Emerald City",
// Handle cases where the nickname is not known
_ => throw new NotImplementedException(),
};
// Definition for the GetCityNickname function, including parameter description
FunctionToolDefinition getCityNicknameTool = new(
name: "getCityNickname",
description: "Gets the nickname of a city, e.g. 'LA' for 'Los Angeles, CA'.",
// Define the expected parameters (___location string)
parameters: BinaryData.FromObjectAsJson(
new
{
Type = "object",
Properties = new
{
Location = new
{
Type = "string",
Description = "The city and state, e.g. San Francisco, CA",
},
},
Required = new[] { "___location" },
},
new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }));
// Function to get weather at a specific ___location, with an optional temperature unit
string GetWeatherAtLocation(string ___location, string temperatureUnit = "f") => ___location switch
{
"Seattle, WA" => temperatureUnit == "f" ? "70f" : "21c",
// Handle cases where weather data is not available
_ => throw new NotImplementedException()
};
// Definition for the GetWeatherAtLocation function, specifying parameters and enum for unit
FunctionToolDefinition getCurrentWeatherAtLocationTool = new(
name: "getCurrentWeatherAtLocation",
description: "Gets the current weather at a provided ___location.",
// Define expected parameters (___location string, optional unit enum)
parameters: BinaryData.FromObjectAsJson(
new
{
Type = "object",
Properties = new
{
Location = new
{
Type = "string",
Description = "The city and state, e.g. San Francisco, CA",
},
Unit = new
{
Type = "string",
Enum = new[] { "c", "f" },
},
},
Required = new[] { "___location" },
},
new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }));
实现函数执行逻辑
创建帮助程序函数, GetResolvedToolOutput
以处理 RequiredToolCall
代理中的对象。 此函数将调用相应的 C# 本地函数,并将其输出返回到代理。
// Helper function to execute the correct local C# function based on the tool call request from the agent
ToolOutput GetResolvedToolOutput(RequiredToolCall toolCall)
{
// Check if the required call is a function call
if (toolCall is RequiredFunctionToolCall functionToolCall)
{
// Execute GetUserFavoriteCity if its name matches
if (functionToolCall.Name == getUserFavoriteCityTool.Name)
{
return new ToolOutput(toolCall, GetUserFavoriteCity());
}
// Parse the arguments provided by the agent for other functions
using JsonDocument argumentsJson = JsonDocument.Parse(functionToolCall.Arguments);
// Execute GetCityNickname if its name matches
if (functionToolCall.Name == getCityNicknameTool.Name)
{
// Extract the '___location' argument
string locationArgument = argumentsJson.RootElement.GetProperty("___location").GetString();
return new ToolOutput(toolCall, GetCityNickname(locationArgument));
}
// Execute GetWeatherAtLocation if its name matches
if (functionToolCall.Name == getCurrentWeatherAtLocationTool.Name)
{
// Extract the '___location' argument
string locationArgument = argumentsJson.RootElement.GetProperty("___location").GetString();
// Check if the optional 'unit' argument was provided
if (argumentsJson.RootElement.TryGetProperty("unit", out JsonElement unitElement))
{
string unitArgument = unitElement.GetString();
return new ToolOutput(toolCall, GetWeatherAtLocation(locationArgument, unitArgument));
}
// Call without the unit if it wasn't provided
return new ToolOutput(toolCall, GetWeatherAtLocation(locationArgument));
}
}
// Return null if the tool call type isn't handled
return null;
}
创建代理和会话线程
现在,创建 PersistentAgent
,从而提供模型部署名称、描述性名称、其行为说明以及它可以使用的 FunctionToolDefinitions
列表。 然后,创建 PersistentAgentThread
并添加初始用户消息以启动对话。
// Create the agent instance
PersistentAgent agent = client.Administration.CreateAgent(
model: modelDeploymentName,
name: "SDK Test Agent - Functions",
instructions: "You are a weather bot. Use the provided functions to help answer questions. "
+ "Customize your responses to the user's preferences as much as possible and use friendly "
+ "nicknames for cities whenever possible.",
tools: [getUserFavoriteCityTool, getCityNicknameTool, getCurrentWeatherAtLocationTool]);
// Create a new conversation thread for the agent
PersistentAgentThread thread = client.Threads.CreateThread();
// Add the initial user message to the thread
client.Messages.CreateMessage(
thread.Id,
MessageRole.User,
"What's the weather like in my favorite city?");
进程运行和处理函数调用
在线程上为代理创建一个 ThreadRun
。 轮询运行的完成状态。 如果运行状态为 RequiresAction
,则表示代理需要调用本地函数之一。 使用GetResolvedToolOutput
助手获取函数的结果,然后将其提交给运行程序。
// Start a run for the agent to process the messages in the thread
ThreadRun run = client.Runs.CreateRun(thread.Id, agent.Id);
// Loop to check the run status and handle required actions
do
{
// Wait briefly before checking the status again
Thread.Sleep(TimeSpan.FromMilliseconds(500));
// Get the latest status of the run
run = client.Runs.GetRun(thread.Id, run.Id);
// Check if the agent requires a function call to proceed
if (run.Status == RunStatus.RequiresAction
&& run.RequiredAction is SubmitToolOutputsAction submitToolOutputsAction)
{
// Prepare a list to hold the outputs of the tool calls
List<ToolOutput> toolOutputs = [];
// Iterate through each required tool call
foreach (RequiredToolCall toolCall in submitToolOutputsAction.ToolCalls)
{
// Execute the function and get the output using the helper method
toolOutputs.Add(GetResolvedToolOutput(toolCall));
}
// Submit the collected tool outputs back to the run
run = client.Runs.SubmitToolOutputsToRun(run, toolOutputs);
}
}
// Continue looping while the run is in progress or requires action
while (run.Status == RunStatus.Queued
|| run.Status == RunStatus.InProgress
|| run.Status == RunStatus.RequiresAction);
检索和显示结果
运行完成后,从线程检索所有消息以查看完整对话,包括代理的最终响应。
// Retrieve all messages from the completed thread, oldest first
Pageable<PersistentThreadMessage> messages = client.Messages.GetMessages(
threadId: thread.Id,
order: ListSortOrder.Ascending
);
// Iterate through each message in the thread
foreach (PersistentThreadMessage threadMessage in messages)
{
// Iterate through content items in the message (usually just one text item)
foreach (MessageContent content in threadMessage.ContentItems)
{
// Process based on content type
switch (content)
{
// If it's a text message
case MessageTextContent textItem:
// Print the role (user/agent) and the text content
Console.WriteLine($"[{threadMessage.Role}]: {textItem.Text}");
break;
// Add handling for other content types if necessary (e.g., images)
}
}
}
清理资源
最后,通过删除线程和代理来清理已创建的资源。
// Delete the conversation thread
client.Threads.DeleteThread(threadId: thread.Id);
// Delete the agent definition
client.Administration.DeleteAgent(agentId: agent.Id);
定义代理要调用的函数
首先定义代理要调用的函数。 创建代理要调用的函数时,可以使用 docstring 中的任何必需参数来描述其结构。
class FunctionToolExecutor {
functionTools;
constructor() {
this.functionTools = [
{
func: this.getUserFavoriteCity,
...ToolUtility.createFunctionTool({
name: "getUserFavoriteCity",
description: "Gets the user's favorite city.",
parameters: {},
}),
},
{
func: this.getCityNickname,
...ToolUtility.createFunctionTool({
name: "getCityNickname",
description: "Gets the nickname of a city, e.g. 'LA' for 'Los Angeles, CA'.",
parameters: {
type: "object",
properties: {
___location: { type: "string", description: "The city and state, e.g. Seattle, Wa" },
},
},
}),
},
{
func: this.getWeather,
...ToolUtility.createFunctionTool({
name: "getWeather",
description: "Gets the weather for a ___location.",
parameters: {
type: "object",
properties: {
___location: { type: "string", description: "The city and state, e.g. Seattle, Wa" },
unit: { type: "string", enum: ["c", "f"] },
},
},
}),
},
];
}
getUserFavoriteCity() {
return { ___location: "Seattle, WA" };
}
getCityNickname(_location) {
return { nickname: "The Emerald City" };
}
getWeather(_location, unit) {
return { weather: unit === "f" ? "72f" : "22c" };
}
invokeTool(toolCall) {
console.log(`Function tool call - ${toolCall.function.name}`);
const args = [];
if (toolCall.function.parameters) {
try {
const params = JSON.parse(toolCall.function.parameters);
for (const key in params) {
if (Object.prototype.hasOwnProperty.call(params, key)) {
args.push(params[key]);
}
}
} catch (error) {
console.error(`Failed to parse parameters: ${toolCall.function.parameters}`, error);
return undefined;
}
}
const result = this.functionTools
.find((tool) => tool.definition.function.name === toolCall.function.name)
?.func(...args);
return result
? {
toolCallId: toolCall.id,
output: JSON.stringify(result),
}
: undefined;
}
getFunctionDefinitions() {
return this.functionTools.map((tool) => {
return tool.definition;
});
}
}
创建客户端和代理
const { AgentsClient, ToolUtility, isOutputOfType } = require("@azure/ai-agents");
const { delay } = require("@azure/core-util");
const { DefaultAzureCredential } = require("@azure/identity");
require("dotenv/config");
const projectEndpoint = process.env["PROJECT_ENDPOINT"];
const client = new AgentsClient(projectEndpoint, new DefaultAzureCredential());
const functionToolExecutor = new FunctionToolExecutor();
const functionTools = functionToolExecutor.getFunctionDefinitions();
const agent = await client.createAgent("gpt-4o", {
name: "my-agent",
instructions:
"You are a weather bot. Use the provided functions to help answer questions. Customize your responses to the user's preferences as much as possible and use friendly nicknames for cities whenever possible.",
tools: functionTools,
});
console.log(`Created agent, agent ID: ${agent.id}`);
创建线程
// Create thread
const thread = await client.threads.create();
console.log(`Created Thread, thread ID: ${thread.id}`);
// Create message
const message = await client.messages.create(
thread.id,
"user",
"What's the weather like in my favorite city?",
);
console.log(`Created message, message ID ${message.id}`);
创建运行并检查输出
// Create run
let run = await client.runs.create(thread.id, agent.id);
console.log(`Created Run, Run ID: ${run.id}`);
while (["queued", "in_progress", "requires_action"].includes(run.status)) {
await delay(1000);
run = await client.runs.get(thread.id, run.id);
console.log(`Current Run status - ${run.status}, run ID: ${run.id}`);
if (run.status === "requires_action" && run.requiredAction) {
console.log(`Run requires action - ${run.requiredAction}`);
if (isOutputOfType(run.requiredAction, "submit_tool_outputs")) {
const submitToolOutputsActionOutput = run.requiredAction;
const toolCalls = submitToolOutputsActionOutput.submitToolOutputs.toolCalls;
const toolResponses = [];
for (const toolCall of toolCalls) {
if (isOutputOfType(toolCall, "function")) {
const toolResponse = functionToolExecutor.invokeTool(toolCall);
if (toolResponse) {
toolResponses.push(toolResponse);
}
}
}
if (toolResponses.length > 0) {
run = await client.runs.submitToolOutputs(thread.id, run.id, toolResponses);
console.log(`Submitted tool response - ${run.status}`);
}
}
}
}
console.log(`Run status - ${run.status}, run ID: ${run.id}`);
const messages = client.messages.list(thread.id);
for await (const threadMessage of messages) {
console.log(
`Thread Message Created at - ${threadMessage.createdAt} - Role - ${threadMessage.role}`,
);
threadMessage.content.forEach((content) => {
if (isOutputOfType(content, "text")) {
const textContent = content;
console.log(`Text Message Content - ${textContent.text.value}`);
} else if (isOutputOfType(content, "image_file")) {
const imageContent = content;
console.log(`Image Message Content - ${imageContent.imageFile.fileId}`);
}
});
}
// Delete agent
await client.deleteAgent(agent.id);
console.log(`Deleted agent, agent ID: ${agent.id}`);
定义代理要调用的函数
首先定义代理要调用的函数。 创建代理要调用的函数时,可以使用 docstring 中的任何必需参数来描述其结构。 有关示例函数,请参阅其他 SDK 语言。
创建代理
请按照 REST API 快速入门 为环境变量 AGENT_TOKEN
、AZURE_AI_FOUNDRY_PROJECT_ENDPOINT
和 API_VERSION
设置正确的值。
curl --request POST \
--url $AZURE_AI_FOUNDRY_PROJECT_ENDPOINT/assistants?api-version=$API_VERSION \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"instructions": "You are a weather bot. Use the provided functions to answer questions.",
"model": "gpt-4o-mini",
tools=[{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the weather in ___location",
"parameters": {
"type": "object",
"properties": {
"___location": {"type": "string", "description": "The city name, for example San Francisco"}
},
"required": ["___location"]
}
}
}]
}'
创建线程
curl --request POST \
--url $AZURE_AI_FOUNDRY_PROJECT_ENDPOINT/threads?api-version=$API_VERSION \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d ''
将用户问题添加到线程
curl --request POST \
--url $AZURE_AI_FOUNDRY_PROJECT_ENDPOINT/threads/thread_abc123/messages?api-version=$API_VERSION \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"role": "user",
"content": "What is the weather in Seattle?"
}'
运行线程
curl --request POST \
--url $AZURE_AI_FOUNDRY_PROJECT_ENDPOINT/threads/thread_abc123/runs?api-version=$API_VERSION \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"assistant_id": "asst_abc123",
}'
检索运行状态
curl --request GET \
--url $AZURE_AI_FOUNDRY_PROJECT_ENDPOINT/threads/thread_abc123/runs/run_abc123?api-version=$API_VERSION \
-H "Authorization: Bearer $AGENT_TOKEN"
检索代理响应
curl --request GET \
--url $AZURE_AI_FOUNDRY_PROJECT_ENDPOINT/threads/thread_abc123/messages?api-version=$API_VERSION \
-H "Authorization: Bearer $AGENT_TOKEN"