Wprowadzenie do AG-UI

W tym samouczku pokazano, jak kompilować aplikacje serwerowe i klienckie przy użyciu protokołu AG-UI z programem Agent Framework. Dowiesz się, jak hostować agenta za punktem końcowym AG-UI i połączyć klienta na potrzeby konwersacji interakcyjnych.

Co będziesz budować

Po ukończeniu tego samouczka będziesz mieć następujące elementy:

  • Serwer AG-UI hostuje agenta sztucznej inteligencji dostępnego za pośrednictwem protokołu HTTP
  • Aplikacja kliencka, która łączy się z serwerem i przesyła strumieniowo odpowiedzi
  • Informacje o sposobie działania protokołu AG-UI z platformą Agent Framework

Wymagania wstępne

  • .NET 8 lub nowszy
  • Projekt ASP.NET Core
  • Skonfigurowany program MAF AIAgent

W przykładzie użyto Azure interfejsu OpenAI, ale MapAGUIServer współpracuje z dowolnym agentem MAF.

Tworzenie serwera AG-UI

Zainstaluj pakiet hostingowy:

dotnet add package Microsoft.Agents.AI.Hosting.AGUI.AspNetCore --prerelease

Zarejestruj AG-UI hostingu i zamapuj agenta:

using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Hosting.AGUI.AspNetCore;

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddAGUIServer();

AIAgent agent = CreateAgent();

WebApplication app = builder.Build();
app.MapAGUIServer("/", agent);
await app.RunAsync();

MapAGUIServer akceptuje żądania AG-UI RunAgentInput i przesyła strumieniowo odpowiedź agenta jako zdarzenia AG-UI za pośrednictwem zdarzeń wysyłanych przez serwer (SSE).

Uruchom serwer pod adresem URL używanym przez przykład klienta:

dotnet run --urls http://localhost:8888

Wskazówka

Zobacz przykład .NET wprowadzenie dla kompletnego klienta serwera i konsoli.

Nawiązywanie połączenia z klientem .NET

Zestaw SDK AG-UI .NET udostępnia AGUIChatClientelement , który implementuje IChatClient i może zostać dostosowany do agenta MAF:

dotnet add package AGUI.Client --prerelease
dotnet add package Microsoft.Agents.AI --prerelease
using AGUI.Abstractions;
using AGUI.Client;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;

using HttpClient httpClient = new() { BaseAddress = new Uri("http://localhost:8888") };
AGUIChatClient chatClient = new(new AGUIChatClientOptions(httpClient, "/"));
AIAgent remoteAgent = chatClient.AsAIAgent();
AgentSession session = await remoteAgent.CreateSessionAsync();

List<AgentResponseUpdate> firstTurnUpdates = [];
await foreach (AgentResponseUpdate update in
    remoteAgent.RunStreamingAsync("Hello", session))
{
    firstTurnUpdates.Add(update);

    foreach (TextContent text in update.Contents.OfType<TextContent>())
    {
        Console.Write(text.Text);
    }
}

Możesz również nawiązać połączenie z dowolnym klientem, który implementuje protokół AG-UI.

Ciągłość konwersacji

AG-UI używa threadId metody i parentRunId do identyfikowania żądań kontynuacji. Te identyfikatory to dane protokołu, a nie poświadczenia autoryzacji.

AGUIChatClient jest bezstanowy. Aby kontynuować konwersację będącą własnością serwera, pobierz identyfikatory z pierwszego etapu RunStartedEvent, a następnie uwzględnij te same threadId i poprzednie runId , co parentRunId w następnym żądaniu:

RunStartedEvent started = firstTurnUpdates
    .Select(update => update.AsChatResponseUpdate().RawRepresentation)
    .OfType<RunStartedEvent>()
    .FirstOrDefault()
    ?? throw new InvalidOperationException("The server didn't return a run-started event.");

