Freigeben über


Frontend-Tool-Rendering mit AG-UI

In diesem Lernprogramm erfahren Sie, wie Sie Ihren AG-UI Clients Frontend-Funktionstools hinzufügen. Frontend-Tools sind Funktionen, die auf clientseitiger Seite ausgeführt werden, sodass der KI-Agent mit der lokalen Umgebung des Benutzers interagieren kann, auf clientspezifische Daten zugreifen oder UI-Vorgänge ausführt. Der Server koordiniert, wann diese Tools aufgerufen werden sollen, die Ausführung erfolgt jedoch vollständig auf dem Client.

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.AGUI Paket installiert
  • Microsoft.Agents.AI Paket installiert
  • Grundlegendes Verständnis des AG-UI Clientsetups

Was sind Frontend-Tools?

Frontend-Tools sind Funktionstools, die:

  • Werden definiert und auf dem Client registriert
  • Ausführen in der Umgebung des Clients (nicht auf dem Server)
  • Zulassen, dass der KI-Agent mit clientspezifischen Ressourcen interagiert
  • Übermittlung von Ergebnissen zurück an den Server, damit der Agent die Ergebnisse in Antworten integrieren kann
  • Aktivieren personalisierter, kontextbezogener Erfahrungen

Häufige Anwendungsfälle:

  • Lesen lokaler Sensordaten (GPS, Temperatur usw.)
  • Zugreifen auf clientseitigen Speicher oder Einstellungen
  • Ausführen von UI-Vorgängen (Ändern von Designs, Anzeigen von Benachrichtigungen)
  • Interaktion mit gerätespezifischen Features (Kamera, Mikrofon)

Registrieren von Frontend-Tools auf dem Client

Der hauptunterschied zum Lernprogramm "Erste Schritte" besteht darin, Tools beim Client-Agent zu registrieren. Hier sind die Änderungen:

// Define a frontend function tool
[Description("Get the user's current location from GPS.")]
static string GetUserLocation()
{
    // Access client-side GPS
    return "Amsterdam, Netherlands (52.37°N, 4.90°E)";
}

// Create frontend tools
AITool[] frontendTools = [AIFunctionFactory.Create(GetUserLocation)];

// Pass tools when creating the agent
AIAgent agent = chatClient.AsAIAgent(
    name: "agui-client",
    description: "AG-UI Client Agent",
    tools: frontendTools);

Der rest des Clientcodes bleibt identisch wie im Lernprogramm "Erste Schritte".

Wie Tools an den Server gesendet werden

Wenn Sie Tools bei AsAIAgent() registrieren, erfolgt die AGUIChatClient automatisch.

  1. Erfasst die Tooldefinitionen (Namen, Beschreibungen, Parameterschemas)
  2. Sendet die Tools mit jeder Anforderung an den Server-Agent, der sie zuordnet ChatAgentRunOptions.ChatOptions.Tools

Der Server empfängt die Clienttooldeklarationen, und das KI-Modell kann entscheiden, wann sie aufgerufen werden sollen.

Überprüfen und Ändern von Tools mit Middleware

Sie können Agent-Middleware verwenden, um die Ausführung des Agents zu prüfen oder zu ändern, einschließlich des Zugriffs auf die Tools:

// Create agent with middleware that inspects tools
AIAgent inspectableAgent = baseAgent
    .AsBuilder()
    .Use(runFunc: null, runStreamingFunc: InspectToolsMiddleware)
    .Build();

static async IAsyncEnumerable<AgentResponseUpdate> InspectToolsMiddleware(
    IEnumerable<ChatMessage> messages,
    AgentSession? session,
    AgentRunOptions? options,
    AIAgent innerAgent,
    CancellationToken cancellationToken)
{
    // Access the tools from ChatClientAgentRunOptions
    if (options is ChatClientAgentRunOptions chatOptions)
    {
        IList<AITool>? tools = chatOptions.ChatOptions?.Tools;
        if (tools != null)
        {
            Console.WriteLine($"Tools available for this run: {tools.Count}");
            foreach (AITool tool in tools)
            {
                if (tool is AIFunction function)
                {
                    Console.WriteLine($"  - {function.Metadata.Name}: {function.Metadata.Description}");
                }
            }
        }
    }

    await foreach (AgentResponseUpdate update in innerAgent.RunStreamingAsync(messages, session, options, cancellationToken))
    {
        yield return update;
    }
}

Dieses Middleware-Muster ermöglicht Folgendes:

  • Überprüfen von Tooldefinitionen vor der Ausführung

Wichtige Konzepte

