AI agents are becoming more capable of interacting with applications instead of simply returning text. An agent can search data, call APIs, update records, request approval, and coordinate multiple steps before completing a task.

That creates a new application-development problem: how should the agent communicate its state and actions to the user interface?

A traditional request-response API is often too limited for this type of workflow. The application needs to represent events such as agent execution, tool calls, state changes, streamed responses, and user interaction.

This is where AG-UI, or Agent-User Interaction, becomes useful.

AG-UI provides an event-oriented approach for connecting AI agents with interactive frontends. For .NET developers, the concept can be implemented using familiar technologies such as ASP.NET Core, Blazor, SignalR, WebSockets, or streaming HTTP.

This article explains the architecture, shows how AG-UI concepts map to .NET, and demonstrates how to build an interactive agent application.

What Is AG-UI?

AG-UI is an approach for connecting an AI agent to a user interface through structured events.

Instead of treating an agent as a function that receives text and returns text:

User
  |
  v
Agent
  |
  v
Text Response

the application can represent the interaction as a stream of events:

User
  |
  v
Agent
  |
  +--> Agent Started
  |
  +--> Tool Started
  |
  +--> Tool Result
  |
  +--> State Updated
  |
  +--> Message Delta
  |
  +--> Agent Completed
  |
  v
Interactive UI

This distinction matters because modern agents are not always single-step operations.

For example, a user might ask:

Find my delayed orders and email me a summary.

The agent could perform several operations:

1. Identify the customer
2. Query orders
3. Filter delayed orders
4. Generate summary
5. Prepare email
6. Request approval
7. Send email

A good UI should be able to represent those state changes.

Why Event-Based Agent Communication Matters

A normal HTTP request often looks like this:

POST /api/agent
       |
       v
   Wait...
       |
       v
   Response

This works well for short operations.

However, an agent may take several seconds or even longer to complete a workflow.

If the user sees nothing during that time, the application can feel unresponsive.

An event-driven interface can instead show:

Analyzing request...

Searching orders...

3 delayed orders found.

Preparing email...

Waiting for approval.

The user gets useful feedback while the workflow is running.

AG-UI and .NET Architecture

A .NET implementation can be structured like this:

+------------------------+
|      Blazor UI         |
+------------------------+
            |
            | AG-UI Events
            v
+------------------------+
| ASP.NET Core API       |
+------------------------+
            |
            v
+------------------------+
| Agent Service          |
+------------------------+
       |           |
       v           v
    AI Model     Tools
                   |
          +--------+--------+
          |        |        |
        API      Database   Search

The important separation is:

This makes it possible to replace the frontend without rebuilding the agent itself.

Defining Agent Events in .NET

Start by defining a common event model.

public enum AgentEventType
{
    Started,
    Message,
    ToolStarted,
    ToolCompleted,
    StateChanged,
    ApprovalRequired,
    Completed,
    Failed
}

Then define the event:

public class AgentEvent
{
    public AgentEventType Type { get; set; }

    public string Message { get; set; } = string.Empty;

    public string? ToolName { get; set; }

    public DateTimeOffset Timestamp { get; set; }
}

The model can be extended as the application grows.

For example, tool results might require structured data rather than a string.

public class AgentEvent
{
    public AgentEventType Type { get; set; }

    public string Message { get; set; } = string.Empty;

    public string? ToolName { get; set; }

    public object? Data { get; set; }

    public DateTimeOffset Timestamp { get; set; }
}

In production, strongly typed event payloads are usually preferable to arbitrary objects when the event contract is stable.

Creating an Agent Service

The agent service can expose an asynchronous stream.

public interface IAgentService
{
    IAsyncEnumerable<AgentEvent> RunAsync(
        string request,
        CancellationToken cancellationToken = default);
}

Using IAsyncEnumerable<T> is useful because events can be produced as the workflow progresses.

A simplified implementation could look like this:

