Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этом руководстве показано, как добавить инструменты для работы с интерфейсом в клиенты AG-UI. Интерфейсные инструменты — это функции, которые выполняются на стороне клиента, позволяя агенту ИИ взаимодействовать с локальной средой пользователя, получать доступ к данным клиента или выполнять операции пользовательского интерфейса. Сервер определяет, когда вызывать эти средства, но выполнение полностью происходит на стороне клиента.
Предпосылки
Прежде чем приступить к работе, убедитесь, что вы выполнили руководство по началу работы и выполните следующие действия.
- .NET 8.0 или более поздней версии
-
Microsoft.Agents.AI.AGUIустановленный пакет -
Microsoft.Agents.AIустановленный пакет - Основные сведения о настройке клиента AG-UI
Что такое интерфейсные инструменты?
Интерфейсные инструменты — это средства функций, которые:
- Определены и зарегистрированы на клиентской стороне
- Выполнение в среде клиента (не на сервере)
- Разрешить агенту ИИ взаимодействовать с ресурсами, зависящими от клиента
- Предоставьте результаты обратно на сервер для включения агента в ответы
- Обеспечивайте персонализированный, контекстно-ориентированный опыт
Распространенные варианты использования:
- Чтение данных локального датчика (GPS, температура и т. д.)
- Доступ к клиентскому хранилищу или предпочтениям
- Выполнение операций пользовательского интерфейса (изменение тем, отображение уведомлений)
- Взаимодействие с функциями, зависящими от устройства (камера, микрофон)
Регистрация интерфейсных средств на клиенте
Основным отличием от учебника по началу работы является регистрация инструментов у клиента-агента. Ниже приведены изменения.
// 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);
Остальная часть клиентского кода остается той же, что и в руководстве по началу работы.
Как инструменты отправляются на сервер
При регистрации инструментов в AsAIAgent(), AGUIChatClient автоматически:
- Записывает определения инструментов (имена, описания, схемы параметров)
- Отправляет инструменты при каждом запросе агенту сервера, который сопоставляет их с
ChatAgentRunOptions.ChatOptions.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;
}
}
Это паттерн промежуточного программного обеспечения позволяет:
- Проверка определений инструментов перед выполнением
Основные понятия
Ниже приведены новые понятия для интерфейсных инструментов:
-
Регистрация на стороне клиента: средства регистрируются на клиенте с помощью
AIFunctionFactory.Create()и передаются вAsAIAgent() -
Автоматическая запись: средства автоматически записываются и отправляются с помощью
ChatAgentRunOptions.ChatOptions.Tools
Как работают интерфейсные инструменты
серверный поток
Сервер не знает сведения о реализации интерфейсных средств. Он знает только:
- Имена и описания инструментов (из регистрации клиента)
- Схемы параметров
- Когда запрашивать выполнение средства
Когда агент ИИ решает вызвать интерфейсное средство:
- Сервер отправляет запрос на вызов средства клиенту через SSE
- Сервер ожидает, пока клиент выполнит средство и возвращает результаты.
- Сервер включает результаты в контекст агента
- Агент продолжает обработку с результатами инструмента
поток клиентской стороны
Клиент обрабатывает выполнение интерфейсного инструмента.
-
FunctionCallContentПолучает от сервера сообщение, содержащее запрос на вызов инструмента - Соотносит имя инструмента с локально зарегистрированной функцией.
- Десериализует параметры из запроса
- Выполняет функцию локально
- Сериализация результата
- Отправляет
FunctionResultContentобратно на сервер - Продолжает получать ответы агента
Ожидаемые выходные данные с фронтенд инструментами
Когда агент вызывает интерфейсные инструменты, вы увидите вызов инструмента и получите результат потоковой передачи.
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.
Настройка сервера для инструментов внешнего интерфейса
Сервер не нуждается в специальной конфигурации для поддержки интерфейсных средств. Используйте типовой сервер AG-UI из учебника для начинающих — он автоматически:
- Получает объявления фронтенд-инструмента во время подключения клиента
- Запрашивает выполнение инструмента, когда агент искусственного интеллекта нуждается в нем.
- Ожидает результатов от клиента
- Включает результаты в принятие решений агента
Дальнейшие шаги
Теперь, когда вы понимаете интерфейсные инструменты, вы можете:
- Объединение с внутренними инструментами: совместное использование интерфейсных и внутренних инструментов
Дополнительные ресурсы
В этом руководстве показано, как добавить инструменты для работы с интерфейсом в клиенты AG-UI. Интерфейсные инструменты — это функции, которые выполняются на стороне клиента, позволяя агенту ИИ взаимодействовать с локальной средой пользователя, получать доступ к данным клиента или выполнять операции пользовательского интерфейса.
Предпосылки
Прежде чем приступить к работе, убедитесь, что вы выполнили руководство по началу работы и выполните следующие действия.
- Python 3.10 или более поздней версии
-
httpxустановлен для функциональных возможностей КЛИЕНТА HTTP - Основные сведения о настройке клиента AG-UI
- Служба Azure OpenAI настроена
Что такое интерфейсные инструменты?
Интерфейсные инструменты — это средства функций, которые:
- Определены и зарегистрированы на клиентской стороне
- Выполнение в среде клиента (не на сервере)
- Разрешить агенту ИИ взаимодействовать с ресурсами, зависящими от клиента
- Предоставьте результаты обратно на сервер для включения агента в ответы
Распространенные варианты использования:
- Чтение данных локального датчика
- Доступ к клиентскому хранилищу или предпочтениям
- Выполнение операций пользовательского интерфейса
- Взаимодействие с функциями, зависящими от устройства
Создание интерфейсных инструментов
Инструменты интерфейса в Python определяются аналогично инструментам бэкенда, но регистрируются с клиентом:
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 с помощью интерфейсных средств
Вот полная реализация клиента с интерфейсными инструментами:
"""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())
Как работают интерфейсные инструменты
Поток протокола
- Регистрация клиента: клиент отправляет объявления инструментов (имена, описания, параметры) на сервер
- Оркестрация сервера: агент ИИ решает, когда следует вызывать интерфейсные инструменты на основе запроса пользователя
-
Запрос вызова средства: сервер отправляет
TOOL_CALL_REQUESTсобытие клиенту через SSE - Выполнение клиента: клиент выполняет средство локально
- Отправка результатов: клиент отправляет результат обратно на сервер с помощью запроса POST
- Обработка агента: сервер включает результат и продолжает ответ
Ключевые события
-
TOOL_CALL_REQUEST: сервер запрашивает выполнение внешнего средства -
TOOL_CALL_RESULT: клиент отправляет результат выполнения (через HTTP POST)
Ожидаемые выходные данные
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]
Настройка сервера
Стандартный сервер AG-UI из руководства по началу работы автоматически поддерживает интерфейсные инструменты. Изменения на стороне сервера не требуются, он автоматически обрабатывает оркестрацию инструментов.
Лучшие практики
Безопасность
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"
Обработка ошибок
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)}"
Асинхронные операции
async def capture_photo() -> str:
"""Capture a photo from device camera."""
# Simulate camera access
await asyncio.sleep(1)
return "photo_12345.jpg"
Устранение неполадок
Средства, которые не вызываются
- Убедитесь, что декларации инструментов отправляются на сервер
- Описание инструментов должно четко указывать их назначение.
- Проверка журналов сервера для регистрации средства
Ошибки выполнения
- Добавление комплексной обработки ошибок
- Проверка параметров перед обработкой
- Возврат сообщений об ошибках, понятных для пользователя
- Журналировать ошибки для отладки
Проблемы с типами данных
- Использование моделей Pydantic для сложных типов
- Преобразование моделей в словари перед сериализацией
- Обрабатывайте преобразования типов явно
Дальнейшие шаги
- Генерация бэкенд-инструментами: в сочетании с серверными инструментами