Es folgen neue Konzepte für Frontend-Tools:

  • Clientseitige Registrierung: Tools werden auf dem Client registriert AIFunctionFactory.Create() und übergeben an AsAIAgent()
  • Automatische Erfassung: Tools werden automatisch erfasst und per ChatAgentRunOptions.ChatOptions.Tools

Funktionsweise von Front-End-Tools

Server-Side Ablauf

Der Server kennt die Implementierungsdetails von Frontend-Tools nicht. Es weiß nur:

  1. Toolnamen und Beschreibungen (aus der Clientregistrierung)
  2. Parameterschemas
  3. Wann die Ausführung des Tools anfordern

Wenn der KI-Agent entscheidet, ein Frontend-Tool aufzurufen:

  1. Server sendet eine Toolaufrufanforderung über SSE an den Client.
  2. Der Server wartet, bis der Client das Tool ausführt und Ergebnisse zurückgibt.
  3. Der Server integriert die Ergebnisse in den Kontext des Agents.
  4. Agent setzt die Verarbeitung mit den Toolergebnissen fort.

Client-seitiger Ablauf

Der Client verarbeitet die Ausführung des Front-End-Tools:

  1. Empfängt FunctionCallContent vom Server, der eine Toolaufrufanforderung angibt
  2. Entspricht dem Toolnamen einer lokal registrierten Funktion.
  3. Deserialisiert Parameter aus der Anfrage
  4. Führt die Funktion lokal aus.
  5. Serialisiert das Ergebnis
  6. Sendet FunctionResultContent zurück an den Server
  7. Empfängt weiterhin Antworten von Agenten

Erwartete Ausgabe mit Frontend-Tools

Wenn der Agent Frontend-Tools aufruft, werden der Aufruf des Tools und das Ergebnis in der Streamingausgabe angezeigt.

User (:q or quit to exit): Where am I located?

[Client Tool Call - Name: GetUserLocation]
[Client Tool Result: Amsterdam, Netherlands (52.37°N, 4.90°E)]

You are currently in Amsterdam, Netherlands, at coordinates 52.37°N, 4.90°E.

Serversetup für Frontend-Tools

Der Server benötigt keine spezielle Konfiguration, um Frontend-Tools zu unterstützen. Verwenden Sie den Standardserver AG-UI aus dem Lernprogramm "Erste Schritte" – automatisch:

  • Empfängt Frontend-Tooldeklarationen während der Clientverbindung
  • Fordert die Toolausführung an, wenn der KI-Agent sie benötigt
  • Wartet auf Ergebnisse vom Client
  • Integriert Ergebnisse in die Entscheidungsfindung des Agenten

Nächste Schritte

Nachdem Sie nun die Front-End-Tools verstanden haben, haben Sie folgende Möglichkeiten:

Zusätzliche Ressourcen

In diesem Lernprogramm erfahren Sie, wie Sie Ihren AG-UI Clients Frontend-Funktionstools hinzufügen. Frontend-Tools sind Funktionen, die auf clientseitiger Seite ausgeführt werden, sodass der KI-Agent mit der lokalen Umgebung des Benutzers interagieren kann, auf clientspezifische Daten zugreifen oder UI-Vorgänge ausführt.

Voraussetzungen

Bevor Sie beginnen, stellen Sie sicher, dass Sie das Lernprogramm " Erste Schritte " abgeschlossen haben und folgendes haben:

  • Python 3.10 oder höher
  • httpx installiert für HTTP-Clientfunktionen
  • Grundlegendes Verständnis des AG-UI Clientsetups
  • Azure OpenAI-Dienst konfiguriert

Was sind Frontend-Tools?

Frontend-Tools sind Funktionstools, die:

  • Werden definiert und auf dem Client registriert
  • Ausführen in der Umgebung des Clients (nicht auf dem Server)
  • Zulassen, dass der KI-Agent mit clientspezifischen Ressourcen interagiert
  • Übermittlung von Ergebnissen zurück an den Server, damit der Agent die Ergebnisse in Antworten integrieren kann

Häufige Anwendungsfälle:

  • Lesen lokaler Sensordaten
  • Zugreifen auf clientseitigen Speicher oder Einstellungen
  • Ausführen von UI-Vorgängen
  • Interaktion mit gerätespezifischen Features

Erstellen von Frontend-Tools

Frontend-Tools in Python werden ähnlich wie Back-End-Tools definiert, aber beim Client registriert:

from typing import Annotated
from pydantic import BaseModel, Field


class SensorReading(BaseModel):
    """Sensor reading from client device."""
    temperature: float
    humidity: float
    air_quality_index: int


