Agentic UI for AI Agents: Generative A2UI, JSON Patch State Sync, and Interrupt-Driven Approvals

A Coding Deep Dive into Agentic UI, Generative UI, State Synchronization, and Interrupt-Driven Approval Flows

TL;DR: Problem: product teams spend too much time wiring UIs to AI-driven workflows. Solution: treat the LLM as an agent that emits a declarative UI spec (A2UI), stream events (SSE/WebSocket), and synchronize state with compact JSON Patch deltas. Value: faster prototyping, built-in human approvals for risky actions, and observable, incremental updates you can integrate into production with FastAPI, Pydantic, and a trusted widget registry.

Why this pattern matters

AI agents are moving beyond chat. When an LLM can output a UI spec and small state changes instead of freeform prose, product teams can prototype and operate interactive flows faster. That’s the practical promise of an Agentic UI (AG‑UI): the agent reasons, proposes a UI and actions, the renderer displays them, and state is synchronized via snapshots and JSON Patch deltas. Add an INTERRUPT-driven human-in-the-loop and you keep control over risky, high-impact operations.

Architecture at a glance

  • Agent runtime (LLM + tool calls): decides what to do and emits events.
  • Event stream (SSE/WebSocket): a structured, token-by-token wire format carrying events like STATE_SNAPSHOT, STATE_DELTA, TOOL_CALL_* and INTERRUPT.
  • A2UI (Generative UI): a flat, declarative JSON component model the LLM outputs so the frontend can render interfaces incrementally.
  • SharedState + JSON Patch: the source of truth is the agent state machine; the UI receives snapshots and minimal deltas to stay synchronized.
  • Renderer & widget registry: a safe, whitelisted catalog of components (buttons, text fields, tables, date-pickers) that maps A2UI to production components.
  • Human approval flow: INTERRUPT events pause execution and surface approve/reject/modify choices to users.

Core primitives and why they work

Event stream: small messages, big observability

Design the wire protocol around explicit event types instead of unstructured text. Examples include RUN_STARTED, TEXT_MESSAGE_START/CONTENT/END (for token streaming), TOOL_CALL_START/ARGS/RESULT/END, STATE_SNAPSHOT, STATE_DELTA, INTERRUPT, STEP_STARTED/FINISHED, and CUSTOM. Streaming tokens and events lets the UI render progressively and gives operators precise telemetry for each agent step.

A2UI — a streamable, LLM-friendly UI format

A2UI is a flat adjacency list: each component is a record with an ID and references to children by ID. That makes it trivial to stream partial trees, and encourages the LLM to emit components as ordered records rather than deeply nested structures that arrive out of order.

{
  "components": [
    { "id": "c1", "type": "card", "children": ["c2","c3"] },
    { "id": "c2", "type": "text", "props": {"text": "Reserve a table at /restaurant/name"} },
    { "id": "c3", "type": "button", "props": {"label": "Confirm", "action": {"type":"update","path":"/reservation/confirmed","value":true}} }
  ]
}

That snippet shows a card containing a text node bound to the model path /restaurant/name, and a button that will create a JSON Patch update when pressed.

LLM prompting and parsing

Prompt the model to output strict JSON that matches a schema and nothing else. Strip Markdown fences and parse the JSON. Expect to iterate on prompts; add schema validators (Pydantic) to catch and sanitize malformed component props or disallowed actions.

Prompt template (short):
"Output only JSON that matches: {components:[{id,type,props,children}]}. Use JSON Pointer model paths like /model/path for dynamic bindings. Do not include any markdown fences or explanatory text."

State sync with JSON Patch

Use a SharedState engine that emits full STATE_SNAPSHOT events and incremental STATE_DELTA events expressed as JSON Patch (RFC 6902) operations. Deltas keep bandwidth low and make it easy to apply precise changes on the client.

Example JSON Patch delta:
[
  { "op": "replace", "path": "/reservation/confirmed", "value": true }
]

SSE framing for a delta might look like this (conceptual):

event: STATE_DELTA
data: {"patch":[{"op":"replace","path":"/reservation/confirmed","value":true}]}

LiveSurface: incremental UI updates

Regenerating entire component trees is expensive and jarring. A LiveSurface supports add/update/remove component operations plus model updates so you can apply incremental UI changes—vital for collaborative dashboards and low-latency admin flows.

Human-in-the-loop via INTERRUPT events

When the agent intends a risky action (for example, “Email all 12,000 users”), it emits an INTERRUPT event describing the action and offering choices (approve/reject/modify). The renderer presents an approval dialog, the agent pauses, and execution resumes only after a human resolves the interrupt. That pattern preserves automation velocity while enforcing governance.

The LLM is an agent that should output structured UI specs rather than freeform text — it “paints” interfaces as JSON component trees.

A2UI is “safe like data, expressive like code”: declarative JSON, not executable code shipped from the agent.

Concrete flows that map to business problems

Two compact examples show why engineering and product teams care.

Booking form generated from plain English

  • Prompt: “Create a booking form for /restaurant/name with date-picker, name field, and a Confirm button bound to /reservation/confirmed.”
  • LLM emits an A2UI surface streamed token-by-token.
  • Client renders form; user submits. Client emits a STATE_DELTA with the new reservation data. The agent sees the delta, calls a booking tool, emits TOOL_CALL_START/RESULT, and optionally issues an INTERRUPT if the booking affects inventory or requires approval.

