Skip to content

Choosing a generative UI tier

An agent's answer can be prose, a card your app draws, a surface built from your components, a one-off visual, an app from a partner, or a whole canvas. All of them work from any agent in the stack — the agent answering the chat or a sub-agent three delegates deep — so the choice is about the answer, not the plumbing. This guide says which to reach for and why.

The tiers

Tier Who designs it Reach for it when Lab scenario
Controlled: your component for a tool You A tool's call is worth showing as it happens: SQL being written, widgets being plotted plain-dashboard
A2UI: a surface from your catalog You, composed by the agent A figure, a trend or a small table that answers the question now a2ui-inline
Open Generative UI: a sandboxed visual The agent A one-off visual no component covers: a diagram, a heat strip ogui-visual
MCP Apps: an app from a server The server's owner UI that belongs to a service you don't own mcp-partner
Canvas: a dashboard beside the chat You Several parts, or anything worth keeping delegate-dashboard

The tier rule follows from who designs it: our components use the controlled tier or A2UI, one-off visuals use Open Generative UI, and UI from an MCP server is an MCP App. Never ask the model to generate HTML for something you could have drawn yourself: your components are consistent, accessible and cheap to render; a generated visual is none of those, and costs tokens every time.

The placement rubric

Where an answer goes is the agent's decision, guided by its prompt. The lab's agents carry this rubric, and it is the one to start from:

Where an answer goes:
- A short fact: one or two sentences.
- A figure, a trend or a small table that answers the question now: render it
  inline with render_a2ui, using the catalog's widgets with a saved query's id.
- Several parts, or anything worth keeping: a dashboard on the canvas.
- A one-off visual no catalog component covers: generateSandboxedUi.
Never paste rows of data into the chat.

Give each agent only the tiers it should use, with client_tools. A visualizer that builds dashboards doesn't need render_a2ui; an analyst that answers quick questions does. Every tier an agent holds is a tool the model has to choose between, and Kaboo holds agents to 20 tools.

Compare the inline and canvas answers to the same question with yarn lab compare inline-vs-canvas --live: the tool calls, tokens, bytes and time of each side, from a real model.

Data by reference

Every tier carries ids, never rows. A widget, an A2UI component or a sandbox function names a saved query, and fetches its rows when it renders:

  • The answer stays small. A surface is a few kilobytes whatever the data, and so is the conversation that carries it.
  • The figures stay live. Change a dashboard variable and every widget that reads the query refreshes; nothing in the transcript goes stale.
  • A widget can move. "Add to dashboard" on an inline answer plots the same widget spec on the canvas, unchanged.

The agent saves the query first (sql-workbench___create_queries), then refers to it. The same rule holds for tool results: return a preview and an id, not the full result set.

Where to look next