为什么搜索框装不下大模型

过去二十年,绝大部分软件界面的底层契约是同一个:输入框 + 提交按钮 + 一屏结果。这套契约里藏着一组从未被写出来的假设——用户的意图是明确且可一句话说清的;系统返回的是事实而不是概率;交互是同步的,问答之间没有过程。把大语言模型塞进这个契约里,于是有了一个极其省事的产品形态:在搜索框左边加一个小灯泡,用户点一下,右侧面板弹出一段文字。

这个形态做出来不难,难的是它会在上线第三周开始漏水。因为模型的能力边界和搜索框的假设之间有三处结构性错位。

第一处:意图不再是一个字符串。用户想改一段文案,他手上只有半句残稿;他想把散落在五个文档里的信息汇总,他自己也不知道该用哪个关键词;他发现一个 bug,他只能描述症状。前两代交互要求用户在提问前完成"把问题翻译成检索式"这件苦力活,而这恰恰是模型最擅长替人干掉的活。界面如果只提供一个单行输入框,等于逼用户在没有助手的情况下先完成助手的工作。

第二处:输出是概率,不是事实。搜索结果的排序不好,用户换一个词再搜;模型输出不好,用户没有同样的退路——他无法通过调整查询串来逼近正确答案,因为他自己也不知道正确答案长什么样。这直接推翻了"结果页 = 排序问题"的思路:真正的设计对象变成了一个会出错、并且会诚实地表现出出错可能性的东西。

第三处:过程是可感知的,而且是长的。生成一个三千字的分析要几十秒,做一次多步检索规划要几分钟,跑一整套代码修改要几分钟到十几分钟。搜索时代没有"一个操作要跑两分钟但界面是死的"这种体验,因为服务器端耗时的操作早就被设计成了异步任务 + 通知。生成式 AI 是第一个把重计算搬到了前台、并且让用户坐在等待区里的产品类别。

这三处错位共同推出一个判断:AI 原生产品的界面,主要工作量不是"呈现答案",而是"暴露并调度一个不确定的、耗时的、随时可以被叫停的过程"。这条线是 AI-native 和 AI-augmented 的分界:后者是给旧界面加一个入口,前者是从"展示结果"重新设计成"陪伴过程"。如果你回头看自己的产品,界面上找不到任何一个位置是留给"过程"的——没有进度语义、没有中间态、没有撤销点——那它就是一个套了模型 API 的搜索框。

三个被长期低估的界面变量

把上面那段抽象判断往下推一层,任何一个 AI 界面设计最终都会落在三个变量上,后面全文也都在这三个变量里打转。

  • 意图澄清的机会成本。澄清不是打断用户的罪证。当模型有 85% 的把握知道自己要什么,界面就该直接做;有 40% 的把握,做一半 + 提供三个方向,往往比反问一次更受欢迎。判据是"猜错的代价"和"反问的代价"哪个更高。
  • 可见的中间态。用户需要看到"系统正在干什么、走到哪一步了、我能做什么",而不仅仅是"我在等"。中间态设计得好,等待时间的主观感受能砍掉一半以上;设计得差,一个 40 秒的操作会被感知成卡死。
  • 可撤销点。AI 系统的错误具有连锁性:一次错误的规划会让后面每一步都错得更离谱。界面必须持续存在"我在这里可以反悔"的锚点,否则用户唯一的策略就是"关掉重来",而重来的成本是全量重算。

这三个变量共同解释了为什么很多 AI 功能"能用但不好用":它们不是模型能力不够,而是这三条都没做。模型再强,用户也无法给一个没有撤销点的界面建立信任。

流式输出:逐字、骨架屏,和第三种选项

流式输出几乎已经是 AI 界面的默认配置了,但绝大多数团队在做流式的时候,只回答了一个问题:怎么让用户更快看到第一个字?真正需要回答的是三个问题:第一个字之前给什么,第一个字之后以什么粒度给,生成的瞬间用户能不能打断。这三个问题的组合决定了流式是"体验提升"还是"体验噪音"。

先明确两个成本。第一是首 token 延迟(TTFT),也就是用户按下回车到出现第一个字的时间,它决定了界面在"空白期"里展示什么。第二是总生成时长,这是用户抱怨的来源,而它几乎不可能靠前端渲染优化解决——你只能靠让用户在这段时间里有事可做、有信息可读、有选择可按来缓解。流式输出的本质是把一段不可见的等待切成许多个可见的片段,让用户的主观等待和实际等待脱钩。

策略一:逐字打字机,以及它的五种失败模式

