Megosztás:


Frontend eszköz megjelenítése AG-UI

Ez az oktatóanyag bemutatja, hogyan adhat hozzá előtérbeli függvényeszközöket a AG-UI-ügyfelekhez. Az előtérbeli eszközök olyan függvények, amelyek az ügyféloldalon futnak, lehetővé téve az AI-ügynök számára a felhasználó helyi környezetével való interakciót, az ügyfélspecifikus adatok elérését vagy felhasználói felületi műveletek végrehajtását. A kiszolgáló vezényli, hogy mikor hívja meg ezeket az eszközöket, de a végrehajtás teljes egészében az ügyfélen történik.

Előfeltételek

Mielőtt hozzákezdene, győződjön meg arról, hogy elvégezte az Első lépések oktatóanyagot, és az alábbiakat:

  • .NET 8.0 vagy újabb
  • Microsoft.Agents.AI.AGUI telepített csomag
  • Microsoft.Agents.AI telepített csomag
  • AG-UI ügyfélbeállítás alapszintű ismerete

Mik azok a Frontend Tools?

A frontend eszközök olyan funkció eszközök, amelyek:

  • Az ügyfélen definiálva és regisztrálva vannak.
  • Végrehajtás az ügyfél környezetében (nem a kiszolgálón)
  • Ügyfélspecifikus erőforrások használatának engedélyezése az AI-ügynök számára
  • Adja vissza az eredményeket a kiszolgálóra, hogy az ügynök beépítse azokat a válaszokba.
  • Személyre szabott, környezettudatos élmények engedélyezése

Gyakori használati esetek:

  • Helyi érzékelőadatok (GPS, hőmérséklet stb.) olvasása
  • Ügyféloldali tároló vagy beállítások elérése
  • Felhasználói felületi műveletek végrehajtása (témák módosítása, értesítések megjelenítése)
  • Eszközspecifikus funkciók (kamera, mikrofon) használata

Előtérbeli eszközök regisztrálása az ügyfélen

Az első lépések oktatóanyagtól való fő különbség az eszközök ügyfélügynökkel való regisztrálása. A változások a következők:

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

Az ügyfélkód többi része ugyanaz marad, mint az Első lépések oktatóanyagban látható.

Eszközök küldése a kiszolgálóra

Amikor eszközöket AsAIAgent() regisztrál, az AGUIChatClient automatikusan:

  1. Rögzíti az eszközdefiníciókat (neveket, leírásokat, paramétersémákat)
  2. Minden kéréssel együtt elküldi az eszközöket a kiszolgálóügynöknek, amely megfelelteti azokat ChatAgentRunOptions.ChatOptions.Tools

A kiszolgáló megkapja az ügyféleszköz-deklarációkat, és az AI-modell eldöntheti, hogy mikor hívja meg őket.

Eszközök vizsgálata és módosítása köztes szoftverrel

Az ügynök közbenső szoftverével megvizsgálhatja vagy módosíthatja az ügynök futását, az eszközök elérésével együtt:

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

Ez a köztes szoftverminta lehetővé teszi a következők használatát:

  • Eszközdefiníciók ellenőrzése végrehajtás előtt

Alapfogalmak

Az előtérbeli eszközök új fogalmai a következők:

  • Ügyféloldali regisztráció: Az eszközök regisztrálva vannak az ügyfélen AIFunctionFactory.Create() használatával, és átadják AsAIAgent()
  • Automatikus rögzítés: A rendszer automatikusan rögzíti és elküldi az eszközöket ChatAgentRunOptions.ChatOptions.Tools

Hogyan működnek a frontend eszközök

Server-Side folyamatmenet

A kiszolgáló nem ismeri az előtérbeli eszközök implementálási részleteit. Csak a következőt tudja:

  1. Eszköznevek és leírások (ügyfélregisztrációból)
  2. Paramétersémák
  3. Mikor kell eszközvégrehajtást kérni?

Amikor az AI-ügynök úgy dönt, hogy előtéreszközt hív meg:

  1. A kiszolgáló eszközhívási kérelmet küld az ügyfélnek az SSE-en keresztül
  2. A kiszolgáló megvárja, amíg az ügyfél végrehajtja az eszközt, és visszaadja az eredményeket
  3. A kiszolgáló beépíti az eredményeket az ügynök környezetébe
  4. Az ügynök folytatja a feldolgozást az eszköz eredményeivel

Ügyféloldali műveletfolyam

Az ügyfél kezeli az előtérbeli eszközök végrehajtását:

  1. Szerver által küldött eszközhívási FunctionCallContent kérelem fogadása
  2. Az eszköz nevét összekapcsolja egy helyileg regisztrált függvénnyel.
  3. A kérelemből deszerializáljuk a paramétereket.
  4. A függvény helyi végrehajtása
  5. Szerializálja az eredményt
  6. Visszaküldi FunctionResultContent a szerverre
  7. Továbbra is megkapja az ügynök válaszait

Várt kimenet a Frontend Tools használatával