ChatMessage nextMessage = new(ChatRole.User, "What did I just say?");
ChatClientAgentRunOptions continuationOptions = new()
{
    ChatOptions = new ChatOptions
    {
        RawRepresentationFactory = _ => new RunAgentInput
        {
            ThreadId = started.ThreadId,
            ParentRunId = started.RunId,
            Messages = new[] { nextMessage }.AsAGUIMessages().ToList(),
        },
    },
};

await foreach (AgentResponseUpdate update in
    remoteAgent.RunStreamingAsync([nextMessage], session, continuationOptions))
{
    // Process the continued response.
}

Wysyłaj tylko nowe komunikaty w żądaniu kontynuacji. MapAGUIServer używa threadId polecenia , aby wybrać hostowaną sesję agenta i parentRunId zidentyfikować kontynuowanie przebiegu. Bez trwałości sesji hostowanej każde żądanie otrzymuje nową sesję serwera; klient może zamiast tego ponownie wysłać historię konwersacji.

Aby zachować stan własności AgentSession serwera między żądaniami, skonfiguruj trwałość i izolację hostowanej sesji, a następnie zamapuj nazwanego hostowanego agenta na MapAGUIServer. Aby zapoznać się z granicą zaufania specyficzną dla interfejsu użytkownika grupy dostępności, zobacz Zagadnienia dotyczące produkcji i zabezpieczeń.

Następne kroki

Wymagania wstępne

Przed rozpoczęciem upewnij się, że masz następujące elementy:

Note

Te przykłady korzystają z modeli usługi Azure OpenAI. Aby uzyskać więcej informacji, zobacz wdrażanie modeli usługi Azure OpenAI za pomocą rozwiązania Foundry.

Note

Przykłady te używają DefaultAzureCredential do uwierzytelniania. Upewnij się, że uwierzytelniasz się przy użyciu platformy Azure (np. za pośrednictwem polecenia az login). Aby uzyskać więcej informacji, zobacz dokumentację usługi Azure Identity.

Warning

Protokół AG-UI jest nadal opracowywany i podlega zmianie. Te przykłady będą aktualizowane w miarę rozwoju protokołu.

Krok 1. Tworzenie serwera AG-UI

Serwer AG-UI hostuje agenta sztucznej inteligencji i uwidacznia go za pośrednictwem punktów końcowych HTTP przy użyciu interfejsu FastAPI.

Instalowanie wymaganych pakietów

Zainstaluj niezbędne pakiety dla serwera:

pip install agent-framework-ag-ui --pre

Lub przy użyciu uv:

uv pip install agent-framework-ag-ui --prerelease=allow

Spowoduje to automatyczne zainstalowanie agent-framework-core, fastapi, uvicorn i sse-starlette jako zależności.

Kod serwera

Utwórz plik o nazwie server.py:

"""AG-UI server example."""

import os

from agent_framework import Agent
from agent_framework.openai import OpenAIChatCompletionClient
from agent_framework_ag_ui import add_agent_framework_fastapi_endpoint
from azure.identity import AzureCliCredential
from fastapi import FastAPI

# Read required configuration
endpoint = os.environ.get("AZURE_OPENAI_ENDPOINT")
deployment_name = os.environ.get("AZURE_OPENAI_CHAT_COMPLETION_MODEL")

if not endpoint:
    raise ValueError("AZURE_OPENAI_ENDPOINT environment variable is required")
if not deployment_name:
    raise ValueError("AZURE_OPENAI_CHAT_COMPLETION_MODEL environment variable is required")

chat_client = OpenAIChatCompletionClient(
    model=deployment_name,
    azure_endpoint=endpoint,
    api_version=os.getenv("AZURE_OPENAI_API_VERSION"),
    credential=AzureCliCredential(),
)

# Create the AI agent
agent = Agent(
    name="AGUIAssistant",
    instructions="You are a helpful assistant.",
    client=chat_client,
)

# Create FastAPI app
app = FastAPI(title="AG-UI Server")

# Register the AG-UI endpoint
add_agent_framework_fastapi_endpoint(app, agent, "/")

if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8888)