逐字(character-level / typewriter streaming)是最容易实现的,也是最容易被滥用的。它的正确用法比想象中窄:只有在输出的"单位"本身就是一个字符的时候,比如对话回复、代码补全、终端式的实时生成,逐字才有正当性。一旦输出的单位是段落、章节、方案,逐字就变成了一种表演。

它有五种可辨认的失败模式。

  1. 伪思考。逐字动画给了一种"系统正在努力"的强烈暗示,但它和真实的推理过程没有关系。用户会把打字机动画误读为"模型在思考",于是当模型其实在流式输出一段早就生成好的缓存文本时,信任被透支了。
  2. 复读灾难。逐字动画让模型的一个生成错误被放大成一场公开事故:某个 token 抽错了,屏幕上的错误字符会停留几百毫秒才被覆盖,用户会在心理上永久记住这个错误,即使它从未被输出。
  3. 跳读阻断。人类的阅读速度远高于生成速度。逐字输出下,用户读到一句话结尾时,屏幕底部还在冒新的字,眼睛被强制锁在一个不断移动的终点上,长文本的阅读理解质量明显下降。真正有效的做法是让已经完成的部分停止动画、变成静态可读。
  4. 打断的心理门槛。打字机动画有一种"正在进行中"的不可侵犯感,用户会犹豫要不要打断。即便界面有停止按钮,使用率也低得惊人——这意味着流式生成的机会成本被浪费了,本可以半路取用的中间结果没有被取用。
  5. 无法快速定位。生成一千字要一分钟,用户想找其中一句,只能盯着动画看到那一句出现。段落级提交允许用户在生成过程中就通过 Ctrl+F 或结构大纲跳转,逐字完全做不到。

反过来,逐字真正不可替代的场景有两个:低延迟的短输出(一句话翻译、一个补全、一次快速改写),此时打字机效果本身就是价值;以及需要逐字确认的过程性文本,比如逐字报价、逐字播报。前者是体验加分,后者是语义必需。除此之外,逐字基本是一种默认懒惰——因为它最容易做。

策略二:骨架屏,以及它为什么会撒谎

骨架屏(skeleton)来自传统客户端开发的思路:结构已知、耗时未知时,先把版式搭出来。放进 AI 界面里,它的适用条件比想象的窄得多:只有当输出的宏观结构可以被提前预测时才成立——比如固定五段式报告、固定的 JSON schema、固定字段的表格、固定的章节大纲。

骨架屏的失败模式是结构幻觉。一个写着"正在生成分析报告:1. 数据概述 2. 关键发现 3. 建议"的三条骨架,如果最终模型只输出了两段,或者第三段完全跑偏,这个界面就在撒谎。它承诺了一个不会兑现的结构,而用户会把那个承诺记很久。结构不确定的时候,宁可给一个诚实的模糊占位,也不要给一个精确的假结构。

骨架屏还有一个隐蔽的坏处:它把用户的注意力锁在版式上,而不是内容上。用户会在骨架消失前就开始脑补内容,或者在骨架消失时产生"这就是我要的吗"的落差。更稳的做法是骨架 + 真实内容混合——版式占位的同时,先把已经确定的头部字段(比如标题、来源、时间戳)真实填上,让用户看到有信息在增长。

策略三:语义块流式,2026 年的默认解

第三种做法,也是目前工程上性价比最高的做法:在客户端做流式聚合,按语义边界提交状态。token 到达后先进入缓冲区,遇到段落边界、列表项边界、标题标记、代码块围栏、表格行结束等信号时,把已完成的块一次性提交给状态层并渲染。用户看到的不是一个个字,而是一段段文字以可读的粒度落定。

它同时解决了上面四种失败:不会产生伪思考(块本身就是真实内容)、不会有复读(未完成的部分根本不显示)、不妨碍跳读(已提交的块是稳定 DOM)、打断的心理门槛低(用户看到的是"文档在长大",可以随时说"够了")。代价是实现复杂度上升一档,你需要一套不完整输入下的安全渲染逻辑,因为流式到达时表格可能是半张、代码块可能还没遇到结束围栏、Markdown 可能被截断在半个列表项里。

下面这段是聚合层的骨架,核心是缓冲 + 边界提交 + 尾部增量:

// 语义块流式聚合: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()) };
}

配套的渲染层必须能处理"不完整内容":Markdown 渲染器要容忍未闭合围栏和半个表格,代码高亮器要对半行代码做降级处理。有个朴素但有效的技巧——把最后那个未完成块渲染成半透明或者加一个极短的呼吸光标,视觉上就自然读作"正在写"而不是"显示不全"。

