装闭 RenoPit 源码解析(06):SSE如何实时推送AI装修分析进度

Celery 让 AI 装修分析脱离 HTTP 请求执行,但浏览器仍然需要知道任务是否完成。装闭 RenoPit 的开源代码 fthux/RenoPit 使用 Server-Sent Events(SSE)连接项目页和后台状态,本篇分析这条进度链路。
一、为什么使用 SSE
项目详情页只需要接收服务器消息,不需要通过同一连接向服务器发送数据,因此使用浏览器原生 EventSource 即可。相比不断发起普通请求,SSE 能保持一个 text/event-stream 响应,并按事件名称推送进度、完成、失败和停止状态。
完整交互过程如下:
二、前端如何建立 EventSource
ProjectPage.tsx 的 startSSE() 先设置本地分析状态,再关闭可能存在的旧连接:
eventSourceRef.current?.close()
const es = new EventSource(
`${API}/projects/${projectId}/analyze/stream`
)
eventSourceRef.current = es
页面监听四类事件:
progress:更新百分比和提示文字;completed:进度设为 100%,刷新项目并跳转报告页;failed:显示后端错误信息并结束分析状态;stopped:恢复项目状态并提示分析已停止。
如果用户刷新了正在分析的项目页,初始化逻辑会读取 project.status。只要状态仍为 analyzing,页面就会重新建立 EventSource,而不是依赖刷新前的 React 状态。
组件卸载时会关闭局部连接和 eventSourceRef,避免离开页面后继续接收事件。
三、SSE 接口为什么轮询数据库
后端路由是 GET /api/projects/{id}/analyze/stream。项目中虽然存在 SSEManager 内存订阅管理器,但当前主接口明确使用数据库轮询:FastAPI 和 Celery Worker 是两个独立进程,Worker 内存中的消息队列无法直接被 Web 进程读取,而数据库状态可以跨进程共享。
event_generator() 为每个连接创建独立 SessionLocal,确认项目存在后先发送连接消息:
event: progress
data: {"progress": 5, "message": "正在连接 AI 服务..."}
随后每两秒执行一次 sse_db.refresh(project),读取 Celery 写入的最新状态。
四、进度百分比从哪里来
Celery 当前写入的是业务状态,并没有为每一个 LLM 步骤记录精确百分比。因此 SSE 接口在状态仍为 analyzing 时维护 simulated_progress,从 10% 开始,每轮增加 2%,最高推进到 85%。
不同区间对应不同提示,例如加载设计图、检查卫生死角、评估空间布局、排查隐性成本和生成报告。它们负责表达任务仍在运行,不参与 Celery 的实际控制。
当数据库状态变成 completed 时,接口先发送 95% 的“正在保存结果”,再发送 100% 的 completed。状态为 failed 时,则查询最近一条 Analysis,把 error_message 放入事件数据。
五、超时、停止和连接头
轮询最多执行 150 次,每次间隔两秒,也就是五分钟。超过次数后发送 failed 事件和“分析超时,请重试”。如果项目状态回到 pending,接口发送 stopped。
返回值使用 StreamingResponse,并设置:
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no",
}
生产环境的 Nginx 同时设置 proxy_buffering off、长读取超时和分块传输,确保事件生成后立即到达浏览器,而不是被代理缓存成完整响应。
六、完成事件如何驱动页面跳转
前端收到 completed 后关闭连接、刷新项目状态,并显示完成 Toast。随后通过定时器跳转到 /project/{id}/analysis。报告页再请求 /result 获取结构化结果,因此 SSE 事件不需要携带整份分析 JSON。
失败事件同理:它只携带错误信息,项目详情页仍通过普通 API 重新读取项目。实时通道负责通知,REST API 负责获取持久化数据,两者分工清晰。
七、SSE 调用链小结
RenoPit 的进度机制可以概括为:Celery 更新 PostgreSQL,FastAPI 的 SSE 生成器轮询 PostgreSQL,React 的 EventSource 消费事件。这样即使刷新页面或 Web 与 Worker 分属不同进程,任务状态仍能重新连接。
相关前后端实现都可以在 fthux/RenoPit 中对照阅读。下一篇将进入 AI 分析内部,先看装修闭坑知识库如何被加载并组装成系统 Prompt。
后台 Celery 正在分析装修图纸,React 页面如何实时显示进度?从 EventSource、StreamingResponse 到数据库状态轮询,拆解 RenoPit 的 SSE 事件、进度模拟、失败提示和自动跳转机制。
浙公网安备 33010602011771号