Bagikan melalui


Manajemen Status dengan AG-UI

Tutorial ini menunjukkan kepada Anda cara menerapkan manajemen status dengan AG-UI, memungkinkan sinkronisasi status dua arah antara klien dan server. Ini penting untuk membangun aplikasi interaktif seperti UI generatif, dasbor real-time, atau pengalaman kolaboratif.

Prasyarat

Sebelum memulai, pastikan Anda memahami:

Apa itu Manajemen Status?

Manajemen status di AG-UI memungkinkan:

  • Status Bersama: Klien dan server mempertahankan tampilan status aplikasi yang disinkronkan
  • Sinkronisasi Dua Arah: Status dapat diperbarui dari klien atau server
  • Pembaruan Real-time: Perubahan segera dialirkan menggunakan peristiwa status
  • Pembaruan Prediktif: Aliran pembaruan status saat LLM menggenerasikan argumen peralatan (UI optimis)
  • Data Terstruktur: Status mengikuti skema JSON untuk validasi

Kasus Penggunaan

Pengelolaan status sangat penting untuk:

  • UI generatif: Membangun komponen UI berdasarkan status yang dikontrol agen
  • Form Building: Agen mengisi bidang formulir saat mengumpulkan informasi
  • Pemantauan Kemajuan: Menampilkan kemajuan secara real-time dari operasi multi-langkah
  • Dasbor Interaktif: Menampilkan data yang diperbarui saat agen memprosesnya
  • Pengeditan Kolaboratif: Beberapa pengguna melihat pembaruan status yang konsisten

Membuat Agen yang Sadar Status di C#

Tentukan Model Status Anda

Pertama, tentukan kelas untuk struktur status Anda:

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;

Menerapkan Middleware Manajemen Status

Buat middleware yang menangani manajemen status dengan mendeteksi kapan klien mengirim status dan mengoordinasikan respons agen:

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;
        }
    }
}

Mengonfigurasi Agen dengan Manajemen Status

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);
}

Peringatan

DefaultAzureCredential nyaman untuk pengembangan tetapi membutuhkan pertimbangan yang cermat dalam produksi. Dalam produksi, pertimbangkan untuk menggunakan kredensial tertentu (misalnya, ManagedIdentityCredential) untuk menghindari masalah latensi, pemeriksaan kredensial yang tidak diinginkan, dan potensi risiko keamanan dari mekanisme fallback.

Memetakan Titik Akhir Agen

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();

Konsep utama

  • Deteksi Status: Middleware memeriksa di dalam ag_ui_state untuk ChatOptions.AdditionalProperties mendeteksi kapan klien meminta manajemen status
  • Two-Phase Respons: Pertama menghasilkan status terstruktur (skema JSON), lalu menghasilkan ringkasan yang mudah digunakan
  • Model Status Terstruktur: Tentukan kelas C# untuk struktur status Anda dengan nama properti JSON
  • Format Respons Skema JSON: Gunakan ChatResponseFormat.ForJsonSchema<T>() untuk memastikan output terstruktur
  • Peristiwa STATE_SNAPSHOT: Dikeluarkan sebagai DataContent dengan jenis media application/json, yang secara otomatis dikonversi oleh kerangka kerja AG-UI menjadi peristiwa STATE_SNAPSHOT.
  • Konteks Status: Status saat ini disuntikkan sebagai pesan sistem untuk memberikan konteks kepada agen

Cara Kerjanya

  1. Client mengirim permintaan dengan state di ChatOptions.AdditionalProperties["ag_ui_state"]
  2. Middleware mendeteksi status dan melakukan eksekusi pertama dengan format respons skema JSON
  3. Middleware menambahkan status saat ini sebagai konteks dalam pesan sistem
  4. Agen menghasilkan pembaruan status terstruktur yang cocok dengan model status Anda
  5. Middleware menserialisasikan status dan memancarkan sebagai DataContent (menjadi peristiwa STATE_SNAPSHOT)
  6. Middleware melakukan eksekusi kedua untuk menghasilkan ringkasan yang mudah digunakan
  7. Klien menerima rekam jepret status dan ringkasan bahasa alami

Petunjuk / Saran

Pendekatan dua fase memisahkan manajemen status dari komunikasi pengguna. Fase pertama memastikan pembaruan status terstruktur dan andal sementara fase kedua memberikan umpan balik bahasa alami kepada pengguna.