Amikor az ügynök front-end eszközöket hív meg, látni fogja az eszköz hívását, valamint annak eredményét a valós idejű stream kimenetben.

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.

Kiszolgáló beállítása előtérbeli eszközökhöz

A kiszolgálónak nincs szüksége speciális konfigurációra az előtérbeli eszközök támogatásához. Használja a standard AG-UI kiszolgálót az Első lépések oktatóanyagból – automatikusan:

  • Előtérbeli eszközdeklarációk fogadása az ügyfélkapcsolat során
  • Eszközvégrehajtást kér, amikor az AI-ügynöknek szüksége van rájuk
  • Várakozás az ügyfél eredményeire
  • Az eredményeket beépíti az ügynök döntéshozatalába

Következő lépések

Most, hogy megismerte az előtérbeli eszközöket, a következőt teheti:

További források

Ez az oktatóanyag bemutatja, hogyan adhat hozzá előtérbeli függvényeszközöket a AG-UI-ügyfelekhez. Az előtérbeli eszközök olyan függvények, amelyek az ügyféloldalon futnak, lehetővé téve az AI-ügynök számára a felhasználó helyi környezetével való interakciót, az ügyfélspecifikus adatok elérését vagy felhasználói felületi műveletek végrehajtását.

Előfeltételek

Mielőtt hozzákezdene, győződjön meg arról, hogy elvégezte az Első lépések oktatóanyagot, és az alábbiakat:

  • Python 3.10 vagy újabb
  • httpx HTTP-ügyfélfunkciókhoz telepítve
  • AG-UI ügyfélbeállítás alapszintű ismerete
  • Azure OpenAI-szolgáltatás konfigurálva

Mik azok a Frontend Tools?

A frontend eszközök olyan funkció eszközök, amelyek:

  • Az ügyfélen definiálva és regisztrálva vannak.
  • Végrehajtás az ügyfél környezetében (nem a kiszolgálón)
  • Ügyfélspecifikus erőforrások használatának engedélyezése az AI-ügynök számára
  • Adja vissza az eredményeket a kiszolgálóra, hogy az ügynök beépítse azokat a válaszokba.

Gyakori használati esetek:

  • Helyi érzékelőadatok olvasása
  • Ügyféloldali tároló vagy beállítások elérése
  • Felhasználói felületi műveletek végrehajtása
  • Eszközspecifikus funkciók használata

Előtérbeli eszközök létrehozása

A Python előtérbeli eszközei a háttéreszközökhöz hasonlóan vannak definiálva, de regisztrálva vannak az ügyfélnél:

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

AG-UI-ügyfél létrehozása előtérbeli eszközökkel

Íme egy teljes ügyfél-implementáció előtérbeli eszközökkel:

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

Hogyan működnek a frontend eszközök

Protokollfolyamat

  1. Ügyfélregisztráció: Az ügyfél eszközdeklarációkat (neveket, leírásokat, paramétereket) küld a kiszolgálónak
  2. Kiszolgáló vezénylése: Az AI-ügynök dönti el, hogy mikor hívja meg az előtérbeli eszközöket a felhasználói kérés alapján
  3. Eszközhívási kérelem: A kiszolgáló eseményt küld TOOL_CALL_REQUEST az ügyfélnek az SSE-en keresztül
  4. Ügyfélvégrehajtás: Az ügyfél helyileg hajtja végre az eszközt
  5. Eredmény beküldése: Az ügyfél POST-kéréssel küldi vissza az eredményt a kiszolgálónak
  6. Ügynökfeldolgozás: A kiszolgáló tartalmazza az eredményt, és folytatja a választ

Főbb események

  • TOOL_CALL_REQUEST: A kiszolgáló kéri egy előtérbeli eszköz végrehajtását
  • TOOL_CALL_RESULT: Az ügyfél elküldi a végrehajtási eredményt (HTTP POST-en keresztül)

Várt kimenet

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]

Kiszolgáló beállítása

Az első lépések oktatóanyagának standard AG-UI kiszolgálója automatikusan támogatja az előtérbeli eszközöket. Nincs szükség módosításokra a kiszolgáló oldalán – automatikusan kezeli az eszközök vezénylését.

Ajánlott eljárások

Biztonság

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"

Hibakezelés

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

Aszinkron műveletek

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

Hibaelhárítás

Az eszközök meghívása nem történik meg

  1. Győződjön meg arról, hogy az eszközdeklarációk elküldve vannak a kiszolgálóra
  2. Annak ellenőrzése, hogy az eszköz leírása egyértelműen jelzi-e a célt
  3. Kiszolgálónaplók ellenőrzése eszközregisztrációhoz

Végrehajtási hibák

  1. Átfogó hibakezelés hozzáadása
  2. Paraméterek érvényesítése feldolgozás előtt
  3. Felhasználóbarát hibaüzenetek visszaküldése
  4. Hibák rögzítése a hibakereséshez

Típusproblémák

  1. Pydantic-modellek használata összetett típusok esetén
  2. Modellek konvertálása diktálásokká szerializálás előtt
  3. Típuskonverziók kezelése explicit módon

Következő lépések

További források