Kluczowe pojęcia

  • add_agent_framework_fastapi_endpoint: rejestruje punkt końcowy AG-UI z automatyczną obsługą żądań/odpowiedzi i przesyłaniem strumieniowym SSE
  • Agent: Agent frameworku, który będzie obsługiwać przychodzące żądania
  • Integracja FastAPI: używa natywnego wsparcia dla asynchronicznych operacji FastAPI w odpowiedziach strumieniowych
  • Instrukcje: agent jest tworzony za pomocą domyślnych instrukcji, które mogą być zastępowane przez komunikaty klienta
  • Konfiguracja: OpenAIChatCompletionClient akceptuje jawne dane wejściowe routingu platformy Azure, takie jak model, azure_endpoint, api_versioni credential, i mogą również odczytywać ze zmiennych środowiskowych

Konfigurowanie i uruchamianie serwera

Ustaw wymagane zmienne środowiskowe:

export AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/"
export AZURE_OPENAI_CHAT_COMPLETION_MODEL="gpt-4o-mini"

Uruchom serwer:

python server.py

Lub bezpośrednio przy użyciu uvicorn:

uvicorn server:app --host 127.0.0.1 --port 8888

Serwer rozpocznie nasłuchiwanie na http://127.0.0.1:8888.

Krok 2. Tworzenie klienta AG-UI

Klient AG-UI łączy się z serwerem zdalnym i wyświetla strumieniowe odpowiedzi.

Instalowanie wymaganych pakietów

Pakiet AG-UI jest już zainstalowany, który zawiera element AGUIChatClient:

# Already installed with agent-framework-ag-ui
pip install agent-framework-ag-ui --pre

Kod klienta

Utwórz plik o nazwie client.py:

"""AG-UI client example."""

import asyncio
import os

from agent_framework import Agent
from agent_framework_ag_ui import AGUIChatClient


async def main():
    """Main client loop."""
    # Get server URL from environment or use default
    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(endpoint=server_url)

    # Create agent with the chat client
    agent = Agent(
        name="ClientAgent",
        client=chat_client,
        instructions="You are a helpful assistant.",
    )

    # Get a thread for conversation continuity
    thread = agent.create_session()

    try:
        while True:
            # Get user input
            message = input("\nUser (:q or quit to exit): ")
            if not message.strip():
                print("Request cannot be empty.")
                continue

            if message.lower() in (":q", "quit"):
                break

            # Stream the agent response
            print("\nAssistant: ", end="", flush=True)
            async for update in agent.run(message, session=thread, stream=True):
                # Print text content as it streams
                if update.text:
                    print(f"\033[96m{update.text}\033[0m", end="", flush=True)

            print("\n")

    except KeyboardInterrupt:
        print("\n\nExiting...")
    except Exception as e:
        print(f"\n\033[91mAn error occurred: {e}\033[0m")


if __name__ == "__main__":
    asyncio.run(main())

Kluczowe pojęcia

  • Server-Sent Events (SSE): Protokół używa formatu SSE (data: {json}\n\n)
  • Typy zdarzeń: Różne zdarzenia zapewniają metadane i zawartość (WIELKIMI LITERAMI z podkreśleniami):
    • RUN_STARTED: Agent rozpoczął przetwarzanie
    • TEXT_MESSAGE_START: Początek wiadomości SMS od agenta
    • TEXT_MESSAGE_CONTENT: tekst przyrostowy przesyłany strumieniowo z agenta (z polem delta)
    • TEXT_MESSAGE_END: Koniec wiadomości SMS
    • RUN_FINISHED: Pomyślne ukończenie
    • RUN_ERROR: Informacje o błędzie
  • Nazewnictwo pól: Pola zdarzeń używają camelCase (np. threadId, runId, messageId)
  • Zarządzanie wątkami: threadId utrzymuje kontekst konwersacji między żądaniami
  • Instrukcje po stronie klienta: komunikaty systemowe są wysyłane po stronie klienta

Konfigurowanie i uruchamianie klienta

Opcjonalnie ustaw niestandardowy adres URL serwera:

export AGUI_SERVER_URL="http://127.0.0.1:8888/"

