跳转到内容

实时事件

RTC Agent 通过 Centrifuge WebSocket 推送实时事件,采用 双频道架构:Topic 频道保证可靠性,Live 频道提供低延迟。前端收到事件后更新本地状态,实现前后端的实时同步。

频道标识存储用途特点
Topictopic:u=<userID>持久化状态变更事件可靠、有序、支持离线恢复
Livelive:u=<userID>非持久化流式消息中间 chunks低延迟、即发即弃

💡 为什么需要两个频道? 状态变更(如消息创建、Turn 完成)不能丢失——需要持久化和顺序保证。但流式输出的中间 chunk 只需要”尽快到达”——丢了也不要紧,因为最终会收到完整消息。分开处理,各取所长。


事件类型Topic 频道Live 频道说明
session.created✅❌会话创建
session.updated✅❌会话更新(标题、状态、Token 统计等)
turn.created✅❌Turn 创建
turn.updated✅❌Turn 状态变更
message.created✅❌消息创建
message.updated(流完成)✅❌流式消息的最终完整版本
message.updated(流中间 chunk)❌✅打字机效果的实时片段
rtc.created❌❌预留(当前未发出)
rtc.updated✅❌RTC 状态变更

每个事件都是一个 Update——描述实体(Session / Turn / Message / RTC / File)的变化:

{
"id": "update-uuid",
"items": [
{ "entity": "message", "action": "created", "entity_id": "msg-uuid" }
],
"data_list": [{ "id": "msg-uuid", "role": "assistant", "..." : "..." }],
"offset": 42
}
字段类型说明
idUUIDUpdate 唯一标识
itemsarray变化条目列表
items[].entitystring实体类型:session / turn / message / rtc(file 为预留,当前未使用)
items[].actionstring操作类型:created / updated(deleted 为预留,当前未使用;删除通过 data_list 中的 deleted_at 表达)
items[].entity_idUUID实体 ID
data_listarray实体完整数据(可选,与 items 一一对应)
offsetinteger用户维度单调递增偏移量

session.updated 事件在 Session 数据中包含 Token 统计 和 压缩状态 字段,用于前端实时展示用量和压缩进度:

字段类型说明
total_tokensint64累计总 Token 数(包含所有类型)
total_input_tokensint64累计纯输入 Token 数(不含 cached read/write)
total_output_tokensint64累计输出 Token 数
total_cached_read_tokensint64累计缓存读取 Token 数
total_cached_write_tokensint64累计缓存写入 Token 数
total_reasoning_tokensint64累计推理 Token 数
total_cost_usdfloat64累计成本(美元)
last_token_update_attime最后一次 Token 统计更新时间
字段类型说明
current_context_tokensint64当前上下文实际 token 数(由服务端 cumulativeTokenCounter 回写,压缩后更新为真实值)
estimated_next_round_tokensint64基于 EWMA 的下一轮 Token 预估
compression_progressfloat64压缩进度 (0-100),基于 current_context_tokens 与 compression_threshold 计算
compression_thresholdint64压缩触发阈值(contextTokensLimit - autoCompactBufferTokens)
rounds_until_compressionint距离压缩的轮次(-1 表示已超过阈值)

💡 current_context_tokens 替换了旧的 total_tokens 作为压缩进度显示的基准。压缩完成后,该值会被回写为压缩后的实际上下文大小,前端进度条会即时反映真实状态。对于尚未初始化该字段的旧 Session,前端会 fallback 到 total_tokens。

字段类型说明
device_idstring创建此 Session 的设备 ID(来自 JWT Token),用于前端判断 RTC 请求归属

当 Session 由 Sub-agent 系统创建时,以下字段描述其在 Session 树中的位置:

字段类型说明
parent_client_session_idstring?父 Session 的 Client ID
parent_server_session_idUUID?父 Session 的 Server ID
root_client_session_idstring?根 Session 的 Client ID(树的顶层)
root_server_session_idUUID?根 Session 的 Server ID(树的顶层)
sub_agent_parent_message_idUUID?父 Session 中触发 Sub-agent 的消息 ID

💡 Session 树:Sub-agent 创建的 Session 通过 parent_* 字段指向其父 Session,通过 root_* 字段指向整棵树的根 Session。前端可据此渲染层级导航(如 rtc-session-tree 组件)。顶层 Session 这些字段均为 null。

💡 Token 统计字段通过节流机制(Throttle)控制推送频率,避免高频事件风暴。前端通过 rtc-token-usage 组件展示这些数据,详见 会话管理 和 上下文管理。


Offset 是事件可靠性的核心保障——每个 Topic 事件分配严格递增的 Offset,客户端通过检测 Offset 连续性发现丢失的事件。

特性说明
单调递增同一用户的 Topic 事件 Offset 严格递增
连续性检测客户端发现 Offset 跳跃时自动补全
持久化Offset 存储在 IndexedDB,页面刷新后恢复
Epoch 机制历史数据被清理时 Epoch 变更,客户端从最新位置开始
Gap 占位离线恢复时,服务端发送 {"type": "gap", "data": {}} 事件填充 Offset 空洞,客户端收到后只推进 Offset,不做业务处理

💡 类比:Offset 就像邮件的编号。如果你收到了第 1、2、4 封信,你会意识到第 3 封丢了——然后去邮局补领。


AI 回复采用流式输出,中间 chunks 通过 Live 频道实时推送,最终完整消息通过 Topic 频道保证可靠性。

阶段频道行为
首 chunkTopic创建消息记录,推送 message.created,同时写入 Redis 缓冲
中间 chunksLive追加到 Redis 缓冲,推送到 Live 频道实时显示
流结束Topic从 Redis 读取所有 chunks 拼接完整内容,更新数据库,推送 message.updated

客户端断网重连后,系统自动检测 Offset 并补全缺失的事件。

场景行为用户感知
短暂断网自动推送离线期间的消息无缝,消息自动出现
长时间断网检测 Offset 跳跃,拉取历史短暂加载后消息补齐
历史被清理Epoch 变更,从最新位置开始历史消息不再显示
组件职责
Centrifuge SDK自动重连、Token 刷新
IndexedDB持久化 Offset,页面刷新后恢复
Topic 频道保证离线事件不丢失