def read_climate_sensors(
    include_temperature: Annotated[bool, Field(description="Include temperature reading")] = True,
    include_humidity: Annotated[bool, Field(description="Include humidity reading")] = True,
) -> SensorReading:
    """Read climate sensor data from the client device."""
    # Simulate reading from local sensors
    return SensorReading(
        temperature=22.5 if include_temperature else 0.0,
        humidity=45.0 if include_humidity else 0.0,
        air_quality_index=75,
    )


def change_background_color(color: Annotated[str, Field(description="Color name")] = "blue") -> str:
    """Change the console background color."""
    # Simulate UI change
    print(f"\n🎨 Background color changed to {color}")
    return f"Background changed to {color}"

Erstellen eines AG-UI Clients mit Frontend-Tools

Hier ist eine vollständige Clientimplementierung mit Frontend-Tools:

"""AG-UI client with frontend tools."""

import asyncio
import json
import os
from typing import Annotated, AsyncIterator

import httpx
from pydantic import BaseModel, Field


class SensorReading(BaseModel):
    """Sensor reading from client device."""
    temperature: float
    humidity: float
    air_quality_index: int


# Define frontend tools
def read_climate_sensors(
    include_temperature: Annotated[bool, Field(description="Include temperature")] = True,
    include_humidity: Annotated[bool, Field(description="Include humidity")] = True,
) -> SensorReading:
    """Read climate sensor data from the client device."""
    return SensorReading(
        temperature=22.5 if include_temperature else 0.0,
        humidity=45.0 if include_humidity else 0.0,
        air_quality_index=75,
    )


def get_user_location() -> dict:
    """Get the user's current GPS location."""
    # Simulate GPS reading
    return {
        "latitude": 52.3676,
        "longitude": 4.9041,
        "accuracy": 10.0,
        "city": "Amsterdam",
    }


# Tool registry maps tool names to functions
FRONTEND_TOOLS = {
    "read_climate_sensors": read_climate_sensors,
    "get_user_location": get_user_location,
}


class AGUIClientWithTools:
    """AG-UI client with frontend tool support."""

    def __init__(self, server_url: str, tools: dict):
        self.server_url = server_url
        self.tools = tools
        self.thread_id: str | None = None

    async def send_message(self, message: str) -> AsyncIterator[dict]:
        """Send a message and handle streaming response with tool execution."""
        # Prepare tool declarations for the server
        tool_declarations = []
        for name, func in self.tools.items():
            tool_declarations.append({
                "name": name,
                "description": func.__doc__ or "",
                # Add parameter schema from function signature
            })

        request_data = {
            "messages": [
                {"role": "system", "content": "You are a helpful assistant with access to client tools."},
                {"role": "user", "content": message},
            ],
            "tools": tool_declarations,  # Send tool declarations to server
        }

        if self.thread_id:
            request_data["thread_id"] = self.thread_id

        async with httpx.AsyncClient(timeout=60.0) as client:
            async with client.stream(
                "POST",
                self.server_url,
                json=request_data,
                headers={"Accept": "text/event-stream"},
            ) as response:
                response.raise_for_status()

                async for line in response.aiter_lines():
                    if line.startswith("data: "):
                        data = line[6:]
                        try:
                            event = json.loads(data)

                            # Handle tool call requests from server
                            if event.get("type") == "TOOL_CALL_REQUEST":
                                await self._handle_tool_call(event, client)
                            else:
                                yield event

                            # Capture thread_id
                            if event.get("type") == "RUN_STARTED" and not self.thread_id:
                                self.thread_id = event.get("threadId")

                        except json.JSONDecodeError:
                            continue

    async def _handle_tool_call(self, event: dict, client: httpx.AsyncClient):
        """Execute frontend tool and send result back to server."""
        tool_name = event.get("toolName")
        tool_call_id = event.get("toolCallId")
        arguments = event.get("arguments", {})

        print(f"\n\033[95m[Client Tool Call: {tool_name}]\033[0m")
        print(f"  Arguments: {arguments}")

        try:
            # Execute the tool
            tool_func = self.tools.get(tool_name)
            if not tool_func:
                raise ValueError(f"Unknown tool: {tool_name}")

            result = tool_func(**arguments)

            # Convert Pydantic models to dict
            if hasattr(result, "model_dump"):
                result = result.model_dump()

            print(f"\033[94m[Client Tool Result: {result}]\033[0m")

            # Send result back to server
            await client.post(
                f"{self.server_url}/tool_result",
                json={
                    "tool_call_id": tool_call_id,
                    "result": result,
                },
            )

        except Exception as e:
            print(f"\033[91m[Tool Error: {e}]\033[0m")
            # Send error back to server
            await client.post(
                f"{self.server_url}/tool_result",
                json={
                    "tool_call_id": tool_call_id,
                    "error": str(e),
                },
            )


