Why a Search Box Can Never Hold a Large Language Model

For the past twenty years, the underlying contract of most software interfaces has been the same: an input box, a submit button, and one screen of results. Buried inside that contract is a set of assumptions nobody ever wrote down — the user's intent is clear and sayable in one sentence; the system returns facts rather than probabilities; the interaction is synchronous, with no process between the question and the answer. Drop a large language model into this contract and you get an extremely convenient product form: add a little lightbulb icon to the left of the search box, the user clicks it, and a side panel spits out a paragraph of text.

Building that form is easy. What's hard is that it starts leaking in the third week after launch. There are three structural mismatches between the model's capability boundary and the search box's assumptions.

First: intent is no longer a single string. A user wants to revise a piece of copy but only has half a draft in hand; they want to consolidate information scattered across five documents and don't themselves know which keyword to use; they spot a bug and can only describe the symptom. The first two generations of interaction required the user to do the grunt work of "translating the problem into a query" before asking anything — and that is precisely the work a model is best at eliminating. If the interface offers only a single-line input, it is forcing the user to finish the assistant's job before the assistant exists.

Second: the output is a probability, not a fact. When search results rank badly, the user changes a word and searches again. A model output that lands badly gives the user no such escape — he cannot approximate the correct answer by tweaking the query string, because he does not know what the correct answer looks like either. That directly overturns the "results page = a ranking problem" framing: the real design object becomes something that will be wrong, and that honestly signals the possibility of being wrong.

Third: the process is visible, and it is long. Generating a three-thousand-word analysis takes tens of seconds; a multi-step retrieval plan takes minutes; a full code change takes minutes to a dozen minutes. The search era never produced an experience like "this action runs for two minutes while the interface sits dead", because any slow server-side action was long ago designed as an asynchronous task plus a notification. Generative AI is the first category of product that moves heavy computation to the foreground and puts the user in the waiting area.

These three mismatches together yield one judgment: the interface of an AI-native product spends most of its effort not on "presenting the answer" but on "exposing and orchestrating an uncertain, time-consuming, stoppable-at-any-moment process". This line is the boundary between AI-native and AI-augmented: the latter bolts an entry point onto the old interface, the former redesigns from "showing results" into "accompanying the process". If you look back at your own product and cannot find a single spot in the interface reserved for the "process" — no progress semantics, no intermediate states, no undo points — then it is just a search box with a model API bolted on.

Three Interface Variables That Have Been Underestimated for a Long Time

Pushing that abstract judgment one layer down, every AI interface design eventually lands on three variables — and the rest of this article stays inside them.

  • The opportunity cost of intent clarification. Clarification is not a sin against the user. When the model is 85% sure what it needs, the interface should just do it; when it is 40% sure, doing half the work and offering three directions is usually better received than asking a question. The criterion is which costs more: guessing wrong, or asking.
  • Visible intermediate states. Users need to see "what the system is doing, which step it is on, what I can do", not merely "I'm waiting". Design the intermediate states well and the subjective wait can be cut by more than half; design them badly and a forty-second operation gets perceived as a hang.
  • Undo points. Errors in an AI system are contagious: one bad plan makes every later step worse. The interface must keep offering anchors of the form "I can change my mind here", otherwise the user's only strategy is "close it and start over" — and starting over costs a full recompute.

These three variables together explain why so many AI features are "usable but not good to use": it is not that model capability falls short, it's that none of these three were built. However strong the model, a user cannot build trust in an interface that has no undo point.

Streaming Output: Character-by-Character, Skeleton Screens, and a Third Option

Streaming output is practically the default configuration for AI interfaces now, yet most teams, while building streaming, answer exactly one question: how do we get the first character on screen faster? The three questions that actually need answering are: what do you show before the first character, at what granularity you deliver after it, and whether the user can interrupt during the moment of generation. The combination of the three decides whether streaming is an "experience upgrade" or "experience noise".

Two costs come first. The first is time to first token (TTFT) — the time from the user hitting Enter to the first character appearing — which decides what the interface shows during the "blank period". The second is total generation time, the source of user complaints, and it is almost impossible to solve through front-end rendering optimization; you can only mitigate it by giving the user something to do, something to read, and something to press during that span. The essence of streaming output is to cut an invisible wait into many visible fragments, so that the user's subjective wait decouples from the actual wait.

Strategy One: Character-Level Typewriter Streaming, and Its Five Failure Modes

Character-level (character-level / typewriter streaming) is the easiest to implement and the easiest to abuse. Its correct use is narrower than you would think: only when the "unit" of output is itself a character — conversation replies, code completion, terminal-style live generation. Once the unit is a paragraph, a section, or a plan, character-level streaming becomes a performance.

