Begin met het definiëren van een functie die uw agent moet aanroepen. Wanneer u een functie maakt die door een agent moet worden aangeroepen, beschrijft u de structuur ervan met alle vereiste parameters in een docstring. Neem al uw functiedefinities op in één bestand, user_functions.py
dat u vervolgens kunt importeren in uw hoofdscript.
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 (str): The location to fetch weather for.
:return: Weather information as a JSON string.
:rtype: str
"""
# In a real-world scenario, you'd integrate with a weather API.
# Here, we'll mock the response.
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.")
weather_json = json.dumps({"weather": weather})
return weather_json
# Statically defined user functions for fast reference
user_functions: Set[Callable[..., Any]] = {
fetch_weather,
}
Zie het Python-bestand op GitHub voor een voorbeeld van een volledige reeks functiedefinities. Dit bestand wordt in user_functions.py
het volgende voorbeeld hieronder genoemd.
// Example of a function that defines no parameters
string GetUserFavoriteCity() => "Seattle, WA";
FunctionToolDefinition getUserFavoriteCityTool = new("getUserFavoriteCity", "Gets the user's favorite city.");
// Example of a function with a single required parameter
string GetCityNickname(string location) => location switch
{
"Seattle, WA" => "The Emerald City",
_ => throw new NotImplementedException(),
};
FunctionToolDefinition getCityNicknameTool = new(
name: "getCityNickname",
description: "Gets the nickname of a city, e.g. 'LA' for 'Los Angeles, CA'.",
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 }));
In het volgende voorbeeld maken we een helperfunctie om de uitvoer van de opgeloste hulpprogramma's op te halen en te parseren en te retourneren.
ToolOutput GetResolvedToolOutput(RequiredToolCall toolCall)
{
if (toolCall is RequiredFunctionToolCall functionToolCall)
{
if (functionToolCall.Name == getUserFavoriteCityTool.Name)
{
return new ToolOutput(toolCall, GetUserFavoriteCity());
}
using JsonDocument argumentsJson = JsonDocument.Parse(functionToolCall.Arguments);
if (functionToolCall.Name == getCityNicknameTool.Name)
{
string locationArgument = argumentsJson.RootElement.GetProperty("location").GetString();
return new ToolOutput(toolCall, GetCityNickname(locationArgument));
}
}
return null;
}
class FunctionToolExecutor {
private functionTools: { func: Function, definition: FunctionToolDefinition }[];
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'] } } }
})
}];
}
private getUserFavoriteCity(): {} {
return { "location": "Seattle, WA" };
}
private getCityNickname(location: string): {} {
return { "nickname": "The Emerald City" };
}
private getWeather(location: string, unit: string): {} {
return { "weather": unit === "f" ? "72f" : "22c" };
}
public invokeTool(toolCall: RequiredToolCallOutput & FunctionToolDefinitionOutput): ToolOutput | undefined {
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;
}
public getFunctionDefinitions(): FunctionToolDefinition[] {
return this.functionTools.map(tool => {return tool.definition});
}
}
Het maken van de functiedefinitie en agent wordt gecombineerd in de volgende sectie.
In het onderstaande voorbeeld maken we een client en definiëren we een toolset
die wordt gebruikt voor het verwerken van de functies die zijn gedefinieerd in user_functions
.
toolset
: Wanneer u de parameter toolset gebruikt, geeft u niet alleen de functiedefinities en beschrijvingen op, maar ook de bijbehorende implementaties. De SDK voert deze functies uit binnen create_and_run_process of streaming. Deze functies worden aangeroepen op basis van hun definities.
import os
from azure.ai.projects import AIProjectClient
from azure.identity import DefaultAzureCredential
from azure.ai.projects.models import FunctionTool, ToolSet
from user_functions import user_functions # user functions which can be found in a user_functions.py file.
# Create an Azure AI Client from a connection string, copied from your Azure AI Foundry project.
# It should be in the format "<HostName>;<AzureSubscriptionId>;<ResourceGroup>;<HubName>"
# Customers need to login to Azure subscription via Azure CLI and set the environment variables
project_client = AIProjectClient.from_connection_string(
credential=DefaultAzureCredential(),
conn_str=os.environ["PROJECT_CONNECTION_STRING"],
)
# Initialize agent toolset with user functions
functions = FunctionTool(user_functions)
toolset = ToolSet()
toolset.add(functions)
agent = project_client.agents.create_agent(
model="gpt-4o-mini", name="my-agent", instructions="You are a weather bot. Use the provided functions to help answer questions.", toolset=toolset
)
print(f"Created agent, ID: {agent.id}")
// note: parallel function calling is only supported with newer models like gpt-4-1106-preview
Response<Agent> agentResponse = await client.CreateAgentAsync(
model: "gpt-4-1106-preview",
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: new List<ToolDefinition> { getUserFavoriteCityTool, getCityNicknameTool, getCurrentWeatherAtLocationTool }
);
Agent agent = agentResponse.Value;
const functionToolExecutor = new FunctionToolExecutor();
const functionTools = functionToolExecutor.getFunctionDefinitions();
const agent = await client.agents.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}`);
Volg de quickstart voor de REST API om de juiste waarden in te stellen voor de omgevingsvariabelen AZURE_AI_AGENTS_TOKEN
en AZURE_AI_AGENTS_ENDPOINT
.
curl $AZURE_AI_AGENTS_ENDPOINT/assistants?api-version=2024-12-01-preview \
-H "Authorization: Bearer $AZURE_AI_AGENTS_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"]
}
}
}]
}'
# Create thread for communication
thread = project_client.agents.create_thread()
print(f"Created thread, ID: {thread.id}")
# Create message to thread
message = project_client.agents.create_message(
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}")
Response<AgentThread> threadResponse = await client.CreateThreadAsync();
AgentThread thread = threadResponse.Value;
Response<ThreadMessage> messageResponse = await client.CreateMessageAsync(
thread.Id,
MessageRole.User,
"What's the weather like in my favorite city?");
ThreadMessage message = messageResponse.Value;
// create a thread
const thread = await client.agents.createThread();
// add a message to thread
await client.agents.createMessage(
thread.id, {
role: "user",
content: "What is the weather in Seattle?",
});
Een thread maken
curl $AZURE_AI_AGENTS_ENDPOINT/threads?api-version=2024-12-01-preview \
-H "Authorization: Bearer $AZURE_AI_AGENTS_TOKEN" \
-H "Content-Type: application/json" \
-d ''
Een gebruikersvraag toevoegen aan de thread
curl $AZURE_AI_AGENTS_ENDPOINT/threads/thread_abc123/messages?api-version=2024-12-01-preview \
-H "Authorization: Bearer $AZURE_AI_AGENTS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"role": "user",
"content": "What is the weather in Seattle?"
}'
# Create and process agent run in thread with tools
run = project_client.agents.create_and_process_run(thread_id=thread.id, agent_id=agent.id)
print(f"Run finished with status: {run.status}")
if run.status == "failed":
print(f"Run failed: {run.last_error}")
# Delete the agent when done
project_client.agents.delete_agent(agent.id)
print("Deleted agent")
# Fetch and log all messages
messages = project_client.agents.list_messages(thread_id=thread.id)
print(f"Messages: {messages}")
Response<ThreadRun> runResponse = await client.CreateRunAsync(thread, agent);
#region Snippet:FunctionsHandlePollingWithRequiredAction
do
{
await Task.Delay(TimeSpan.FromMilliseconds(500));
runResponse = await client.GetRunAsync(thread.Id, runResponse.Value.Id);
if (runResponse.Value.Status == RunStatus.RequiresAction
&& runResponse.Value.RequiredAction is SubmitToolOutputsAction submitToolOutputsAction)
{
List<ToolOutput> toolOutputs = new();
foreach (RequiredToolCall toolCall in submitToolOutputsAction.ToolCalls)
{
toolOutputs.Add(GetResolvedToolOutput(toolCall));
}
runResponse = await client.SubmitToolOutputsToRunAsync(runResponse.Value, toolOutputs);
}
}
while (runResponse.Value.Status == RunStatus.Queued
|| runResponse.Value.Status == RunStatus.InProgress);
#endregion
Response<PageableList<ThreadMessage>> afterRunMessagesResponse
= await client.GetMessagesAsync(thread.Id);
IReadOnlyList<ThreadMessage> messages = afterRunMessagesResponse.Value.Data;
// Note: messages iterate from newest to oldest, with the messages[0] being the most recent
foreach (ThreadMessage threadMessage in messages)
{
Console.Write($"{threadMessage.CreatedAt:yyyy-MM-dd HH:mm:ss} - {threadMessage.Role,10}: ");
foreach (MessageContent contentItem in threadMessage.ContentItems)
{
if (contentItem is MessageTextContent textItem)
{
Console.Write(textItem.Text);
}
else if (contentItem is MessageImageFileContent imageFileItem)
{
Console.Write($"<image from ID: {imageFileItem.FileId}");
}
Console.WriteLine();
}
}
// create a run
const streamEventMessages = await client.agents.createRun(thread.id, agent.id).stream();
for await (const eventMessage of streamEventMessages) {
switch (eventMessage.event) {
case RunStreamEvent.ThreadRunCreated:
break;
case MessageStreamEvent.ThreadMessageDelta:
{
const messageDelta = eventMessage.data;
messageDelta.delta.content.forEach((contentPart) => {
if (contentPart.type === "text") {
const textContent = contentPart;
const textValue = textContent.text?.value || "No text";
}
});
}
break;
case RunStreamEvent.ThreadRunCompleted:
break;
case ErrorEvent.Error:
console.log(`An error occurred. Data ${eventMessage.data}`);
break;
case DoneEvent.Done:
break;
}
}
// Print the messages from the agent
const messages = await client.agents.listMessages(thread.id);
// Messages iterate from oldest to newest
// messages[0] is the most recent
for (let i = messages.data.length - 1; i >= 0; i--) {
const m = messages.data[i];
if (isOutputOfType<MessageTextContentOutput>(m.content[0], "text")) {
const textContent = m.content[0];
console.log(`${textContent.text.value}`);
console.log(`---------------------------------`);
}
}
De thread uitvoeren
curl $AZURE_AI_AGENTS_ENDPOINT/threads/thread_abc123/runs?api-version=2024-12-01-preview \
-H "Authorization: Bearer $AZURE_AI_AGENTS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"assistant_id": "asst_abc123",
}'
De status van de uitvoering ophalen
curl $AZURE_AI_AGENTS_ENDPOINT/threads/thread_abc123/runs/run_abc123?api-version=2024-12-01-preview \
-H "Authorization: Bearer $AZURE_AI_AGENTS_TOKEN"
Het antwoord van de agent ophalen
curl $AZURE_AI_AGENTS_ENDPOINT/threads/thread_abc123/messages?api-version=2024-12-01-preview \
-H "Authorization: Bearer $AZURE_AI_AGENTS_TOKEN"