Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Questa esercitazione illustra come aggiungere strumenti di funzione agli agenti AG-UI. Gli strumenti per le funzioni sono metodi C# personalizzati che l'agente può chiamare per eseguire attività specifiche, ad esempio il recupero di dati, l'esecuzione di calcoli o l'interazione con sistemi esterni. Con AG-UI, questi strumenti vengono eseguiti nel backend e i loro risultati vengono trasferiti automaticamente al client.
Prerequisiti
Prima di iniziare, assicurarsi di aver completato l'esercitazione introduttiva e di avere:
- .NET 8.0 o versione successiva
-
Microsoft.Agents.AI.Hosting.AGUI.AspNetCorepacchetto installato - Servizio OpenAI di Azure configurato
- Conoscenza di base della configurazione di AG-UI server e client
Che cos'è il rendering dello strumento backend?
Il rendering degli strumenti nel contesto back-end consiste in:
- Gli strumenti per le funzioni sono definiti nel server
- L'agente di intelligenza artificiale decide quando chiamare questi strumenti
- Strumenti eseguiti nel back-end (lato server)
- Gli eventi e i risultati delle chiamate degli strumenti vengono trasmessi al client in tempo reale
- Il client riceve aggiornamenti sullo stato di esecuzione degli strumenti
Creazione di un server AG-UI con gli strumenti per le funzioni
Ecco un'implementazione completa del server che illustra come registrare gli strumenti con tipi di parametri complessi:
// Copyright (c) Microsoft. All rights reserved.
using System.ComponentModel;
using System.Text.Json.Serialization;
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Hosting.AGUI.AspNetCore;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Options;
using OpenAI.Chat;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient().AddLogging();
builder.Services.ConfigureHttpJsonOptions(options =>
options.SerializerOptions.TypeInfoResolverChain.Add(SampleJsonSerializerContext.Default));
builder.Services.AddAGUI();
WebApplication app = builder.Build();
string endpoint = builder.Configuration["AZURE_OPENAI_ENDPOINT"]
?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
string deploymentName = builder.Configuration["AZURE_OPENAI_DEPLOYMENT_NAME"]
?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT_NAME is not set.");
// Define request/response types for the tool
internal sealed class RestaurantSearchRequest
{
public string Location { get; set; } = string.Empty;
public string Cuisine { get; set; } = "any";
}
internal sealed class RestaurantSearchResponse
{
public string Location { get; set; } = string.Empty;
public string Cuisine { get; set; } = string.Empty;
public RestaurantInfo[] Results { get; set; } = [];
}
internal sealed class RestaurantInfo
{
public string Name { get; set; } = string.Empty;
public string Cuisine { get; set; } = string.Empty;
public double Rating { get; set; }
public string Address { get; set; } = string.Empty;
}
// JSON serialization context for source generation
[JsonSerializable(typeof(RestaurantSearchRequest))]
[JsonSerializable(typeof(RestaurantSearchResponse))]
internal sealed partial class SampleJsonSerializerContext : JsonSerializerContext;
// Define the function tool
[Description("Search for restaurants in a location.")]
static RestaurantSearchResponse SearchRestaurants(
[Description("The restaurant search request")] RestaurantSearchRequest request)
{
// Simulated restaurant data
string cuisine = request.Cuisine == "any" ? "Italian" : request.Cuisine;
return new RestaurantSearchResponse
{
Location = request.Location,
Cuisine = request.Cuisine,
Results =
[
new RestaurantInfo
{
Name = "The Golden Fork",
Cuisine = cuisine,
Rating = 4.5,
Address = $"123 Main St, {request.Location}"
},
new RestaurantInfo
{
Name = "Spice Haven",
Cuisine = cuisine == "Italian" ? "Indian" : cuisine,
Rating = 4.7,
Address = $"456 Oak Ave, {request.Location}"
},
new RestaurantInfo
{
Name = "Green Leaf",
Cuisine = "Vegetarian",
Rating = 4.3,
Address = $"789 Elm Rd, {request.Location}"
}
]
};
}
// Get JsonSerializerOptions from the configured HTTP JSON options
Microsoft.AspNetCore.Http.Json.JsonOptions jsonOptions = app.Services.GetRequiredService<IOptions<Microsoft.AspNetCore.Http.Json.JsonOptions>>().Value;
// Create tool with serializer options
AITool[] tools =
[
AIFunctionFactory.Create(
SearchRestaurants,
serializerOptions: jsonOptions.SerializerOptions)
];
// Create the AI agent with tools
ChatClient chatClient = new AzureOpenAIClient(
new Uri(endpoint),
new DefaultAzureCredential())
.GetChatClient(deploymentName);
ChatClientAgent agent = chatClient.AsIChatClient().AsAIAgent(
name: "AGUIAssistant",
instructions: "You are a helpful assistant with access to restaurant information.",
tools: tools);
// Map the AG-UI agent endpoint
app.MapAGUI("/", agent);
await app.RunAsync();
Concetti chiave
- Esecuzione lato server: strumenti eseguiti nel processo del server
- Streaming automatico: le chiamate e i risultati degli strumenti vengono trasmessi ai client in tempo reale
Importante
Quando si creano strumenti con tipi di parametri complessi (oggetti, matrici e così via), è necessario fornire il serializerOptions parametro a AIFunctionFactory.Create(). Le opzioni del serializzatore devono essere ottenute dall'applicazione configurate JsonOptions tramite IOptions<Microsoft.AspNetCore.Http.Json.JsonOptions> per garantire la coerenza con il resto della serializzazione JSON dell'applicazione.
Esecuzione del server
Impostare le variabili di ambiente ed eseguire:
export AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/"
export AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o-mini"
dotnet run --urls http://localhost:8888
Osservazione delle chiamate degli strumenti nel client
Il client di base del tutorial "Getting Started" visualizza la risposta di testo finale dell'agente. È tuttavia possibile estenderlo per osservare le chiamate e i risultati degli strumenti man mano che vengono trasmessi dal server.
Visualizzazione dei dettagli di esecuzione degli strumenti
Per visualizzare le chiamate e i risultati degli strumenti in tempo reale, estendere il ciclo di streaming del client per gestire FunctionCallContent e FunctionResultContent:
// Inside the streaming loop from getting-started.md
await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, session))
{
ChatResponseUpdate chatUpdate = update.AsChatResponseUpdate();
// ... existing run started code ...
// Display streaming content
foreach (AIContent content in update.Contents)
{
switch (content)
{
case TextContent textContent:
Console.ForegroundColor = ConsoleColor.Cyan;
Console.Write(textContent.Text);
Console.ResetColor();
break;
case FunctionCallContent functionCallContent:
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"\n[Function Call - Name: {functionCallContent.Name}]");
// Display individual parameters
if (functionCallContent.Arguments != null)
{
foreach (var kvp in functionCallContent.Arguments)
{
Console.WriteLine($" Parameter: {kvp.Key} = {kvp.Value}");
}
}
Console.ResetColor();
break;
case FunctionResultContent functionResultContent:
Console.ForegroundColor = ConsoleColor.Magenta;
Console.WriteLine($"\n[Function Result - CallId: {functionResultContent.CallId}]");
if (functionResultContent.Exception != null)
{
Console.WriteLine($" Exception: {functionResultContent.Exception}");
}
else
{
Console.WriteLine($" Result: {functionResultContent.Result}");
}
Console.ResetColor();
break;
case ErrorContent errorContent:
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"\n[Error: {errorContent.Message}]");
Console.ResetColor();
break;
}
}
}
Output previsto con chiamate degli strumenti
Quando l'agente chiama gli strumenti back-end, verrà visualizzato:
User (:q or quit to exit): What's the weather like in Amsterdam?
[Run Started - Thread: thread_abc123, Run: run_xyz789]
[Function Call - Name: SearchRestaurants]
Parameter: Location = Amsterdam
Parameter: Cuisine = any
[Function Result - CallId: call_def456]
Result: {"Location":"Amsterdam","Cuisine":"any","Results":[...]}
The weather in Amsterdam is sunny with a temperature of 22°C. Here are some
great restaurants in the area: The Golden Fork (Italian, 4.5 stars)...
[Run Finished - Thread: thread_abc123]
Concetti chiave
-
FunctionCallContent: rappresenta uno strumento chiamato con i suoiNameeArguments(coppie di chiavi-valore dei parametri) -
FunctionResultContent: contiene ilResultoExceptiondello strumento, identificato daCallId
Passaggi successivi
Ora che è possibile aggiungere strumenti per le funzioni, è possibile:
- Strumenti front-end: aggiungere strumenti front-end.
- Test con Dojo: Usa l'app Dojo di AG-UI per testare gli agenti
Risorse aggiuntive
Questa esercitazione illustra come aggiungere strumenti di funzione agli agenti AG-UI. Gli strumenti per le funzioni sono funzioni Python personalizzate che l'agente può chiamare per eseguire attività specifiche, ad esempio il recupero di dati, l'esecuzione di calcoli o l'interazione con sistemi esterni. Con AG-UI, questi strumenti vengono eseguiti nel backend e i loro risultati vengono trasferiti automaticamente al client.
Prerequisiti
Prima di iniziare, assicurarsi di aver completato l'esercitazione introduttiva e di avere:
- Python 3.10 o versione successiva
-
agent-framework-ag-uiinstallato - Servizio OpenAI di Azure configurato
- Conoscenza di base della configurazione di AG-UI server e client
Annotazioni
Questi esempi usano DefaultAzureCredential per l'autenticazione. Assicurarsi di essere autenticati con Azure (ad esempio, tramite az login). Per altre informazioni, vedere la documentazione sull'identità di Azure.
Che cos'è il rendering dello strumento backend?
Il rendering degli strumenti nel contesto back-end consiste in:
- Gli strumenti per le funzioni sono definiti nel server
- L'agente di intelligenza artificiale decide quando chiamare questi strumenti
- Strumenti eseguiti nel back-end (lato server)
- Gli eventi e i risultati delle chiamate degli strumenti vengono trasmessi al client in tempo reale
- Il client riceve aggiornamenti sullo stato di esecuzione degli strumenti
Questo approccio offre:
- Sicurezza: le operazioni sensibili rimangono sul server
- Coerenza: tutti i client usano le stesse implementazioni degli strumenti
- Trasparenza: i client possono visualizzare lo stato di esecuzione degli strumenti
- Flessibilità: aggiornare gli strumenti senza modificare il codice client
Creazione di strumenti per le funzioni
Strumento Funzione Base
È possibile trasformare qualsiasi funzione Python in uno strumento usando l'elemento @tool Decorator:
from typing import Annotated
from pydantic import Field
from agent_framework import tool
@tool
def get_weather(
location: Annotated[str, Field(description="The city")],
) -> str:
"""Get the current weather for a location."""
# In a real application, you would call a weather API
return f"The weather in {location} is sunny with a temperature of 22°C."
Concetti chiave
-
@tooldecorator: contrassegna una funzione come disponibile per l'agente - Annotazioni di tipo: fornire informazioni sul tipo per i parametri
-
AnnotatedeField: aggiungere descrizioni per aiutare l'agente a comprendere i parametri - Docstring: descrive le operazioni che la funzione esegue (aiuta l'agente a decidere quando usarla)
- Valore restituito: il risultato restituito all'agente (e trasmesso al client)
Strumenti Multi-Funzione
È possibile fornire più strumenti per offrire all'agente più funzionalità:
from typing import Any
from agent_framework import tool
@tool
def get_weather(
location: Annotated[str, Field(description="The city.")],
) -> str:
"""Get the current weather for a location."""
return f"The weather in {location} is sunny with a temperature of 22°C."
@tool
def get_forecast(
location: Annotated[str, Field(description="The city.")],
days: Annotated[int, Field(description="Number of days to forecast")] = 3,
) -> dict[str, Any]:
"""Get the weather forecast for a location."""
return {
"location": location,
"days": days,
"forecast": [
{"day": 1, "weather": "Sunny", "high": 24, "low": 18},
{"day": 2, "weather": "Partly cloudy", "high": 22, "low": 17},
{"day": 3, "weather": "Rainy", "high": 19, "low": 15},
],
}
Creazione di un server AG-UI con gli strumenti per le funzioni
Ecco un'implementazione completa del server con gli strumenti per le funzioni:
"""AG-UI server with backend tool rendering."""
import os
from typing import Annotated, Any
from agent_framework import Agent, tool
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework_ag_ui import add_agent_framework_fastapi_endpoint
from azure.identity import AzureCliCredential
from fastapi import FastAPI
from pydantic import Field
# Define function tools
@tool
def get_weather(
location: Annotated[str, Field(description="The city")],
) -> str:
"""Get the current weather for a location."""
# Simulated weather data
return f"The weather in {location} is sunny with a temperature of 22°C."
@tool
def search_restaurants(
location: Annotated[str, Field(description="The city to search in")],
cuisine: Annotated[str, Field(description="Type of cuisine")] = "any",
) -> dict[str, Any]:
"""Search for restaurants in a location."""
# Simulated restaurant data
return {
"location": location,
"cuisine": cuisine,
"results": [
{"name": "The Golden Fork", "rating": 4.5, "price": "$$"},
{"name": "Bella Italia", "rating": 4.2, "price": "$$$"},
{"name": "Spice Garden", "rating": 4.7, "price": "$$"},
],
}
# Read required configuration
endpoint = os.environ.get("AZURE_OPENAI_ENDPOINT")
deployment_name = os.environ.get("AZURE_OPENAI_DEPLOYMENT_NAME")
if not endpoint:
raise ValueError("AZURE_OPENAI_ENDPOINT environment variable is required")
if not deployment_name:
raise ValueError("AZURE_OPENAI_DEPLOYMENT_NAME environment variable is required")
chat_client = AzureOpenAIChatClient(
credential=AzureCliCredential(),
endpoint=endpoint,
deployment_name=deployment_name,
)
# Create agent with tools
agent = Agent(
name="TravelAssistant",
instructions="You are a helpful travel assistant. Use the available tools to help users plan their trips.",
chat_client=chat_client,
tools=[get_weather, search_restaurants],
)
# Create FastAPI app
app = FastAPI(title="AG-UI Travel Assistant")
add_agent_framework_fastapi_endpoint(app, agent, "/")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8888)
Informazioni sugli eventi degli strumenti
Quando l'agente chiama uno strumento, il client riceve diversi eventi:
Eventi di chiamata dello strumento
# 1. TOOL_CALL_START - Tool execution begins
{
"type": "TOOL_CALL_START",
"toolCallId": "call_abc123",
"toolCallName": "get_weather"
}
# 2. TOOL_CALL_ARGS - Tool arguments (may stream in chunks)
{
"type": "TOOL_CALL_ARGS",
"toolCallId": "call_abc123",
"delta": "{\"location\": \"Paris, France\"}"
}
# 3. TOOL_CALL_END - Arguments complete
{
"type": "TOOL_CALL_END",
"toolCallId": "call_abc123"
}
# 4. TOOL_CALL_RESULT - Tool execution result
{
"type": "TOOL_CALL_RESULT",
"toolCallId": "call_abc123",
"content": "The weather in Paris, France is sunny with a temperature of 22°C."
}
Client avanzato per eventi dei tool
Ecco un client avanzato che AGUIChatClient visualizza l'esecuzione degli strumenti:
"""AG-UI client with tool event handling."""
import asyncio
import os
from agent_framework import Agent, ToolCallContent, ToolResultContent
from agent_framework_ag_ui import AGUIChatClient
async def main():
"""Main client loop with tool event display."""
server_url = os.environ.get("AGUI_SERVER_URL", "http://127.0.0.1:8888/")
print(f"Connecting to AG-UI server at: {server_url}\n")
# Create AG-UI chat client
chat_client = AGUIChatClient(server_url=server_url)
# Create agent with the chat client
agent = Agent(
name="ClientAgent",
chat_client=chat_client,
instructions="You are a helpful assistant.",
)
# Get a thread for conversation continuity
thread = agent.create_session()
try:
while True:
message = input("\nUser (:q or quit to exit): ")
if not message.strip():
continue
if message.lower() in (":q", "quit"):
break
print("\nAssistant: ", end="", flush=True)
async for update in agent.run(message, session=thread, stream=True):
# Display text content
if update.text:
print(f"\033[96m{update.text}\033[0m", end="", flush=True)
# Display tool calls and results
for content in update.contents:
if isinstance(content, ToolCallContent):
print(f"\n\033[95m[Calling tool: {content.name}]\033[0m")
elif isinstance(content, ToolResultContent):
result_text = content.result if isinstance(content.result, str) else str(content.result)
print(f"\033[94m[Tool result: {result_text}]\033[0m")
print("\n")
except KeyboardInterrupt:
print("\n\nExiting...")
except Exception as e:
print(f"\n\033[91mError: {e}\033[0m")
if __name__ == "__main__":
asyncio.run(main())
Interazione di esempio
Con il server e il client migliorati operativi:
User (:q or quit to exit): What's the weather like in Paris and suggest some Italian restaurants?
[Run Started]
[Tool Call: get_weather]
[Tool Result: The weather in Paris, France is sunny with a temperature of 22°C.]
[Tool Call: search_restaurants]
[Tool Result: {"location": "Paris", "cuisine": "Italian", "results": [...]}]
Based on the current weather in Paris (sunny, 22°C) and your interest in Italian cuisine,
I'd recommend visiting Bella Italia, which has a 4.2 rating. The weather is perfect for
outdoor dining!
[Run Finished]
Procedure consigliate per l'implementazione degli strumenti
Gestione degli errori
Gestire gli errori in modo elegante nei tuoi strumenti.
@tool
def get_weather(
location: Annotated[str, Field(description="The city.")],
) -> str:
"""Get the current weather for a location."""
try:
# Call weather API
result = call_weather_api(location)
return f"The weather in {location} is {result['condition']} with temperature {result['temp']}°C."
except Exception as e:
return f"Unable to retrieve weather for {location}. Error: {str(e)}"
Tipi di ritorno
Restituire dati strutturati quando appropriato:
@tool
def analyze_sentiment(
text: Annotated[str, Field(description="The text to analyze")],
) -> dict[str, Any]:
"""Analyze the sentiment of text."""
# Perform sentiment analysis
return {
"text": text,
"sentiment": "positive",
"confidence": 0.87,
"scores": {
"positive": 0.87,
"neutral": 0.10,
"negative": 0.03,
},
}
Documentazione descrittiva
Fornire descrizioni chiare per aiutare l'agente a comprendere quando usare gli strumenti:
@tool
def book_flight(
origin: Annotated[str, Field(description="Departure city and airport code, e.g., 'New York, JFK'")],
destination: Annotated[str, Field(description="Arrival city and airport code, e.g., 'London, LHR'")],
date: Annotated[str, Field(description="Departure date in YYYY-MM-DD format")],
passengers: Annotated[int, Field(description="Number of passengers")] = 1,
) -> dict[str, Any]:
"""
Book a flight for specified passengers from origin to destination.
This tool should be used when the user wants to book or reserve airline tickets.
Do not use this for searching flights - use search_flights instead.
"""
# Implementation
pass
Organizzazione degli strumenti con classi
Per gli strumenti correlati, organizzarli in una classe:
from agent_framework import tool
class WeatherTools:
"""Collection of weather-related tools."""
def __init__(self, api_key: str):
self.api_key = api_key
@tool
def get_current_weather(
self,
location: Annotated[str, Field(description="The city.")],
) -> str:
"""Get current weather for a location."""
# Use self.api_key to call API
return f"Current weather in {location}: Sunny, 22°C"
@tool
def get_forecast(
self,
location: Annotated[str, Field(description="The city.")],
days: Annotated[int, Field(description="Number of days")] = 3,
) -> dict[str, Any]:
"""Get weather forecast for a location."""
# Use self.api_key to call API
return {"location": location, "forecast": [...]}
# Create tools instance
weather_tools = WeatherTools(api_key="your-api-key")
# Create agent with class-based tools
agent = Agent(
name="WeatherAgent",
instructions="You are a weather assistant.",
chat_client=AzureOpenAIChatClient(...),
tools=[
weather_tools.get_current_weather,
weather_tools.get_forecast,
],
)
Passaggi successivi
Dopo aver compreso il rendering degli strumenti back-end, è possibile:
- Creare strumenti avanzati: altre informazioni sulla creazione di strumenti per le funzioni con Agent Framework