Agent 前端如何展示过程:SSE、JSONL、文件树、代码预览与安全状态
Agent 生成项目时,如果前端只显示一个 loading,用户无法判断系统是在思考、写文件、构建、修复,还是已经卡死。更好的体验是把生成过程拆成事件流,让前端实时展示状态和产物。
API 形态
推荐接口是任务式的:
POST /generate/jobs
GET /generate/jobs/{job_id}
GET /generate/jobs/{job_id}/stream
GET /generate/jobs/{job_id}/result
DELETE /generate/jobs/{job_id}
POST 创建任务,返回 job_id。前端用 EventSource 订阅 SSE。结果完成后再通过 result 接口获取最终文件集。
这种形态比同步 /generate 更容易处理长任务、断线重连和前端状态恢复。
事件协议
事件应该是稳定 JSON,而不是把模型的自然语言输出直接转给前端。
关键事件包括:
job.created:任务创建。thinking.status:安全的阶段摘要,例如“正在规划项目结构”。project.tree:当前文件树。file.snapshot:某个文件的完整快照。preview.code:用于展示的少量代码行。job.status:queued、running、succeeded、failed。job.result:最终文件结果。
这个协议可以同时满足三个需求:展示文件结构、展示已有文件、展示当前阶段。
不暴露原始 reasoning
前端需要“过程感”,但不需要模型的原始推理。thinking.status 应该来自后端阶段摘要,而不是原样转发模型内部 reasoning。
可展示的状态是:
- 分析需求中
- 规划项目结构中
- 正在编写 package.json
- 正在修复启动脚本
- 正在校验最终结果
这样足够让用户理解进度,也不会把不该暴露的内容放到页面上。
后端桥接
一个实用实现是:生成器输出 JSONL 到 stdout,FastAPI 读取后转成 SSE。Bun 或 Node 侧负责在文件变化、阶段变化、结束时输出结构化事件;FastAPI 负责保存任务状态并向前端推流。
同步 /generate 可以保留,流式任务作为增强路径存在。这样不会破坏已有调用方。
这套设计的核心是:前端看到的不是模型文本,而是后端认可的任务事实。
知识补全:SSE、WebSocket 和轮询怎么选
前端展示 Agent 过程时,常见选择有三种:轮询、SSE、WebSocket。
轮询最简单,前端每隔一段时间请求一次任务状态。它适合状态变化少、实时性要求低的任务。缺点是延迟和请求浪费都明显,生成代码这种高频变化场景会显得迟钝。
WebSocket 是双向通道,适合多人协作、在线编辑、终端交互等双方都频繁发送消息的场景。但它会增加连接管理和重连复杂度。
SSE 是 HTTP 上的单向事件流,浏览器原生 EventSource 支持重连。Agent 生成过程通常是“后端不断推状态,前端只负责展示”,所以 SSE 是更轻的选择。
JSONL 则解决生成器和 FastAPI 之间的协议问题。每一行都是一个独立 JSON 事件,后端可以边读边转发,不必等整个任务结束。
学习检查清单
一个好的 Agent UX 不只是“有流式输出”,还应满足:
- 用户能看到当前阶段,而不是只看到 token 流。
- 文件树和文件内容来自真实写盘结果,而不是模型口头描述。
- 错误状态能显示在哪个 step 失败。
- 断线重连后能恢复当前快照。
- 不展示原始 reasoning,只展示安全阶段摘要。
- 最终结果和过程事件来自同一个任务状态源。
这样前端才不是给模型输出加动画,而是在展示系统真实进度。