三种流式策略的选择表

下面这张表是实际项目里用的决策依据,横向是输出形态,纵向是三种策略的适配度。注意最后一行:用户中途介入的可能性高不高,比输出有多长更能决定策略。

输出形态逐字骨架屏语义块理由
对话回复、短改写、翻译推荐不适用可用单位本身是字符,逐字即语义
代码补全、单函数生成推荐不适用推荐需要逐字确认与随时打断
固定结构报告、JSON、表格禁用有条件推荐结构可预测时骨架成立,但正文仍需块提交
长文分析、多步规划禁用禁用推荐伪思考与跳读阻断代价最高
需要中途取用部分结果禁用禁用必需块是取用的最小单位

还有一个容易被忽略的工程细节:流式渲染的帧预算。如果每个 token 都触发一次 React 重渲染,一段三千字的回答会做上千次协调,在低端设备上直接掉帧甚至卡住。正确做法是把 token 到达和状态提交解耦——token 进队列,用 requestAnimationFrame 或 30–60ms 的节流把队列里的增量合并成一次状态更新。感知上仍然是流式的,但渲染次数下降一个数量级。这个细节不影响设计决策,但它决定了这个设计能不能真的在生产环境里活着。

部分结果与可撤销点:让用户提前收工

如果语义块流式解决了"怎么看",那么下一个问题是"什么时候可以不等了"。传统软件里,用户在操作中途插手通常意味着取消(cancel);AI 界面里,用户在中途插手应该意味着取用(accept-partial)。这是两种截然不同的交互契约。

取用部分结果的设计要点,比看起来要细。

取用点的粒度

可取用的最小单位必须和内容的最小有意义单位对齐。段落级是默认选择,但对结构化输出应该更细——一份报告里,用户常常只要"第 2 节的表格"而不是第 2 节全文。对代码输出,粒度应该是函数或文件;对表格输出,粒度应该是行块;对检索型输出,粒度是单条结果加它的引用。规则是:可取用粒度 ≤ 用户能复用的最小单位,太粗用户就得为不需要的东西付出等待代价。

早退之后生成端怎么办

用户点下"就用这些"之后,服务端那条还在跑的生成流必须立刻取消,否则会持续烧 token 并且在服务端留下一个无人认领的会话。取消要真取消:断开流、把已生成部分固化落库、把后续未生成部分标记为截断而不是失败。这一点在界面上也要体现——被截断的内容不能显示成"生成中断"这种错误态,因为用户是主动结束的,它应该显示成"已采纳前 N 段"。主动截断和失败截断必须用不同的视觉语言,混为一谈会让用户误以为系统出了故障。

可撤销点:让"反悔"变成一等公民

可撤销点和可编辑点不是一回事。可编辑点允许用户改结果,可撤销点允许用户否定某一步决策。AI 界面里两者都要有,而且撤销点要按错误的"爆炸半径"来放。

具体做法是给生成过程建立一条可回溯的决策链,每一步记录:这一步的意图是什么、依据是什么、产出了什么。界面上给每个决策点一个轻量的入口,让用户能只否定其中一步,然后从那一步重新分叉,而不是从头再来。这套模式在多步 Agent 场景里几乎是从"不可用"变成"可用"的分界线——因为 Agent 的错误是系统性的,用户不能接受全量重来。

分叉的实现有个容易踩的坑:新分支必须继承分叉点之前的所有上下文和产物,而不是只保留改动的部分。很多人第一版实现是从分叉点发起一个新请求,结果模型丢失了之前的全部约定,输出质量断崖式下跌,然后就被判定为"这个功能不行"。正确做法是把分叉点之前的所有消息、工具调用结果、已确认的约束全部回灌进新分支。

一个可撤销点状态机

把上面的想法落成状态机,代码比文字更能说明问题。核心是:每个状态都是一个可持久化的快照,撤销 = 回到某个快照 + 携带一条否定指令。

// 生成任务的显式状态机:撤销点即快照,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 };
  }
}

对应的界面规则有三条。第一,撤销入口必须紧贴产生它的那一步,而不是统一放在顶部工具栏——统一入口会让用户失去"我到底该撤销哪一步"的判断力。第二,撤销要能预览后果:"从这一步重来"后面跟一句轻量的预期描述("将重新检索 3 个来源,不影响已确认的结论"),因为撤销的成本感是用户不撤销的主要原因。第三,撤销历史本身要可见,能回到任意历史状态,否则多次纠错会退化成"我在原地反复刷"。

思考可见性:露多少,取决于用户要做什么

