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

Celery 让 AI 装修分析脱离 HTTP 请求执行,但浏览器仍然需要知道任务是否完成。装闭 RenoPit 的开源代码 fthux/RenoPit 使用 Server-Sent Events(SSE)连接项目页和后台状态,本篇分析这条进度链路。

一、为什么使用 SSE

项目详情页只需要接收服务器消息,不需要通过同一连接向服务器发送数据,因此使用浏览器原生 EventSource 即可。相比不断发起普通请求,SSE 能保持一个 text/event-stream 响应,并按事件名称推送进度、完成、失败和停止状态。

完整交互过程如下:
sequenceDiagram participant UI as ProjectPage participant API as FastAPI participant DB as PostgreSQL participant Worker as Celery Worker UI->>API: POST /analyze API->>DB: status = analyzing API-->>UI: 任务已启动 UI->>API: GET /analyze/stream loop 每 2 秒 API->>DB: refresh Project.status API-->>UI: progress 事件 end Worker->>DB: status = completed/failed API-->>UI: completed/failed 事件

二、前端如何建立 EventSource

ProjectPage.tsxstartSSE() 先设置本地分析状态,再关闭可能存在的旧连接:

tsx 复制代码
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,确认项目存在后先发送连接消息:

text 复制代码
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,并设置:

python 复制代码
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。

相关推荐
AI备案指南-满满1 小时前
大模型与算法备案全流程详解:从零到通过的完整指南
人工智能·算法·备案·大模型备案·算法备案
江畔柳前堤1 小时前
LLM 训练核心机制深度解析:Warmup、Cosine Decay 与 Perplexity 的完整知识体系
网络·人工智能·深度学习·算法·机器学习·语音识别
龙虾PRO1 小时前
把握人工智能时代机遇,夯实科技强国建设根基
人工智能·科技·百度
冬奇Lab2 小时前
企业知识库系列(00):在写第一行代码之前,先把评测数据集做好
人工智能
二川bro2 小时前
Obsidian+AI自生长知识库,告别无效笔记整理
人工智能
冬奇Lab2 小时前
开源项目第184期:MiroFish — 盛大出品的群体智能预测引擎,用数千 AI Agent 模拟社会演化来预测未来
人工智能·开源·资讯
科研小刘带你玩学术3 小时前
【IEEE论文解析】深度强化学习如何优化未来智能无线通信系统?
人工智能·边缘计算·无线通信·6g·深度强化学习·智能系统·ieee论文
LyridRelan3 小时前
Skill Cli MCP 的三者关系以及部分Q&A
人工智能·ai·个人开发