Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
V tomto tutoriálu se dozvíte, jak implementovat správu stavu pomocí AG-UI, což umožňuje obousměrnou synchronizaci stavu mezi klientem a serverem. To je nezbytné pro vytváření interaktivních aplikací, jako jsou generování uživatelského rozhraní, řídicí panely v reálném čase nebo prostředí pro spolupráci.
Požadavky
Než začnete, ujistěte se, že rozumíte:
Co je Správa stavu?
Správa stavu v AG-UI umožňuje:
- Sdílený stav: Klient i server udržují synchronizované zobrazení stavu aplikace.
- Obousměrná synchronizace: Stav lze aktualizovat z klienta nebo serveru.
- Aktualizace v reálném čase: Změny se streamují okamžitě pomocí událostí stavu.
- Prediktivní aktualizace: Stream aktualizací stavu, protože LLM generuje argumenty nástroje (optimistické uživatelské rozhraní)
- Strukturovaná data: Stav se řídí schématem JSON pro ověření.
Případy použití
Správa stavu je cenná pro:
- Generování uživatelského rozhraní: Sestavení komponent uživatelského rozhraní na základě stavu řízeného agentem
- Sestavení formuláře: Agent naplní pole formuláře při shromažďování informací
- Sledování průběhu: Zobrazení průběhu vícekrokových operací v reálném čase
- Interaktivní informační panely: Zobrazení dat, která se aktualizují během zpracování agentem.
- Úpravy pro spolupráci: Více uživatelů vidí konzistentní aktualizace stavu
Vytváření agentů State-Aware v jazyce C#
Definování modelu stavu
Nejprve definujte třídy pro vaši strukturu stavu:
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;
Implementace middlewaru správy stavů
Vytvořte middleware, který zpracovává správu stavu tím, že zjistí, kdy klient odesílá stav a koordinuje odpovědi agenta:
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;
}
}
}
Konfigurace agenta se správou stavu
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);
}
Výstraha
DefaultAzureCredential je vhodný pro vývoj, ale vyžaduje pečlivé zvážení v produkčním prostředí. V produkčním prostředí zvažte použití konkrétních přihlašovacích údajů (např ManagedIdentityCredential. ) k zabránění problémům s latencí, neúmyslnému testování přihlašovacích údajů a potenciálním bezpečnostním rizikům z náhradních mechanismů.
Mapování koncového bodu agenta
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();
Klíčové koncepty
-
Detekce stavu: Middleware kontroluje
ag_ui_statevChatOptions.AdditionalProperties, zda klient žádá o správu stavu. - Dvoufázová odpověď: Nejprve vygeneruje strukturovaný stav (schéma JSON), pak vygeneruje uživatelsky přívětivý souhrn.
- Modely strukturovaného stavu: Definování tříd jazyka C# pro vaši strukturu stavů s názvy vlastností JSON
-
Formát odpovědi schématu JSON: Slouží
ChatResponseFormat.ForJsonSchema<T>()k zajištění strukturovaného výstupu. -
STATE_SNAPSHOT události: Vygenerované jako
DataContentsapplication/jsontypem média, který rozhraní AG-UI automaticky převede na události STATE_SNAPSHOT - Kontext stavu: Aktuální stav se vloží jako systémová zpráva pro poskytnutí kontextu agenta.
Jak to funguje
- Klient odešle požadavek se stavem v
ChatOptions.AdditionalProperties["ag_ui_state"] - Middleware zjistí stav a provede první spuštění s formátem odpovědi schématu JSON.
- Middleware přidá aktuální stav jako kontext v systémové zprávě.
- Agent generuje aktualizaci strukturovaného stavu odpovídající vašemu modelu stavu.
- Middleware serializuje stav a emituje se jako
DataContent(stává se událostí STATE_SNAPSHOT) - Middleware provede druhé spuštění a vygeneruje uživatelsky přívětivý souhrn.
- Klient obdrží snímek stavu i souhrn přirozeného jazyka.
Návod
Dvoufázový přístup odděluje správu stavu od komunikace uživatelů. První fáze zajišťuje strukturované a spolehlivé aktualizace stavu, zatímco druhá fáze poskytuje uživateli zpětnou vazbu přirozeného jazyka.
Implementace klienta (C#)
Důležité
Implementace klienta jazyka C# není součástí tohoto kurzu. Správa stavu na straně serveru je dokončená, ale klienti potřebují:
- Inicializace stavu s prázdným objektem (nikoli null):
RecipeState? currentState = new RecipeState(); - Odeslat stav jako
DataContentveChatRole.Systemzprávě - Přijímejte snímky stavu jako
DataContentsmediaType = "application/json"
Vrstva hostování AG-UI automaticky extrahuje stav z DataContent a umístí ho do ChatOptions.AdditionalProperties["ag_ui_state"] jako JsonElement.
Kompletní příklad implementace klienta najdete v následujícím vzoru klienta Pythonu, který ukazuje úplný obousměrný tok stavu.
Definování modelů stavů
Nejprve definujte Pydantické modely pro vaši stavovou strukturu. Tím zajistíte bezpečnost a ověřování typů:
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")
Schéma stavu
Definujte schéma stavu pro určení struktury a typů stavu:
state_schema = {
"recipe": {"type": "object", "description": "The current recipe"},
}
Poznámka:
Schéma stavu používá jednoduchý formát s volitelným formátem typedescription. Skutečnou strukturu definuje vaše Pydantické modely.
Prediktivní aktualizace stavu
Argumenty streamovacího nástroje pro prediktivní aktualizace stavu jsou přidávány do stavu, jakmile je generuje LLM, což umožňuje optimistické aktualizace uživatelského rozhraní.
predict_state_config = {
"recipe": {"tool": "update_recipe", "tool_argument": "recipe"},
}
Tato konfigurace mapuje recipe stavové pole na recipe argument update_recipe nástroje. Když agent volá nástroj, argumenty se v reálném čase streamují do systému, zatímco je generuje LLM.
Definovat nástroj aktualizace stavu
Vytvořte funkci nástroje, která přijímá váš Pydantický model:
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."
Důležité
Název parametru funkce nástroje (recipe) se musí shodovat s vaším tool_argumentparametrem predict_state_config .
Vytvořte agenta se správou stavu
Tady je kompletní implementace serveru se správou stavu:
"""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)
Klíčové koncepty
- Pydantické modely: Definování strukturovaného stavu s bezpečností a ověřováním typů
- Schéma stavu: Jednoduchý formát určující typy polí stavu
- Prediktivní konfigurace stavu: Mapuje pole stavů na parametry nástrojů pro průběžné aktualizace.
- Injektáž stavu: Aktuální stav se automaticky vloží jako systémové zprávy pro poskytnutí kontextu.
- Úplné aktualizace: Nástroje musí zapsat úplný stav, nejen rozdíly.
- Strategie potvrzení: Přizpůsobení schvalovacích zpráv pro vaši doménu (recept, dokument, plánování úkolů atd.)
Porozumění událostem stavu
Událost snímku stavu
Úplný snímek aktuálního stavu, který se vygeneruje po dokončení nástroje:
{
"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"
]
}
}
}
Událost Delta stavu
Postupné aktualizace stavu pomocí formátu JSON Patch emitované jako argumenty nástroje LLM streamů.
{
"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"]
}
}
]
}
Poznámka:
Stavový rozdílový datový proud událostí v reálném čase, protože LLM generuje argumenty nástroje a poskytuje optimistické aktualizace uživatelského rozhraní. Po dokončení provádění nástroje se vygeneruje snímek konečného stavu.
Implementace klienta
Balíček agent_framework_ag_ui poskytuje AGUIChatClient pro propojení k serverům AG-UI a přináší zkušenost klienta v Pythonu na úroveň parity s .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())
Klíčové výhody
Poskytuje AGUIChatClient :
- Zjednodušené připojení: Automatické zpracování komunikace HTTP/SSE
- Správa vláken: Integrované sledování ID vlákna pro kontinuitu konverzací
-
Integrace agenta: Bezproblémová integrace se známým API
Agent - Zpracování stavu: Automatická analýza událostí stavu ze serveru
- Parita s .NET: Konzistentní prostředí napříč jazyky
Návod
S využitím této AGUIChatClientAgent funkce získáte plnou výhodu funkcí architektury agentů, jako je historie konverzací, spouštění nástrojů a podpora middlewaru.
Použití strategií potvrzení
Tento confirmation_strategy parametr umožňuje přizpůsobit schvalovací zprávy pro vaši doménu:
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(),
)
Dostupné strategie:
-
DefaultConfirmationStrategy()- Obecné zprávy pro libovolného agenta -
RecipeConfirmationStrategy()- Zprávy specifické k receptu -
DocumentWriterConfirmationStrategy()- Zprávy o úpravách dokumentu -
TaskPlannerConfirmationStrategy()- Zprávy plánování úkolů
Vlastní strategie můžete také vytvořit tak, že zdědíte ConfirmationStrategy a implementujete požadované metody.
Příklad interakce
Se spuštěným serverem a klientem:
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
============================================================
Návod
:state Pomocí příkazu můžete kdykoli během konverzace zobrazit aktuální stav.
Prediktivní aktualizace stavu v praxi
Při použití aktualizací prediktivního stavu s predict_state_config klient obdrží STATE_DELTA události, jakmile LLM generuje argumenty nástroje v reálném čase před spuštěním nástroje.
// 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
To klientovi umožňuje zobrazit optimistické aktualizace uživatelského rozhraní v reálném čase, protože agent přemýšlí a poskytuje uživatelům okamžitou zpětnou vazbu.
Stav s člověkem v kruhuřízení
Správu stavu můžete kombinovat s pracovními postupy schválení nastavením 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(),
)
Pokud je povoleno:
- Stream aktualizací stavu, protože agent generuje argumenty nástroje (prediktivní aktualizace prostřednictvím
STATE_DELTAudálostí) - Agent požádá o schválení před spuštěním nástroje (prostřednictvím
FUNCTION_APPROVAL_REQUESTudálosti). - Pokud je nástroj schválen, spustí se a vygeneruje se konečný stav (prostřednictvím
STATE_SNAPSHOTudálosti). - Pokud je zamítnuto, změny prediktivního stavu se zahodí.
Pokročilé vzory stavu
Komplexní stav s více poli
Pomocí různých nástrojů můžete spravovat více polí stavu:
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"},
},
)
Použití argumentů nástroje se zástupnými důkazy
Když nástroj vrátí složitá vnořená data, použijte "*" k mapování všech argumentů nástroje na stav:
@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": "*"}
}
Tím se mapuje celé volání nástroje (všechny argumenty) na document pole stavu.
Osvědčené postupy
Použití Pydantických modelů
Definování strukturovaných modelů pro bezpečnost typů:
class Recipe(BaseModel):
"""Use Pydantic models for structured, validated state."""
title: str
skill_level: SkillLevel
ingredients: list[Ingredient]
instructions: list[str]
Výhody:
- Bezpečnost typů: Automatické ověřování datových typů
- Dokumentace: Popisy polí slouží jako dokumentace
- Podpora integrovaného vývojového prostředí (IDE): Automatické dokončování a kontrola typů
- Serializace: Automatický převod JSON
Úplné aktualizace stavu
Vždy zapisujte úplný stav, nejen rozdíly:
@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."
Tím zajistíte konzistenci stavu a správné prediktivní aktualizace.
Shoda názvů parametrů
Ujistěte se, že názvy parametrů nástroje odpovídají tool_argument konfiguraci:
# 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
}
Zadání kontextu v pokynech
Uveďte jasné pokyny ke správě stavu:
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
""",
...
)
Použití strategií potvrzení
Přizpůsobení schvalovacích zpráv pro vaši doménu:
from agent_framework_ag_ui import RecipeConfirmationStrategy
recipe_agent = AgentFrameworkAgent(
agent=agent,
confirmation_strategy=RecipeConfirmationStrategy(), # Domain-specific messages
)
Další kroky
Naučili jste se teď všechny základní funkce AG-UI! Dále můžete:
- Prozkoumání dokumentace k rozhraní Agent Framework
- Sestavení kompletní aplikace kombinující všechny funkce AG-UI
- Nasazení služby AG-UI do produkčního prostředí