Oharra
Baimena behar duzu orria atzitzeko. Direktorioetan saioa has dezakezu edo haiek alda ditzakezu.
Baimena behar duzu orria atzitzeko. Direktorioak alda ditzakezu.
En este tutorial se muestra cómo implementar la administración de estado con AG-UI, lo que permite la sincronización bidireccional de estado entre el cliente y el servidor. Esto es esencial para crear aplicaciones interactivas como la interfaz de usuario generativa, los paneles en tiempo real o las experiencias colaborativas.
Prerrequisitos
Antes de empezar, asegúrese de que comprende lo siguiente:
¿Qué es Administración de estado?
La administración de estados en AG-UI habilita:
- Estado compartido: tanto el cliente como el servidor mantienen una vista sincronizada del estado de la aplicación
- Sincronización bidireccional: el estado se puede actualizar desde el cliente o el servidor.
- Actualizaciones en tiempo real: los cambios se transmiten inmediatamente mediante eventos de estado
- Actualizaciones predictivas: flujo de actualizaciones de estado a medida que LLM genera argumentos de herramienta (interfaz de usuario optimista)
- Datos estructurados: el estado sigue un esquema JSON para la validación
Casos de uso
La administración del estado es valiosa para:
- Interfaz de usuario generativa: compilación de componentes de interfaz de usuario basados en el estado controlado por agente
- Creación de formularios: el agente rellena los campos de formulario a medida que recopila información.
- Seguimiento del progreso: mostrar el progreso en tiempo real de las operaciones de varios pasos
- Paneles interactivos: mostrar datos que se actualizan a medida que el agente lo procesa
- Edición colaborativa: varios usuarios ven actualizaciones de estado coherentes
Creación de agentes de State-Aware en C#
Definir el modelo de estado
En primer lugar, defina las clases para la estructura de estado:
using System.Text.Json.Serialization;
namespace RecipeAssistant;
// State response wrapper
internal sealed class RecipeResponse
{
[JsonPropertyName("recipe")]
public RecipeState Recipe { get; set; } = new();
}
// Recipe state model
internal sealed class RecipeState
{
[JsonPropertyName("title")]
public string Title { get; set; } = string.Empty;
[JsonPropertyName("cuisine")]
public string Cuisine { get; set; } = string.Empty;
[JsonPropertyName("ingredients")]
public List<string> Ingredients { get; set; } = [];
[JsonPropertyName("steps")]
public List<string> Steps { get; set; } = [];
[JsonPropertyName("prep_time_minutes")]
public int PrepTimeMinutes { get; set; }
[JsonPropertyName("cook_time_minutes")]
public int CookTimeMinutes { get; set; }
[JsonPropertyName("skill_level")]
public string SkillLevel { get; set; } = string.Empty;
}
// JSON serialization context
[JsonSerializable(typeof(RecipeResponse))]
[JsonSerializable(typeof(RecipeState))]
[JsonSerializable(typeof(System.Text.Json.JsonElement))]
internal sealed partial class RecipeSerializerContext : JsonSerializerContext;
Implementación del middleware de administración de estado
Cree middleware que controle la administración de estado mediante la detección cuando el cliente envía el estado y coordina las respuestas del agente:
using System.Runtime.CompilerServices;
using System.Text.Json;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
internal sealed class SharedStateAgent : DelegatingAIAgent
{
private readonly JsonSerializerOptions _jsonSerializerOptions;
public SharedStateAgent(AIAgent innerAgent, JsonSerializerOptions jsonSerializerOptions)
: base(innerAgent)
{
this._jsonSerializerOptions = jsonSerializerOptions;
}
protected override Task<AgentResponse> RunCoreAsync(
IEnumerable<ChatMessage> messages,
AgentSession? session = null,
AgentRunOptions? options = null,
CancellationToken cancellationToken = default)
{
return this.RunStreamingAsync(messages, session, options, cancellationToken)
.ToAgentResponseAsync(cancellationToken);
}
protected override async IAsyncEnumerable<AgentResponseUpdate> RunCoreStreamingAsync(
IEnumerable<ChatMessage> messages,
AgentSession? session = null,
AgentRunOptions? options = null,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
// Check if the client sent state in the request
if (options is not ChatClientAgentRunOptions { ChatOptions.AdditionalProperties: { } properties } chatRunOptions ||
!properties.TryGetValue("ag_ui_state", out object? stateObj) ||
stateObj is not JsonElement state ||
state.ValueKind != JsonValueKind.Object)
{
// No state management requested, pass through to inner agent
await foreach (var update in this.InnerAgent.RunStreamingAsync(messages, session, options, cancellationToken).ConfigureAwait(false))
{
yield return update;
}
yield break;
}
// Check if state has properties (not empty {})
bool hasProperties = false;
foreach (JsonProperty _ in state.EnumerateObject())
{
hasProperties = true;
break;
}
if (!hasProperties)
{
// Empty state - treat as no state
await foreach (var update in this.InnerAgent.RunStreamingAsync(messages, session, options, cancellationToken).ConfigureAwait(false))
{
yield return update;
}
yield break;
}
// First run: Generate structured state update
var firstRunOptions = new ChatClientAgentRunOptions
{
ChatOptions = chatRunOptions.ChatOptions.Clone(),
AllowBackgroundResponses = chatRunOptions.AllowBackgroundResponses,
ContinuationToken = chatRunOptions.ContinuationToken,
ChatClientFactory = chatRunOptions.ChatClientFactory,
};
// Configure JSON schema response format for structured state output
firstRunOptions.ChatOptions.ResponseFormat = ChatResponseFormat.ForJsonSchema<RecipeResponse>(
schemaName: "RecipeResponse",
schemaDescription: "A response containing a recipe with title, skill level, cooking time, preferences, ingredients, and instructions");
// Add current state to the conversation - state is already a JsonElement
ChatMessage stateUpdateMessage = new(
ChatRole.System,
[
new TextContent("Here is the current state in JSON format:"),
new TextContent(JsonSerializer.Serialize(state, this._jsonSerializerOptions.GetTypeInfo(typeof(JsonElement)))),
new TextContent("The new state is:")
]);
var firstRunMessages = messages.Append(stateUpdateMessage);
// Collect all updates from first run
var allUpdates = new List<AgentResponseUpdate>();
await foreach (var update in this.InnerAgent.RunStreamingAsync(firstRunMessages, session, firstRunOptions, cancellationToken).ConfigureAwait(false))
{
allUpdates.Add(update);
// Yield all non-text updates (tool calls, etc.)
bool hasNonTextContent = update.Contents.Any(c => c is not TextContent);
if (hasNonTextContent)
{
yield return update;
}
}
var response = allUpdates.ToAgentResponse();
// Try to deserialize the structured state response
JsonElement stateSnapshot;
try
{
stateSnapshot = JsonSerializer.Deserialize<JsonElement>(response.Text, this._jsonSerializerOptions);
}
catch (JsonException)
{
yield break;
}
// Serialize and emit as STATE_SNAPSHOT via DataContent
byte[] stateBytes = JsonSerializer.SerializeToUtf8Bytes(
stateSnapshot,
this._jsonSerializerOptions.GetTypeInfo(typeof(JsonElement)));
yield return new AgentResponseUpdate
{
Contents = [new DataContent(stateBytes, "application/json")]
};
// Second run: Generate user-friendly summary
var secondRunMessages = messages.Concat(response.Messages).Append(
new ChatMessage(
ChatRole.System,
[new TextContent("Please provide a concise summary of the state changes in at most two sentences.")]));
await foreach (var update in this.InnerAgent.RunStreamingAsync(secondRunMessages, session, options, cancellationToken).ConfigureAwait(false))
{
yield return update;
}
}
}
Configuración del agente con gestión de estado
using Microsoft.Agents.AI;
using Azure.AI.Projects;
using Azure.Identity;
AIAgent CreateRecipeAgent(JsonSerializerOptions jsonSerializerOptions)
{
string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")
?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME")
?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT_NAME is not set.");
// Create base agent
AIAgent baseAgent = new AIProjectClient(
new Uri(endpoint),
new DefaultAzureCredential())
.AsAIAgent(
model: deploymentName,
name: "RecipeAgent",
instructions: """
You are a helpful recipe assistant. When users ask you to create or suggest a recipe,
respond with a complete RecipeResponse JSON object that includes:
- recipe.title: The recipe name
- recipe.cuisine: Type of cuisine (e.g., Italian, Mexican, Japanese)
- recipe.ingredients: Array of ingredient strings with quantities
- recipe.steps: Array of cooking instruction strings
- recipe.prep_time_minutes: Preparation time in minutes
- recipe.cook_time_minutes: Cooking time in minutes
- recipe.skill_level: One of "beginner", "intermediate", or "advanced"
Always include all fields in the response. Be creative and helpful.
""");
// Wrap with state management middleware
return new SharedStateAgent(baseAgent, jsonSerializerOptions);
}
Advertencia
DefaultAzureCredential es conveniente para el desarrollo, pero requiere una consideración cuidadosa en producción. En producción, considere usar una credencial específica (por ejemplo, ManagedIdentityCredential) para evitar problemas de latencia, sondeos de credenciales no deseados y posibles riesgos de seguridad de los mecanismos de respaldo.
Mapear el endpoint del agente
using Microsoft.Agents.AI.Hosting.AGUI.AspNetCore;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient().AddLogging();
builder.Services.ConfigureHttpJsonOptions(options =>
options.SerializerOptions.TypeInfoResolverChain.Add(RecipeSerializerContext.Default));
builder.Services.AddAGUI();
WebApplication app = builder.Build();
var jsonOptions = app.Services.GetRequiredService<IOptions<Microsoft.AspNetCore.Http.Json.JsonOptions>>().Value;
AIAgent recipeAgent = CreateRecipeAgent(jsonOptions.SerializerOptions);
app.MapAGUI("/", recipeAgent);
await app.RunAsync();
Conceptos clave
-
Detección de estado: el middleware comprueba
ag_ui_stateenChatOptions.AdditionalPropertiespara detectar cuándo el cliente solicita la administración de estado. - Respuesta en dos fases: primero genera el estado estructurado (esquema JSON) y, a continuación, genera un resumen intuitivo.
- Modelos de estado estructurado: definir clases de C# para la estructura de estado con nombres de propiedad JSON
-
Formato de respuesta del esquema JSON: use
ChatResponseFormat.ForJsonSchema<T>()para garantizar la salida estructurada -
EVENTOS STATE_SNAPSHOT: Emitidos como
DataContentcon el tipo de medioapplication/json, que el marco AG-UI convierte automáticamente en eventos STATE_SNAPSHOT. - Contexto de estado: el estado actual se inserta como un mensaje del sistema para proporcionar contexto al agente.
Funcionamiento
- El cliente envía la solicitud con el estado en
ChatOptions.AdditionalProperties["ag_ui_state"] - El middleware detecta el estado y realiza la primera ejecución con formato de respuesta de esquema JSON
- El middleware agrega el estado actual como contexto en un mensaje del sistema
- El agente genera una actualización estructurada del estado que coincide con su modelo de estado.
- El middleware serializa el estado y lo genera como
DataContent(se convierte en un evento STATE_SNAPSHOT) - El middleware realiza la segunda ejecución para generar un resumen fácil de usar
- El cliente recibe la instantánea de estado y el resumen del lenguaje natural.
Sugerencia
El enfoque de dos fases separa la administración de estado de la comunicación del usuario. La primera fase garantiza actualizaciones de estado estructurados y confiables, mientras que la segunda fase proporciona comentarios de lenguaje natural al usuario.
Implementación de cliente (C#)
Importante
La implementación del cliente de C# no se incluye en este tutorial. La administración del estado del lado servidor está completa, pero los clientes deben:
- Inicializar el estado con un objeto vacío (no null):
RecipeState? currentState = new RecipeState(); - Enviar estado como
DataContenten unChatRole.Systemmensaje - Recibir instantáneas de estado como
DataContentconmediaType = "application/json"
La capa de hospedaje de AG-UI extrae automáticamente el estado de DataContent y lo coloca en ChatOptions.AdditionalProperties["ag_ui_state"] como un JsonElement.
Para obtener un ejemplo completo de implementación de cliente, consulte el patrón de cliente de Python siguiente, que muestra el flujo de estado bidireccional completo.
Definir modelos de estado
En primer lugar, defina modelos pydantices para la estructura de estado. Esto garantiza la seguridad y validación de tipos:
from enum import Enum
from pydantic import BaseModel, Field
class SkillLevel(str, Enum):
"""The skill level required for the recipe."""
BEGINNER = "Beginner"
INTERMEDIATE = "Intermediate"
ADVANCED = "Advanced"
class CookingTime(str, Enum):
"""The cooking time of the recipe."""
FIVE_MIN = "5 min"
FIFTEEN_MIN = "15 min"
THIRTY_MIN = "30 min"
FORTY_FIVE_MIN = "45 min"
SIXTY_PLUS_MIN = "60+ min"
class Ingredient(BaseModel):
"""An ingredient with its details."""
icon: str = Field(..., description="Emoji icon representing the ingredient (e.g., 🥕)")
name: str = Field(..., description="Name of the ingredient")
amount: str = Field(..., description="Amount or quantity of the ingredient")
class Recipe(BaseModel):
"""A complete recipe."""
title: str = Field(..., description="The title of the recipe")
skill_level: SkillLevel = Field(..., description="The skill level required")
special_preferences: list[str] = Field(
default_factory=list, description="Dietary preferences (e.g., Vegetarian, Gluten-free)"
)
cooking_time: CookingTime = Field(..., description="The estimated cooking time")
ingredients: list[Ingredient] = Field(..., description="Complete list of ingredients")
instructions: list[str] = Field(..., description="Step-by-step cooking instructions")
Esquema de estado
Defina un esquema de estado para especificar la estructura y los tipos de su estado:
state_schema = {
"recipe": {"type": "object", "description": "The current recipe"},
}
Nota:
El esquema de estado usa un formato simple con type y opcional description. La estructura real se define mediante los modelos pydantices.
Actualizaciones de estado predictivo
Los argumentos de la herramienta se transmiten al estado predictivo a medida que los genera el LLM, permitiendo actualizaciones optimistas de la interfaz de usuario.
predict_state_config = {
"recipe": {"tool": "update_recipe", "tool_argument": "recipe"},
}
Esta configuración asigna el recipe campo de estado al recipe argumento de la update_recipe herramienta. Cuando el agente invoca la herramienta, los argumentos se transmiten al estado en tiempo real a medida que el LLM los genera.
Definir herramienta de actualización de estado
Cree una función de utilidad que acepte su modelo Pydantic:
from agent_framework import tool
@tool
def update_recipe(recipe: Recipe) -> str:
"""Update the recipe with new or modified content.
You MUST write the complete recipe with ALL fields, even when changing only a few items.
When modifying an existing recipe, include ALL existing ingredients and instructions plus your changes.
NEVER delete existing data - only add or modify.
Args:
recipe: The complete recipe object with all details
Returns:
Confirmation that the recipe was updated
"""
return "Recipe updated."
Importante
El nombre del parámetro de la función de la herramienta (recipe) debe coincidir con tool_argument en tu predict_state_config.
Crear el agente con administración de estado
Esta es una implementación completa del servidor con administración de estado:
"""AG-UI server with state management."""
from agent_framework import Agent
from agent_framework.openai import OpenAIChatCompletionClient
from agent_framework_ag_ui import (
AgentFrameworkAgent,
RecipeConfirmationStrategy,
add_agent_framework_fastapi_endpoint,
)
from azure.identity import AzureCliCredential
from fastapi import FastAPI
# Create the chat agent with tools
agent = Agent(
name="recipe_agent",
instructions="""You are a helpful recipe assistant that creates and modifies recipes.
CRITICAL RULES:
1. You will receive the current recipe state in the system context
2. To update the recipe, you MUST use the update_recipe tool
3. When modifying a recipe, ALWAYS include ALL existing data plus your changes in the tool call
4. NEVER delete existing ingredients or instructions - only add or modify
5. After calling the tool, provide a brief conversational message (1-2 sentences)
When creating a NEW recipe:
- Provide all required fields: title, skill_level, cooking_time, ingredients, instructions
- Use actual emojis for ingredient icons (🥕 🧄 🧅 🍅 🌿 🍗 🥩 🧀)
- Leave special_preferences empty unless specified
- Message: "Here's your recipe!" or similar
When MODIFYING or IMPROVING an existing recipe:
- Include ALL existing ingredients + any new ones
- Include ALL existing instructions + any new/modified ones
- Update other fields as needed
- Message: Explain what you improved (e.g., "I upgraded the ingredients to premium quality")
- When asked to "improve", enhance with:
* Better ingredients (upgrade quality, add complementary flavors)
* More detailed instructions
* Professional techniques
* Adjust skill_level if complexity changes
* Add relevant special_preferences
Example improvements:
- Upgrade "chicken" → "organic free-range chicken breast"
- Add herbs: basil, oregano, thyme
- Add aromatics: garlic, shallots
- Add finishing touches: lemon zest, fresh parsley
- Make instructions more detailed and professional
""",
client=OpenAIChatCompletionClient(
model=deployment_name,
azure_endpoint=endpoint,
api_version=os.getenv("AZURE_OPENAI_API_VERSION"),
credential=AzureCliCredential(),
),
tools=[update_recipe],
)
# Wrap agent with state management
recipe_agent = AgentFrameworkAgent(
agent=agent,
name="RecipeAgent",
description="Creates and modifies recipes with streaming state updates",
state_schema={
"recipe": {"type": "object", "description": "The current recipe"},
},
predict_state_config={
"recipe": {"tool": "update_recipe", "tool_argument": "recipe"},
},
confirmation_strategy=RecipeConfirmationStrategy(),
)
# Create FastAPI app
app = FastAPI(title="AG-UI Recipe Assistant")
add_agent_framework_fastapi_endpoint(app, recipe_agent, "/")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8888)
Conceptos clave
- Modelos de Pydantic: definición del estado estructurado con seguridad y validación de tipos
- Esquema de estado: formato simple que especifica tipos de campo de estado
- Configuración de estado predictivo: asigna campos de estado a argumentos de herramienta para actualizaciones de streaming
- Inyección de estado: el estado actual se inserta automáticamente como mensajes del sistema para proporcionar contexto
- Actualizaciones completas: las herramientas deben escribir el estado completo, no solo deltas
- Estrategia de confirmación: personalice los mensajes de aprobación para su dominio (receta, documento, planificación de tareas, etc.)
Comprender los eventos de estado
Evento de captura de estado
Instantánea completa del estado actual, emitida cuando se completa la herramienta:
{
"type": "STATE_SNAPSHOT",
"snapshot": {
"recipe": {
"title": "Classic Pasta Carbonara",
"skill_level": "Intermediate",
"special_preferences": ["Authentic Italian"],
"cooking_time": "30 min",
"ingredients": [
{"icon": "🍝", "name": "Spaghetti", "amount": "400g"},
{"icon": "🥓", "name": "Guanciale or bacon", "amount": "200g"},
{"icon": "🥚", "name": "Egg yolks", "amount": "4"},
{"icon": "🧀", "name": "Pecorino Romano", "amount": "100g grated"},
{"icon": "🧂", "name": "Black pepper", "amount": "To taste"}
],
"instructions": [
"Bring a large pot of salted water to boil",
"Cut guanciale into small strips and fry until crispy",
"Beat egg yolks with grated Pecorino and black pepper",
"Cook spaghetti until al dente",
"Reserve 1 cup pasta water, then drain pasta",
"Remove pan from heat, add hot pasta to guanciale",
"Quickly stir in egg mixture, adding pasta water to create creamy sauce",
"Serve immediately with extra Pecorino and black pepper"
]
}
}
}
Evento Delta de estado
Actualizaciones de estado incrementales mediante el formato de parche JSON, emitidas como argumentos de la herramienta de transmisión LLM.
{
"type": "STATE_DELTA",
"delta": [
{
"op": "replace",
"path": "/recipe",
"value": {
"title": "Classic Pasta Carbonara",
"skill_level": "Intermediate",
"cooking_time": "30 min",
"ingredients": [
{"icon": "🍝", "name": "Spaghetti", "amount": "400g"}
],
"instructions": ["Bring a large pot of salted water to boil"]
}
}
]
}
Nota:
Los eventos delta de estado se transmiten en tiempo real a medida que LLM genera los argumentos de la herramienta, lo que proporciona actualizaciones optimistas de la interfaz de usuario. La instantánea de estado final se genera cuando la herramienta completa la ejecución.
Implementación de cliente
El agent_framework_ag_ui paquete proporciona AGUIChatClient para conectarse a servidores AG-UI, lo que aporta la experiencia de cliente de Python a la paridad con .NET:
"""AG-UI client with state management."""
import asyncio
import json
import os
from typing import Any
import jsonpatch
from agent_framework import Agent, Message, Role
from agent_framework_ag_ui import AGUIChatClient
async def main():
"""Example client with state tracking."""
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")
# Create AG-UI chat client
chat_client = AGUIChatClient(server_url=server_url)
# Wrap with Agent for convenient API
agent = Agent(
name="ClientAgent",
client=chat_client,
instructions="You are a helpful assistant.",
)
# Get a thread for conversation continuity
thread = agent.create_session()
# Track state locally
state: dict[str, Any] = {}
try:
while True:
message = input("\nUser (:q to quit, :state to show state): ")
if not message.strip():
continue
if message.lower() in (":q", "quit"):
break
if message.lower() == ":state":
print(f"\nCurrent state: {json.dumps(state, indent=2)}")
continue
print()
# Stream the agent response with state
async for update in agent.run(message, session=thread, stream=True):
# Handle text content
if update.text:
print(update.text, end="", flush=True)
# Handle state updates
for content in update.contents:
# STATE_SNAPSHOT events come as DataContent with application/json
if hasattr(content, 'media_type') and content.media_type == 'application/json':
# Parse state snapshot
state_data = json.loads(content.data.decode() if isinstance(content.data, bytes) else content.data)
state = state_data
print("\n[State Snapshot Received]")
# STATE_DELTA events are handled similarly
# Apply JSON Patch deltas to maintain state
if hasattr(content, 'delta') and content.delta:
patch = jsonpatch.JsonPatch(content.delta)
state = patch.apply(state)
print("\n[State Delta Applied]")
print(f"\n\nCurrent state: {json.dumps(state, indent=2)}")
print()
except KeyboardInterrupt:
print("\n\nExiting...")
if __name__ == "__main__":
# Install dependencies: pip install agent-framework-ag-ui jsonpatch --pre
asyncio.run(main())
Ventajas clave
AGUIChatClient proporciona:
- Conexión simplificada: control automático de la comunicación HTTP/SSE
- Administración de subprocesos: seguimiento del identificador de subproceso integrado para la continuidad de la conversación
-
Integración del agente: funciona perfectamente con
Agentpara una API conocida - Control de estado: análisis automático de eventos de estado desde el servidor
- Paridad con .NET: experiencia consistente en todos los idiomas
Sugerencia
Úselo AGUIChatClient con Agent para obtener la ventaja completa de las características del marco de agente, como el historial de conversaciones, la ejecución de herramientas y la compatibilidad con middleware.
Usar estrategias de confirmación
El confirmation_strategy parámetro permite personalizar los mensajes de aprobación para el dominio:
from agent_framework_ag_ui import RecipeConfirmationStrategy
recipe_agent = AgentFrameworkAgent(
agent=agent,
state_schema={"recipe": {"type": "object", "description": "The current recipe"}},
predict_state_config={"recipe": {"tool": "update_recipe", "tool_argument": "recipe"}},
confirmation_strategy=RecipeConfirmationStrategy(),
)
Estrategias disponibles:
-
DefaultConfirmationStrategy()- Mensajes genéricos para cualquier agente -
RecipeConfirmationStrategy()- Mensajes específicos de la receta -
DocumentWriterConfirmationStrategy()- Mensajes de edición de documentos -
TaskPlannerConfirmationStrategy()- Mensajes de planeación de tareas
También puede crear estrategias personalizadas heredando de ConfirmationStrategy e implementando los métodos necesarios.
Interacción de ejemplo
Con el servidor y el cliente en ejecución:
User (:q to quit, :state to show state): I want to make a classic Italian pasta carbonara
[Run Started]
[Calling Tool: update_recipe]
[State Updated]
[State Updated]
[State Updated]
[Tool Result: Recipe updated.]
Here's your recipe!
[Run Finished]
============================================================
CURRENT STATE
============================================================
recipe:
title: Classic Pasta Carbonara
skill_level: Intermediate
special_preferences: ['Authentic Italian']
cooking_time: 30 min
ingredients:
- 🍝 Spaghetti: 400g
- 🥓 Guanciale or bacon: 200g
- 🥚 Egg yolks: 4
- 🧀 Pecorino Romano: 100g grated
- 🧂 Black pepper: To taste
instructions:
1. Bring a large pot of salted water to boil
2. Cut guanciale into small strips and fry until crispy
3. Beat egg yolks with grated Pecorino and black pepper
4. Cook spaghetti until al dente
5. Reserve 1 cup pasta water, then drain pasta
6. Remove pan from heat, add hot pasta to guanciale
7. Quickly stir in egg mixture, adding pasta water to create creamy sauce
8. Serve immediately with extra Pecorino and black pepper
============================================================
Sugerencia
Use el :state comando para ver el estado actual en cualquier momento durante la conversación.
Actualizaciones de estado predictivo en acción
Cuando se usan actualizaciones de estado predictivo con predict_state_config, el cliente recibe STATE_DELTA eventos a medida que LLM genera argumentos de herramienta en tiempo real, antes de que se ejecute la herramienta:
// Agent starts generating tool call for update_recipe
// Client receives STATE_DELTA events as the recipe argument streams:
// First delta - partial recipe with title
{
"type": "STATE_DELTA",
"delta": [{"op": "replace", "path": "/recipe", "value": {"title": "Classic Pasta"}}]
}
// Second delta - title complete with more fields
{
"type": "STATE_DELTA",
"delta": [{"op": "replace", "path": "/recipe", "value": {
"title": "Classic Pasta Carbonara",
"skill_level": "Intermediate"
}}]
}
// Third delta - ingredients starting to appear
{
"type": "STATE_DELTA",
"delta": [{"op": "replace", "path": "/recipe", "value": {
"title": "Classic Pasta Carbonara",
"skill_level": "Intermediate",
"cooking_time": "30 min",
"ingredients": [
{"icon": "🍝", "name": "Spaghetti", "amount": "400g"}
]
}}]
}
// ... more deltas as the LLM generates the complete recipe
Esto permite al cliente mostrar las actualizaciones optimistas de la interfaz de usuario en tiempo real, ya que el agente está pensando, proporcionando comentarios inmediatos a los usuarios.
Estado con humano-en-el-bucle
Puede combinar la administración de estado con flujos de trabajo de aprobación estableciendo require_confirmation=True:
recipe_agent = AgentFrameworkAgent(
agent=agent,
state_schema={"recipe": {"type": "object", "description": "The current recipe"}},
predict_state_config={"recipe": {"tool": "update_recipe", "tool_argument": "recipe"}},
require_confirmation=True, # Require approval for state changes
confirmation_strategy=RecipeConfirmationStrategy(),
)
Cuando esté habilitado:
- Actualizaciones de estado del flujo mientras el agente genera parámetros de herramienta (actualizaciones predictivas a través de eventos
STATE_DELTA) - El agente solicita aprobación antes de ejecutar la herramienta (mediante
FUNCTION_APPROVAL_REQUESTevento) - Si se aprueba, la herramienta se ejecuta y se emite el estado final (a través
STATE_SNAPSHOTdel evento) - Si se rechaza, se descartan los cambios de estado predictivo.
Patrones de estado avanzados
Estado complejo con varios campos
Puede administrar varios campos de estado con diferentes herramientas:
from pydantic import BaseModel
class TaskStep(BaseModel):
"""A single task step."""
description: str
status: str = "pending"
estimated_duration: str = "5 min"
@tool
def generate_task_steps(steps: list[TaskStep]) -> str:
"""Generate task steps for a given task."""
return f"Generated {len(steps)} steps."
@tool
def update_preferences(preferences: dict[str, Any]) -> str:
"""Update user preferences."""
return "Preferences updated."
# Configure with multiple state fields
agent_with_multiple_state = AgentFrameworkAgent(
agent=agent,
state_schema={
"steps": {"type": "array", "description": "List of task steps"},
"preferences": {"type": "object", "description": "User preferences"},
},
predict_state_config={
"steps": {"tool": "generate_task_steps", "tool_argument": "steps"},
"preferences": {"tool": "update_preferences", "tool_argument": "preferences"},
},
)
Uso de argumentos de la herramienta comodín
Cuando una herramienta devuelve datos anidados complejos, use "*" para asignar todos los argumentos de la herramienta al estado:
@tool
def create_document(title: str, content: str, metadata: dict[str, Any]) -> str:
"""Create a document with title, content, and metadata."""
return "Document created."
# Map all tool arguments to document state
predict_state_config = {
"document": {"tool": "create_document", "tool_argument": "*"}
}
Esto asigna toda la llamada de herramienta (todos los argumentos) al document campo de estado.
Procedimientos recomendados
Uso de modelos pydantices
Definir modelos estructurados para la seguridad de tipos:
class Recipe(BaseModel):
"""Use Pydantic models for structured, validated state."""
title: str
skill_level: SkillLevel
ingredients: list[Ingredient]
instructions: list[str]
Ventajas:
- Seguridad de Tipos: validación automática de tipos de datos
- Documentación: las descripciones de campo sirven como documentación
- Compatibilidad con IDE: finalización automática y comprobación de tipos
- Serialización: Conversión automática de JSON
Actualizaciones de estado completas
Escriba siempre el estado completo, no solo deltas:
@tool
def update_recipe(recipe: Recipe) -> str:
"""
You MUST write the complete recipe with ALL fields.
When modifying a recipe, include ALL existing ingredients and
instructions plus your changes. NEVER delete existing data.
"""
return "Recipe updated."
Esto garantiza la coherencia de estado y las actualizaciones predictivas adecuadas.
Concordar nombres de parámetros
Asegúrese de que los nombres de parámetros de la herramienta coincidan con tool_argument la configuración:
# Tool parameter name
def update_recipe(recipe: Recipe) -> str: # Parameter name: 'recipe'
...
# Must match in predict_state_config
predict_state_config = {
"recipe": {"tool": "update_recipe", "tool_argument": "recipe"} # Same name
}
Proporcionar contexto en instrucciones
Incluya instrucciones claras sobre la administración de estado:
agent = Agent(
instructions="""
CRITICAL RULES:
1. You will receive the current recipe state in the system context
2. To update the recipe, you MUST use the update_recipe tool
3. When modifying a recipe, ALWAYS include ALL existing data plus your changes
4. NEVER delete existing ingredients or instructions - only add or modify
""",
...
)
Usar estrategias de confirmación
Personalice los mensajes de aprobación para el dominio:
from agent_framework_ag_ui import RecipeConfirmationStrategy
recipe_agent = AgentFrameworkAgent(
agent=agent,
confirmation_strategy=RecipeConfirmationStrategy(), # Domain-specific messages
)
Pasos siguientes
Ahora ha aprendido todas las características principales de AG-UI. A continuación, puede:
- Exploración de la documentación de Agent Framework
- Crear una aplicación completa que combine todas las características de AG-UI
- Implementación del servicio AG-UI en producción