It has five recognizable failure modes.

  1. Fake thinking. A typewriter animation strongly implies "the system is working hard", but it has nothing to do with real reasoning. Users misread the animation as "the model is thinking", so when the model is actually streaming a chunk of text that was generated long ago, trust has been overdrawn.
  2. The repeat disaster. Character-level animation blows up a single generation error into a public incident: one token sampled wrong, the wrong character sits on screen for several hundred milliseconds before being overwritten, and the user remembers that error permanently — even though it was never really output.
  3. Skimming blockage. Humans read far faster than models generate. Under character-level output, when the user reaches the end of a sentence, new characters are still appearing at the bottom; the eye is forcibly locked onto a moving endpoint and reading comprehension of long text drops noticeably. The genuinely effective move is to let the finished portion stop animating and become static and readable.
  4. The psychological barrier to interruption. A typewriter animation has an inviolable "in progress" feel, so users hesitate to interrupt. Even when the interface has a stop button, usage is astonishingly low — which means the opportunity cost of streaming is wasted, because intermediate results that could have been taken halfway are never taken.
  5. No fast location. Generating a thousand characters takes a minute; if the user wants one particular sentence, all they can do is stare at the animation until it appears. Paragraph-level commits let users jump with Ctrl+F or a structural outline while generation is still running. Character-level cannot do this at all.

Conversely, there are two scenarios where character-level output is genuinely irreplaceable: low-latency short outputs (a one-sentence translation, a completion, a quick rewrite), where the typewriter effect is itself the value; and process text that must be confirmed character by character, such as character-by-character quoting or character-by-character announcement. The former is a UX bonus, the latter a semantic necessity. Beyond that, character-level streaming is basically lazy-by-default — because it is the easiest to build.

Strategy Two: Skeleton Screens, and Why They Lie

Skeleton screens come from traditional client development: when the structure is known but the duration is not, lay out the format first. Inside an AI interface, their applicability is much narrower than expected: they hold only when the macro structure of the output can be predicted in advance — a fixed five-part report, a fixed JSON schema, a table with fixed columns, a fixed section outline.

The failure mode of skeleton screens is structural hallucination. A three-item skeleton reading "Generating analysis report: 1. Data overview 2. Key findings 3. Recommendations", when the model ultimately outputs only two sections, or the third goes completely off track, means the interface is lying. It promised a structure that will not be delivered, and users remember that promise for a long time. When the structure is uncertain, it is better to give an honest vague placeholder than a precise fake structure.

Skeleton screens also have a subtle harm: they lock the user's attention onto the layout rather than the content. Users start imagining the content before the skeleton disappears, or feel a "is this really what I asked for?" drop when it does. The sturdier approach mixes skeleton and real content — while the layout placeholder is showing, first fill in the header fields that are already known (title, source, timestamp) with real values, so the user sees information growing.

Strategy Three: Semantic-Block Streaming, the Default Answer of 2026

The third approach, and currently the one with the best engineering cost-performance ratio: aggregate on the client and commit state at semantic boundaries. Tokens arrive into a buffer; when a paragraph break, list-item boundary, heading marker, code fence, or table-row terminator is seen, the completed block is committed to the state layer in one shot and rendered. What the user sees is not characters one by one, but chunks of text settling at a readable granularity.

It simultaneously solves the four failures above: no fake thinking (the block is real content), no repeat disaster (unfinished portions are simply not displayed), no harm to skimming (committed blocks are stable DOM), and a low barrier to interruption (the user sees a "document growing" and can say "that's enough" at any time). The cost is one notch higher implementation complexity: you need rendering logic that is safe under incomplete input, because while streaming arrives a table may be half-built, a code block may not yet have hit its closing fence, and Markdown may be truncated in the middle of a list item.

The following is the skeleton of the aggregation layer; the core is buffer, boundary commit, and tail increment:

// 语义块流式聚合:token 先入缓冲,遇到边界再整块提交
function createBlockAggregator(onBlock, onTail) {
  let buf = '';            // 尚未遇到边界的不完整片段
  let fence = 0;           // 代码围栏计数,判断当前是否在代码块内

  function push(token) {
    buf += token;
    fence += (token.match(/```/g) || []).length;

    // 代码块内不按段落切,否则会把一段代码劈成两半渲染
    if (fence % 2 === 1) return;

    const parts = buf.split(/\n{2,}/);
    buf = parts.pop();              // 最后一段可能还没结束,留作尾部
    parts.forEach(p => onBlock(p.trim()));

    // 尾部若已经能安全成段(比如超过一行且不是未闭合列表),也提交
    if (buf.length > 200 && !/^\s*([-*+]|\d+\.)\s*$/.test(buf)) {
      onBlock(buf.trim());
      buf = '';
    }
  }

  return { push, flush: () => buf.trim() && onBlock(buf.trim()) };
}

The accompanying rendering layer must handle "incomplete content": the Markdown renderer has to tolerate unclosed fences and half tables, and the syntax highlighter needs a degraded mode for half a line of code. There is one plain but effective trick — render the last unfinished block at partial transparency, or give it a very short breathing cursor, so it reads visually as "still being written" rather than "display truncated".

A Selection Table for the Three Streaming Strategies

The table below is the decision aid used in real projects: horizontally, the output form; vertically, how well each strategy fits. Note the last row: how likely the user is to intervene midway predicts the strategy far better than how long the output is.

Output formCharacter-levelSkeletonSemantic blockReason
Conversational replies, short rewrites, translationRecommendedNot applicableUsableThe unit is itself a character, so character-level is the semantics
Code completion, single-function generationRecommendedNot applicableRecommendedNeeds character-by-character confirmation and the ability to stop at any time
Fixed-structure reports, JSON, tablesForbiddenConditionalRecommendedSkeletons hold when the structure is predictable, but body text still needs block commits
Long-form analysis, multi-step planningForbiddenForbiddenRecommendedFake thinking and skimming blockage cost the most here
Needs partial results taken midwayForbiddenForbiddenRequiredThe block is the smallest unit that can be taken

There is one more easily overlooked engineering detail: the frame budget of streaming rendering. If every token triggers a React re-render, a three-thousand-word answer performs thousands of reconciliations and drops frames, or locks up, on low-end devices. The correct move is to decouple token arrival from state commit — tokens go into a queue, and requestAnimationFrame or a 30–60ms throttle merges the queued increments into a single state update. It still feels like streaming, but the number of renders drops by an order of magnitude. This detail does not affect the design decision, yet it decides whether the design can actually survive in production.

Partial Results and Undo Points: Letting Users Wrap Up Early

If semantic-block streaming answered "how do I watch it", the next question is "when can I stop waiting". In traditional software, a user intervening mid-operation usually means cancel; in an AI interface, a user intervening mid-operation should mean acceptance (accept-partial). These are two entirely different interaction contracts.

The design details of taking partial results are subtler than they look.

The Granularity of the Take Point

The smallest takeable unit must align with the smallest meaningful unit of the content. Paragraph level is the default choice, but structured output deserves finer granularity — in a report, users often want "the table in section 2" rather than all of section 2. For code output, the granularity should be the function or the file; for table output, it should be a block of rows; for retrieval-style output, it is a single result plus its citation. The rule is: takeable granularity ≤ the smallest unit the user can reuse; if it is too coarse, the user pays a waiting cost for things they did not want.

What Happens on the Generation Side After an Early Exit

After the user clicks "use these", the still-running generation stream on the server must be cancelled immediately, otherwise it keeps burning tokens and leaves behind an unclaimed session on the server. Cancelling must be a real cancel: disconnect the stream, persist the already-generated portion, and mark the ungenerated remainder as truncated rather than failed. This must also show up in the interface — truncated content must not be displayed as an error state like "generation interrupted", because the user ended it deliberately; it should read as "adopted the first N segments". Deliberate truncation and failure truncation must use different visual languages; conflating them makes users think the system broke.

Undo Points: Making "Changing Your Mind" a First-Class Citizen

An undo point and an edit point are not the same thing. An edit point lets the user change the result; an undo point lets the user reject one particular decision. An AI interface needs both, and undo points must be placed according to the error's "blast radius".

The concrete approach is to build a traceable decision chain for the generation process, recording at each step: what this step intended, what it relied on, and what it produced. Give every decision point a lightweight entry in the interface so the user can reject just one step and re-fork from there instead of starting over. In multi-step agent scenarios, this pattern is almost the dividing line between "unusable" and "usable" — because agent errors are systemic, and users cannot accept a full restart.

Forking has an easy trap: the new branch must inherit all context and artifacts from before the fork point, not merely keep the changed parts. Many first implementations issue a new request from the fork point; the model then loses every previous agreement, output quality collapses, and the feature is judged "not viable". The correct approach is to feed all messages, tool call results, and confirmed constraints from before the fork point back into the new branch.

An Undo-Point State Machine

Turning the above idea into a state machine, code explains it better than prose. The core is: every state is a persistable snapshot, and undo = return to some snapshot + carry a rejection instruction.

// 生成任务的显式状态机:撤销点即快照,redo 走同一条路径
const STATES = ['规划', '检索', '起草', '自检', '完成'];

function onEvent(task, evt) {
  switch (evt.type) {
    case 'STEP_DONE':
      // 关键:每步结束都打快照,这里就是可撤销点
      return {
        ...task,
        state: evt.step,
        snapshot: { state: evt.step, ctx: deepClone(task.ctx) },
        trail: [...task.trail, evt.step],
      };

    case 'USER_REJECT':
      // 用户否定某一步:从最近快照回滚,并携带否定约束
      return {
        ...task,
        state: task.snapshot.state,
        ctx: {
          ...task.snapshot.ctx,
          rejected: [...task.snapshot.ctx.rejected, evt.reason],
        },
        trail: task.trail.slice(0, -1),
      };

    case 'USER_PARTIAL':
      // 主动截断:不是错误态,状态直接落到"已采纳"
      return { ...task, state: '完成', truncated: true, kept: evt.kept };
  }
}

Three interface rules follow. First, the undo entry must sit right next to the step that produced it, not collected in a top toolbar — a unified entry destroys the user's ability to judge "which step do I undo?". Second, undo must preview its consequences: "restart from this step" followed by a light expectation description ("will re-retrieve 3 sources, confirmed conclusions unaffected"), because the perceived cost of undoing is the main reason users do not undo. Third, the undo history itself must be visible and allow returning to any past state, otherwise repeated corrections degrade into "I am just repeatedly rerolling in place".

Visible Thinking: How Much to Expose Depends on What the User Must Do

"Should we show the model's reasoning?" has been debated too many times, but it is not a yes/no question. The value of reasoning to the user comes from one concrete job: letting the user locate which step went wrong when the result is wrong. How much to expose should be derived backwards from that job, not decided by an abstract virtue such as "transparency".

I split visibility into three tiers, and the three are not better/worse relations; they correspond to different user capabilities.

Tier 0: Full Black Box

Only the result, no process at all. The applicable surface is larger than you might imagine: translation, body-copy polishing, format conversion, classification and tagging, and short tasks that are already heavily templated. Users have no ability to evaluate intermediate steps in these tasks; they just want a usable artifact. Showing them "I first analyzed the subject-verb structure of the sentence" only adds noise, plus the burden of keeping a written record — and halfway through reading, the user must realize "does my task really need an explanation this long?".

The hard requirement of the black-box tier is the result is editable and editing is faster than regenerating. If one edit costs the user more than a rerun, the black box is a disaster, because the user's only correction mechanism is repeated retries, and the variance of retries eats all of their time. In the search era users converged by tweaking keywords; in the generative era users converge by "manually fixing it right" — this is the black-box tier's only source of legitimacy.

Tier 1: Stage Summaries

No verbatim chain of thought, only structured stages and their outputs. This is the default tier for the overwhelming majority of mature products in 2026.

The typical form is a timeline-style list of stages: understanding the request → retrieved 4 sources → cross-checked → produced a conclusion, each stage carrying a clickable summary and its key output (for example, "the 4 sources retrieved" is a clickable list of links). It solves the thing users are most anxious about — "what is it doing now, how much longer, can I walk away" — while exposing none of the model's internal phrasing.

Tier 1 has two key design constraints. First, stage summaries must be driven by the model's structured output, not produced by truncating the first few hundred characters of free-form thinking — the latter leaks internal prompts, exposes intermediate assumptions that should stay hidden, and is unstable in format. Second, stages must be few and stable. Three to five stages is enough, and stage names must stay consistent within one product; if the number of stages differs on every run, users can never learn to anticipate them, and the resulting anxiety cancels out the value of showing anything at all.

Tier 2: Collapsible Decision Rationale

On top of Tier 1, expand each stage to the "why did you do it this way" level, while still collapsed by default and without restating internal reasoning. This is the tier that high-risk, high-cost tasks must offer — code migration, data cleaning, comparison of legal clauses, financial analysis: tasks where "being wrong is expensive".

The real value of this tier is not satisfying curiosity but giving the user three actionable things: citations (which passage this conclusion rests on), constraints (which of your requirements I followed), and exclusions (which alternatives I ruled out among the possibilities, and why). The third is the most underestimated. When a user finds the result wrong, what they most want to know is usually not "why did you write it this way" but "did you consider another possibility".

Laying the three tiers side by side:

TierWhat is shownTarget tasksWhat must not be shown
Tier 0Result onlyTranslation, polishing, format conversion, classificationAny stage text — people without evaluation ability cannot read it
Tier 13–5 stages + stage outputsConversational answers, retrieval Q&A, routine tasksThe prefix of free-form reasoning, internal prompts
Tier 2Stage rationale, citations, constraints, exclusionsHigh-risk tasks in code, data, legal, financeThe full internal reasoning token by token

There is also one cross-tier principle: visibility should be derived from uncertainty, not from level of detail. Where the model is confident in a conclusion, a "confirmed" marker suffices; where it is not, the rationale and exclusions should be fully expanded. Spending the attention budget where users genuinely need to review is far more effective than dumping the whole chain of thought — the latter is not even finishable, and it trains users that "this expansion is filler", after which every future display of process becomes worthless.

Honest Presentation of Uncertainty: Confidence Is Not a Progress Bar

Uncertainty is the most fundamental difference between a model and a database, and it is also the thing most easily ruined by interfaces. Bad design typically takes one of two forms: hiding uncertainty, or disguising uncertainty as certainty.

Hiding looks like this: output a conclusion with no marker and no basis. The user accepts the conclusion, but in fact does not know he has accepted a probabilistic judgment, so the whole chain of trust is broken — one obvious error makes him feel deceived, and he attributes it to "this thing is unreliable" rather than "this one was not found".

Disguising is more common, and more deserving of criticism. Using a progress bar, a spinner, or a "thinking" percentage to express model uncertainty is the most widespread mistake in products in 2026. The reason is that it is trivially easy: the progress bar component exists, the animation component exists. But it expresses entirely the wrong semantics — a progress bar says "how much longer until it ends", while model uncertainty says "is what comes out trustworthy". There is no mapping between the two.

The worse case appears in self-check and reflection: the model runs an internal quality assessment on the answer, gets "confidence 82%", and the product renders it as a progress bar filled to 82%. That number is neither a probability nor calibrated, and its only function is to make the interface look rigorous.

Three Usable Expressions of Uncertainty

Only three expressions genuinely work, and none of them needs a precise number.

First, declarative markers. Put a lightweight tag next to the conclusion: "high confidence", "sources disagree", "low confidence". The key is that the tag must be generated from inspectable signals — whether several independent sources agree, how many pieces of evidence were retrieved, whether generation fell back to a degraded path, whether a tool call failed and retried. If you cannot get such signals, do not put up a tag; better to show none at all. An answer with no sources and an answer corroborated by five sources differ objectively, and the interface only needs to state that difference — it does not need to invent a number.

Second, making sources visible. This is the most cost-effective form of honesty. Hang a clickable source on every key conclusion so users can verify it themselves. This is not just transparency; it also shifts the cost of "trust" onto the user — those willing to check will check, those unwilling at least know there is something to check. It is also the most effective anti-hallucination interface device: a sentence without sources and a sentence with the provenance highlighted are distinguishable at a glance.

Third, path branching. Instead of annotating "this answer may be inaccurate", directly offer actions of different strengths: accept as is, request more evidence, try a different route, hand off to a human. If uncertainty can only be "annotated", users can only "accept or not accept"; if uncertainty can become a confirmation strength the user chooses, it turns from a disclaimer into real control.

This principle generalizes: any meta-information that cannot become a user action should be compressed away. That grey "confidence: 0.87" label on the page will almost never be read; but the same information rendered as an "add evidence" button will get an order of magnitude more clicks.

The Reject Button Is a Severely Underestimated Design Element

Most teams train the model to "refuse when it should refuse", yet give the user no entry point in the interface for directly expressing doubt. A user reads a long, confident answer, half-believes it, and has no option other than asking again — and asking again is the most expensive, dumbest way to express doubt.

The correct move is to keep a low-cost doubt entry permanently resident in the conclusion area, such as "this looks off", which on click pops up a few likely directions of error ("the facts are wrong", "the direction is off", "a key point is missing"). The value of this interaction is that it converts one vague act of distrust into one piece of structured feedback. Once that feedback comes back, it can be used both for an immediate retry and as input to the data loop for later optimization and evaluation. This is the key step in turning users from "passive receivers of results" into "collaborating annotators" — and many products' feedback systems are missing exactly this link: the user clicked a thumbs-down and nothing happened.

Should You Show "I Searched but Found Nothing"

This is an edge case, but handling it well earns real points. When the model cannot retrieve evidence, there are three responses: make up a plausible answer, state plainly that nothing was found, or state that nothing was found and suggest alternative directions. The third is clearly best, but it requires the product to admit one thing: not finding something is itself a valuable result. Many products' business metric is "answer generation success rate"; optimizing "not found" as a failure amounts to using the metric to force the system to lie. The design answer is to give "not found" its own, successful presentation form: it should look like a legitimate deliverable (what was tried, why it failed, what else is possible), not like an error message.

Long-Running Tasks: Interrupting, Backgrounding, and Resuming

When an operation takes more than thirty seconds, the interface question shifts from "how to display it" to "how to arrange the user's life". While waiting for a long task, people go do something else, come back, and find it failed — this is the single most concentrated source of negative reviews for AI products right now, far beyond the model answering incorrectly.

Split Long Tasks into Three Categories

Different kinds of long tasks need completely different interface strategies: classify first, then design.

  • Interruptible (3s–30s): one retrieval-augmented call, one long-form generation. Full-screen foreground waiting, stoppable at any time, and the generated portion is takeable. This is the default tier, and most tasks belong here.
  • Backgroundable (30s–10min): multi-step agents, batch processing, code migration. The foreground keeps only a task bar, generation continues in the background, and the user can go to another page. This requires a real job queue on the backend, not an HTTP request destroyed along with the page.
  • Deliverable (over 10min): large-scale data analysis, whole-repository refactoring. The interface's job is "place an order" rather than "wait for a result" — the user submits the request, sets delivery conditions (notify me when done / put the output here / tell me if it fails), and leaves.

The third category deserves separate emphasis, because it is only recently that it has been taken seriously. Its core is not waiting but delegation: the user is no longer a waiter but a customer placing an order. That changes an entire set of designs — delivery confirmation, failure alerts, artifact management, and explainability of "how many resources did this task cost me".

Three Semantics of Interruption

A "Stop" button means completely different things at different stages, and the interface must distinguish them, otherwise users will not dare press it.

  1. Stop generating: stop the remaining tokens, keep what exists. The smallest loss, and the default semantics.
  2. Cancel the task: terminate the whole task, including intermediate artifacts and side effects already produced (files written, APIs called). This must be confirmed once, because external side effects may already exist — especially when the task is a side-effecting operation such as "modify code", "send email" or "write to the database"; the confirmation text must explicitly list "what has already been done".
  3. Pause / background it: do not terminate; just move the task to the background to keep running. This is the most neglected one, yet it is often what users actually want — they want to keep using the app for something else, not to abandon the task.

Making "pause and background" a first-class operation pays off far beyond its development cost. It lowers both the perceived cost and the churn of long tasks, because users no longer have to choose between "sit and wait" and "give up".

Resuming: A Task Must Be a Replayable Session

When the user leaves and comes back, the interface must be able to pick the task back up. To do that, the task itself must be persistable — which is exactly the debt left by the many implementations that improvised a temporary HTTP stream: the moment the connection drops, the first half of the generation is gone forever, and the user returns to a blank screen.

There are four minimum requirements for resumability: first, persist generated content incrementally, not only in a memory buffer, so the user can read the half that exists; second, make stage state visible, so on return they immediately know "did it get to step 3, or did it just restart"; third, retries must be possible without repeating side effects, resuming from the last successful checkpoint rather than calling every tool again from scratch; fourth, continuation across devices — this one is often missed, but it requires almost no extra design; it only demands that task state live on the server rather than in the session.

One more recovery design that is easy to overlook and very effective: the first screen the user sees on return must not be an empty state. Laying out the recovered content, the stage, and the three actions "keep reading / regenerate / try a different direction" saves three clicks compared with showing "task finished, click to view". The longer the user was away, the weaker their motivation on return, so the information density of that first screen should be higher.

Promises Made During the Wait

There is a low-cost, high-yield detail in long tasks: deliver intermediate artifacts before the final artifact. Have the task push already-determined intermediate results (the list of sources retrieved, the constraints already confirmed, the half-generated draft) to the user early, even if they are not watching. It does two things: it kills the anxiety of "nothing produced at all" early; and it gives the user both a reason and an opportunity to stop early — if the direction at step 3 looks wrong, they can interrupt immediately instead of waiting three minutes and receiving a result in the wrong direction.

The Six Most Common Anti-Patterns in AI Interfaces

The six below are error patterns that recur in products and can be named precisely. Not one of them is technically impossible, and each has a reasonable motive behind it — "this seemed faster at the time" — which is exactly why they are so stubborn.

Anti-Pattern 1: Drawing Confidence as a Progress Bar

Symptom: a 78% fill animation under the answer, or "thinking 82%", or a ring that grows linearly with generation time. Users assume it expresses "how reliable is the answer".

Why it fails: it binds two orthogonal quantities together. Waiting ten seconds has nothing to do with the quality of the answer after ten seconds; a flow that crashes at 90% progress and one that crashes at 20% should not, semantically, look different. Worse, it trains an incorrect intuition in users: full progress bar = usable result. Users are therefore completely unprepared when they finally receive a low-quality answer.

Change it to: progress expresses only time, and must be estimable; uncertainty is expressed through sources, rationale, and optional actions, occupying no progress semantics at all. The two must be visually separated and never placed in the same component.

Anti-Pattern 2: Forced Opening Clarification

Symptom: the user types "help me look at this code", and the interface immediately pops a form requiring the user to pick a programming language, a target goal, a scope of modification, and whether comments are needed.

Why it fails: it misattributes the benefit of clarification to the model. In most cases the model can infer the language and intent by itself, and more than half of what the user fills in has nothing to do with the final answer. The user's real psychology is "give me a result first, and I'll adjust if it's wrong"; the form forces him to do a product manager's job first.

Change it to: defaults should bet on "the most likely one" — the interface just produces a result and folds the adjustable items into one lightweight "adjust on demand" row. Clarification is worth front-loading only when different choices would produce entirely different outputs (for example, generating code versus explaining code). You can generate with the most likely option first, while noting next to the result: "I did it as A first; switching to B takes roughly X seconds" — this is far better than a blocking question, because it turns clarification from a cost into an option.

Anti-Pattern 3: Treating the Whole Chain of Thought as an Explanation

Symptom: an expandable "thinking process" disclosure containing the model's raw reasoning text, thousands of characters long, with no structure and no correspondence to the conclusion.

Why it fails: three problems. First, unreadable — the user has no obligation to perform quality evaluation, and promoting their role to peer reviewer of the model is a terrible design. Second, unactionable — after reading it, the user still does not know where to click to correct anything. Third, unverifiable — reasoning text says "how I thought", while what the user needs is "what I relied on". Users quickly learn to ignore the disclosure, and you have thrown away the only asset that makes the product look transparent.

Change it to: swap it for a structured evidence chain — every key conclusion paired with a clickable source, all constraints in effect this run listed out, and the alternative directions that were ruled out made explicit. Users do not need you to explain how you thought; they need to know what you are claiming, and what you did not consider.

Anti-Pattern 4: Offering Only "Regenerate"

Symptom: when the output is unsatisfying, the only action in the interface is a refresh icon. Some products even replace the previous result outright, never giving a chance to compare.

Why it fails: regenerating is an extremely high-variance operation. The user presses it, hands over control, and accepts a lottery draw. If the result is wrong again, the only way out is to press it once more — a random walk with no accumulation, where all the time cost falls on the user. And replacing the old result outright means the system actively destroys the user's only baseline for comparison, so "is this better than last time" can never be answered.

Change it to: provide at least three things: keep and compare (keep the old version, view side by side or toggle back and forth), targeted modification (constrained retries such as "the third paragraph is too long" or "use a more formal tone", rather than drawing again from zero), and partial redo (redo just one paragraph or one step). All three essentially shift variance from "borne by the user" to "absorbed by the system".

Anti-Pattern 5: Making Long Tasks Foreground-Blocking

Symptom: after clicking an action, the page enters a full-screen loading state; the only thing the user can do is wait, and closing the page loses the task.

Why it fails: it violates three user expectations at once: progress should be estimable, my other work should not be blocked, and if I close and come back it should still be there. The user is forced to hold attention for the whole duration while being unable to do anything at all during those tens of seconds — pure waste of time.

Change it to: operations over three seconds enter a "visible in the foreground, running in the background" model: the content area keeps rendering the generated portion, a collapsible task bar sits at the side or top, the task continues when the page is left, and returning shows the reattached state directly. Past thirty seconds, add one more layer: let the user choose "notify me when it finishes in the background".

Anti-Pattern 6: Treating Conversation History as the Only Navigation

Symptom: an ever-growing conversation stream; to find the document generated ten minutes ago, the user has to scroll up. All new content is appended at the bottom.

Why it fails: it treats the conversation stream as a file system, but the temporal order of a conversation and the order in which users care about information are entirely different things. Worse, it lacks takeability and undo points — a great artifact generated halfway is buried in the middle of a screen of chat logs, the user has no "adopt" action, and so that content stays part of history forever and never becomes a user asset.

Change it to: conversation is a container for process, not for output. Artifacts should be extracted into a separate output area (workspace, document library, result list), with a title, editable, exportable, and reusable as input again. The criterion is simple: anything the user will still need tomorrow must leave the conversation stream. Fail that, and the AI product is just a very chatty search box.


Two Uncertainty Interfaces Side by Side

The prototype below puts Anti-Pattern 1 next to the correct approach. On the left is the common fake — a moving bar implying reliability; on the right, uncertainty translated into clickable user actions and verifiable evidence.

<!-- 反面:进度条伪装置信度,语义完全错位 -->
<div class="answer">该方案可以满足需求。</div>
<div class="bar"><i style="width:82%"></i></div>
<small>置信度 82%</small>

<!-- 正面:标注来源 + 暴露分歧 + 给出可选动作 -->
<div class="answer">
  <p>该方案<mark>大体</mark>可以满足需求。</p>
  <ul class="evidence">
    <li>依据 1:接口返回了分页参数 <code>cursor</code> <a>[查看原文]</a></li>
    <li>依据 2:4 个来源中有 1 个提到排序不稳定 <em>(存在分歧)</em></li>
  </ul>
  <div class="actions">
    <button>补充依据</button>
    <button>只重做有分歧的部分</button>
    <button>这段不太对</button>
  </div>
</div>

Folding All of This into One Checklist

Running through this list item by item in a design review is more effective than reading a hundred pages of design guidelines. The left column is the question, the right is the basis for judgment. Anything you cannot answer is simply not done yet.

CheckBasis for judgment
Can the user take results midway?Halfway through generation, is there a non-blocking "use these" action?
Does takeable granularity align with semantics?Is the smallest takeable unit no larger than the smallest unit the user can reuse?
Are steps undoable?Is there an undo entry next to each stage, and can undo redo only that step?
Is progress semantics clean?Has any information expressing "reliability" been mixed into the progress component?
Is time estimable?Does the user know how much longer to wait, and when they can walk away?
Are interruption semantics tiered?Are stop generating, cancel task, and background three distinct actions?
Can leaving the page be resumed?After closing and reopening, are the generated content, stage state, and follow-up actions all still there?
Is process display tiered?Is the tier chosen by task risk, rather than always showing everything?
Is uncertainty actionable?Can the user express doubt in one click and trigger structured feedback?
Do outputs leave the conversation stream?Have the things the user needs tomorrow already entered a separate output area?

AI That Acts for You: Visible Side Effects and Authorization Boundaries

Everything so far has assumed the model only produces text. But in product forms of 2026, models are increasingly granted capabilities — reading files, calling APIs, writing to databases, sending messages, committing code. These actions have side effects: they are not reversible, not idempotent, and they affect other people. The interface acquires one more job: making side effects visible, controllable, and attributable.

The first wrong turn on this path is making tool calls a black box — the interface shows one line saying "processing", while internally it reads whatever and writes whatever. That is very smooth in a demo and very dangerous in production, because the user cannot intervene at all before a critical action happens. The second wrong turn is swinging to the opposite extreme, popping a confirmation dialog for every single call. That is worse than the black box: confirmation fatigue. By the third dialog the user is clicking "Allow" on autopilot, after which every authorization prompt is worthless — and that is exactly when real risk shows up.

Three Granularities of Authorization

A workable approach is to tier authorization by reversibility and blast radius, so that most routine actions never interrupt the user and things stop only when they cross some line.

  • Read-only, low impact: reading a single file, querying one API, retrieving some material once. Execute silently by default, but afterwards leave an auditable record ("which 3 files were just read"). No dialog, but auditable.
  • Reversible writes: creating a draft, writing to a temp directory, saving changes locally. Grant scope once on entry ("files under src/ may be modified this time") rather than asking every time. The key is that this grant is scoped, time-limited, and expires.
  • Irreversible or externally visible: deleting, sending messages to others, paying, merging code, modifying shared data. These must be confirmed step by step, and the confirmation text must state three things clearly — the specific action, who or what it targets, and how to undo it.

The standard for tiering is not importance but who bears the cost when it goes wrong. A wrong write to the user's own local file is borne by the user and recoverable, so it can be loosened; a badly worded email sent to a colleague puts the cost on someone else, so it must be blocked. This criterion is far more reliable than any model self-assessment of "does this operation look safe".

Authorization Should Be Something Manageable

Making authorization a one-off "yes" inside a conversation means renegotiating it every single time. The mature approach is to make it a visible, modifiable, revocable set of settings: what this session allows, what this project allows by default, and where the permanently allowed list lives.

The implementation below combines "scoped grants" with "step checkpoints"; it interrupts less than a dialog per action, yet keeps more braking than a blanket pass:

// 授权判定:先看范围授权,再决定要不要打断用户
function check(tool, args, session) {
  const scope = session.grants.find(g => g.tool === tool);
  // 命中范围授权,且本次动作落在允许边界内,直接放行
  if (scope && inScope(scope, args) && !expired(scope)) return 'ALLOW';

  // 不可逆动作:先给出用户能看懂的影响描述再问
  if (tool.irreversible) {
    return 'CONFIRM:' + describe(tool, args);
    // describe 要明确写出:做什么、对谁做、怎么撤销
  }
  // 其余情况授予窄范围授权,避免逐次打断
  return 'GRANT_SCOPE:' + deriveScope(tool, args);
}

Three interface rules accompany this. First, the default position of the cancel button should be on the "external impact" side; do not set "Confirm" as the default focus just to guard against misclicks — the costs of failure in these interfaces are asymmetric, so one extra click in exchange for one confirmation is a good trade. Second, broad grants must show a visible remaining quota, such as "this task has 3 write operations left", so users can judge whether they need to tighten it. Third, the history of grants and revocations must be viewable afterwards and reversible in one click: "it changed my stuff" must have a clear place to look.

This thread in fact runs through the whole article. Undo points, visible stages, actionable uncertainty, revocable authorization — underneath they are the same thing: exchanging the system's autonomy, hidden in a black box, for a set of handles the user can grip. The more handles the interface has, the wider you can open up model capability; the fewer handles you have, the more you are forced to lock the model inside a search box.

Conclusion: The Interface Designs the Process, Not the Answer

Summing up the whole article in one sentence: the core object of large-model interface design is a process that can be wrong, can take time, and can be stopped — and what the interface must do is make that process visible, controllable, and undoable.

This shift brings more than a few new components; it brings a whole new division of labor. Interface designers used to be responsible for layout and information density; now there is one more job: orchestrating a computational process you do not control. Typographic experience is still useful, but it is no longer enough — because the person in front of you is no longer a querier, but a client who has delegated the work.

Finally, one concrete starting point. If you can only change one thing, change "waiting": make streaming output commit by semantic blocks, let the user take the generated portion at any moment, and give the unfinished portion an explicit path to interruption and resumption. Together these three turn a "stuck AI feature" into a "workbench you can rely on", and none of them depends on the others, so they can be shipped in any order.

Everything else — the tiering of visible thinking, the expression of uncertainty, the management of artifacts — can wait for a second phase. But if this first step is not taken, nothing that follows has anywhere to start.