"要不要展示模型的思考过程"这个问题被讨论了太多次,但它其实不是一个是非题。思考过程对用户的价值,来自它承担一项具体的职能:让用户能在结果不对的时候,定位到是哪一步不对。展示多少,应该由这个职能反推,而不是由"坦诚"这种抽象美德决定。

我把可见性分成三档,这三档不是优劣关系,而是对应不同的用户能力。

第 0 档:全黑盒

只给结果,不给任何过程。适用面比想象的大:翻译、正文润色、格式转换、分类打标、已经高度模板化的短任务。这些任务里用户没有能力评估中间步骤,他要的只是一个能用的产物;给他看"我先分析了句子的主谓结构"只会增加噪音和维护一份白纸黑字的负担——用户读到一半必须意识到"我的任务真的需要这么长的解释吗"。

黑盒档的硬性要求是结果可编辑且改起来比重新生成更快。如果用户修改一次的成本高于重跑一次,黑盒就是灾难,因为用户唯一的纠错方式就是反复重试,而重试的方差会把他的时间全部吃掉。搜索时代里用户能靠改关键词收敛,生成时代里用户能靠"手动改成对的"收敛——这是黑盒档唯一的合法性来源。

第 1 档:阶段摘要

不给逐字的推理链,只给结构化的阶段和阶段产出。这是 2026 年绝大多数成熟产品的默认档位。

典型形态是时间线式的阶段列表:理解需求 → 检索了 4 个来源 → 交叉比对 → 生成结论,每个阶段旁边挂一个可点开的摘要和它的关键产出(比如"检索到的 4 个来源"是可点的链接列表)。它解决的是用户最焦虑的一件事——"它现在在干什么、还要多久、我能不能走开"——同时完全不暴露模型的内部表述。

第 1 档的关键设计约束有两条。其一,阶段摘要必须由模型的结构化输出驱动,而不是把自由文本的思考前几百字截断出来给人看——后者会泄漏内部提示、暴露不该暴露的中间假设,而且格式不稳定。其二,阶段要少而稳定。三到五个阶段就够了,阶段名要在同一个产品里保持一致;如果每次运行的阶段数都不一样,用户就永远学不会预期,随之而来的焦虑抵消了展示本身的价值。

第 2 档:可折叠的决策依据

在第 1 档基础上,把每个阶段展开到"为什么这么做"的层面,但依然默认折叠,且不复述内部推理。这是高风险、高成本任务必须提供的一档——代码迁移、数据清洗、法律条款比对、财务分析这类"错了代价很大"的任务。

这一档真正的价值不是满足好奇心,是给用户三个可操作的东西:引用(这条结论是基于哪一段原文)、约束(我按你给的哪几条要求做的)、排除项(我在哪几种可能里排除了另外几种,以及排除的理由)。第三个最被低估。用户发现结果不对时,最想知道的往往不是"你为什么这么写",而是"你有没有考虑过另一种可能"。

把三档摊开对比:

档位展示什么目标任务不该展示什么
第 0 档只有结果翻译、润色、格式转换、分类任何阶段文字——没有评估能力的人看不了
第 1 档3–5 个阶段 + 阶段产出对话式回答、检索问答、常规任务自由文本推理的前缀、内部提示
第 2 档阶段依据、引用、约束、排除项代码、数据、法律、财务等高风险任务逐 token 的内部推理全文

还有一个跨档位的通用原则:可见性应当由不确定性反推,而不是由详细程度决定。模型对某个结论有把握的地方,露一个"已确认"的标记就够了;对没把握的地方,恰恰要把依据和排除项全展开。把注意力预算花在用户真正需要审查的地方,比把推理链一股脑倒出来要有效得多——后者不但读不完,还会训练用户"这段展开是废话",从此以后所有的过程展示都失效了。

不确定性的诚实呈现:置信度不是进度条

不确定性是模型与数据库之间最本质的区别,也是最容易被界面糟蹋的东西。糟糕的设计里有两种典型做法,一种是把不确定性藏起来,另一种是把不确定性伪装成确定性。

藏起来的做法是:只输出一个结论,不给任何标记,也不给依据。用户接受了这个结论,但事实上他不知道自己接受了一个概率判断,于是整个信任链条是断裂的——一次明显的错误会让用户觉得被欺骗,而且他会归因到"这东西不可靠",而不是"这条没查到"。

