Delen via


Aan de slag met AG-UI

In deze zelfstudie ziet u hoe u server- en clienttoepassingen bouwt met behulp van het protocol AG-UI met .NET of Python en Agent Framework. U leert hoe u een AG-UI-server maakt die als host fungeert voor een AI-agent en een client die er verbinding mee maakt voor interactieve gesprekken.

Wat je gaat bouwen

Aan het einde van deze zelfstudie hebt u het volgende:

  • Een AG-UI-server die als host fungeert voor een AI-agent die toegankelijk is via HTTP
  • Een clienttoepassing die verbinding maakt met de server en reacties streamt
  • Inzicht in de werking van het AG-UI-protocol met Agent Framework

Vereiste voorwaarden

Zorg ervoor dat u het volgende hebt voordat u begint:

Opmerking

In deze voorbeelden worden Azure OpenAI-modellen gebruikt. Zie voor meer informatie hoe u Azure OpenAI-modellen implementeert met Azure AI Foundry.

Opmerking

Deze voorbeelden gebruiken DefaultAzureCredential voor verificatie. Zorg ervoor dat u bent geverifieerd met Azure (bijvoorbeeld via az login). Zie de Documentatie voor Azure Identity voor meer informatie.

Waarschuwing

Het AG-UI-protocol is nog in ontwikkeling en kan worden gewijzigd. We houden deze voorbeelden bijgewerkt naarmate het protocol zich ontwikkelt.

Stap 1: een AG-UI-server maken

De AG-UI-server host uw AI-agent en maakt deze beschikbaar via HTTP-eindpunten met behulp van ASP.NET Core.

Opmerking

Voor het serverproject is de Microsoft.NET.Sdk.Web SDK vereist. Als u een nieuw project helemaal zelf maakt, gebruik dan dotnet new web, of zorg ervoor dat uw .csproj-bestand <Project Sdk="Microsoft.NET.Sdk.Web"> gebruikt in plaats van Microsoft.NET.Sdk.

Vereiste pakketten installeren

Installeer de benodigde pakketten voor de server:

dotnet add package Microsoft.Agents.AI.Hosting.AGUI.AspNetCore --prerelease
dotnet add package Azure.AI.OpenAI --prerelease
dotnet add package Azure.Identity
dotnet add package Microsoft.Extensions.AI.OpenAI --prerelease

Opmerking

Het Microsoft.Extensions.AI.OpenAI pakket is vereist voor de AsIChatClient() extensiemethode die OpenAI's ChatClient converteert naar de IChatClient interface die wordt verwacht door Agent Framework.

Servercode

Maak een bestand met de naam Program.cs:

// Copyright (c) Microsoft. All rights reserved.

using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI.Hosting.AGUI.AspNetCore;
using Microsoft.Extensions.AI;
using OpenAI.Chat;

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpClient().AddLogging();
builder.Services.AddAGUI();

WebApplication app = builder.Build();

string endpoint = builder.Configuration["AZURE_OPENAI_ENDPOINT"]
    ?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
string deploymentName = builder.Configuration["AZURE_OPENAI_DEPLOYMENT_NAME"]
    ?? throw new InvalidOperationException("AZURE_OPENAI_DEPLOYMENT_NAME is not set.");

// Create the AI agent
ChatClient chatClient = new AzureOpenAIClient(
        new Uri(endpoint),
        new DefaultAzureCredential())
    .GetChatClient(deploymentName);

AIAgent agent = chatClient.AsIChatClient().AsAIAgent(
    name: "AGUIAssistant",
    instructions: "You are a helpful assistant.");

// Map the AG-UI agent endpoint
app.MapAGUI("/", agent);

await app.RunAsync();