Uruchom klienta (w osobnym terminalu):

python client.py

Krok 3. Testowanie kompletnego systemu

Po uruchomieniu serwera i klienta można teraz przetestować kompletny system.

Oczekiwane dane wyjściowe

$ python client.py
Connecting to AG-UI server at: http://127.0.0.1:8888/

User (:q or quit to exit): What is 2 + 2?

[Run Started - Thread: abc123, Run: xyz789]
2 + 2 equals 4.
[Run Finished - Thread: abc123, Run: xyz789]

User (:q or quit to exit): Tell me a fun fact about space

[Run Started - Thread: abc123, Run: def456]
Here's a fun fact: A day on Venus is longer than its year! Venus takes
about 243 Earth days to rotate once on its axis, but only about 225 Earth
days to orbit the Sun.
[Run Finished - Thread: abc123, Run: def456]

User (:q or quit to exit): :q

Kodowane kolorem dane wyjściowe

Klient wyświetla różne typy zawartości z różnymi kolorami:

  • Żółty: uruchamianie powiadomień
  • Cyan: Odpowiedzi tekstowe agenta (przesyłane strumieniowo w czasie rzeczywistym)
  • Zielony: uruchamianie powiadomień o zakończeniu
  • Czerwony: Komunikaty o błędach

Testowanie przy użyciu narzędzia curl (opcjonalnie)

Przed uruchomieniem klienta można przetestować serwer ręcznie przy użyciu narzędzia curl:

curl -N http://127.0.0.1:8888/ \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -d '{
    "messages": [
      {"role": "user", "content": "What is 2 + 2?"}
    ]
  }'

Powinny być widoczne zdarzenia typu Server-Sent przesyłane strumieniowo z powrotem:

data: {"type":"RUN_STARTED","threadId":"...","runId":"..."}

data: {"type":"TEXT_MESSAGE_START","messageId":"...","role":"assistant"}

data: {"type":"TEXT_MESSAGE_CONTENT","messageId":"...","delta":"The"}

data: {"type":"TEXT_MESSAGE_CONTENT","messageId":"...","delta":" answer"}

...

data: {"type":"TEXT_MESSAGE_END","messageId":"..."}

data: {"type":"RUN_FINISHED","threadId":"...","runId":"..."}

W przypadku nieaktywnego strumienia curl może również wyświetlać wiersze komentarza : keepalive. To są komentarze transportowe SSE, a nie zdarzenia AG-UI.

Jak to działa

przepływ po stronie serwera

  1. Klient wysyła żądanie HTTP POST z komunikatami
  2. Punkt końcowy fastAPI odbiera żądanie
  3. AgentFrameworkAgent opakowanie orkiestruje wykonywanie
  4. Agent przetwarza komunikaty przy użyciu struktury agenta
  5. AgentFrameworkEventBridge konwertuje aktualizacje agenta na zdarzenia AG-UI
  6. Odpowiedzi są przesyłane strumieniowo jako zdarzenia Server-Sent (SSE)
  7. Połączenie zostanie zamknięte po zakończeniu przebiegu

Przepływ po stronie klienta

  1. Klient wysyła żądanie HTTP POST do punktu końcowego serwera
  2. Serwer odpowiada strumieniem SSE
  3. Klient analizuje wiersze przychodzące data: jako zdarzenia JSON
  4. Każde zdarzenie jest wyświetlane na podstawie jego typu
  5. threadId jest przechwytywany w celu zapewnienia ciągłości konwersacji
  6. Strumień kończy się, gdy nadejdzie zdarzenie RUN_FINISHED

Szczegóły protokołu