Implementasi Klien (C#)

Penting

Implementasi klien C# tidak disertakan dalam tutorial ini. Manajemen status sisi server selesai, tetapi klien perlu:

  1. Menginisialisasi status dengan objek kosong (bukan null): RecipeState? currentState = new RecipeState();
  2. Mengirim status sebagai DataContent dalam pesan ChatRole.System
  3. Menerima cuplikan status dengan DataContent dan mediaType = "application/json"

Lapisan hosting AG-UI secara otomatis mengekstrak status dari DataContent dan menempatkannya ChatOptions.AdditionalProperties["ag_ui_state"] sebagai JsonElement.

Untuk contoh implementasi klien lengkap, lihat pola klien Python di bawah ini yang menunjukkan alur status dua arah penuh.

Tentukan Model Status

Pertama, tentukan model Pydantic untuk struktur status Anda. Ini memastikan keamanan dan validasi jenis:

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")

Skema Keadaan

Tentukan skema status untuk menentukan struktur dan jenis status Anda:

state_schema = {
    "recipe": {"type": "object", "description": "The current recipe"},
}

Nota

Skema status menggunakan format sederhana dengan type dan opsional description. Struktur yang sebenarnya ditentukan oleh model Pydantic Anda.

Pembaruan Status Prediktif

Pembaruan status prediktif mengalirkan argumen alat ke status saat LLM menghasilkannya, memungkinkan pembaruan UI secara optimis.

predict_state_config = {
    "recipe": {"tool": "update_recipe", "tool_argument": "recipe"},
}

Konfigurasi ini memetakan recipe bidang status ke recipe argumen update_recipe alat. Ketika agen memanggil alat, argumen mengalir ke status secara real time saat LLM menghasilkannya.

Tentukan Alat Pembaruan Status

Buat fungsi alat yang menerima model Pydantic Anda:

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."

Penting

Nama parameter fungsi alat tersebut (recipe) harus cocok dengan tool_argument dalam predict_state_config Anda.

Membuat Agen dengan Manajemen Status

Berikut adalah implementasi server lengkap dengan manajemen status:

"""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)

Konsep utama

  • Model Pydantic: Tentukan status terstruktur dengan keamanan tipe dan validasi
  • Skema Status: Format sederhana yang menentukan jenis bidang status
  • Konfigurasi Status Prediktif: Memetakan bidang status ke argumen alat untuk pembaruan streaming
  • Injeksi Status: Status saat ini secara otomatis disuntikkan sebagai pesan sistem untuk memberikan konteks
  • Pembaruan Lengkap: Alat harus menulis status lengkap, bukan hanya delta
  • Strategi Konfirmasi: Menyesuaikan pesan persetujuan untuk domain Anda (resep, dokumen, perencanaan tugas, dll.)

Memahami Kejadian Keadaan

Peristiwa Cuplikan Status

Rekam jepret lengkap status saat ini, dipancarkan saat alat selesai:

{
    "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"
            ]
        }
    }
}

Peristiwa Status Delta

Pembaruan status bertahap menggunakan format Patch JSON, dipancarkan sebagai argumen alat streaming 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

Aliran peristiwa delta status secara real-time karena LLM menghasilkan argumen alat, memberikan pembaruan antarmuka pengguna yang optimis. Rekam jepret status akhir dipancarkan ketika alat menyelesaikan eksekusi.

Implementasi Klien

Paket ini agent_framework_ag_ui menyediakan AGUIChatClient untuk menyambungkan ke server AG-UI, menghadirkan pengalaman klien Python setara dengan .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())

Manfaat Utama

AGUIChatClient menyediakan:

  • Koneksi Yang Disederhanakan: Penanganan otomatis komunikasi HTTP/SSE
  • Manajemen Utas: Pelacakan ID utas bawaan untuk kesinambungan percakapan
  • Integrasi Agen: Bekerja dengan mulus bersama Agent untuk API yang sudah dikenal
  • Penanganan Status: Penguraian otomatis peristiwa status dari server
  • Paritas dengan .NET: Pengalaman konsisten di seluruh bahasa

Petunjuk / Saran

Gunakan AGUIChatClient dengan Agent untuk mendapatkan manfaat penuh dari fitur kerangka kerja agen seperti riwayat percakapan, eksekusi alat, dan dukungan middleware.

Menggunakan Strategi Konfirmasi

Parameter confirmation_strategy memungkinkan Anda menyesuaikan pesan persetujuan untuk domain Anda:

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(),
)

Strategi yang tersedia:

  • DefaultConfirmationStrategy() - Pesan generik untuk agen apa pun
  • RecipeConfirmationStrategy() - Pesan khusus resep
  • DocumentWriterConfirmationStrategy() - Pesan pengeditan dokumen
  • TaskPlannerConfirmationStrategy() - Pesan perencanaan tugas

Anda juga dapat membuat strategi khusus dengan mewarisi dari ConfirmationStrategy dan menerapkan metode yang diperlukan.

Contoh Interaksi

Dengan server dan klien yang berjalan:

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

============================================================

Petunjuk / Saran

:state Gunakan perintah untuk menampilkan status saat ini kapan saja selama percakapan.

Pembaruan Status Prediktif dalam Aksi

Saat menggunakan pembaruan status prediktif dengan predict_state_config, klien menerima peristiwa STATE_DELTA saat LLM menghasilkan argumen alat secara real-time, sebelum alat dijalankan.

// 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

Ini memungkinkan klien untuk menunjukkan pembaruan antarmuka pengguna yang optimis secara real-time saat agen berpikir, memberikan umpan balik segera kepada pengguna.

Status dengan Human-in-the-Loop

Anda dapat menggabungkan manajemen status dengan alur kerja persetujuan dengan mengatur 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(),
)

Saat diaktifkan:

  1. Aliran pembaruan status saat agen menghasilkan argumen alat (pembaruan prediktif melalui STATE_DELTA peristiwa)
  2. Agen meminta persetujuan sebelum menjalankan alat (melalui FUNCTION_APPROVAL_REQUEST peristiwa)
  3. Jika disetujui, alat dijalankan dan status akhir dipancarkan (melalui STATE_SNAPSHOT event)
  4. Jika ditolak, perubahan status prediktif akan dibuang

Pola Status Tingkat Lanjut

Status Kompleks dengan Beberapa Bidang

Anda dapat mengelola beberapa bidang status dengan alat yang berbeda:

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"},
    },
)

Menggunakan Argumen Alat Wildcard

Saat alat mengembalikan data berlapis yang kompleks, gunakan "*" untuk memetakan semua argumen alat ke state:

@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": "*"}
}

Ini memetakan seluruh panggilan alat (semua argumen) ke document bidang status.

Praktik Terbaik

Menggunakan Model Pydantic

Tentukan model terstruktur untuk keamanan jenis:

class Recipe(BaseModel):
    """Use Pydantic models for structured, validated state."""
    title: str
    skill_level: SkillLevel
    ingredients: list[Ingredient]
    instructions: list[str]

Keuntungan:

  • Keamanan Jenis: Validasi otomatis jenis data
  • Dokumentasi: Deskripsi bidang berfungsi sebagai dokumentasi
  • Dukungan IDE: Penyelesaian otomatis dan pemeriksaan jenis
  • Serialisasi: Konversi JSON otomatis

Selesaikan Pembaruan Status

Selalu tulis status lengkap, bukan hanya delta:

@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."

Ini memastikan konsistensi status dan pembaruan prediktif yang tepat.

Sesuaikan Nama Parameter

Pastikan nama parameter alat cocok dengan tool_argument konfigurasi:

# 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
}

Berikan Konteks dalam Instruksi

Sertakan instruksi yang jelas tentang manajemen status:

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
    """,
    ...
)

Menggunakan Strategi Konfirmasi

Kustomisasi pesan persetujuan untuk domain Anda:

from agent_framework_ag_ui import RecipeConfirmationStrategy

recipe_agent = AgentFrameworkAgent(
    agent=agent,
    confirmation_strategy=RecipeConfirmationStrategy(),  # Domain-specific messages
)

Langkah Selanjutnya

Anda sekarang telah mempelajari semua fitur inti AG-UI! Selanjutnya Anda dapat:

  • Jelajahi dokumentasi Kerangka Agen
  • Membangun aplikasi lengkap yang menggabungkan semua fitur AG-UI
  • Menyebarkan layanan AG-UI Anda ke lingkungan produksi

Sumber Daya Tambahan