Agent 长任务页
多步 Agent 任务流的过程可视化:SSE 推送步骤事件,前端渲染 pending/running/done 时间线;串行 vs 并行墙钟对照,取消即省计费
长任务演练(可运行)
GET /api/ai/agent 推送步骤事件流;切换「串行/并行」与「跳过校验」再跑一轮,对比墙钟
- 规划任务等待
- 检索(RAG)等待
- 工具 · 搜索文档等待
- 工具 · 计算指标等待
- 工具 · 查询数据库等待
- 结果校验等待
- 汇总输出等待
对照提示:并行把工具阶段墙钟从 ~2.4s 压到 ~0.8s,跳过校验再省 ~0.4s —— 但跳过的代价是产出未经核对,「快但错」在 Agent 场景通常比「慢但对」更难补救
Agent 页的 UX 核心
长任务体验的两个支柱:过程可见、可取消
- 过程可见:多步任务动辄几十秒,只转一个 loading 圈等于黑盒; 步骤时间线把「现在在干什么」摊开,等待从焦虑变成可读信息, 出错时也能定位到具体步骤
- 可取消:长任务 + 计费场景,取消按钮是成本控制不是 UX 礼貌; 客户端 AbortController 断开后,服务端要感知 cancel 并停掉后续步骤与上游调用
- 指标口径:TTFT(首 token)管「什么时候开始有动静」, TTFUI(首个可用 UI)管「什么时候能读能点」,墙钟管「总共等了多久」; 这套口径的系统梳理见 react-playground 练习场(port 3002) 性能分类下的 AI-Native 专题,本页只落地「墙钟对照」这一项
生产里不手写这个:AI SDK 的 data parts
手写版的价值是理解协议;生产里步骤事件交给 createUIMessageStream 的 transient data parts
// Route Handler 侧(示意,ai@7;可运行版见 /ai-native/ai-sdk 专题):
import {
createUIMessageStream,
createUIMessageStreamResponse,
streamText,
toUIMessageStream,
} from "ai";
export async function POST() {
return createUIMessageStreamResponse({
stream: createUIMessageStream({
execute: async ({ writer }) => {
// transient data part:推到前端但不进消息历史,
// 正是本页「步骤时间线」事件的官方载体
writer.write({
type: "data-step",
data: { step: "retrieve", status: "running" },
transient: true,
});
const result = streamText({ model, prompt });
writer.merge(toUIMessageStream({ stream: result.stream }));
},
}),
});
}
// Client 侧(示意):useChat 的 onData 回调逐条收到 data-step,
// 按 step/status 渲染时间线,替代手写的帧解析
useChat({ transport, onData: (part) => renderStep(part) });手写仍合理的场景:教学(像本页这样把帧格式摊开)、协议定制(事件语义与 AI SDK 的消息模型不匹配)、零依赖场景(不想为一次演示引入 SDK)。 其余情况让 SDK 管帧边界、取消传播与 provider 差异,业务代码只管「发什么步骤」; 对照本站的 SDK 落地版见手写 SSE vs Vercel AI SDK专题。
什么时候别用步骤时间线
时间线是长任务的解药,也是短任务的噪音
- 单轮问答:一轮请求-响应没有「步骤」可言, 套时间线是硬造过程感 —— 用流式文本(见流式端点专题)即可
- 步骤粒度太细:内部重试、子调用全摊开会刷屏, 用户只关心「可解释的里程碑」;粒度以「失败时用户能理解卡在哪」为准
- 步骤不可预知的探索型任务:步骤数与顺序由模型现场决定时, 预置时间线对不上;改用「事件流日志」形态(追加式)而不是固定骨架