public async IAsyncEnumerable<AgentEvent> RunAsync(
    string request,
    [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
    yield return new AgentEvent
    {
        Type = AgentEventType.Started,
        Message = "Agent started.",
        Timestamp = DateTimeOffset.UtcNow
    };

    await Task.Delay(300, cancellationToken);

    yield return new AgentEvent
    {
        Type = AgentEventType.ToolStarted,
        ToolName = "OrderSearch",
        Message = "Searching orders...",
        Timestamp = DateTimeOffset.UtcNow
    };

    var orders = await SearchOrdersAsync(
        request,
        cancellationToken);

    yield return new AgentEvent
    {
        Type = AgentEventType.ToolCompleted,
        ToolName = "OrderSearch",
        Message = $"{orders.Count} orders found.",
        Timestamp = DateTimeOffset.UtcNow
    };

    yield return new AgentEvent
    {
        Type = AgentEventType.Completed,
        Message = "Agent completed.",
        Timestamp = DateTimeOffset.UtcNow
    };
}

The example uses a simulated delay, but a real implementation would connect the workflow to an AI model and application tools.

Streaming Events Through ASP.NET Core

ASP.NET Core can expose a streaming endpoint.

One option is to use Server-Sent Events (SSE).

A simplified endpoint can look like this:

app.MapGet("/api/agent",
    async (
        string request,
        IAgentService agentService,
        HttpResponse response,
        CancellationToken cancellationToken) =>
    {
        response.ContentType = "text/event-stream";

        await foreach (var agentEvent
            in agentService.RunAsync(
                request,
                cancellationToken))
        {
            var json = JsonSerializer.Serialize(agentEvent);

            await response.WriteAsync(
                $"data: {json}\n\n",
                cancellationToken);

            await response.Body.FlushAsync(
                cancellationToken);
        }
    });

The important idea is that the server does not wait for the complete agent workflow before sending information.

Each event can be delivered as it becomes available.

Connecting the Blazor Application

The Blazor application can consume the event stream and update its local state.

For example, the UI may maintain:

private readonly List<AgentEvent> events = new();

private bool isRunning;

private string responseText = string.Empty;

When an event arrives:

private void HandleEvent(AgentEvent agentEvent)
{
    events.Add(agentEvent);

    if (agentEvent.Type == AgentEventType.Message)
    {
        responseText += agentEvent.Message;
    }

    if (agentEvent.Type == AgentEventType.Completed)
    {
        isRunning = false;
    }

    InvokeAsync(StateHasChanged);
}

The exact client implementation depends on whether the application uses Blazor Server, Blazor WebAssembly, or another frontend architecture.

Rendering Agent Activity

Once events are available, the UI can turn them into a timeline.

For example:

@foreach (var agentEvent in events)
{
    <div class="agent-event">

        <strong>
            @agentEvent.Type
        </strong>

        <span>
            @agentEvent.Message
        </span>

    </div>
}

A more user-friendly UI can distinguish events:

Agent started

✓ Searching customer records
✓ Searching orders
✓ Found 3 delayed orders

● Preparing summary

Waiting for approval

The event model allows the presentation layer to decide how each event should appear.

Tool Calls and Interactive Applications

One of the strongest use cases for AG-UI-style communication is tool execution.

Consider an agent that can call:

CustomerService
OrderService
InventoryService
EmailService

The agent might produce events such as:

{
  "type": "ToolStarted",
  "toolName": "OrderService",
  "message": "Checking order status."
}

and then:

{
  "type": "ToolCompleted",
  "toolName": "OrderService",
  "message": "3 delayed orders found."
}

The UI does not need to understand how OrderService works.

It only needs to understand the event contract.

This separation is important because tools can change independently from the frontend.

Adding Human Approval

Agentic applications often need human-in-the-loop interactions.

For example, an agent can prepare an email but should not send it without approval.

The backend can emit:

yield return new AgentEvent
{
    Type = AgentEventType.ApprovalRequired,
    Message = "Approval is required before sending the email.",
    Timestamp = DateTimeOffset.UtcNow
};

The Blazor UI can render an approval panel:

The agent prepared this email:

Subject:
Order Status Update

Recipient:
[email protected]

[Approve] [Reject]

The approval request should be associated with a server-side operation or workflow identifier.

Do not treat a UI button itself as the security boundary.

The backend must verify that the current user is authorized to approve the action.

Representing Application State

AG-UI-style systems become more useful when the agent can update application state.

For example:

Customer Dashboard

Customer: 1042
Orders: 17
Delayed Orders: 3
Account Status: Active

The agent may discover that information while executing a workflow.

Instead of returning only a final paragraph, the backend can emit a structured state update.

public class CustomerState
{
    public int CustomerId { get; set; }

    public int OrderCount { get; set; }

    public int DelayedOrderCount { get; set; }

    public string Status { get; set; } = string.Empty;
}

The UI can then update a dedicated component.

This creates an important distinction:

Agent message
     |
     v
Human-readable information

Application state
     |
     v
Interactive UI state

Keeping those concepts separate makes complex interfaces easier to manage.

Streaming Text and Streaming Events

Agent applications may need to stream both structured events and generated text.

For example:

Event: AgentStarted

Event: ToolStarted
Tool: SearchOrders

Event: ToolCompleted

Event: MessageDelta
Text: "I found"

Event: MessageDelta
Text: " three delayed"

Event: MessageDelta
Text: " orders."

Event: Completed

The UI can process these differently.

Tool events update the activity timeline.

Message deltas update the response text.

State events update application components.

This is more flexible than treating the entire response as one string.

AG-UI Compared With a Basic REST API

Capability

Basic REST

Event-Based Agent UI

Request/response

Yes

Yes

Streaming progress

Limited

Strong

Tool activity

Usually hidden

Structured

Long-running workflow

Less suitable

Suitable

Human approval

Requires custom handling

Natural

State updates

Separate APIs often needed

Can be event-driven

Incremental text

Possible but not inherent

Supported by event model

Interactive agent workflow

Limited

Strong

REST APIs are still useful.

The goal is not to replace every REST endpoint with an agent event stream.

Instead, use event-driven communication where the application needs continuous interaction with an agent.

Error Handling

Agent workflows can fail at several layers.

For example:

User Request
     |
     v
Agent
     |
     +---- Model Error
     |
     +---- Tool Error
     |
     +---- Authorization Error
     |
     +---- Timeout

The event contract should represent failures clearly.

yield return new AgentEvent
{
    Type = AgentEventType.Failed,
    Message = "The order service could not be reached.",
    Timestamp = DateTimeOffset.UtcNow
};

The UI can then show an actionable message:

Order search failed.

The order service did not respond.

[Retry]

Avoid sending raw exception details to the browser.

Detailed diagnostic information should be logged server-side.

Cancellation Matters

Long-running agent operations should support cancellation.

For example:

public async IAsyncEnumerable<AgentEvent> RunAsync(
    string request,
    [EnumeratorCancellation]
    CancellationToken cancellationToken)
{
    cancellationToken.ThrowIfCancellationRequested();

    // Agent operations...
}

The frontend can provide a Cancel button:

Agent is working...

[Cancel]

Cancellation prevents the backend from continuing expensive model and tool operations after the user no longer needs the result.

Security Considerations

Connecting an agent directly to interactive application functionality introduces security concerns.

The most important rules are:

  1. Never trust model output as authorization.

  2. Validate every tool request on the server.

  3. Apply user permissions independently of the agent.

  4. Validate tool arguments.

  5. Avoid exposing secrets through event payloads.

  6. Do not send sensitive internal errors to the browser.

  7. Audit important agent actions.

  8. Require approval for high-impact operations where appropriate.

A useful security architecture is:

User
 |
 v
Blazor UI
 |
 v
ASP.NET Core
 |
 +--> Authentication
 |
 +--> Authorization
 |
 +--> Agent
       |
       +--> Tool Request
                |
                v
          Permission Check
                |
                v
            Tool Execution

The model can request an operation, but the application decides whether that operation is allowed.

Common Mistakes

Treating the Agent as a Chat Endpoint

If the backend only returns:

{
  "message": "Task completed."
}

the frontend has little information about what happened.

Structured events provide much richer interaction.

Mixing UI Logic With Agent Logic

Avoid putting tool execution and model orchestration directly into Blazor components.

Keep those responsibilities in application services.

Sending Every Internal Event to the User

Not every internal operation is useful to users.

Expose meaningful application events rather than implementation details.

Ignoring Reconnection

Streaming connections can fail.

The application should consider reconnection, retries, duplicate events, and workflow state recovery where required.

Trusting Client-Side Approval

A client-side Approved = true flag is not enough.

The backend should validate:

before executing the operation.

Recommended .NET Architecture

A practical solution can use the following structure:

AgentApplication/
│
├── Components/
│   ├── AgentChat.razor
│   ├── AgentTimeline.razor
│   ├── ToolActivity.razor
│   ├── ApprovalPanel.razor
│   └── AgentResult.razor
│
├── Agents/
│   ├── AgentService.cs
│   ├── AgentEvent.cs
│   └── AgentState.cs
│
├── Tools/
│   ├── OrderTool.cs
│   ├── CustomerTool.cs
│   └── EmailTool.cs
│
├── Services/
│   ├── AuthorizationService.cs
│   └── WorkflowService.cs
│
└── Program.cs

This organization separates the UI, agent orchestration, tools, and application services.

Best Practices

When connecting AI agents to .NET applications, consider these practices:

  1. Use structured events rather than only text responses.

  2. Keep agent orchestration outside Blazor components.

  3. Use asynchronous streaming for long-running workflows.

  4. Separate agent messages from application state.

  5. Represent tool calls explicitly.

  6. Support human approval for sensitive actions.

  7. Enforce authorization on the server.

  8. Support cancellation.

  9. Handle reconnects and partial failures.

  10. Keep event payloads small and purposeful.

  11. Log important agent and tool actions.

  12. Design the event contract so additional UI clients can consume it later.

Conclusion

AG-UI-style architecture changes how applications interact with AI agents.

Instead of treating an agent as a black box that receives a prompt and eventually returns text, the application can treat agent execution as an interactive stream of structured events.

For .NET developers, this approach fits naturally with ASP.NET Core, Blazor, asynchronous streams, and real-time communication technologies.

The resulting architecture can support:

User Request
     |
     v
Agent Execution
     |
     +--> Progress
     +--> Tool Calls
     +--> Tool Results
     +--> State Updates
     +--> Approval Requests
     +--> Streaming Response
     |
     v
Interactive Application

The key is to keep responsibilities separate. The agent determines what work may be needed, application services enforce business rules and security, and the UI renders the workflow in a way users can understand and interact with.

As AI agents become part of normal business applications, this event-driven approach provides a strong foundation for building interfaces that feel less like chatbots and more like interactive software.

Summary

AG-UI provides a useful conceptual model for connecting AI agents with interactive applications through structured events. In a .NET application, ASP.NET Core can expose those events while Blazor components render agent progress, tool activity, application state, approvals, and streamed responses.

The architecture works best when agent orchestration, authorization, tool execution, and UI rendering remain separate. This makes the system easier to secure, test, extend, and connect to different frontend experiences.