Delen via


Frontend-toolweergave met AG-UI

In deze zelfstudie leert u hoe u hulpprogramma's voor front-endfuncties toevoegt aan uw AG-UI-clients. Front-endhulpprogramma's zijn functies die aan de clientzijde worden uitgevoerd, zodat de AI-agent kan communiceren met de lokale omgeving van de gebruiker, toegang heeft tot clientspecifieke gegevens of ui-bewerkingen kan uitvoeren. De server organiseert wanneer deze hulpprogramma's moeten worden aangeroepen, maar de uitvoering vindt volledig plaats op de client.

Vereiste voorwaarden

Voordat u begint, moet u ervoor zorgen dat u de zelfstudie Aan de slag hebt voltooid en dat u het volgende hebt gedaan:

  • .NET 8.0 of hoger
  • Microsoft.Agents.AI.AGUI pakket geïnstalleerd
  • Microsoft.Agents.AI pakket geïnstalleerd
  • Basiskennis van het instellen van AG-UI-client

Wat zijn front-endhulpprogramma's?

Front-endhulpprogramma's zijn functiehulpprogramma's die:

  • Zijn gedefinieerd en geregistreerd op de client
  • Uitvoeren in de clientomgeving (niet op de server)
  • Toestaan dat de AI-agent communiceert met clientspecifieke resources
  • Resultaten teruggeven aan de server zodat de agent ze kan opnemen in antwoorden
  • Persoonlijke, contextbewuste ervaringen inschakelen

Veelvoorkomende gebruiksvoorbeelden:

  • Lokale sensorgegevens lezen (GPS, temperatuur, enzovoort)
  • Toegang tot opslag of voorkeuren aan de clientzijde
  • UI-bewerkingen uitvoeren (thema's wijzigen, meldingen weergeven)
  • Interactie met apparaatspecifieke functies (camera, microfoon)

Front-endhulpprogramma's registreren op de client

Het belangrijkste verschil met de handleiding Aan de slag is het registreren van hulpprogramma's bij de clientagent. Dit zijn de wijzigingen:

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

De rest van uw clientcode blijft hetzelfde zoals getoond in de handleiding Aan de slag.

Hoe hulpprogramma's naar de server worden verzonden

Wanneer je tools registreert bij AsAIAgent(), wordt AGUIChatClient automatisch:

  1. Legt de hulpprogrammadefinities vast (namen, beschrijvingen, parameterschema's)
  2. Hiermee worden de hulpprogramma's met elke aanvraag verzonden naar de serveragent waarnaar ze worden toegewezen ChatAgentRunOptions.ChatOptions.Tools

De server ontvangt de declaraties van het clienthulpprogramma en het AI-model kan bepalen wanneer deze moeten worden aangeroepen.

Hulpprogramma's inspecteren en wijzigen met Middleware

U kunt agent-middleware gebruiken om de agentuitvoering te inspecteren of te wijzigen, inclusief toegang tot de hulpprogramma's:

// 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;
    }
}

Met dit middlewarepatroon kunt u het volgende doen:

  • Hulpprogrammadefinities valideren vóór uitvoering

Sleutelbegrippen

Hier volgen nieuwe concepten voor front-endhulpprogramma's:

  • Registratie aan de clientzijde: Hulpprogramma's worden geregistreerd op de client met behulp van AIFunctionFactory.Create() en doorgegeven aan AsAIAgent()
  • Automatische opname: Hulpprogramma's worden automatisch vastgelegd en verzonden via ChatAgentRunOptions.ChatOptions.Tools

Hoe front-endhulpprogramma's werken

Server-side flow

De server kent de implementatiedetails van front-endhulpprogramma's niet. Het weet alleen:

  1. Namen en beschrijvingen van hulpprogramma's (van clientregistratie)
  2. Parameterschema's
  3. Wanneer moet een verzoek tot uitvoering van het hulpprogramma worden ingediend

Wanneer de AI-agent besluit een front-endhulpprogramma aan te roepen:

  1. Server verzendt een aanvraag voor het aanroepen van een hulpprogramma naar de client via SSE
  2. Server wacht totdat de client het hulpprogramma uitvoert en resultaten retourneert
  3. De server voegt de resultaten toe aan de context van de agent
  4. Agent gaat door met verwerken van de resultaten van het hulpprogramma

Client-Side flow

De client verwerkt de uitvoering van frontend-hulpprogramma's.

  1. FunctionCallContent Ontvangt van de server die een aanvraag voor het aanroepen van een hulpprogramma aangeeft
  2. Stem de naam van het hulpprogramma af op een lokaal geregistreerde functie
  3. Deserialiseert parameters vanuit het verzoek
  4. Hiermee wordt de functie lokaal uitgevoerd
  5. Hiermee wordt het resultaat geserialiseerd
  6. Stuurt FunctionResultContent terug naar de server
  7. Blijft agentantwoorden ontvangen

Verwachte uitvoer met frontend-tools

