用户消息队列
当 Agent 正在处理回复时,用户可以继续输入并排队发送消息,无需等待。行为规则
| 场景 | 行为 |
|---|---|
| Agent 处理中 + 用户发送 | 消息加入队列(不立即发送) |
| Agent 正常完成 | 100 ms 后自动发送队列中的下一条 |
| Agent 被中断(Stop) | 队列不自动消费,由用户决定发送哪条 |
| 用户点击队列中的立即发送(↑) | 停止当前生成(如有),然后发送该消息 |
队列 UI
当有排队消息时,队列面板显示在聊天输入框上方:- 编辑(铅笔图标)— 行内编辑排队消息内容
- 立即发送(↑ 箭头图标)— 停止当前生成并立即发送此消息
- 移除(垃圾桶图标)— 从队列中删除
截断上下文
当回复被中途停止时,AI 的部分内容会保留在对话历史中,作为后续消息的上下文。同时持久化到数据库,刷新页面后不丢失。对话与文件回退
用户可以回退到任意一轮对话,类似 Cursor 的分支模型。对话回退
- 鼠标悬停在用户消息上 → 点击回退按钮(↺)
- 回退点之后的消息变灰(40% 透明度),不会删除
- 出现分叉分隔线,带有取消回退选项
- 新消息添加在分隔线下方
- 发送给后端的聊天历史只包含回退点之前的消息
文件回退
当用户回退时,回退点之后被修改的沙盒文件会恢复到之前的状态:preRollbackFiles 恢复最新文件版本。
刷新后文件恢复
沙盒文件从数据库事件中恢复,而非仅依赖 E2B 沙盒存活状态,因此即使沙盒过期文件也不会丢失。 文件内容按需加载:当用户点击内容为空的文件标签时,前端调用GET /api/sandbox/read-file 从沙盒获取内容。
Event Persistence
RealtimeEventSaver
RealtimeEventSaver 负责将实时 SSE 事件批量持久化到数据库:
配置
| 配置项 | 值 | 说明 |
|---|---|---|
| 批量大小 | 3 个事件 | 缓冲区达到此数量时触发写入 |
| 批量间隔 | 100ms | 即使未满也定时刷新 |
| 重试次数 | 3 次 | 最大重试次数 |
| 重试策略 | 指数退避 | 1s → 2s → 4s |
| 降级方案 | LocalStorage | 所有重试失败后的备份存储 |
Message Batching
前端消息合并
前端合并快速连续的文本更新,避免过度渲染:| 策略 | 说明 |
|---|---|
| UI 批量刷新 | 约 60fps 频率批量更新,合并快速连续的 token |
| 虚拟滚动 | 只渲染可视区域的消息卡片 |
| 选择性持久化 | 只持久化 sessionId 和 messageIds,消息内容按需加载 |
Chat History 构建
前端从 Zustand Store 加载当前会话的消息历史,排除当前正在发送的消息,传递给后端。后端将其转换为 LangChain 消息类型,限制最多 30 条。Concurrency & Isolation
| 隔离维度 | 机制 | 说明 |
|---|---|---|
| Session | thread_id | 每个 Session 拥有独立的 Checkpointer 状态 |
| Context | session_id | Context Cache 按 session_id 隔离,resume 只恢复对应会话 |
| 工具执行 | LangGraph | 同一 Session 内工具串行执行,不会并发 |
| 工作空间 | user_id | 用户文件系统按 user_id 完全隔离 |
Session Recovery
系统支持会话中断后的恢复:保存时机
- 工具调用完成后
- 消息发送后
- HITL 中断时(通过 Checkpointer)
恢复流程
Related Docs
Messages
完整的消息流程、请求参数和状态管理
Retry Policy
错误处理、重试策略和降级方案