Sleutelbegrippen

  • AddAGUI: Registreert AG-UI services met de container voor afhankelijkheidsinjectie
  • MapAGUI: Extensiemethode waarmee het AG-UI-eindpunt wordt geregistreerd met automatische verwerking van aanvragen/antwoorden en SSE-streaming
  • ChatClient en AsIChatClient(): AzureOpenAIClient.GetChatClient() retourneert OpenAI's ChatClient type. De AsIChatClient() extensiemethode (van Microsoft.Extensions.AI.OpenAI) converteert deze naar de IChatClient interface die is vereist voor Agent Framework
  • AsAIAgent: Hiermee maakt u een Agent Framework-agent van een IChatClient
  • ASP.NET Core-integratie: maakt gebruik van de systeemeigen asynchrone ondersteuning van ASP.NET Core voor streamingantwoorden
  • Instructies: De agent wordt gemaakt met standaardinstructies, die kunnen worden overschreven door clientberichten
  • Configuratie: AzureOpenAIClient biedt DefaultAzureCredential beveiligde verificatie

De server configureren en uitvoeren

Stel de vereiste omgevingsvariabelen in:

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

Voer de server uit:

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

De server zal beginnen met luisteren op http://localhost:8888.

Opmerking

Houd deze server actief terwijl u de client instelt en uitvoert in stap 2. Zowel de server als de client moeten tegelijkertijd worden uitgevoerd om het volledige systeem te laten werken.

Stap 2: een AG-UI-client maken

De AG-UI-client maakt verbinding met de externe server en geeft streamingantwoorden weer.

Belangrijk

Voordat u de client uitvoert, moet u ervoor zorgen dat de AG-UI server uit stap 1 wordt uitgevoerd op http://localhost:8888.

Vereiste pakketten installeren

Installeer de AG-UI-clientbibliotheek:

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

Opmerking

Het Microsoft.Agents.AI pakket biedt de AsAIAgent() extensiemethode.

Cliëntcode

Maak een bestand met de naam Program.cs:

// Copyright (c) Microsoft. All rights reserved.

using Microsoft.Agents.AI;
using Microsoft.Agents.AI.AGUI;
using Microsoft.Extensions.AI;

string serverUrl = Environment.GetEnvironmentVariable("AGUI_SERVER_URL") ?? "http://localhost:8888";

Console.WriteLine($"Connecting to AG-UI server at: {serverUrl}\n");

// Create the AG-UI client agent
using HttpClient httpClient = new()
{
    Timeout = TimeSpan.FromSeconds(60)
};

AGUIChatClient chatClient = new(httpClient, serverUrl);

AIAgent agent = chatClient.AsAIAgent(
    name: "agui-client",
    description: "AG-UI Client Agent");

AgentSession session = await agent.CreateSessionAsync();
List<ChatMessage> messages =
[
    new(ChatRole.System, "You are a helpful assistant.")
];

try
{
    while (true)
    {
        // Get user input
        Console.Write("\nUser (:q or quit to exit): ");
        string? message = Console.ReadLine();

        if (string.IsNullOrWhiteSpace(message))
        {
            Console.WriteLine("Request cannot be empty.");
            continue;
        }

        if (message is ":q" or "quit")
        {
            break;
        }

        messages.Add(new ChatMessage(ChatRole.User, message));

        // Stream the response
        bool isFirstUpdate = true;
        string? threadId = null;

        await foreach (AgentResponseUpdate update in agent.RunStreamingAsync(messages, session))
        {
            ChatResponseUpdate chatUpdate = update.AsChatResponseUpdate();

            // First update indicates run started
            if (isFirstUpdate)
            {
                threadId = chatUpdate.ConversationId;
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine($"\n[Run Started - Thread: {chatUpdate.ConversationId}, Run: {chatUpdate.ResponseId}]");
                Console.ResetColor();
                isFirstUpdate = false;
            }

            // Display streaming text content
            foreach (AIContent content in update.Contents)
            {
                if (content is TextContent textContent)
                {
                    Console.ForegroundColor = ConsoleColor.Cyan;
                    Console.Write(textContent.Text);
                    Console.ResetColor();
                }
                else if (content is ErrorContent errorContent)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine($"\n[Error: {errorContent.Message}]");
                    Console.ResetColor();
                }
            }
        }

        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine($"\n[Run Finished - Thread: {threadId}]");
        Console.ResetColor();
    }
}
catch (Exception ex)
{
    Console.WriteLine($"\nAn error occurred: {ex.Message}");
}