Wanneer de agent front-endhulpprogramma's aanroept, ziet u de aanroep van het hulpprogramma en resulteert dit in de streaming-uitvoer:

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.

Serverinstellingen voor front-endhulpprogramma's

De server heeft geen speciale configuratie nodig ter ondersteuning van front-endhulpprogramma's. Gebruik de AG-UI-standaardserver uit de handleiding Aan de slag - deze wordt automatisch:

  • Ontvangt declaraties van front-endhulpprogramma's tijdens clientverbinding
  • Uitvoering van hulpprogramma's aanvragen wanneer de AI-agent deze nodig heeft
  • Wacht op resultaten van de client
  • Neemt resultaten op in de besluitvorming van de agent

Volgende stappen

Nu u de front-endhulpprogramma's begrijpt, kunt u het volgende doen:

Aanvullende informatiebronnen

In deze zelfstudie leert u hoe u hulpprogramma's voor front-endfuncties toevoegt aan uw AG-UI-clients. Front-endhulpprogramma's zijn functies die aan de clientzijde worden uitgevoerd, zodat de AI-agent kan communiceren met de lokale omgeving van de gebruiker, toegang heeft tot clientspecifieke gegevens of ui-bewerkingen kan uitvoeren.

Vereiste voorwaarden

Voordat u begint, moet u ervoor zorgen dat u de zelfstudie Aan de slag hebt voltooid en dat u het volgende hebt gedaan:

  • Python 3.10 of hoger
  • httpx geïnstalleerd voor HTTP-clientfunctionaliteit
  • Basiskennis van het instellen van AG-UI-client
  • Azure OpenAI-service geconfigureerd

Wat zijn front-endhulpprogramma's?

Front-endhulpprogramma's zijn functiehulpprogramma's die:

  • Zijn gedefinieerd en geregistreerd op de client
  • Uitvoeren in de clientomgeving (niet op de server)
  • Toestaan dat de AI-agent communiceert met clientspecifieke resources
  • Resultaten teruggeven aan de server zodat de agent ze kan opnemen in antwoorden

Veelvoorkomende gebruiksvoorbeelden:

  • Lokale sensorgegevens lezen
  • Toegang tot opslag of voorkeuren aan de clientzijde
  • UI-bewerkingen uitvoeren
  • Interactie met apparaatspecifieke functies

Front-endhulpprogramma's maken

Front-endhulpprogramma's in Python worden op dezelfde manier gedefinieerd als back-endhulpprogramma's, maar zijn geregistreerd bij de client:

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

Een AG-UI-client maken met frontendhulpprogramma's

Hier volgt een volledige client-implementatie met front-endhulpprogramma's:

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

Hoe front-endhulpprogramma's werken

Protocolstroom

  1. Clientregistratie: Client verzendt hulpprogrammadeclaraties (namen, beschrijvingen, parameters) naar de server
  2. Serverindeling: AI-agent bepaalt wanneer front-endhulpprogramma's moeten worden aangeroepen op basis van gebruikersaanvraag
  3. Aanvraag voor aanroep van hulpprogramma: Server verzendt TOOL_CALL_REQUEST een gebeurtenis naar de client via SSE
  4. Clientuitvoering: Client voert het hulpprogramma lokaal uit
  5. Resultaat verzenden: Client stuurt het resultaat terug naar de server via POST-aanvraag
  6. Agentverwerking: Server verwerkt resultaat en zet de reactie voort

Belangrijke gebeurtenissen

  • TOOL_CALL_REQUEST: Server vraagt uitvoering van front-endhulpprogramma's aan
  • TOOL_CALL_RESULT: Client verzendt het uitvoeringsresultaat (via HTTP POST)

Verwachte uitvoer

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]

Serverinstellingen

De standaardserver AG-UI uit de zelfstudie Aan de slag ondersteunt automatisch front-endhulpprogramma's. Er zijn geen wijzigingen nodig aan de serverzijde. Hiermee wordt de indeling van hulpprogramma's automatisch verwerkt.

Beste praktijken

Security

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"

Foutafhandeling

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 bewerkingen

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

Probleemoplossingsproces

Hulpprogramma's die niet worden aangeroepen

  1. Controleren of hulpprogrammadeclaraties naar de server worden verzonden
  2. Zorg ervoor dat beschrijvingen van hulpprogramma's duidelijk maken wat het doel ervan is.
  3. Serverlogboeken controleren op registratie van hulpprogramma's

Uitvoeringsfouten

  1. Uitgebreide foutafhandeling toevoegen
  2. Parameters valideren voordat ze worden verwerkt
  3. Gebruiksvriendelijke foutberichten retourneren
  4. Fouten loggen voor foutopsporing

Typefouten

  1. Pydantic-modellen gebruiken voor complexe typen
  2. Modellen converteren naar dicteren vóór serialisatie
  3. Typeconversies expliciet verwerken

Volgende stappen

Aanvullende informatiebronnen