Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Ce tutoriel vous montre comment ajouter des outils de fonction front-end à vos clients AG-UI. Les outils frontaux sont des fonctions qui s’exécutent côté client, ce qui permet à l’agent IA d’interagir avec l’environnement local de l’utilisateur, d’accéder aux données spécifiques au client ou d’effectuer des opérations d’interface utilisateur. Le serveur orchestre quand appeler ces outils, mais l’exécution se produit entièrement sur le client.
Prerequisites
Avant de commencer, vérifiez que vous avez terminé le didacticiel De prise en main et que vous disposez des points suivants :
- .NET 8.0 ou version ultérieure
-
Microsoft.Agents.AI.AGUIpaquet installé -
Microsoft.Agents.AIpaquet installé - Compréhension de base de la configuration du client AG-UI
Qu’est-ce que les outils frontaux ?
Les outils frontaux sont des outils de fonction qui :
- Sont définis et inscrits sur le client
- Exécuter dans l’environnement du client (et non sur le serveur)
- Autoriser l’agent IA à interagir avec des ressources spécifiques au client
- Fournir des résultats au serveur afin que l'agent puisse les intégrer dans ses réponses.
- Activer des expériences personnalisées et contextuelles
Cas d’usage courants :
- Lecture des données de capteur locales (GPS, température, etc.)
- Accès au stockage ou aux préférences côté client
- Exécution d’opérations d’interface utilisateur (modification des thèmes, affichage des notifications)
- Interaction avec des fonctionnalités spécifiques à l’appareil (caméra, microphone)
Enregistrement d'outils front-end sur le client
La principale différence par rapport au didacticiel de démarrage consiste à enregistrer des outils auprès de l'agent client. Voici les modifications suivantes :
// 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);
Le reste de votre code client reste le même que celui indiqué dans le didacticiel De prise en main.
Comment les outils sont envoyés au serveur
Lorsque vous inscrivez des outils avec AsAIAgent(), le AGUIChatClient procède automatiquement.
- Capture les définitions d’outils (noms, descriptions, schémas de paramètres)
- Envoie les outils avec chaque requête à l’agent serveur qui les mappe à
ChatAgentRunOptions.ChatOptions.Tools
Le serveur reçoit les déclarations de l’outil client et le modèle IA peut décider quand les appeler.
Inspection et modification d’outils avec middleware
Vous pouvez utiliser l’intergiciel de l’agent pour inspecter ou modifier l’exécution de l’agent, y compris accéder aux outils :
// 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;
}
}
Ce modèle d’intergiciel vous permet de :
- Valider les définitions d’outils avant l’exécution
Concepts clés
Voici de nouveaux concepts pour les outils front-end :
-
Inscription côté client : les outils sont inscrits sur le client à l’aide
AIFunctionFactory.Create()et transmis àAsAIAgent() -
Capture automatique : les outils sont automatiquement capturés et envoyés via
ChatAgentRunOptions.ChatOptions.Tools
Fonctionnement des outils frontaux
flux du côté serveur
Le serveur ne connaît pas les détails de l’implémentation des outils frontend. Il ne sait que :
- Noms et descriptions des outils (à partir de l’inscription du client)
- Schémas de paramètres
- Quand demander l’exécution de l’outil
Lorsque l’agent IA décide d’appeler un outil frontal :
- Le serveur envoie une demande d’appel d’outil au client via SSE
- Le serveur attend que le client exécute l’outil et retourne les résultats
- Le serveur intègre les résultats dans le contexte de l’agent
- L’agent poursuit le traitement avec les résultats de l’outil
Flux Côté Client
Le client gère l’exécution de l’outil frontal :
- Reçoit
FunctionCallContentdu serveur indiquant une demande d'appel d'outil - Correspond au nom de l’outil à une fonction inscrite localement
- Désérialise les paramètres de la requête
- Exécute la fonction localement
- Sérialise le résultat
- Renvoie
FunctionResultContentau serveur - Continue la réception des réponses de l’agent
Sortie attendue avec les outils frontaux
Lorsque l’agent appelle des outils frontend, vous verrez l’appel de l’outil et le résultat dans la sortie en streaming.
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.
Configuration du serveur pour les outils frontaux
Le serveur n’a pas besoin d’une configuration spéciale pour prendre en charge les outils front-end. Utilisez le serveur AG-UI standard à partir du didacticiel de prise en main - il le fait automatiquement :
- Reçoit les déclarations d’outil front-end lors de la connexion cliente
- Demande l’exécution de l’outil lorsque l’agent IA en a besoin
- Attend les résultats du client
- Incorpore les résultats dans la prise de décision de l’agent
Étapes suivantes
Maintenant que vous comprenez les outils front-end, vous pouvez :
- Combiner avec les outils back-end : utiliser les outils front-end et back-end ensemble
Ressources additionnelles
Ce tutoriel vous montre comment ajouter des outils de fonction front-end à vos clients AG-UI. Les outils frontaux sont des fonctions qui s’exécutent côté client, ce qui permet à l’agent IA d’interagir avec l’environnement local de l’utilisateur, d’accéder aux données spécifiques au client ou d’effectuer des opérations d’interface utilisateur.
Prerequisites
Avant de commencer, vérifiez que vous avez terminé le didacticiel De prise en main et que vous disposez des points suivants :
- Python 3.10 ou version ultérieure
-
httpxinstallé pour la fonctionnalité client HTTP - Compréhension de base de la configuration du client AG-UI
- Le service Azure OpenAI est configuré
Qu’est-ce que les outils frontaux ?
Les outils frontaux sont des outils de fonction qui :
- Sont définis et inscrits sur le client
- Exécuter dans l’environnement du client (et non sur le serveur)
- Autoriser l’agent IA à interagir avec des ressources spécifiques au client
- Fournir des résultats au serveur afin que l'agent puisse les intégrer dans ses réponses.
Cas d’usage courants :
- Lecture des données de capteur local
- Accès au stockage ou aux préférences côté client
- Exécution d’opérations d’interface utilisateur
- Interaction avec des fonctionnalités spécifiques à l’appareil
Création d’outils frontaux
Les outils front-end dans Python sont définis de la même façon que les outils principaux, mais sont inscrits auprès du 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}"
Création d’un client AG-UI avec les outils front-end
Voici une implémentation complète du client avec les outils front-end :
"""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())
Fonctionnement des outils frontaux
Flux de protocole
- Inscription du client : le client envoie des déclarations d’outils (noms, descriptions, paramètres) au serveur
- Orchestration de serveur : l’agent IA décide quand appeler des outils front-end en fonction de la demande de l’utilisateur
-
Demande d’appel d’outil : le serveur envoie
TOOL_CALL_REQUESTun événement au client via SSE - Exécution du client : le client exécute l’outil localement
- Envoi de résultat : le client renvoie le résultat au serveur via une requête POST
- Traitement de l’agent : le serveur intègre les résultats et poursuit la réponse
Événements clés
-
TOOL_CALL_REQUEST: Serveur demande l’exécution de l’outil front-end -
TOOL_CALL_RESULT: le client envoie le résultat d’exécution (via HTTP POST)
Sortie attendue
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]
Configuration du serveur
Le serveur AG-UI standard à partir du didacticiel de prise en main prend automatiquement en charge les outils frontend. Aucune modification n’est nécessaire côté serveur : elle gère automatiquement l’orchestration des outils.
Meilleures pratiques
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"
Gestion des erreurs
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)}"
Opérations asynchrones
async def capture_photo() -> str:
"""Capture a photo from device camera."""
# Simulate camera access
await asyncio.sleep(1)
return "photo_12345.jpg"
Résolution des problèmes
Outils non invoqués
- Vérifier que les déclarations d’outil sont envoyées au serveur
- Vérifiez que les descriptions des outils indiquent clairement leur objectif.
- Vérifier les journaux du serveur pour l’enregistrement des outils
Erreurs d’exécution
- Ajouter une gestion complète des erreurs
- Valider les paramètres avant le traitement
- Retourner des messages d'erreur faciles à comprendre
- Erreurs de journalisation pour le débogage
Problèmes de typage
- Utiliser des modèles Pydantic pour les types complexes
- Convertir des modèles en dictes avant sérialisation
- Gérer explicitement les conversions de types
Étapes suivantes
- Rendu des outils back-end : combiner avec les outils côté serveur