伪装的做法更常见,也更值得批评。用进度条、转圈、或者"正在思考"的百分比来表达模型的不确定性,是 2026 年产品里最普遍的一个错误。原因是它太容易做了:进度条组件现成,动画组件现成。但它表达的是完全错误的语义——进度条说的是"还要多久结束",而模型的不确定性说的是"出来的东西可不可信"。这两件事没有映射关系。

更糟的情况出现在自检与反思环节:模型在内部对答案做了一次质量评估,得到一个"置信度 82%",产品把它渲染成一条 82% 填充的进度条。这个数字既不是概率,也没有经过校准,它唯一的作用是让界面看起来很严谨。

三种可用的不确定性表达

真正能用的表达方式只有三种,而且都不需要精确数字。

第一,声明式标记。在结论旁边放一个轻量标签:"高把握"、"存在分歧"、"低把握"。关键是标签的生成必须来自可检查的信号——比如多个独立来源是否一致、检索到的证据条数、生成时是否触发了降级路径、工具调用是否失败重试过。如果拿不到这类信号,那就不要贴标签,宁可不给。一个来源都没有的回答和有五个来源互相印证的回答,差别是客观存在的,界面只需要把这个差别说出来,不需要发明一个数字。

第二,来源可见化。这是性价比最高的一种诚实。给每条关键结论挂上可点击的来源,让用户能自己复核。这不只是透明度,它还把"信任"的成本转移给了用户——愿意核的用户自己核,不愿意核的用户至少知道有得核。同时它是抗幻觉最有效的界面手段:没有来源的句子和高亮出处的句子,用户一眼就能分辨出可信度差异。

第三,路径分流。与其标注"这个答案可能不准",不如直接提供不同强度的动作:直接采纳、要求补充依据、换一条路重来、交给人来处理。不确定性如果只能被"标注",用户就只能"接受或者不接受";不确定性如果能变成用户可选的确认强度,它就从一句免责声明变成了一次真正的控制。

这条原则可以推广:凡是不能变成用户动作的元信息,都应该压缩掉。页面上那个"置信度:0.87"的灰色标签,用户几乎永远不会看;但同一份信息做成一个"补充依据"的按钮,点击率会高一个数量级。

拒答按钮是被严重低估的设计

大多数团队在训练模型"该拒答时拒答",但在界面上没有给用户任何直接表达怀疑的入口。用户看完一段洋洋洒洒的回答,半信半疑,但除了重新提问没有别的办法——而重新提问是成本最高、最笨的一种表达怀疑的方式。

正确的做法是在结论区域常驻一个低成本的怀疑入口,比如"这段不太对",点击后弹出几个最可能的偏差方向("事实不对""方向偏了""漏了重点")。这个交互的价值在于:它把一次模糊的不信任,转化成了一次结构化的反馈。反馈进来之后,它既可以用于当次重试,也可以进入数据闭环,成为后续优化和评测的输入。这是把用户从"被动的结果接收方"变成"协作的标注者"的关键一步,而很多产品的用户反馈体系恰恰缺了这一环——用户点了个踩,什么也没发生。

要不要显示"我搜过但没找到"

这是一个边界情况,但处理得好很加分。模型检索不到证据时,有三种处理:编一个像样的答案、明确说没找到、说没找到并给出建议的替代方向。第三种明显最好,但它要求产品承认一件事:没有找到本身就是一种有价值的结果。很多产品的商业指标是"回答生成成功率",把"没找到"当成失败来优化,等于用指标逼系统撒谎。设计上的解法是给"没找到"一个独立的、成功的呈现形态:它应该长得像一个正经的交付物(说明尝试过的方向、失败的原因、可行的替代),而不是像一条报错。

长耗时任务:中断、后台化与恢复

当一个操作要跑三十秒以上,界面的问题就从"怎么展示"变成了"用户的生活怎么安排"。人在等一个长任务时会去干别的事,回来发现它失败了——这是 AI 产品当前最集中的差评来源,远超模型答错本身。

把长任务分成三类

不同类型的长任务需要完全不同的界面策略,先分类再设计。

  • 可打断型(3 秒 – 30 秒):一次检索增强、一段长文生成。前台全屏等待 + 随时可停 + 已生成部分可取用。这是默认档,绝大多数任务属于这里。
  • 可后台型(30 秒 – 10 分钟):多步 Agent、批量处理、代码迁移。前台只保留一个任务条,生成继续在后台跑,用户可以去别的页面。这要求后端有真正的任务队列,而不是一个被页面销毁的 HTTP 请求。
  • 可交付型(10 分钟以上):大规模数据分析、全库重构。界面应该做的是"下订单"而不是"等结果"——用户提交需求、设置交付条件(完成后通知我 / 生成后放在这里 / 失败时告诉我),然后离开。