Sleutelbegrippen

  • Server-Sent gebeurtenissen (SSE):het protocol maakt gebruik van SSE voor streamingantwoorden
  • AGUIChatClient: clientklasse die verbinding maakt met AG-UI servers en implementeert IChatClient
  • AsAIAgent: extensiemethode aan AGUIChatClient om een agent te maken op basis van de client
  • RunStreamingAsync: streamt antwoorden als AgentResponseUpdate objecten
  • AsChatResponseUpdate: extensiemethode voor toegang tot chatspecifieke eigenschappen, zoals ConversationId en ResponseId
  • Sessiebeheer: de AgentSession gesprekscontext tussen aanvragen wordt onderhouden
  • Inhoudstypen: Antwoorden zijn onder andere TextContent voor berichten en ErrorContent voor fouten

De client configureren en uitvoeren

Stel desgewenst een aangepaste server-URL in:

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

Voer de client uit in een afzonderlijke terminal (zorg ervoor dat de server uit stap 1 wordt uitgevoerd):

dotnet run

Stap 3: Het volledige systeem testen

Nu zowel de server als de client draaien, kunt u het volledige systeem testen.

Verwachte uitvoer

$ dotnet run
Connecting to AG-UI server at: http://localhost:8888

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

[Run Started - Thread: thread_abc123, Run: run_xyz789]
2 + 2 equals 4.
[Run Finished - Thread: thread_abc123]

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

[Run Started - Thread: thread_abc123, Run: 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: thread_abc123]

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

Gekleurde uitvoer

De client geeft verschillende inhoudstypen weer met verschillende kleuren:

  • Geel: Gestarte meldingen uitvoeren
  • Cyaan: Tekstreacties van agenten (gestreamd in realtime)
  • Groen: Voltooiingsmeldingen uitvoeren
  • Rood: Foutberichten

Hoe het werkt

Server-side proces

  1. Client verzendt HTTP POST-aanvraag met berichten
  2. ASP.NET Core-eindpunt ontvangt de aanvraag via MapAGUI
  3. Agent verwerkt de berichten met Agent Framework
  4. Antwoorden worden geconverteerd naar AG-UI gebeurtenissen
  5. Gebeurtenissen worden teruggestreamd als Server-Sent Events (SSE)
  6. De verbinding wordt gesloten wanneer de uitvoering is voltooid

Client-Side flow

  1. AGUIChatClient VERZENDT HTTP POST-aanvraag naar servereindpunt
  2. Server reageert met SSE-stream
  3. Client parseert binnenkomende gebeurtenissen in AgentResponseUpdate objecten
  4. Elke update wordt weergegeven op basis van het inhoudstype
  5. ConversationId wordt vastgelegd voor gesprekscontinuïteit
  6. Stream wordt voltooid wanneer de uitvoering eindigt

Protocol details

Het AG-UI-protocol maakt gebruik van:

  • HTTP POST voor het verzenden van aanvragen
  • Server-Sent Events (SSE) voor streaming-antwoorden
  • JSON voor gebeurtenisserialisatie
  • Thread-id's (als ConversationId) voor het onderhouden van gesprekscontext
  • Uitvoerings-ID's (als ResponseId) om afzonderlijke uitvoeringen bij te houden

Volgende stappen

Nu u de basisprincipes van AG-UI begrijpt, kunt u het volgende doen:

Aanvullende informatiebronnen

Vereiste voorwaarden

Zorg ervoor dat u het volgende hebt voordat u begint:

