How a render spec turns one JSON payload into native UI
Why agent projects stall in month two
The first version is always quick. A text field, a model behind it, a stream of words coming back — a week of work and a convincing demo. The project stalls later, when someone asks whether the assistant can actually place the order it just described, and whether it can show the user what it is about to do instead of narrating it in a paragraph.
That is the real work, and it is not model work. It is interface work and tool work: what the assistant is allowed to touch, what the user sees before anything happens, and what the whole thing looks like when the network is slow. Teams that plan for a chat box get surprised by all three.
The three patterns we keep seeing
- The assistant can only talk. It explains how to change a delivery address rather than changing it, so users go back to the screens they already knew.
- The answer arrives as a wall of prose. Anything structured — a price, a status, a choice between three options — is flattened into a sentence the user has to parse.
- Nobody decided what it must never do. Without an explicit list, the safe answer becomes read-only, and a read-only assistant rarely earns its place on the home screen.
Decide what the agent is for
Before choosing a stack, write down the single job the assistant is accountable for in your app. Not a category like support — a task a user would recognise. Reorder my usual. Tell me why this charge appeared. Move my delivery to Thursday. One sentence, with a verb in it.
- Answering from your data: the agent reads, summarises and cites. The work is retrieval and phrasing, and there is nothing to confirm.
- Acting on the user's behalf: the agent writes something. The work is tool design and confirmation, and this is where most of the week goes.
- Navigating the product: the agent moves the user to the right screen with the right state already filled in.
- Handling voice: the agent listens and speaks. The work is turn-taking and interruption, not vocabulary.
The narrower this sentence, the shorter your tool list, and the tool list is what determines whether a week is realistic. Everything else — more intents, more surfaces, more languages — is a second release.
Build the runtime or adopt one
There are three honest options, and the right one depends less on engineering taste than on who is going to own the assistant six months from now. Assembling it yourself gives you total control over a surface that changes monthly. A chat SDK gets you a conversation quickly but leaves the interface and the actions to you. A runtime takes the plumbing and gives you back an opinion you have to live with.
| Approach | Works best when | Watch out for |
|---|---|---|
| Build in-house | The assistant is core to the product and you have people who can own model, transport and UI together. | The maintenance surface is wider than it looks: streaming, interruption, retries, tool schemas, and every SDK you ship on. |
| Chat SDK | You want a conversation in the product quickly and are happy for it to look like a conversation. | You still design and build every widget, and every action is yours to make safe. |
| Agent runtime | You want the assistant to act and to render as part of your app, with one contract across platforms. | You inherit someone else's model of what a turn is. Check that the tool and widget contracts fit before you commit. |
Whichever route you take, keep the tools on your side of the line. Tool definitions are product decisions, and the team that owns the backend should own them — regardless of who runs the loop that calls them.
Let the agent draw the interface
The change that makes an in-app assistant stop looking bolted on is small: alongside its spoken or written answer, the agent returns a short description of the widgets the app should draw. A balance card. A list of three orders. A confirm sheet with the amount filled in. Your app receives that description and renders it with the components it already ships.
Nothing about your design system leaves your codebase. The agent is choosing from a vocabulary you defined, not sending you markup, so the assistant inherits your typography, your spacing and your dark mode for free. When you restyle a card, the assistant's version changes with it.
Concretely, the tail of a turn looks like this — a spoken answer, then the widgets that answer should be drawn as:
[
{ "kind": "balance", "label": "Available", "amount": 2480.55, "currency": "EGP" },
{
"kind": "list",
"title": "Recent orders",
"items": [
{ "title": "Order #4182", "subtitle": "Delivered Tuesday", "trailing": "EGP 320.00" },
{ "title": "Order #4166", "subtitle": "Delivered last week", "trailing": "EGP 145.50" }
]
},
{ "kind": "confirm", "title": "Reorder #4182?", "confirmLabel": "Reorder", "requiresBiometric": false }
]Wire actions without handing over the keys
A model that can read is a feature. A model that can write is a liability unless the path from intent to execution passes through something the user actually saw. The rule worth adopting early: no state-changing call happens on the strength of a transcript. The agent proposes, the interface confirms, the backend executes.
In practice that means a state-changing tool does not run when the model calls it. It produces a confirm card — the action, the amount, the target, in your own components — and only the user's tap sends it onward. Higher-risk actions can ask for device biometrics on top.
- Split tools into read and write, and treat the write list as a security surface with an owner.
- Make the confirm card state the consequence in plain language, not the tool name.
- Tier the friction: a tap for routine changes, biometrics for money leaving the account.
- Log what was proposed alongside what was confirmed. Divergence between the two is your best early warning.
An assistant that can act is only as trustworthy as the moment before it acts. Spend your time on that screen, not on the prompt.Product design principle
What a realistic week looks like
Two days on tools: pick the three or four your one job needs, write their schemas properly, and test them without a model in the loop. Two days on widgets: build or adapt the handful of components the assistant may draw, including the confirm card. One day wiring the agent to both, and the rest on the parts nobody budgets for — empty states, network failure, and what the assistant says when it does not know.
Ship it to your own team first, on real accounts, and read the transcripts rather than the metrics. The first week of internal traffic will tell you which of your tools has a confusing name and which widget you forgot to build, and both are cheap to fix before anyone outside sees it.
A narrow assistant that reliably does one job earns the right to do a second. A broad one that half-does six gets switched off after a quarter, and the team concludes that agents do not work — when what did not work was the scope.
Common questions
Is a week actually realistic?
For one job, with a short tool list and widgets you already own, yes — that is the shape of build most teams manage in a week. What does not fit in a week is an assistant that spans several product areas, because the tool surface and the confirmation design grow with every area you add.
Do we have to expose our whole backend?
No, and you should not. Expose the specific operations the assistant's job needs, described by their schemas. Everything else stays invisible to the agent, and the operations you do expose keep the same auth, validation and rate limits they already have.
What happens when the agent gets it wrong?
For reads, it says something inaccurate and the user corrects it, which is why citations and visible sources matter. For writes, nothing happens at all, because the confirm step sits between the model's intent and execution. Design so that being wrong is boring.
Does this replace our support chat?
Usually not at first. An in-app agent is good at tasks inside the product — finding an order, changing a booking, explaining a charge — while support handles the long tail and anything needing a human. Teams that route the task-shaped questions to the agent tend to see the support queue get shorter, not disappear.
Related tags