第三类的界面范式值得单独强调,因为它是最近才被真正重视起来的一类。它的核心不是等待,而是委托:用户不再是一个等待者,而是一个下单者。这会连带改变一整套设计——需要交付确认、需要失败告警、需要产出物管理、需要"这个任务用了我多少资源"的可解释性。

中断的三种语义

"停止"这个按钮在不同阶段含义完全不同,界面必须区分,否则用户不敢按。

  1. 停止生成:停止后续 token,保留已有内容。损失最小,是默认语义。
  2. 取消任务:终止整个任务,包括已经产生的中间产物和副作用(写过的文件、调过的接口)。这里必须有一次确认,因为可能已经产生了外部副作用——尤其当任务是"修改代码""发送邮件""写入数据库"这类有副作用的操作时,确认文案要明确列出"已经做了什么"。
  3. 暂停/后台化:不终止,只是把任务挪到后台继续跑。这是最被忽略的一个,但它往往才是用户真正想要的——他想继续用这个应用做别的事,而不是放弃任务。

把"暂停并后台化"做成一个一等操作,收益远高于它的开发成本。它同时降低了长任务的感知成本和流失率,因为用户不再需要在"干等着"和"放弃"之间二选一。

恢复:任务必须是一个可以重放的会话

用户离开再回来,界面要能把任务接上。要接上,就得让任务本身可持久化——这就是很多实现临时搭的 HTTP 流留下的债:连接一断,生成的前半截就永远丢了,用户回来只看到一片空白。

可恢复的最小要求有四条:第一步,生成内容增量落库,不要只留在内存缓冲区里,用户回来能读到已有的一半内容;第二步,阶段状态可见,回来时能立刻知道"它做到了第 3 步,还是刚重启";第三步,失败可重试且不重复副作用,重试从最后一个成功的检查点继续,而不是从头再调一遍所有工具;第四步,跨设备可继续——这条经常被漏掉,但它几乎不需要额外设计,只是要求任务状态存在服务端而不是会话里。

最后补一条容易被忽略但很有效的恢复设计:用户回来时的第一屏不要是空状态。直接把恢复好的内容、阶段、以及"接着看 / 重新生成 / 换个方向"三个动作摆出来,比显示"任务已结束,点击查看"要多走三次点击。用户离开得越久,回来时的动机越弱,第一屏的信息密度就应该越高。

等待期间的承诺

长任务里有一个低成本高收益的细节:先给中间产物,再给最终产物。让任务在早期阶段就把已经确定的中间结果(检索到的来源清单、已经确认的约束、生成到一半的草稿)推给用户,即使用户不在看。它有两个作用:一是把"完全没产出"的焦虑提前消灭;二是给用户一个提前叫停的理由和机会——看到第三步的方向不对,他可以立刻打断,而不是等三分钟后拿到一个错误方向的结果。

AI 界面里最常见的六个反模式

下面六条是产品里反复出现的、可以被明确指认的错误做法。它们没有一条是技术做不到,每一条背后都有一个"当时觉得比较快"的合理动机,这也正是它们顽固的原因。

反模式一:把置信度画成进度条

症状:回答下方有一条 78% 的填充动画,或者"思考中 82%",或者一个随生成时间线性增长的圆环。用户默认它在表达"答案有多可靠"。

为什么失败:它把两个正交的量绑在一起了。等 10 秒和等 10 秒的答案质量没有关系;一个进度到 90% 才崩掉的流程,和一个进度 20% 就崩掉的流程,语义上也不该长得不一样。更严重的是它训练出了用户的错误直觉:进度条满 = 结果可用。用户会因此在真正拿到低质量答案时完全没有准备。

改成:进度只表达时间,且必须可预估;不确定性用来源、依据、以及可选的动作来表达,不占用任何进度语义。这两者在视觉上必须分开,不要放在同一个组件里。

反模式二:强制开场澄清

症状:用户输入"帮我看下这段代码",界面立刻弹出一个表单,要求选择编程语言、期望的目标、修改范围、是否需要注释。

为什么失败:它把澄清的收益错算在了模型身上。多数情况下模型能自己推断出语言和意图,用户填的那些选项里有一大半和最终答案无关。用户的真实心理是"你先给我看看结果,不对再改",而这个表单强迫他先做一遍产品经理的工作。