Opmerking

In deze voorbeelden worden Azure OpenAI-modellen gebruikt. Zie voor meer informatie hoe u Azure OpenAI-modellen implementeert met Azure AI Foundry.

Opmerking

Deze voorbeelden gebruiken DefaultAzureCredential voor verificatie. Zorg ervoor dat u bent geverifieerd met Azure (bijvoorbeeld via az login). Zie de Documentatie voor Azure Identity voor meer informatie.

Waarschuwing

Het AG-UI-protocol is nog in ontwikkeling en kan worden gewijzigd. We houden deze voorbeelden bijgewerkt naarmate het protocol zich ontwikkelt.

Stap 1: een AG-UI-server maken

De AG-UI-server fungeert als host voor uw AI-agent en maakt deze beschikbaar via HTTP-eindpunten met behulp van FastAPI.

Vereiste pakketten installeren

Installeer de benodigde pakketten voor de server:

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

Of door uv te gebruiken:

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

Hiermee worden agent-framework-core, fastapi en uvicorn automatisch als afhankelijkheden geïnstalleerd.

Servercode

Maak een bestand met de naam server.py:

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

import os

from agent_framework import Agent
from agent_framework.azure import AzureOpenAIChatClient
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_DEPLOYMENT_NAME")

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

chat_client = AzureOpenAIChatClient(
    credential=AzureCliCredential(),
    endpoint=endpoint,
    deployment_name=deployment_name,
)

# Create the AI agent
agent = Agent(
    name="AGUIAssistant",
    instructions="You are a helpful assistant.",
    chat_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)

Sleutelbegrippen

  • add_agent_framework_fastapi_endpoint: Registreert het AG-UI-eindpunt met automatische verwerking van aanvragen/antwoorden en SSE-streaming
  • Agent: De Agent Framework-agent die binnenkomende aanvragen verwerkt
  • FastAPI-integratie: maakt gebruik van systeemeigen asynchrone ondersteuning van FastAPI voor streamingantwoorden
  • Instructies: De agent wordt gemaakt met standaardinstructies, die kunnen worden overschreven door clientberichten
  • Configuratie: AzureOpenAIChatClient leest uit omgevingsvariabelen of accepteert rechtstreeks parameters

De server configureren en uitvoeren

Stel de vereiste omgevingsvariabelen in:

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

Voer de server uit:

python server.py

Of uvicorn rechtstreeks gebruiken:

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

De server zal beginnen met luisteren op http://127.0.0.1:8888.

Stap 2: een AG-UI-client maken

De AG-UI-client maakt verbinding met de externe server en geeft streamingantwoorden weer.

Vereiste pakketten installeren

Het AG-UI-pakket is al geïnstalleerd, waaronder het AGUIChatClientvolgende:

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

Cliëntcode

Maak een bestand met de naam 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(server_url=server_url)

    # Create agent with the chat client
    agent = Agent(
        name="ClientAgent",
        chat_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())

Sleutelbegrippen

  • Server-Sent Events (SSE): het protocol maakt gebruik van SSE-indeling (data: {json}\n\n)
  • Gebeurtenistypen: verschillende gebeurtenissen bieden metagegevens en inhoud (HOOFDLETTERS met onderstrepingstekens):
    • RUN_STARTED: Agent is begonnen met de verwerking
    • TEXT_MESSAGE_START: Begin van een sms-bericht van de agent
    • TEXT_MESSAGE_CONTENT: Incrementele tekst die vanuit de agent wordt gestreamd (met delta veld)
    • TEXT_MESSAGE_END: Einde van een tekstbericht
    • RUN_FINISHED: Geslaagde voltooiing
    • RUN_ERROR: Foutinformatie
  • Veldnaam: Gebeurtenisvelden maken gebruik van camelCase (bijvoorbeeld threadId, , runIdmessageId)
  • Threadbeheer: de threadId gesprekscontext tussen aanvragen wordt onderhouden
  • Client-Side instructies: systeemberichten worden verzonden vanaf de client