async def main():
    """Main client loop with frontend tools."""
    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")

    client = AGUIClientWithTools(server_url, FRONTEND_TOOLS)

    try:
        while True:
            message = input("\nUser (:q or quit to exit): ")
            if not message.strip():
                continue

            if message.lower() in (":q", "quit"):
                break

            print()
            async for event in client.send_message(message):
                event_type = event.get("type", "")

                if event_type == "RUN_STARTED":
                    print(f"\033[93m[Run Started]\033[0m")

                elif event_type == "TEXT_MESSAGE_CONTENT":
                    print(f"\033[96m{event.get('delta', '')}\033[0m", end="", flush=True)

                elif event_type == "RUN_FINISHED":
                    print(f"\n\033[92m[Run Finished]\033[0m")

                elif event_type == "RUN_ERROR":
                    error_msg = event.get("message", "Unknown error")
                    print(f"\n\033[91m[Error: {error_msg}]\033[0m")

            print()

    except KeyboardInterrupt:
        print("\n\nExiting...")
    except Exception as e:
        print(f"\n\033[91mError: {e}\033[0m")


if __name__ == "__main__":
    asyncio.run(main())

Funktionsweise von Front-End-Tools

Protokollfluss

  1. Clientregistrierung: Der Client sendet Tooldeklarationen (Namen, Beschreibungen, Parameter) an den Server.
  2. Server-Orchestrierung: KI-Agent entscheidet, wann Frontend-Tools basierend auf Benutzeranforderung aufgerufen werden sollen.
  3. Toolaufrufanforderung: Server sendet TOOL_CALL_REQUEST ereignis über SSE an den Client
  4. Clientausführung: Client führt das Tool lokal aus
  5. Ergebnisübermittlung: Der Client sendet das Ergebnis über POST-Anforderung an den Server zurück.
  6. Agent-Verarbeitung: Der Server verarbeitet Ergebnisse und setzt die Antwort fort.

Wichtige Ereignisse

  • TOOL_CALL_REQUEST: Server fordert die Ausführung des Front-End-Tools an
  • TOOL_CALL_RESULT: Client sendet Ausführungsergebnis (über HTTP POST)

Erwartete Ausgabe

User (:q or quit to exit): What's the temperature reading from my sensors?

[Run Started]

[Client Tool Call: read_climate_sensors]
  Arguments: {'include_temperature': True, 'include_humidity': True}
[Client Tool Result: {'temperature': 22.5, 'humidity': 45.0, 'air_quality_index': 75}]

Based on your sensor readings, the current temperature is 22.5°C and the 
humidity is at 45%. These are comfortable conditions!
[Run Finished]

Server-Einrichtung

Der Standardserver AG-UI server aus dem Lernprogramm "Erste Schritte" unterstützt automatisch Frontend-Tools. Auf der Serverseite sind keine Änderungen erforderlich – sie verarbeitet die Tool-Orchestrierung automatisch.

Bewährte Methoden

Sicherheit

def access_sensitive_data() -> str:
    """Access user's sensitive data."""
    # Always check permissions first
    if not has_permission():
        return "Error: Permission denied"

    try:
        # Access data
        return "Data retrieved"
    except Exception as e:
        # Don't expose internal errors
        return "Unable to access data"

Fehlerbehandlung

def read_file(path: str) -> str:
    """Read a local file."""
    try:
        with open(path, "r") as f:
            return f.read()
    except FileNotFoundError:
        return f"Error: File not found: {path}"
    except PermissionError:
        return f"Error: Permission denied: {path}"
    except Exception as e:
        return f"Error reading file: {str(e)}"

Asynchrone Vorgänge

async def capture_photo() -> str:
    """Capture a photo from device camera."""
    # Simulate camera access
    await asyncio.sleep(1)
    return "photo_12345.jpg"

Problembehandlung

Nicht aufgerufene Tools

  1. Sicherstellen, dass Tooldeklarationen an den Server gesendet werden
  2. Überprüfen von Toolbeschreibungen, die eindeutig den Zweck angeben
  3. Überprüfen von Serverprotokollen für die Toolregistrierung

Ausführungsfehler

  1. Umfassende Fehlerbehandlung hinzufügen
  2. Überprüfen von Parametern vor der Verarbeitung
  3. Zurückgeben von benutzerfreundlichen Fehlermeldungen
  4. Protokollfehler für das Debuggen

Typprobleme

  1. Verwenden von Pydantischen Modellen für komplexe Typen
  2. Konvertieren von Modellen in Diktaten vor der Serialisierung
  3. Behandeln Sie Typkonvertierungen explizit.

Nächste Schritte

Zusätzliche Ressourcen