主题
React + AI 应用开发
AI 生成如何实现停止、取消、重试和断线恢复?
停止按钮只是前端动作,真正可靠的实现还要处理服务端任务、已产生内容和重复提交。
面试官想考什么
- 点击停止后模型真的停止了吗? 考察客户端与服务端边界。
- 网络重试会不会产生两条答案? 考察幂等设计。
- 断线恢复从哪里继续? 考察 cursor、重放和最终一致。
- 哪些错误应该自动重试? 考察错误分类和成本控制。
一句话回答
text
取消要同时 abort 客户端读取和取消服务端任务,重试必须携带幂等键,断线恢复依赖服务端持久化的事件游标或最终消息;只对瞬时错误做有限退避,不能把业务拒绝和模型超时无限重试。面试回答详解
1. 生命周期状态机
text
idle -> queued -> running -> completed
|-> cancelled
|-> failed
|-> reconnecting -> running前端保存 requestId、conversationId、messageId、lastCursor 和 abortController。这些字段不能只存在组件局部变量里,否则路由切换或恢复时丢失上下文。
2. 取消链路
- 用户点击停止,立即停止继续渲染并调用
controller.abort()。 - 若服务端任务仍在运行,发送带 requestId 的 cancel 命令。
- 服务端把上游模型请求绑定到取消信号,尽力释放连接和计费资源。
- 已收到的内容保留为 cancelled 消息,不把它伪装成 completed。
取消是尽力而为,不应向用户承诺已经撤回模型侧所有计算。
3. 重试策略
- DNS、网络断开、网关 502/503、明确的速率限制通常可有限重试。
- 参数错误、权限拒绝、内容安全拒绝不应自动重试。
- 超时重试前先查询 requestId 的服务端状态,避免原任务其实已经完成。
- 指数退避加随机抖动,并设置总预算、最大次数和用户可见反馈。
4. 断线恢复
服务端应保存事件或可查询的消息快照。客户端重连时带上 Last-Event-ID 或业务 cursor,服务端重放缺失事件;若不支持重放,则查询最终消息并用 revision 覆盖本地 draft。恢复期间禁止用户再次提交同一个请求。
5. 验证与观测
测试断网、切后台、浏览器刷新、重复点击、服务端完成后客户端断线、取消竞态和 token 超时。记录 cancel_requested、cancel_confirmed、reconnect_count、resume_gap、retry_reason 和最终状态。
可直接背诵的 30 秒回答
text
我会把生成任务建模成有 requestId 的状态机。停止时前端立即 abort reader,同时向服务端发送取消,让服务端继续取消上游模型;重试必须先查询原任务状态并使用幂等键。断线恢复用事件 id 或 cursor 重放,不能简单重新调用模型。错误要分类,只对瞬时错误做有限退避,并记录取消、重连和恢复缺口。扩展知识
客户端取消和服务端取消的差异
- 客户端取消:停止等待和 UI 更新。
- 服务端取消:停止上游调用、释放资源、写入审计状态。
- 两者之间存在网络延迟,所以状态必须允许
cancelling。
面试官追问链
追问一:AbortController 能取消服务端模型调用吗?
- 考察点:边界意识。
- 回答方向:它只能取消浏览器侧 fetch;服务端必须接收取消意图并把 signal 传给上游,且要处理取消竞态。
追问二:重试是否应该复用 messageId?
- 考察点:数据建模。
- 回答方向:用户语义相同但每次执行应有新的 attemptId;原 messageId 可关联多个尝试,最终只选择一个有效版本。
追问三:流已经完成但 done 事件没到怎么办?
- 考察点:异常结束处理。
- 回答方向:服务端落库是权威状态,客户端可查询任务或消息快照,用 revision 和 checksum 判断是否完整。