改成:默认值要押在"最可能的那个"上,界面直接出结果,并且把可调项折叠成一行轻量的"按需调整"。只有在不同选择会导致完全不同的产出(比如生成代码还是解释代码)时,澄清才值得前置。可以先按最可能的选项生成,同时在结果旁标注"我先按 A 做了,切换到 B 大概需要 X 秒"——这比阻塞式提问好得多,因为它把澄清从成本变成了选项。

反模式三:把整段推理链当解释

症状:一个可展开的"思考过程"折叠区,里面是模型的原始推理文本,长度上千字,没有结构,也没有和结论的对应关系。

为什么失败:三个问题。一是不可读——用户没有义务做质量评估,把自己的角色升级成模型的同行评审人是极其糟糕的设计;二是不可操作——看完之后用户依然不知道该点哪里去纠正;三是不可核查——推理文本说的是"我如何思考",用户需要的是"我依据什么"。用户会快速学会忽略这个折叠区,于是你把唯一能让产品显得透明的资产给废掉了。

改成:换成结构化的依据链——每条关键结论配一个可点的来源、列出本次生效的所有约束、明确指出哪些可能的替代方向被排除了。用户不需要你解释你怎么想的,他需要知道你凭什么这么说,以及你没想到什么。

反模式四:只有"重新生成"

症状:输出不满意时,界面上唯一的动作是一个刷新图标。有的产品甚至把上次结果直接替换掉,连对比的机会都不给。

为什么失败:重新生成是一个方差极高的操作。用户按下去,等于交出控制权,然后接受一次抽签。如果结果又不对,唯一的出路还是再按一次——这是一个没有任何累积的随机游走,所有的时间成本都由用户承担。而且替换式地丢掉旧结果,等于系统主动销毁了用户唯一的对照基准,让"这次是不是比上次好"这个问题永远无法回答。

改成:至少要提供三件事:保留并对比(旧版本保留,可以并排或者来回切换)、定向修改("第三段太长了""换个更正式的语气"这种带约束的重试,而不是从零重抽)、局部重做(只重做某一段或某一步)。这三件事本质上都是把方差从"用户承担"转移到"系统收敛"。

反模式五:把长任务做成前台阻塞

症状:点击一个操作后,页面进入全屏 loading,用户能做的只有等待,关掉页面任务就丢了。

为什么失败:它同时违反了三条用户预期:进度应该是可预估的、我的其他工作不该被阻塞、我关掉再回来它应该还在。用户被迫在整个过程中保持注意力集中,而这几十秒里他其实什么也做不了——这是纯粹的时间浪费。

改成:超过三秒的操作进入"前台可见、后台运行"模型:内容区继续渲染已生成部分,侧边或顶部挂一个可折叠的任务条,离开页面任务继续,回来时直接看到接上的状态。超过三十秒再加一层:让用户可以选择"后台跑完通知我"。

反模式六:把对话历史当成唯一的导航

症状:一个越来越长的对话流,用户要找到十分钟前那份生成好的文档,只能往上翻。新内容全部追加在底部。

为什么失败:它把对话流当成了文件系统,但对话流的时间顺序和用户关心的信息组织顺序完全不是一回事。更糟的是可取用性和可撤销点的缺失——生成了一半的好东西被埋在一屏聊天记录中间,用户没有"采纳"这个动作,于是那些内容永远只是历史的一部分,不会变成用户的资产。

改成:对话是过程的容器,不是产出的容器。产出物应该被提取到独立的产出区(工作区、文档库、结果列表),带标题、可编辑、可导出、可再次作为输入。判据很简单:用户明天回来还会用到的东西,必须离开对话流。做不到这一点,AI 产品就只是一个很啰嗦的搜索框。


两种不确定性界面的对照

下面这个原型把反模式一和正确的做法放在一起。左边是常见的假做法——用一个会动的条来暗示可靠度;右边是把不确定性翻译成用户可点击的动作和可核查的依据。

<!-- 反面:进度条伪装置信度,语义完全错位 -->
<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>

把前面这些收成一张检查表

设计评审时按这张表逐条过,比读一百页设计准则有效。左边一列是问题,右边是判断依据。凡是答不上来的,就是还没做完的地方。

检查项判断依据
用户能半路取用结果吗生成到一半时,是否存在一个非阻塞的"就用这些"动作
取用粒度对齐语义吗可取用的最小单位,是否不大于用户复用的最小单位
步骤可撤销吗每个阶段旁边是否有撤销入口,且撤销后能只重做该步
进度语义干净吗进度组件里是否混入了任何表达"可靠度"的信息
时间可预估吗用户是否知道还要等多久、什么时候能走开
中断语义分级吗停止生成、取消任务、后台化是否为三个不同的动作
离开页面能恢复吗关掉再打开,已生成内容、阶段状态、后续动作是否都还在
过程展示是分级的吗是否按任务风险选档,而不是一律全量展示
不确定性可操作吗用户能否用一次点击表达怀疑,并触发结构化反馈
产出脱离对话流吗用户明天要用的东西,是否已经进入独立的产出区

