Not
Åtkomst till denna sida kräver auktorisation. Du kan prova att logga in eller byta katalog.
Åtkomst till denna sida kräver auktorisation. Du kan prova att byta katalog.
Den här handledningen visar hur du lägger till frontend-funktionsverktyg för dina AG-UI-klienter. Klientdelsverktyg är funktioner som körs på klientsidan, vilket gör att AI-agenten kan interagera med användarens lokala miljö, komma åt klientspecifika data eller utföra användargränssnittsåtgärder. Servern orkestrerar när dessa verktyg ska anropas, men körningen sker helt på klienten.
Förutsättningar
Innan du börjar, se till att du har slutfört självstudien "Komma igång" och:
- .NET 8.0 eller senare
-
Microsoft.Agents.AI.AGUIpaketet är installerat -
Microsoft.Agents.AIpaketet är installerat - Grundläggande förståelse för AG-UI klientkonfiguration
Vad är Frontend-verktyg?
Frontend-verktyg är funktionella verktyg som
- Definieras och registreras på klienten
- Kör i klientens miljö (inte på servern)
- Tillåt ATT AI-agenten interagerar med klientspecifika resurser
- Ange resultat tillbaka till servern så att agenten kan införliva i svar
- Aktivera anpassade, kontextmedvetna upplevelser
Vanliga användningsfall:
- Läsa lokala sensordata (GPS, temperatur osv.)
- Åtkomst till lagring på klientsidan eller inställningar
- Utföra användargränssnittsåtgärder (ändra teman, visa meddelanden)
- Interagera med enhetsspecifika funktioner (kamera, mikrofon)
Registrera frontend-verktyg på klienten
Den viktigaste skillnaden från introduktionsguiden är att registrera verktygen med klientagenten. Här är vad som ändras:
// 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);
Resten av klientkoden förblir densamma som i självstudien "Komma igång".
Så här skickas verktyg till servern
När du registrerar verktyg med AsAIAgent(), utför AGUIChatClient automatiskt:
- Samlar in verktygsdefinitionerna (namn, beskrivningar, parameterscheman)
- Skickar verktygen med varje begäran till serveragenten som mappar dem till
ChatAgentRunOptions.ChatOptions.Tools
Servern tar emot klientverktygsdeklarationerna och AI-modellen kan bestämma när de ska anropas.
Inspektera och ändra verktyg med mellanprogram
Du kan använda agentmellanprogram för att inspektera eller ändra agentkörningen, inklusive åtkomst till verktygen:
// 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;
}
}
Med det här mellanprogramsmönstret kan du:
- Verifiera verktygsdefinitioner före implementering
Viktiga begrepp
Följande är nya begrepp för klientdelsverktyg:
-
Registrering på klientsidan: Verktyg registreras på klienten med hjälp av
AIFunctionFactory.Create()och skickas tillAsAIAgent() -
Automatisk upptagning: Verktyg samlas in automatiskt och skickas via
ChatAgentRunOptions.ChatOptions.Tools
Så här fungerar klientdelsverktyg
Flöde på Serversidan
Servern känner inte till implementeringsinformationen för klientdelsverktyg. Den vet bara:
- Verktygsnamn och beskrivningar (från klientregistrering)
- Parameterscheman
- När man ska begära verktygsutförande
När AI-agenten bestämmer sig för att anropa ett klientdelsverktyg:
- Servern skickar en begäran om verktygsanrop till klienten via SSE
- Servern väntar på att klienten ska köra verktyget och returnera resultat
- Servern införlivar resultaten i agentens kontext
- Agenten fortsätter bearbetningen med verktygsresultatet
Klientsideflöde
Klienten hanterar körning av gränssnittsverktyg.
-
FunctionCallContentTar emot från servern som anger en begäran om verktygsanrop - Matchar verktygsnamnet med en lokalt registrerad funktion
- Deserialiserar parametrar från begäran
- Kör funktionen lokalt
- Serialiserar resultatet
- Skickar
FunctionResultContenttillbaka till servern - Fortsätter att ta emot svar från agenter
Förväntade utdata med klientdelsverktyg
När agenten anropar klientdelsverktygen ser du verktygets anrop och resulterar i strömmande utdata:
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.
Serverinställning för frontendverktyg
Servern behöver inte någon särskild konfiguration för att stödja klientdelsverktyg. Använd standardservern AG-UI från Komma igång med självstudiekursen – den fungerar automatiskt:
- Tar emot frontend-verktygsdeklarationer vid klientanslutningen
- Begär körning av verktyg när AI-agenten behöver dem
- Väntar på resultat från klienten
- Innehåller resultat i agentens beslutsfattande
Nästa steg
Nu när du förstår klientdelsverktygen kan du:
- Kombinera med serverdelsverktyg: Använd både klientdels- och serverdelsverktyg tillsammans
Ytterligare resurser
Den här handledningen visar hur du lägger till frontend-funktionsverktyg för dina AG-UI-klienter. Klientdelsverktyg är funktioner som körs på klientsidan, vilket gör att AI-agenten kan interagera med användarens lokala miljö, komma åt klientspecifika data eller utföra användargränssnittsåtgärder.
Förutsättningar
Innan du börjar, se till att du har slutfört självstudien "Komma igång" och:
- Python 3.10 eller senare
-
httpxinstallerat för HTTP-klientfunktioner - Grundläggande förståelse för AG-UI klientkonfiguration
- Azure OpenAI-tjänsten har konfigurerats
Vad är Frontend-verktyg?
Frontend-verktyg är funktionella verktyg som
- Definieras och registreras på klienten
- Kör i klientens miljö (inte på servern)
- Tillåt ATT AI-agenten interagerar med klientspecifika resurser
- Ange resultat tillbaka till servern så att agenten kan införliva i svar
Vanliga användningsfall:
- Läsa lokala sensordata
- Åtkomst till lagring på klientsidan eller inställningar
- Utföra användargränssnittsåtgärder
- Interagera med enhetsspecifika funktioner
Skapa frontendverktyg
Klientdelsverktyg i Python definieras på samma sätt som serverdelsverktyg men registreras med klienten:
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}"
Skapa en AG-UI-klient med frontend-verktyg
Här är en komplett klientimplementering med frontend-verktyg:
"""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())
Så här fungerar klientdelsverktyg
Protokollflöde
- Klientregistrering: Klienten skickar verktygsdeklarationer (namn, beskrivningar, parametrar) till servern
- Serverorkestrering: AI-agenten bestämmer när klientdelsverktyg ska anropas baserat på användarbegäran
-
Begäran om verktygsanrop: Servern skickar
TOOL_CALL_REQUESThändelsen till klienten via SSE - Klientkörning: Klienten kör verktyget lokalt
- Resultatöverföring: Klienten skickar tillbaka resultatet till servern via POST-begäran
- Agentbearbetning: Servern inkorporerar resultatet och fortsätter med att svara
Viktiga händelser
-
TOOL_CALL_REQUEST: Server begär körning av frontend-verktyget -
TOOL_CALL_RESULT: Klienten skickar körningsresultat (via HTTP POST)
Förväntade utdata
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]
Serverkonfiguration
Standardservern AG-UI från kom igång-självstudien stöder automatiskt gränssnittsverktyg. Inga ändringar behövs på serversidan – den hanterar verktygsorkestrering automatiskt.
Metodtips
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"
Felhantering
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)}"
Asynkrona åtgärder
async def capture_photo() -> str:
"""Capture a photo from device camera."""
# Simulate camera access
await asyncio.sleep(1)
return "photo_12345.jpg"
Felsökning
Verktyg som inte anropas
- Se till att verktygsdeklarationer skickas till servern
- Kontrollera verktygsbeskrivningar som tydligt anger syftet
- Kontrollera om det finns verktygsregistrering i serverloggarna
Körningsfel
- Lägga till omfattande felhantering
- Verifiera parametrar före bearbetning
- Returnera användarvänliga felmeddelanden
- Loggfel för felsökning
Typproblem
- Använda pydantiska modeller för komplexa typer
- Konvertera modeller till ordlistor före serialisering
- Hantera typkonverteringar explicit.
Nästa steg
- Rendering av serverdelsverktyg: Kombinera med verktyg på serversidan