How to Build a Real‑Time AI Agent UI with SSE and Session Replay

This article walks through the design and implementation of a visually striking, real‑time front‑end for an AI agent, explaining why Server‑Sent Events were chosen, showing Go and JavaScript code for streaming, and detailing a session‑replay feature that lets users review every agent decision step.

BirdNest Tech Talk
BirdNest Tech Talk
BirdNest Tech Talk
How to Build a Real‑Time AI Agent UI with SSE and Session Replay

The front‑end of agent-web is crafted to give users a modern, tech‑savvy visual experience, starting with a dark theme highlighted by neon blue/purple accents and a typewriter‑style streaming output powered by Server‑Sent Events (SSE).

Why SSE?

Unidirectional data flow : The agent’s workflow follows a "server thinks → pushes logs/results → client displays" pattern that matches SSE’s one‑way streaming model.

HTTP compatibility : SSE works over a long‑lived HTTP request, avoiding the protocol upgrade required by WebSocket and thus passing firewalls and proxies more easily.

Automatic reconnection : Browsers’ EventSource API automatically retries connections when they drop, eliminating custom reconnection logic.

Backend implementation ( cmd/agent-web/main.go )

// 1. Set SSE headers
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
w.Header().Set("X-Accel-Buffering", "no") // disable Nginx buffering

flusher, ok := w.(http.Flusher)
if !ok {
    http.Error(w, "Streaming unsupported", http.StatusInternalServerError)
    return
}

// 2. Listen to event channel and push
handler := session.Handler
for {
    select {
    case event := <-handler.eventChan:
        data, err := json.Marshal(event)
        if err != nil { continue }
        fmt.Fprintf(w, "data: %s

", data)
        flusher.Flush()
    case <-r.Context().Done():
        return
    }
}

Front‑end handling ( cmd/agent-web/ui/app.js )

function connectSSE() {
    // ... close old connection ...
    eventSource = new EventSource(`/events?session_id=${sessionId}`);
    eventSource.onmessage = (event) => {
        const data = JSON.parse(event.data);
        handleEvent(data);
    };
}

function handleEvent(data) {
    switch (data.type) {
        case 'log':
            handleLog(data.content);
            break;
        case 'plan_review':
            showPlanReview(data.plan);
            break;
        case 'response':
            const tabId = createReportTab(data.content);
            activateTab(tabId);
            if (data.ppt) renderPPTButton(data.ppt);
            if (data.podcast) renderPodcastButton(data.podcast);
            break;
        case 'done':
            addLog('success', '任务已完成!');
            setLoading(false);
            break;
    }
}

This event‑driven architecture keeps the front‑end lightweight, delegating complex state management to the back‑end.

Session Replay – Recording, Storing, and Replaying Agent Thoughts

The replay feature captures every Event generated by WebInteractionHandler, serializes the sequence to a JSON file under sessions/, and later replays it on demand.

Recording : All events are kept in memory during a session.

Storing : On session termination, the event list is written to a JSON file.

Playback : When a user selects a past session, the front‑end loads the JSON.

Simulation : The UI replays events using setTimeout to mimic original timing, invoking the same handleEvent logic so logs appear line‑by‑line as if the agent were running again.

// Pseudo‑code for front‑end replay
events.forEach((event, index) => {
    setTimeout(() => {
        handleEvent(event); // reuse real‑time handler
    }, index * 100); // simple delay; could use event.timestamp
});

This "time‑capsule" replay helps developers debug decision paths and users understand the agent’s reasoning.

Dynamic Interaction Components

4.1 Plan Review – Human‑in‑the‑Loop

Visual display : The JSON plan is rendered as a clear task list.

User intervention : Users can approve the plan or edit it (e.g., "remove step 3, add competitor analysis").

Dynamic adjustment : If edited, the back‑end PlanningAgent receives the feedback, regenerates the plan, and repeats until the user is satisfied.

4.2 Multimodal Display

Tab system : Each generated report opens in a new tab, allowing independent navigation between "Terminal", "Report 1", "Report 2", etc.

PPT preview : When a PPT is produced, a purple "View PPT" button appears, opening a full‑screen HTML slide deck.

Podcast script : If a podcast script is generated, a blue "View Podcast" button opens a tab with a dialogue script and provides a .txt download.

These components keep complex agent output organized, giving users a seamless, all‑in‑one interface for reading and interacting with AI results.

JavaScriptGoAI AgentSSESession ReplayAgent UIReal-time UI
BirdNest Tech Talk
Written by

BirdNest Tech Talk

Author of the rpcx microservice framework, original book author, and chair of Baidu's Go CMC committee.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.