Rendu de l’outil principal avec AG-UI

Les outils principaux utilisent le pipeline d’outils MAF normal. AG-UI ajoute des événements de transport afin qu’un client puisse observer l’appel et le résultat ; elle n’introduit pas d’abstraction d’outil distincte.

Ajouter un outil d’administration

Définissez et inscrivez l’outil comme vous le feriez pour n’importe quel agent MAF :

using System.ComponentModel;
using Microsoft.Extensions.AI;

[Description("Get the weather for a location.")]
static string GetWeather(
    [Description("The city to look up.")] string location) =>
    $"The weather in {location} is sunny.";

AITool getWeather = AIFunctionFactory.Create(GetWeather, name: "get_weather");
AIAgent agent = chatClient.AsAIAgent(tools: [getWeather]);

app.MapAGUIServer("/", agent);

Pour les types de requête ou de réponse complexes, configurez de la même manière JsonSerializerOptions pour ASP.NET Core et AIFunctionFactory.Create.

Tip

Consultez l’exemple .NET outils principaux pour une implémentation complète.

Pour les schémas d’outil, l’injection de dépendances, la gestion des erreurs et la conception générale des outils, consultez Utiliser des outils de fonction avec un agent.

correspondance d’événements AG-UI

Lorsque l’agent appelle l’outil :

  • FunctionCallContent est émis sous forme d’événements AG-UI TOOL_CALL_START, TOOL_CALL_ARGS et TOOL_CALL_END.
  • FunctionResultContent est émis en tant qu’événement TOOL_CALL_RESULT .
  • Le texte et les autres contenus de l’agent continuent de s’afficher normalement.

Un client .NET reçoit le contenu traduit en tant que :FunctionCallContentFunctionResultContent

await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, session))
{
    foreach (AIContent content in update.Contents)
    {
        if (content is FunctionCallContent call)
        {
            Console.WriteLine($"Calling {call.Name}");
        }
        else if (content is FunctionResultContent result)
        {
            Console.WriteLine($"Result: {result.Result}");
        }
    }
}

Les résultats de l’outil sont des valeurs destinées au modèle, qu’AG-UI expose également au client. Pour émettre un état d’interface utilisateur partagé en plus d’un résultat d’outil, utilisez les mappages explicites décrits dans la gestion de l’état.

Étapes suivantes

Ce tutoriel vous montre comment ajouter des outils de fonction à vos agents AG-UI. Les outils de fonction sont des fonctions Python personnalisées que l’agent peut appeler pour effectuer des tâches spécifiques telles que la récupération de données, l’exécution de calculs ou l’interaction avec des systèmes externes. Avec AG-UI, ces outils s'exécutent en backend et leurs résultats sont automatiquement diffusés au client.

Prerequisites

Avant de commencer, vérifiez que vous avez terminé le didacticiel De prise en main et que vous disposez des points suivants :

  • Python 3.10 ou version ultérieure
  • agent-framework-ag-ui installé
  • Le service OpenAI d'Azure est configuré
  • Compréhension de base de la configuration du serveur et du client AG-UI

Note

Ces exemples utilisent DefaultAzureCredential pour l’authentification. Vérifiez que vous êtes authentifié auprès d’Azure (par exemple, via az login). Pour plus d’informations, consultez la documentation d’Azure Identity.

Qu’est-ce que le rendu de l’outil backend ?

L'affichage de l'outil back-end signifie :

  • Les outils de fonction sont définis sur le serveur
  • L’agent IA décide quand appeler ces outils
  • Les outils s’exécutent sur le serveur principal (côté serveur)
  • Les événements et les résultats des appels d'outils sont diffusés en continu vers le client en temps réel.
  • Le client reçoit des mises à jour sur la progression de l’exécution de l’outil

Cette approche fournit les éléments suivants :

  • Sécurité : Les opérations sensibles restent sur le serveur
  • Cohérence : tous les clients utilisent les mêmes implémentations d’outils
  • Transparence : les clients peuvent afficher la progression de l’exécution des outils
  • Flexibilité : Mettre à jour les outils sans modifier le code client

