๐Ÿšง Under construction โ€” I'm migrating this site from Framer to Next.js and publishing it early for testing, so a lot of the content is still in flux.๐Ÿšง Under construction โ€” I'm migrating this site from Framer to Next.js and publishing it early for testing, so a lot of the content is still in flux.๐Ÿšง Under construction โ€” I'm migrating this site from Framer to Next.js and publishing it early for testing, so a lot of the content is still in flux.๐Ÿšง Under construction โ€” I'm migrating this site from Framer to Next.js and publishing it early for testing, so a lot of the content is still in flux.๐Ÿšง Under construction โ€” I'm migrating this site from Framer to Next.js and publishing it early for testing, so a lot of the content is still in flux.๐Ÿšง Under construction โ€” I'm migrating this site from Framer to Next.js and publishing it early for testing, so a lot of the content is still in flux.
Open menu
Switch to Darkhello@product.inc
Writing

Wizard or conversation: when an agent shouldn't be a chat

ยท Alex Zapadenko

Use a conversation when you can't know in advance what the user is going to ask for. Use a structured flow when you can โ€” when the required inputs are a list you could print, later answers depend on earlier ones, and something irreversible happens at the end. A lot of agent features are the second kind and get shipped as the first, because a text box is the fastest thing to build and it never looks obviously wrong.

I should say where I'm standing: I sell design leadership for AI products, and the wizard primitives I'm going to quote are ones I maintain, so I have an interest in the structured answer looking good. I've also built the other thing, which is where this argument came from rather than from a preference.

The chat product taught me this, not the form

Poppy is a voice-first assistant for teachers โ€” you talk a lesson plan into existence in the few minutes between classes. It is, structurally, a chat app: streaming responses, persisted threads, a two-panel layout. The part that made it usable is the part that isn't chat. The home screen opens on four one-tap actions โ€” plan a lesson, write to parents, build a quiz, adapt content to a reading level โ€” because a teacher with ninety seconds and a blank prompt does not have a conversation, they have a staring contest.

That's the whole tension in miniature. Chat is the right interface when you genuinely don't know the distribution of what people will ask, which is true on day one of every product. It stops being true about a month after launch, when the logs show that four jobs account for most of the traffic and the model is being asked to re-derive the same shape twenty times a day.

The question isn't chat versus form

It's whether the set of required inputs is knowable before the user arrives.

If it is, a form is the honest representation of that: a published contract saying here is what this needs, here is what's still missing, here is what it will do when you press the button. If it isn't โ€” if the inputs only emerge from what the person says โ€” then no form you draw will fit, and the interface has to be able to ask.

SignalConversationStructured flow
What the user wantsCan't be enumerated in advanceOne of a handful of known jobs
Required inputsDiscovered by askingA fixed list you could print today
OrderDoesn't matterLater steps depend on earlier answers
What happens at the endText the user reads and can ignoreA change to real data
Cost of being wrongAnother turnA rollback

Most of the disagreement about this is really disagreement about the first row, and it's usually settled by looking at usage rather than by arguing.

The version worth building is neither

The interesting option is not picking one. It's a structured flow that the agent fills in.

The user says what they want in their own words, once. The model reads it and proposes values into the flow's draft โ€” every field it can infer, left visible and editable. The person then works the way people actually work with a nearly-right document: they scan it, fix the two things that are wrong, and press the button. Nothing is committed until they do.

The reason this beats a pure conversation is narrow and practical. Re-prompting is a terrible editing instrument. If the model got one of eleven values wrong, you want to change that value โ€” not describe the change in prose, hope the rewrite preserves the ten that were right, and re-read all eleven to check. Prose is a good way to specify something the first time and a bad way to amend it the fourth.

The reason it beats a pure form is obvious: the person didn't have to fill in eleven fields.

What a wizard has that a transcript doesn't

The internal pattern library I maintain runs to 18 pages and about 13,900 words, and ten of those eighteen are about one moment: a change being committed, and what the person does if it was wrong. The multi-step wizard's answer to that is a set of properties a chat log structurally cannot have.

Draft, not commit. Every step writes to a draft object; the real entity is created once, at the end. Committing each step as you go leaks half-finished records into production data, which is the single most common way these flows go wrong.

A step is an address. The current step lives in the URL, so refresh, browser Back, and sending someone the link all work. There is no address for "the part of the conversation where the price got set."

Back is lossless, and enforced. The controller I ship is 199 lines and renders nothing at all โ€” it owns step order, forward gating, the furthest step reached, and per-step errors. Its back() never validates. That's a policy rather than an omission: going backwards is navigation, and navigation must not be able to destroy what you typed.

A review step. One screen listing everything about to happen, each line linking back to where it came from. This is the part an agent-filled flow needs most, and the part that disappears entirely when the interface is a transcript.

Three to six steps is the range that works. Below three, a sectioned single form is less clicking for the same result. Above six, drop-off climbs and the flow starts to feel like a form someone is administering to you.

Where a conversation is straightforwardly better

Being fair about this matters, because the structured answer is easy to over-apply.

Chat wins whenever the input is genuinely unbounded โ€” writing, analysis, "why is this build failing." It wins when the user's vocabulary for the problem is better than yours, which is normal for experts using a tool built by non-experts. It wins in the long tail: if a product supports two hundred rare jobs, building two hundred flows is absurd, and the text box is the only economical interface for the two hundred and first. And it wins when the output is prose the person will read and edit themselves, because then the conversation and the artifact are the same object.

The pattern I'd aim for is a small number of named flows for the jobs that dominate the logs, with a conversation underneath for everything else โ€” not one or the other everywhere.

Two hybrids that don't work

The interrogation. A known form asked one question at a time in a chat box. This is the worst available option: no overview of what's being asked, no way to change answer three, no sense of how much is left, and every reply is a parsing problem you invented for yourself. If you know all twelve questions, showing all twelve is not a worse experience for being less conversational.

The quiet commit. A conversation that performs a real action mid-stream โ€” sends the email, moves the money, updates the record โ€” with the confirmation arriving as a sentence in the transcript. There's no review surface, no single place showing what's about to happen, and often no way back. That's a failure state nobody designed, and the fix is usually a commit you can take back rather than another dialog asking whether you're sure.

The test

Three questions, and they're answerable in an afternoon with the logs open.

  1. Could you print the list of required inputs before the user arrives? If you could, print it. The user should not have to guess what the model needs.
  2. When the eleventh value is wrong, what does the person do? If the honest answer is "ask again and hope," the interface is wrong regardless of how good the model is.
  3. What is the last irreversible thing that happens, and is there one screen showing everything it's about to do? If not, that screen is the next thing to build.

None of this is an argument against conversation. It's an argument that the conversation is the input method, and it shouldn't also have to be the record, the editor, and the commit.

Related: undo over confirm, design the failure state first, the Poppy build, and how I work.