De client configureren en uitvoeren

Stel desgewenst een aangepaste server-URL in:

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

Voer de client uit (in een afzonderlijke terminal):

python client.py

Stap 3: Het volledige systeem testen

Nu zowel de server als de client draaien, kunt u het volledige systeem testen.

Verwachte uitvoer

$ 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

Gekleurde uitvoer

De client geeft verschillende inhoudstypen weer met verschillende kleuren:

  • Geel: Gestarte meldingen uitvoeren
  • Cyaan: Tekstreacties van agenten (gestreamd in realtime)
  • Groen: Voltooiingsmeldingen uitvoeren
  • Rood: Foutberichten

Testen met curl (optioneel)

Voordat u de client uitvoert, kunt u de server handmatig testen met behulp van 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?"}
    ]
  }'

U ziet nu Server-Sent gebeurtenissen die worden gestreamd:

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

Hoe het werkt

Server-side proces

  1. Client verzendt HTTP POST-aanvraag met berichten
  2. Het FastAPI-eindpunt ontvangt de aanvraag
  3. AgentFrameworkAgent wrapper organiseert de uitvoering
  4. Agent verwerkt de berichten met Agent Framework
  5. AgentFrameworkEventBridge converteert agentupdates naar AG-UI gebeurtenissen
  6. Antwoorden worden teruggestreamd als Server-Sent Events (SSE)
  7. De verbinding wordt gesloten wanneer de uitvoering is voltooid

Client-Side flow

  1. Client verzendt HTTP POST-aanvraag naar servereindpunt
  2. Server reageert met SSE-stream
  3. Client parseert binnenkomende data: regels als JSON-gebeurtenissen
  4. Elke gebeurtenis wordt weergegeven op basis van het type
  5. threadId wordt vastgelegd voor gesprekscontinuïteit
  6. Stream wordt voltooid wanneer RUN_FINISHED de gebeurtenis binnenkomt

Protocol details

Het AG-UI-protocol maakt gebruik van:

  • HTTP POST voor het verzenden van aanvragen
  • Server-Sent Events (SSE) voor streaming-antwoorden
  • JSON voor gebeurtenisserialisatie
  • Thread-id's voor het onderhouden van gesprekscontext
  • Id's uitvoeren voor het bijhouden van afzonderlijke uitvoeringen
  • Naamgeving van gebeurtenistype: HOOFDLETTERS met onderstrepingstekens (bijvoorbeeld RUN_STARTED, TEXT_MESSAGE_CONTENT)
  • Veldnaam: camelCase (bijvoorbeeld , threadId, runIdmessageId)

Algemene patronen

Aangepaste serverconfiguratie

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

Meerdere agenten

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

Foutafhandeling

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

Probleemoplossingsproces

Verbinding geweigerd

Zorg ervoor dat de server actief is voordat je de client start.

# Terminal 1
python server.py

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

Authenticatiefouten

Zorg ervoor dat u bent geverifieerd met Azure:

az login

Controleer of u de juiste roltoewijzing hebt voor de Azure OpenAI-resource.

Streaming werkt niet

Controleer of uw cliënttime-out voldoende ingesteld is:

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

Voor langlopende agents verhoogt u de time-out dienovereenkomstig.

Het Threadcontext is verloren gegaan

De client beheert automatisch threadcontinuïteit. Als de context verloren gaat:

  1. Controleren of threadId wordt vastgelegd vanuit RUN_STARTED gebeurtenissen
  2. Zorg ervoor dat hetzelfde clientexemplaar wordt gebruikt over berichten.
  3. Controleer of de server de thread_id in de volgende aanvragen ontvangt

Volgende stappen

Nu u de basisprincipes van AG-UI begrijpt, kunt u het volgende doen:

Aanvullende informatiebronnen