会替你动手的 AI:副作用可见与授权边界

前面所有讨论都默认模型只产出文本。但 2026 年的产品形态里,模型越来越多地被授予能力——读文件、调接口、写数据库、发消息、提交代码。这些动作有副作用,它们不可撤销、不幂等、会影响到别人。界面的任务从此多了一项:让副作用可见、可控、可追认。

这条路走的第一个弯路是把工具调用做成黑箱——界面上就是一句话"正在处理中",内部该读什么读什么、该写什么写什么。这在 demo 里非常顺,在生产里非常危险,因为用户完全无法在关键动作发生前介入。第二个弯路是走向另一个极端,把每一次调用都弹窗确认。结果比黑箱更糟:确认疲劳。用户在第三个弹窗就开始无脑点"允许",此后所有的授权提示都失效了,而真实风险恰恰出现在那之后。

授权的三级粒度

可行的做法是按可逆性和影响半径分级授权,让大多数常规动作不需要打断用户,只在越过某条线时才停下来。

  • 只读、低影响:读单个文件、查一个接口、检索一次资料。默认静默执行,但事后必须留下可查的记录("刚才读了哪 3 个文件")。不弹窗,但可审计。
  • 可逆的写:新建草稿、写临时目录、在本地保存改动。可以在进入时一次性给出范围授权("本次允许修改 src/ 下的文件"),而不是每次都问。关键是这个授权有范围、有期限、会过期。
  • 不可逆或有外部影响:删除、发消息给别人、付款、合并代码、修改共享数据。必须逐步确认,并且确认文案里必须写清三件事——具体动作、对象是谁或是什么、以及撤销方式。

分级判断的标准不是重要性,而是出错的代价由谁承担。自己本地文件写错了,用户承担且可恢复,可以放宽;给同事发了一封措辞不当的邮件,代价由别人承担,必须拦。这一条判据比任何基于模型自我评估的"这个操作看起来安全吗"都可靠得多。

授权应该是一个可管理的东西

把授权做成对话里的一次性"是",等于每次都重新谈判。成熟的做法是把它做成一个用户可见、可修改、可回收的设置集合:本次会话允许什么、这个项目默认允许什么、永久允许的列表在哪里。

下面这段实现的是"范围授权 + 步骤检查点"的组合,它比逐次弹窗少打断,又比一次性放行多了刹车:

// 授权判定:先看范围授权,再决定要不要打断用户
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);
}

配套的界面规则有三条。取消按钮的默认位置应该在"外部影响"那一侧,不应该为了防误触把"确定"设成默认焦点——这类界面的失败成本不对称,多一次点击换一次确认是划算的。第二,宽授权必须带可见的剩余额度,比如"本次任务还剩 3 次写操作",用户才能判断自己是否需要收紧。第三,允许和撤销的历史要能被用户事后看到并一键回滚:"它改了我的东西"必须有一个明确的入口去找。

这条线索其实贯穿全文。可撤销点、阶段可见、不确定性可操作、授权可回收,它们底层是同一件事——把系统的自主性从不可见的黑箱,换成一串用户可以握住的手柄。界面上手柄越多,模型能力就可以放得越开;手柄越少,你就只能把模型关在搜索框里。

结语:界面设计的是过程,不是答案

把全文收成一句话:大模型界面设计的核心对象,是一个会出错、会耗时、可以被叫停的过程,而界面要做的是把这个过程变得可见、可控、可撤销。

这个转向带来的不只是新增几个组件,而是一整套新的分工。过去界面设计师负责的是排版和信息密度,现在多了一份工作:调度一个自己不可控的计算过程。排版的经验仍然有用,但不够用了——因为你面对的用户不再是查询者,而是委托人。

最后留一个具体的起点建议。如果只能改一处,就改"等待"这件事:让流式输出按语义块提交,让用户能在任何时刻取用已生成的部分,让未完成的部分有明确的中断和恢复路径。这三件事合起来,能把一个"卡住的 AI 功能"变成一个"可以放心使用的工作台",而且它们之间互不依赖,可以按任意顺序分批做。

其余那些——思考可见性的分档、不确定性的表达、产出物的管理——都可以在第二阶段再考虑。但第一阶段的这一步不做,后面全都无从谈起。