Création d’outils de fonction

Outil de fonction de base

Vous pouvez transformer n’importe quelle fonction Python en un outil à l’aide du @tool décorateur :

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."

Concepts clés

  • @tool décorateur : marque une fonction comme disponible pour l’agent
  • Annotations de type : fournir des informations de type pour les paramètres
  • Annotated et Field: Ajouter des descriptions pour aider l’agent à comprendre les paramètres
  • Docstring : décrit ce que fait la fonction (aide l’agent à décider quand l’utiliser)
  • Valeur de retour : résultat retourné à l’agent (et transmis en continu au client)

Outils de fonction multiples

Vous pouvez fournir plusieurs outils pour offrir à l’agent plus de fonctionnalités :

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},
        ],
    }

Création d’un serveur AG-UI avec Function Tools

Voici une implémentation complète du serveur avec les outils de fonction :

"""AG-UI server with backend tool rendering."""

import os
from typing import Annotated, Any

from agent_framework import Agent, tool
from agent_framework.openai import OpenAIChatCompletionClient
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_CHAT_COMPLETION_MODEL")

if not endpoint:
    raise ValueError("AZURE_OPENAI_ENDPOINT environment variable is required")
if not deployment_name:
    raise ValueError("AZURE_OPENAI_CHAT_COMPLETION_MODEL environment variable is required")

chat_client = OpenAIChatCompletionClient(
    model=deployment_name,
    azure_endpoint=endpoint,
    api_version=os.getenv("AZURE_OPENAI_API_VERSION"),
    credential=AzureCliCredential(),
)

# 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.",
    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)

Présentation des événements de l’outil

Lorsque l’agent appelle un outil, le client reçoit plusieurs événements :

Événements d’appel d’outil

# 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 optimisé pour les événements de logiciel

Voici un client AGUIChatClient amélioré qui affiche l’exécution de l’outil :

"""AG-UI client with tool event handling."""

import asyncio
import os

from agent_framework import Agent
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(endpoint=server_url)

    # Create agent with the chat client
    agent = Agent(
        name="ClientAgent",
        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 content.type == "function_call":
                        print(f"\n\033[95m[Calling tool: {content.name}]\033[0m")
                    elif content.type == "function_result":
                        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())

Exemple d’interaction

Avec le serveur et le client améliorés en fonctionnement :

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]

Meilleures pratiques d’implémentation des outils

Gestion des erreurs

Gérez les erreurs correctement dans vos outils :

@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)}"

Types de retour enrichis

Retournez des données structurées le cas échéant :

@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,
        },
    }

Documentation descriptive

Fournissez des descriptions claires pour aider l’agent à comprendre quand utiliser des outils :

@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

Structuration de l'outil avec des classes

Pour les outils connexes, organisez-les dans une 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.",
    client=OpenAIChatCompletionClient(...),
    tools=[
        weather_tools.get_current_weather,
        weather_tools.get_forecast,
    ],
)

Prochaines étapes

Maintenant que vous comprenez le rendu des outils principaux, vous pouvez :

Ressources additionnelles

Les serveurs AG-UI Go peuvent exposer les outils de fonction standard d’Agent Framework. Créez des outils avec tool/functool, attachez-les à l’agent hébergé et servez-le avec aguiprovider.NewJSONHTTPHandler.

searchRestaurants := functool.MustNew(functool.Config{
    Name:        "search_restaurants",
    Description: "Search for restaurants in a location.",
}, func(ctx context.Context, in restaurantSearchRequest) (restaurantSearchResponse, error) {
    return restaurantSearchResponse{
        Location: in.Location,
        Cuisine:  in.Cuisine,
        Results:  []restaurantInfo{{Name: "The Golden Fork", Cuisine: in.Cuisine}},
    }, nil
})

a := foundryprovider.NewAgent(endpoint, token, foundryprovider.ModelDeployment(model), foundryprovider.AgentConfig{
    Config: agent.Config{
        Tools: []tool.Tool{searchRestaurants},
    },
})

Tip

Consultez l’exemple d’outils principauxAG-UI pour obtenir un exemple exécutable complet.