Rendering av serverdelsverktyg med AG-UI

Backendverktyg använder den vanliga MAF-verktygspipelinen. AG-UI lägger till transporthändelser så att en klient kan observera anropet och resultatet. Det introducerar inte en separat verktygsabstraktion.

Lägg till ett backend-verktyg

Definiera och registrera verktyget på samma sätt som för alla MAF-agenter:

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);

För komplexa typer av begäranden eller svar konfigurerar du samma JsonSerializerOptions för ASP.NET Core och AIFunctionFactory.Create.

Tip

Se exemplet .NET serverdelsverktyg för en fullständig implementering.

Information om verktygsscheman, beroendeinmatning, felhantering och allmän verktygsdesign finns i Använda funktionsverktyg med en agent.

AG-UI mappning av händelser

När agenten anropar verktyget:

  • FunctionCallContent genereras som AG-UI TOOL_CALL_START, TOOL_CALL_ARGSoch TOOL_CALL_END händelser.
  • FunctionResultContent genereras som en TOOL_CALL_RESULT händelse.
  • Text och annat agentinnehåll fortsätter att strömmas normalt.

En .NET-klient tar emot det översatta innehållet som FunctionCallContent och FunctionResultContent:

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

Verktygsresultat är modellinriktade värden som AG-UI också exponerar för klienten. Om du vill generera delat användargränssnittstillstånd utöver ett verktygsresultat använder du de explicita mappningar som beskrivs i Tillståndshantering.

Nästa steg

Den här handledningen visar hur du lägger till funktionsverktyg i dina AG-UI agenter. Funktionsverktyg är anpassade Python-funktioner som agenten kan anropa för att utföra specifika uppgifter som att hämta data, utföra beräkningar eller interagera med externa system. Med AG-UI körs dessa verktyg på serverdelen och deras resultat strömmas automatiskt till klienten.

Förutsättningar

Innan du börjar kontrollerar du att du har slutfört handledningen Komma igång och har:

  • Python 3.10 eller senare
  • agent-framework-ag-ui installerad
  • Azure OpenAI-tjänsten har konfigurerats
  • Grundläggande förståelse för AG-UI server- och klientkonfiguration

Anmärkning

Dessa exempel använder DefaultAzureCredential för autentisering. Kontrollera att du är autentiserad med Azure (t.ex. via az login). Mer information finns i dokumentationen om Azure Identity.

Vad är Backend-verktygsrendering?

Återgivningen av backend-verktyg betyder:

  • Funktionsverktyg definieras på servern
  • AI-agenten bestämmer när dessa verktyg ska anropas
  • Verktyg körs på serverdelen (serversidan)
  • Händelser och resultat för verktygsanrop strömmas till klienten i realtid
  • Klienten får uppdateringar om förloppet för verktygskörning

Den här metoden ger:

  • Säkerhet: Känsliga åtgärder finns kvar på servern
  • Konsekvens: Alla klienter använder samma verktygsimplementering
  • Transparens: Klienter kan visa förloppet för verktygskörning
  • Flexibilitet: Uppdatera verktyg utan att ändra klientkod

Skapa funktionsverktyg

Grundläggande funktionsverktyg

Du kan omvandla valfri Python-funktion till ett verktyg med hjälp av dekoratören @tool :

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

Viktiga begrepp

  • @tool dekoratör: Markerar en funktion som tillgänglig för agenten
  • Typanteckningar: Ange typinformation för parametrar
  • Annotated och Field: Lägg till beskrivningar som hjälper agenten att förstå parametrar
  • Docstring: Beskriver vad funktionen gör (hjälper agenten att bestämma när den ska användas)
  • Returvärde: Resultatet som returneras till agenten (och strömmas till klienten)

Verktyg för flera funktioner

Du kan tillhandahålla flera verktyg för att ge agenten fler funktioner:

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

Skapa en AG-UI-server med funktionsverktyg

Här är en fullständig serverimplementering med funktionsverktyg:

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

Förstå verktygshändelser

När agenten anropar ett verktyg tar klienten emot flera händelser:

Verktygsanropshändelser

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

Förbättrad klient för verktygshändelser

Här är en förbättrad klient med AGUIChatClient som visar verktygskörning.

"""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())

Exempelinteraktion

Med den förbättrade servern och klienten igång:

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]

Metodtips för verktygsimplementering

Felhantering

Hantera fel på ett korrekt sätt i dina verktyg:

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

Rika returtyper

Returnera strukturerade data när det är lämpligt:

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

Beskrivande dokumentation

Ange tydliga beskrivningar som hjälper agenten att förstå när verktyg ska användas:

@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

Verktygsorganisation med klasser

Ordna dem i en klass för relaterade verktyg:

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

Nästa steg

Nu när du förstår hur backendverktyget återger, kan du:

Ytterligare resurser

Go AG-UI-servrar kan exponera vanliga funktionsverktyg i Agent Framework. Skapa verktyg med tool/functool, koppla dem till den värdbaserade agenten och hantera agenten med 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

Se exemplet AG-UI backend-verktyg för ett fullständigt körbart exempel.