Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Lernprogramm erfahren Sie, wie Sie Ihren AG-UI Agents Funktionstools hinzufügen. Funktionstools sind benutzerdefinierte C#-Methoden, die der Agent aufrufen kann, um bestimmte Aufgaben wie das Abrufen von Daten, das Ausführen von Berechnungen oder die Interaktion mit externen Systemen auszuführen. Mit AG-UI werden diese Tools im Back-End ausgeführt, und ihre Ergebnisse werden automatisch an den Client gestreamt.
Voraussetzungen
Bevor Sie beginnen, stellen Sie sicher, dass Sie das Lernprogramm " Erste Schritte " abgeschlossen haben und folgendes haben:
- .NET 8.0 oder höher
-
Microsoft.Agents.AI.Hosting.AGUI.AspNetCorePaket installiert - Azure OpenAI-Dienst konfiguriert
- Grundlegendes Verständnis von AG-UI Server- und Clientsetup
Was ist Backend-Tool-Rendering?
Das Rendering des Backend-Tools bedeutet:
- Funktionstools werden auf dem Server definiert.
- Der KI-Agent entscheidet, wann diese Tools aufgerufen werden sollen.
- Tools werden im Back-End ausgeführt (serverseitig)
- Toolaufrufereignisse und Ergebnisse werden in Echtzeit an den Client gestreamt.
- Der Client empfängt Updates zum Fortschritt der Toolausführung.
Erstellen eines AG-UI Servers mit Funktionstools
Hier ist eine vollständige Serverimplementierung, die veranschaulicht, wie Tools mit komplexen Parametertypen registriert werden:
// 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();
Wichtige Konzepte
- Serverseitige Ausführung: Im Serverprozess ausgeführte Tools
- Automatisches Streaming: Toolaufrufe und Ergebnisse werden in Echtzeit an Clients gestreamt.
Von Bedeutung
Beim Erstellen von Tools mit komplexen Parametertypen (Objekte, Arrays usw.) müssen Sie den serializerOptions Parameter AIFunctionFactory.Create() angeben. Die Optionen für die Serialisierung sollten mithilfe des konfigurierten JsonOptions, über IOptions<Microsoft.AspNetCore.Http.Json.JsonOptions> abgerufen werden, um die Konsistenz mit der JSON-Serialisierung der restlichen Anwendung sicherzustellen.
Ausführen des Servers
Festlegen von Umgebungsvariablen und Ausführen:
export AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/"
export AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4o-mini"
dotnet run --urls http://localhost:8888
Beobachten von Toolaufrufen im Client
Der grundlegende Client aus dem Lernprogramm "Erste Schritte" zeigt die endgültige Textantwort des Agents an. Sie können sie jedoch erweitern, um Toolaufrufe und Ergebnisse zu beobachten, während sie vom Server gestreamt werden.
Anzeigen von Toolausführungsdetails
Um Toolaufrufe und Ergebnisse in Echtzeit anzuzeigen, erweitern Sie die Streamingschleife des Clients, damit sie FunctionCallContent und FunctionResultContent verarbeitet:
// 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;
}
}
}
Erwartete Ausgabe mit Werkzeugbefehlen
Wenn der Agent Back-End-Tools aufruft, wird Folgendes angezeigt:
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]
Wichtige Konzepte
-
FunctionCallContent: Stellt ein Tool dar, das mit seinenNameundArguments(Parameterschlüssel-Wert-Paaren) aufgerufen wird. -
FunctionResultContent: Enthält dasResult-Tool oderException, identifiziert durchCallId
Nächste Schritte
Jetzt, da Sie Funktionstools hinzufügen können, können Sie:
- Frontend-Tools: Hinzufügen von Frontend-Tools.
- Testen mit Dojo: Verwenden der Dojo-App der AG-UI zum Testen Ihrer Agents
Zusätzliche Ressourcen
In diesem Lernprogramm erfahren Sie, wie Sie Ihren AG-UI Agents Funktionstools hinzufügen. Funktionstools sind benutzerdefinierte Python-Funktionen, die der Agent aufrufen kann, um bestimmte Aufgaben wie das Abrufen von Daten, das Ausführen von Berechnungen oder die Interaktion mit externen Systemen auszuführen. Mit AG-UI werden diese Tools im Back-End ausgeführt, und ihre Ergebnisse werden automatisch an den Client gestreamt.
Voraussetzungen
Bevor Sie beginnen, stellen Sie sicher, dass Sie das Lernprogramm " Erste Schritte " abgeschlossen haben und folgendes haben:
- Python 3.10 oder höher
-
agent-framework-ag-uiinstalliert - Azure OpenAI-Dienst konfiguriert
- Grundlegendes Verständnis von AG-UI Server- und Clientsetup
Hinweis
In diesen Beispielen wird DefaultAzureCredential zur Authentifizierung verwendet. Stellen Sie sicher, dass Sie bei Azure authentifiziert sind (z. B. über az login). Weitere Informationen finden Sie in der Azure Identity-Dokumentation.
Was ist Backend-Tool-Rendering?
Das Rendering des Backend-Tools bedeutet:
- Funktionstools werden auf dem Server definiert.
- Der KI-Agent entscheidet, wann diese Tools aufgerufen werden sollen.
- Tools werden im Back-End ausgeführt (serverseitig)
- Toolaufrufereignisse und Ergebnisse werden in Echtzeit an den Client gestreamt.
- Der Client empfängt Updates zum Fortschritt der Toolausführung.
Dieser Ansatz bietet Folgendes:
- Sicherheit: Vertrauliche Vorgänge bleiben auf dem Server
- Konsistenz: Alle Clients verwenden dieselben Toolimplementierungen
- Transparenz: Clients können den Fortschritt der Toolausführung anzeigen
- Flexibilität: Aktualisieren von Tools ohne Ändern von Clientcode
Erstellen von Funktionstools
Grundlegendes Funktionstool
Sie können jede Python-Funktion in ein Tool umwandeln, indem Sie den @tool Dekorateur verwenden:
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."
Wichtige Konzepte
-
@toolDekorateur: Kennzeichnet eine Funktion als verfügbar für den Agent - Typanmerkungen: Bereitstellen von Typinformationen für Parameter
-
AnnotatedundField: Hinzufügen von Beschreibungen, die dem Agent helfen, Parameter zu verstehen - Docstring: Beschreibt, was die Funktion tut (hilft dem Agent zu entscheiden, wann sie verwendet werden soll)
- Rückgabewert: Das an den Agent zurückgegebene Ergebnis (und wird an den Client gestreamt)
Tools für mehrere Funktionen
Sie können mehrere Tools bereitstellen, um dem Agent mehr Funktionen zu bieten:
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},
],
}
Erstellen eines AG-UI Servers mit Funktionstools
Hier ist eine vollständige Serverimplementierung mit Funktionstools:
"""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)
Grundlegendes zu Toolereignissen
Wenn der Agent ein Tool aufruft, empfängt der Client mehrere Ereignisse:
Ereignisse bei Toolaufrufen
# 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."
}
Erweiterter Client für Toolereignisse
Hier ist ein erweiterter Client, der mit AGUIChatClient die Ausführung von Tools anzeigt.
"""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())
Beispielinteraktion
Mit dem erweiterten Server und dem Client, auf dem Folgendes ausgeführt wird:
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]
Bewährte Methoden für die Toolimplementierung
Fehlerbehandlung
Behandeln Sie Fehler ordentlich in Ihren Tools:
@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)}"
Rich-Rückgabetypen
Geben Sie bei Bedarf strukturierte Daten zurück:
@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,
},
}
Beschreibende Dokumentation
Stellen Sie klare Beschreibungen bereit, mit denen der Agent verstehen kann, wann Tools verwendet werden:
@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
Toolorganisation mit Klassen
Ordnen Sie die zugehörigen Werkzeuge in einer Klasse an.
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,
],
)
Nächste Schritte
Nachdem Sie nun mit dem Rendern von Back-End-Tools vertraut sind, haben Sie folgende Möglichkeiten:
- Erstellen erweiterter Tools: Weitere Informationen zum Erstellen von Funktionstools mit Agent Framework