Protokół AG-UI używa:

  • HTTP POST na potrzeby wysyłania żądań
  • Server-Sent Events (SSE) do strumieniowego przesyłania odpowiedzi
  • Kod JSON na potrzeby serializacji zdarzeń
  • Identyfikatory wątków do utrzymania kontekstu konwersacji
  • Identyfikatory uruchomień do śledzenia poszczególnych wykonów
  • Nazewnictwo typów zdarzeń: WIELKIE LITERY z podkreśleniami (np. RUN_STARTED, TEXT_MESSAGE_CONTENT)
  • Nazewnictwo pól: camelCase (np. threadId, runId, messageId)
  • Komentarze SSE podtrzymujące połączenie co 15 sekund, gdy strumień jest nieaktywny. Klienci, którzy przetwarzają tylko data: wiersze, ignorują te komentarze automatycznie.

Typowe wzorce

Konfiguracja niestandardowego serwera

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

# Add CORS for web clients
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

add_agent_framework_fastapi_endpoint(
    app,
    agent,
    "/agent",
    keepalive_seconds=30,  # Defaults to 15; set to None to disable
)

keepalive_seconds musi być liczbą dodatnią lub None.

Wielu agentów

app = FastAPI()

weather_agent = Agent(name="weather", ...)
finance_agent = Agent(name="finance", ...)

add_agent_framework_fastapi_endpoint(app, weather_agent, "/weather")
add_agent_framework_fastapi_endpoint(app, finance_agent, "/finance")

Obsługa błędów

try:
    async for event in client.send_message(message):
        if event.get("type") == "RUN_ERROR":
            error_msg = event.get("message", "Unknown error")
            print(f"Error: {error_msg}")
            # Handle error appropriately
except httpx.HTTPError as e:
    print(f"HTTP error: {e}")
except Exception as e:
    print(f"Unexpected error: {e}")

Troubleshooting

Odmowa połączenia

Przed uruchomieniem klienta upewnij się, że serwer jest uruchomiony:

# Terminal 1
python server.py

# Terminal 2 (after server starts)
python client.py

Błędy uwierzytelniania

Upewnij się, że uwierzytelniasz się na platformie Azure:

az login

Sprawdź, czy masz poprawne przypisanie roli w zasobie Azure OpenAI.

Przesyłanie strumieniowe nie działa

Sprawdź, czy czas oczekiwania klienta jest wystarczający.

httpx.AsyncClient(timeout=60.0)  # 60 seconds should be enough

W przypadku długotrwałych agentów należy odpowiednio zwiększyć limit czasu.

Nieaktywne strumienie domyślnie wysyłają komentarz keepalive SSE co 15 sekund. Jeśli serwer proxy zamknie wcześniej bezczynne połączenia, skonfiguruj mniejszą wartość dodatnią keepalive_seconds podczas rejestrowania punktu końcowego.

Utracono kontekst wątku

Klient automatycznie zarządza ciągłością wątków. Jeśli kontekst zostanie utracony:

  1. Sprawdź, czy threadId jest przechwytywane z RUN_STARTED zdarzeń
  2. Upewnij się, że to samo wystąpienie klienta jest używane we wszystkich wiadomościach
  3. Sprawdź, czy serwer odbiera thread_id w kolejnych żądaniach

Dalsze kroki

Skoro już znasz podstawy AG-UI, możesz:

Dodatkowe zasoby

Go obsługuje AG-UI za pośrednictwem provider/aguiprovider zarówno dla serwerów, jak i klientów.

import "github.com/microsoft/agent-framework-go/provider/aguiprovider"

mux := http.NewServeMux()
mux.Handle("/", aguiprovider.NewJSONHTTPHandler(myAgent, aguiprovider.HandlerConfig{}))

if err := http.ListenAndServe(":8888", mux); err != nil {
    log.Fatal(err)
}

Użyj aguiprovider.NewAgent, gdy aplikacja Go musi wywołać serwer AG-UI jako agent:

import aguiSSEClient "github.com/ag-ui-protocol/ag-ui/sdks/community/go/pkg/client/sse"

a := aguiprovider.NewAgent(
    aguiSSEClient.NewClient(aguiSSEClient.Config{Endpoint: serverURL}),
    aguiprovider.AgentConfig{},
)

Wskazówka

Zobacz przykłady serwera AG-UI na początek i klienta, aby zapoznać się z kompletnymi, gotowymi do uruchomienia przykładami.