以下判断来自于多个实时系统的工程实践,其中也包括对统一实时行情 API 项目 TickDB 的设计与使用经验。
一个反直觉的工程判断
在设计 Agent 系统时,当需要获取外部数据(如行情、价格、状态),最直接的想法是:建立实时连接,数据一变就推送。
但在实践中发现,这个看似合理的设计,往往是系统不稳定的根源。
核心判断:实时数据流不适合作为 Agent 的同步决策依赖。大多数场景下,Agent 需要的不是"持续数据流",而是"状态快照"或"变化信号"。
系统真正需要什么
1. 状态 vs 流
Agent 在做决策时,需要的是"此时此刻的状态",而不是"过去 10 秒内的所有变化"。
举例:
- Agent 需要判断"当前价格是否超过阈值"
- 不需要知道"价格在过去 5 秒内跳动了 8 次"
但实时数据流的特点是:只要连接建立,数据就会持续推送。这导致 Agent 需要处理大量对决策无用的中间状态。
工程后果:
- Agent 的决策逻辑被迫处理"数据到达顺序"问题
- 系统复杂度从"状态判断"变成"流处理"
- 调试难度指数级上升
2. 必要信息 vs 噪声
在实时系统中,数据更新频率往往远高于决策频率。
典型场景:
- 行情数据每秒更新 10 次
- Agent 每 5 秒做一次决策
- 90% 的数据更新对决策无影响
工程判断:如果 Agent 的决策周期是秒级,使用毫秒级的实时推送,会引入大量噪声。
更合理的做法是:
- Agent 在需要时主动拉取当前状态
- 或订阅"状态变化事件"(而非原始数据流)
3. Agent 的不确定性如何被实时数据放大
Agent 系统本身存在不确定性:
- 推理延迟不固定
- 工具调用可能失败
- 上下文可能丢失
当 Agent 依赖实时数据流时,这些不确定性会被放大:
- 数据到达时,Agent 可能正在处理上一个任务
- 数据到达顺序与 Agent 处理顺序不一致
- 网络抖动导致数据延迟,但 Agent 无法感知
工程后果:系统行为变得不可预测。同样的输入,在不同时刻可能产生不同结果。
为什么实时 API 不适合作为同步能力
推 vs 拉的工程取舍
推(WebSocket / 长连接):
- 优势:延迟低,数据到达即时
- 代价:需要维护连接状态、处理断线重连、管理订阅关系
拉(REST API / 轮询):
- 优势:无状态,调用即返回,易于重试和降级
- 代价:延迟稍高,需要主动轮询
在 Agent 系统中,"推"的代价往往被低估:
-
连接管理成本
- Agent 可能同时处理多个任务,每个任务都需要建立连接
- 连接数随任务数线性增长
- 连接池管理、心跳维护、异常处理的复杂度远高于简单的 HTTP 调用
-
时序依赖问题
- 推送数据的到达时间不可控
- Agent 的决策逻辑必须处理"数据还未到达"的情况
- 引入超时、重试、补偿逻辑
-
调试与可观测性
- 推送系统的问题难以复现("为什么那次没推送?")
- 日志难以关联(数据推送与 Agent 处理是异步的)
- 无法简单地"重放请求"
工程判断:除非 Agent 的决策延迟要求在毫秒级,否则应优先使用拉模式。
同步调用的风险
将实时 API 作为 Agent 的同步依赖,会引入以下风险:
-
延迟放大
- Agent 调用实时 API 获取数据
- 实时 API 内部可能依赖 WebSocket 连接
- 如果连接断开,需要重连后才能返回数据
- 原本 100ms 的调用,可能变成 5 秒
-
错误传播
- 实时系统的错误(连接断开、数据延迟)会直接阻塞 Agent
- Agent 无法区分"数据暂时不可用"和"数据永久不存在"
- 降级策略难以实现
-
资源竞争
- 多个 Agent 同时调用实时 API
- 实时系统的连接数、订阅数有上限
- 容易触发限流或拒绝服务
工程结论:实时 API 应作为"数据源",而非"同步调用接口"。Agent 应通过中间层(缓存、消息队列)与实时系统解耦。
抖动、延迟、不一致性的放大效应
实时系统的特点是:数据更新频繁,但不保证每次更新都能送达。
在 Agent 系统中,这会导致:
-
决策抖动
- 数据在短时间内多次变化
- Agent 基于每次变化做决策
- 决策结果频繁翻转
-
延迟不可控
- 网络延迟、服务端处理延迟、客户端处理延迟叠加
- Agent 看到的数据可能已经过时
- 基于过时数据的决策可能是错误的
-
不一致性
- 不同 Agent 订阅同一数据源
- 由于网络路径不同,看到的数据顺序可能不同
- 协同决策时出现冲突
工程判断:如果系统对一致性有要求,不应直接使用实时推送。应引入"版本号"或"时间戳"机制,确保 Agent 基于同一版本的数据做决策。
常见误区清单
误区 1:把实时数据当 RPC 使用
为什么常见:
- 开发者习惯了同步调用的思维模式
- 实时 API 文档通常展示"订阅-接收"的简单示例
- 忽略了连接管理、错误处理的复杂性
在什么条件下会出问题:
- Agent 需要在决策时立即获取数据
- 实时连接尚未建立或已断开
- Agent 被迫等待连接建立,或使用过时数据
工程后果:
- Agent 的响应时间不可预测
- 错误处理逻辑复杂,难以覆盖所有边界情况
- 系统可靠性下降
误区 2:认为"实时"等于"高价值"
为什么常见:
- 实时数据在营销上更有吸引力
- 开发者倾向于"能实时就实时"
- 忽略了数据更新频率与决策频率的匹配问题
在什么条件下会出问题:
- Agent 的决策周期是分钟级,但数据更新是秒级
- 大量数据更新对决策无影响
- 系统资源浪费在处理无用数据上
工程后果:
- 系统复杂度上升,但决策质量未提升
- 调试时难以区分"数据问题"和"决策问题"
- 成本增加(连接数、带宽、计算资源)
误区 3:在所有模块都建立实时连接
为什么常见:
- 为了"保证数据最新",每个模块都订阅实时数据
- 忽略了连接数的限制
- 忽略了数据一致性问题
在什么条件下会出问题:
- 系统模块数量增长
- 实时系统的连接数达到上限
- 不同模块看到的数据版本不一致
工程后果:
- 系统扩展性受限(连接数上限)
- 数据不一致导致决策冲突
- 排查问题时需要对比多个模块的数据版本
误区 4:用 WebSocket 作为决策链路
为什么常见:
- WebSocket 延迟低,看起来适合实时决策
- 忽略了 WebSocket 的可靠性问题
- 忽略了决策链路需要可重试、可降级
在什么条件下会出问题:
- 网络不稳定,WebSocket 频繁断开
- Agent 需要基于历史数据做决策,但 WebSocket 只推送最新数据
- 决策失败后需要重试,但 WebSocket 无法"重放"数据
工程后果:
- 决策链路不可靠
- 无法实现降级策略(WebSocket 断开后无法切换到其他数据源)
- 调试困难(无法复现"当时的数据状态")
实时数据在系统中的正确位置
基于以上判断,给出以下工程建议:
1. 实时数据应作为"变化信号",而非"数据源"
推荐做法:
- 实时系统推送"数据已变化"的信号
- Agent 收到信号后,主动拉取当前状态
- 拉取使用 REST API,可重试、可降级
优势:
- 解耦数据推送与数据获取
- Agent 始终获取的是"当前状态",而非"变化过程"
- 降低系统复杂度
2. 在什么情况下应该避免实时数据
以下场景不应使用实时推送:
- 决策周期 > 数据更新周期:Agent 每分钟决策一次,数据每秒更新,使用轮询即可
- 对一致性有要求:多个 Agent 需要基于同一版本数据协同,应使用版本化的快照
- 系统处于早期阶段:优先保证功能正确性,而非实时性
3. 什么时候只消费"变化信号"
以下场景适合使用"信号 + 拉取"模式:
- 数据变化不频繁:如品种列表更新、配置变更
- 需要完整数据:实时推送通常只包含变化字段,完整数据需要额外拉取
- 需要可重试:决策失败后需要重新获取数据
4. 什么时候实时系统应退居二线
以下场景应将实时系统作为"加速层",而非"主链路":
- 系统可靠性要求高:主链路使用 REST API,实时推送作为优化
- 需要降级能力:实时系统故障时,自动切换到轮询模式
- 调试与可观测性优先:开发阶段使用轮询,生产环境再引入实时推送
工程结论
在设计 Agent 系统时,应谨慎对待实时数据:
- 不要将实时 API 作为同步依赖:会引入延迟、错误传播、资源竞争问题
- 区分"状态"与"流":大多数 Agent 需要的是状态快照,而非持续数据流
- 优先使用拉模式:除非延迟要求在毫秒级,否则拉模式更简单、可靠
- 实时推送应作为"信号":推送变化通知,Agent 主动拉取数据
- 保留降级能力:实时系统故障时,应能切换到轮询模式
这些判断的核心是:实时性不是免费的,它的代价是系统复杂度和不确定性的上升。在 Agent 系统中,可预测性往往比实时性更重要。
作者背景说明
本文讨论的判断,来自于实时系统的工程实践。 相关实现与设计沉淀在一个统一实时行情 API 项目 TickDB 中: https://github.com/TickDB