This flow shrinks the time from product idea to working UI from days to hours or less.

Multi-agent document review pipeline

Agents form a chain—Researcher → Editor → Reviewer. Each agent emits JSON Patch deltas that modify the shared document model and records STEP_STARTED/FINISHED events for observability. Versioned history of patches provides an auditable trail of who changed what and when.

Numeric examples used in demos make the behavior concrete: tool calls returning revenues (Jan: 42,000; Feb: 58,000), progress updates from 0.75 → 1.0, and high-impact actions like “Email all 5,000 customers.” These illustrate state changes and when an INTERRUPT should be triggered.

Production checklist and gotchas

  • Schema enforcement: Use Pydantic or similar to validate A2UI outputs before rendering. Whitelist widget props and allowed actions to avoid arbitrary code execution.
  • Widget registry & permissions: Maintain a trusted catalog. Disallow widgets that can run arbitrary scripts. Map widget actions to back-end function-call handlers that enforce authorization.
  • Concurrency strategy: Decide early—last-write-wins is simplest; CRDTs or operational transforms are needed for strong merge guarantees across many concurrent editors. For many business UIs, bounded concurrency with optimistic locks plus user-visible conflict resolution is pragmatic.
  • Interrupt audit trail: Log INTERRUPT events, user decisions, timestamps, and the agent state that triggered them to produce a verifiable approval history for compliance.
  • Observability: track event lag, token-stream latency, patch size & frequency, tool-call success rates, and approval latency. Alert on long-running interrupts or large patch spikes (possible runaway actions).
  • Cost & latency controls: cache UI skeletons, use smaller LLM contexts for incremental patches, apply local heuristics to avoid full regenerations, and rate-limit LLM calls for high-frequency UIs.
  • Security: sanitize all model-provided URLs, images, and HTML-like props. Treat agent outputs as data, not executable instructions.
  • Deploy infra: serve SSE endpoints with FastAPI, connect to React renderers (CopilotKit or custom), and run on agent runtimes like AWS Bedrock AgentCore or self-hosted stacks in front of OpenAI/other LLMs.

When to use and when not to

  • Great fit: admin tools, dashboards, one-off prototypes, review pipelines, context-sensitive forms, and internal tooling where human approvals and audit trails are critical.
  • Avoid for now: latency-sensitive public UIs at massive scale (where LLM calls per user are costly), highly regulated payment flows with strict third-party audits unless full governance is in place, and ultra-complex visualizations where precise frontend control is non-negotiable.

Concurrency, merge strategy, and UX for conflicts

Simple systems often adopt last-write-wins with optimistic locking. For collaborative editors expect to invest in stronger strategies:

  • Operational transforms or CRDTs for text-rich documents.
  • Patch-level ordering and version vectors for structured models.
  • User-facing conflict dialogs that show conflicting patches and let a human choose the canonical change.

Choosing the right approach depends on workload: dashboards and forms tolerate simpler models; collaborative authoring requires the heavier synchronization primitives.

Key takeaways and pragmatic next steps

  • Agentic UI is a small set of practical primitives—event streams, declarative A2UI surfaces, and JSON Patch state sync—that together let LLMs produce interactive, production-viable interfaces.
  • Human-in-the-loop INTERRUPT events balance automation velocity with safety and compliance.
  • Productionizing requires governance: trusted widget registries, schema validation (Pydantic), observability, concurrency strategy, and cost controls.
  • Quick starter experiment — create a one-screen prototype: prompt ChatGPT to output A2UI for a simple form, validate with Pydantic, render in a small React app, and wire a mock STATE_DELTA apply handler. Iterate until prompts are stable.

Frequently asked questions

How does an agent keep the UI and its state in agreement?

By treating the agent as the authoritative state machine and broadcasting STATE_SNAPSHOTs for full sync plus incremental STATE_DELTA events (JSON Patch) for minimal updates. The renderer applies patches and requests snapshots when divergence is detected.

Can LLMs reliably generate UIs?

Yes—when constrained by a strict JSON schema, robust prompt engineering, and server-side validation. Expect iteration: add schema checks, sanitize props, and implement retries or fallback surfaces for malformed outputs.

How do you prevent dangerous or large-scale agent actions?

Emit INTERRUPT events for risky keywords or high-impact operations (e.g., emailing thousands of users). Present approve/reject/modify options to a human and pause execution until resolved. Log the decision for auditability.

Is this ready for production?

The pattern is production-feasible with FastAPI SSE hosting, React renderers, Pydantic adapters, a trusted widget registry, and a clear concurrency/validation strategy. The main effort is governance and operationalizing observability and cost controls.

Final note

Generative UI moves agencies and product teams from describing interfaces to shipping them: the LLM becomes a generator of declarative UI blueprints, agents drive the workflow, and JSON Patch keeps state tight and auditable. The payoff is faster iteration and safer automation—provided teams invest early in validation, governance, and observability. If you’re building AI agents for business workflows, this stack is a practical blueprint to experiment with today.