一、更新概述
2025 年 10 月 23 日,TradingAgents-CN 完成了一次重要的架构升级和数据修复工作。
本次更新通过 25 个提交,完成了以下关键改进:
- 将通知系统从 SSE + Redis PubSub 迁移到 WebSocket。
- 彻底解决长期困扰的 Redis 连接泄漏 问题。
- 修复 AKShare 数据源 trade_date 字段格式错误。
- 清理 82,631 条错误数据。
- 优化配置管理逻辑。
- 新增 硅基流动 SiliconFlow 大模型支持。
- 修复多个 UI、新闻同步和任务管理问题。
这次更新的重点,不只是修补表面问题,而是从架构层面解决实时通知和数据质量的根因。
二、WebSocket 通知系统:彻底解决 Redis 连接泄漏
1. 问题背景
用户持续反馈系统出现 Redis 连接过多的问题,典型错误如下:
redis.exceptions.ConnectionError: Too many connections
经过排查,问题根源来自原有的 SSE + Redis PubSub 架构。
2. 根本原因
原架构存在以下固有缺陷:
- 每个 SSE 连接 都会创建一个独立的 Redis PubSub 连接。
- PubSub 连接不使用连接池,而是独立 TCP 连接。
- 用户刷新页面时,旧连接未必能被及时清理。
- 多用户同时在线时,Redis 连接数会快速增长。
- 即使增加连接池大小,也无法真正解决 PubSub 独立连接问题。
此前尝试过多种修复方式:
- 将连接池大小从 20 增加到 200。
- 限制每个用户只能有一个 SSE 连接。
- 添加 TCP keepalive 和健康检查。
- 强化 PubSub 异常处理和资源清理。
但这些方式只能缓解,无法彻底解决。
最终结论是:SSE + Redis PubSub 架构存在固有缺陷,需要迁移到 WebSocket。
三、为什么选择 WebSocket
相比原来的 SSE + Redis PubSub,WebSocket 更适合系统的实时通知场景。
| 对比维度 | SSE + Redis PubSub | WebSocket |
|---|---|---|
| 连接管理 | 每个 SSE 创建独立 PubSub | 后端直接管理连接 |
| Redis 依赖 | 依赖 Redis PubSub | 不再依赖 Redis PubSub |
| 通信方向 | 服务器到客户端单向通信 | 服务器与客户端双向通信 |
| 实时性 | 较好 | 更好 |
| 连接限制 | 受 Redis 连接影响 | 主要受服务器资源影响 |
| 连接泄漏风险 | 较高 | 可控 |
迁移后,通知系统不再需要为每个客户端创建 Redis PubSub 连接,从根源上避免了 Redis 连接数失控。
四、WebSocket 后端实现
1. 新增 WebSocket 路由
新增文件:
app/routers/websocket_notifications.py
核心接口:
@router.websocket("/ws/notifications")
async def websocket_notifications_endpoint(
websocket: WebSocket,
token: str = Query(...),
current_user: dict = Depends(get_current_user_ws)
):
user_id = current_user.get("user_id")
await manager.connect(websocket, user_id)
try:
await websocket.send_json({
"type": "connected",
"data": {
"message": "WebSocket connected",
"user_id": user_id
}
})
while True:
await asyncio.sleep(30)
await websocket.send_json({"type": "heartbeat"})
except WebSocketDisconnect:
await manager.disconnect(websocket, user_id)
该接口完成:
- 用户鉴权。
- WebSocket 连接建立。
- 连接确认消息发送。
- 30 秒心跳。
- 断开连接清理。
2. 全局连接管理器
新增全局连接管理器,用于维护每个用户的 WebSocket 连接:
class ConnectionManager:
def __init__(self):
self.active_connections: Dict[str, Set[WebSocket]] = {}
self._lock = asyncio.Lock()
主要能力包括:
- 按用户 ID 管理连接。
- 支持单用户多连接。
- 发送个人通知。
- 自动清理失效连接。
- 使用锁保证并发安全。
3. 通知服务集成
通知发送逻辑调整为:
- 优先通过 WebSocket 发送通知。
- 如果 WebSocket 发送失败,再降级到 Redis PubSub。
- 保持与旧 SSE 客户端的兼容性。
这种设计既完成了架构升级,也避免了影响旧客户端。
五、WebSocket 前端实现
1. 前端连接逻辑
前端在通知状态管理中新增 WebSocket 连接逻辑:
function connectWebSocket() {
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
const url = `${wsProtocol}//${base}/api/ws/notifications?token=${token}`
const socket = new WebSocket(url)
}
连接成功后:
- 标记 WebSocket 已连接。
- 重置重连次数。
- 开始接收通知消息。
2. 消息处理
前端根据消息类型进行处理:
function handleWebSocketMessage(message: any) {
switch (message.type) {
case 'connected':
console.log('[WS] 连接确认:', message.data)
break
case 'notification':
if (message.data?.title && message.data?.type) {
addNotification(message.data)
}
break
case 'heartbeat':
break
}
}
支持的消息类型包括:
- connected:连接确认。
- notification:业务通知。
- heartbeat:心跳消息。
3. 自动重连与降级
当前端连接关闭时,会自动重连:
- 使用指数退避。
- 最多重连 5 次。
- 重连失败后自动降级到 SSE。
这保证了在部分网络环境下仍然可以接收通知。
六、Nginx WebSocket 配置优化
为了支持 WebSocket 长连接,Nginx 配置也进行了调整:
location /api/ {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_connect_timeout 120s;
proxy_send_timeout 3600s;
proxy_read_timeout 3600s;
proxy_buffering off;
proxy_cache off;
}
关键变化:
- 开启 WebSocket 协议升级支持。
- 将
proxy_send_timeout调整为 3600 秒。 - 将
proxy_read_timeout调整为 3600 秒。 - 禁用代理缓存。
- 禁用缓冲。
后端每 30 秒 发送一次心跳,配合 Nginx 的 1 小时超时设置,可以有效避免连接被意外关闭。
七、WebSocket 修复效果
迁移前后对比如下:
| 场景 | 修改前:SSE + Redis PubSub | 修改后:WebSocket |
|---|---|---|
| Redis 连接数 | 用户数 × 2 | 不再依赖 PubSub |
| 连接泄漏 | 频繁发生 | 彻底解决 |
| 用户停留 1 小时 | 多次重连 | 稳定连接 |
| 实时性 | 较好 | 更好 |
| 双向通信 | 不支持 | 支持 |
新增监控工具:
/api/ws/stats
scripts/check_redis_connections.py
可用于查看 WebSocket 连接统计和 Redis 连接数。
八、AKShare trade_date 字段格式错误修复
1. 问题背景
用户反馈分析任务提示:
未找到 000001 的 daily 数据
但数据库中实际上存在数据。
进一步排查发现,数据库中有 82,631 条 trade_date 格式错误记录。
错误数据示例:
"0", "1", "2", "3"
正确格式应该是:
"2025-10-23"
由于查询条件按日期格式匹配,错误数据无法被正常查询,导致分析任务返回空结果。
2. 根本原因
问题出在历史数据标准化逻辑中:
for date_index, row in data.iterrows():
record = self._standardize_record(
row=row,
date_index=date_index
)
data.iterrows() 返回的是:
(index, row)
其中 index 是 RangeIndex,也就是:
0, 1, 2, 3...
而 _standardize_record() 又优先使用 date_index 作为日期,最终就把 0、1、2 写入了 trade_date 字段。
3. 代码修复
修复后,系统优先从数据列中读取日期:
date_from_column = row.get("date") or row.get("trade_date")
if date_from_column is not None:
trade_date = self._format_date(date_from_column)
elif date_index is not None and isinstance(date_index, (date, datetime, pd.Timestamp)):
trade_date = self._format_date(date_index)
else:
trade_date = self._format_date(None)
关键改动:
- 优先读取
date或trade_date列。 - 只有当索引确实是日期类型时,才使用索引。
- 避免将普通数字索引误写成交易日期。
4. 数据清理
新增清理脚本:
scripts/clean_invalid_trade_date.py
清理逻辑:
result = collection.delete_many({
"trade_date": {"$regex": "^[0-9]+$"},
"data_source": "akshare"
})
最终清理结果:
删除了 82,631 条格式错误的记录
5. 验证结果
新增验证脚本:
scripts/verify_fix.py
验证最近同步的 AKShare 数据,确认 trade_date 是否符合:
YYYY-MM-DD
验证结果:
格式正确:10 条
格式错误:0 条
6. 修复效果
| 指标 | 修复前 | 修复后 |
|---|---|---|
| 错误数据 | 82,631 条 | 0 条 |
| trade_date 格式 | "0"、"1"、"2" | "2025-10-23" |
| 查询结果 | 返回空 | 正常返回 |
| 分析任务 | 提示未找到数据 | 正常分析 |
| 新同步数据 | 格式错误 | 格式正确 |
九、配置管理优化
1. 区分必需配置与推荐配置
此前配置验证页面对所有缺失项都显示红色错误,容易给用户造成压力。
本次调整为:
- 必需配置:红色错误。
- 推荐配置:黄色警告。
必需配置包括:
- MongoDB
- Redis
- JWT
推荐配置包括:
- DeepSeek
- 阿里百炼
- Tushare
这样用户可以更清楚地区分“系统必须配置”和“增强功能推荐配置”。
2. API Key 配置管理统一
本次进一步明确配置优先级:
MongoDB > 环境变量 > 默认值
并统一 API Key 管理方式:
- 所有 API Key 都可通过配置管理页面设置。
- MongoDB 中没有配置时,自动使用环境变量。
- 避免不同配置来源互相冲突。
3. 配置桥接异步事件循环冲突修复
此前配置桥接中存在异步事件循环冲突,可能导致配置加载失败。
修复思路:
- 优先使用
asyncio.run()。 - 如果当前已经在事件循环中,则使用
await。
这让配置桥接在不同运行环境下更加稳定。
十、SiliconFlow 硅基流动大模型支持
本次新增 硅基流动 SiliconFlow 作为新的 LLM 厂家。
支持能力包括:
- 支持 Qwen 系列模型。
- 支持 DeepSeek 系列模型。
- 支持配置测试。
- 支持 API 连接验证。
- 可在分析任务中使用。
配置示例:
SILICONFLOW_API_KEY=sk-xxx
SILICONFLOW_ENABLED=true
SILICONFLOW_BASE_URL=https://api.siliconflow.cn/v1
SILICONFLOW_MODEL=Qwen/Qwen2.5-7B-Instruct
使用步骤:
- 在配置管理页面添加 SiliconFlow 厂家。
- 设置 API Key 和 Base URL。
- 选择模型,例如
Qwen/Qwen2.5-7B-Instruct。 - 测试连接。
- 在分析任务中使用。
十一、UI 改进
1. 移除仪表台市场快讯“查看更多”按钮
由于新闻中心页面尚未实现,仪表台市场快讯中的“查看更多”按钮点击后没有响应。
本次移除该按钮,避免用户困惑。
2. 修复仪表台市场快讯为空的问题
此前市场快讯区域可能显示为空。
本次新增智能回退逻辑:
- 优先查询最近 24 小时新闻。
- 如果没有数据,则查询最近 365 天新闻。
- 增加“同步新闻”按钮。
- 显示新闻数量和同步状态。
3. 修复分析报告下单后跳转错误
此前从分析报告页面下单后,会跳转到不存在的路由:
/paper-trading
修复后改为使用命名路由:
router.push({ name: 'PaperTradingHome' })
确保跳转到正确的模拟交易页面。
十二、Redis 连接泄漏修复历程
在最终采用 WebSocket 前,团队进行了多次修复尝试。
1. 修复 Redis 连接池配置
尝试内容:
- 将连接池大小从 20 调整为 200。
- 添加 TCP keepalive。
- 添加健康检查。
结果:问题仍然存在。
2. 限制每个用户只能有一个 SSE 连接
尝试内容:
- 实现全局 SSE 连接管理器。
- 新连接建立时关闭旧连接。
结果:问题有所缓解,但未彻底解决。
3. 修复 PubSub 连接泄漏
尝试内容:
- 确保 PubSub 连接在 SSE 断开时关闭。
- 增加异常处理。
- 强化资源清理。
结果:问题仍然存在。
4. 编写 Redis 连接泄漏分析报告
最终通过分析发现:
- PubSub 连接不使用普通连接池。
- SSE + Redis PubSub 的组合天然容易造成连接增长。
- WebSocket 是更合理的替代方案。
最终结论:
要彻底解决问题,必须从架构上替换 SSE + Redis PubSub。
十三、新闻同步功能改进
1. 启用新闻同步定时任务
新增新闻同步定时任务:
每天 17:00 执行
并提供配置指南和使用说明。
2. 修复新闻同步任务不显示问题
此前新闻同步任务如果被禁用,可能不会出现在定时任务管理界面。
修复后:
- 任务始终添加到调度器。
- 如果任务禁用,则添加后立即暂停。
- 用户可以在 UI 中看到并管理该任务。
3. 更新新闻同步任务配置指南
文档同步更新,补充:
- 新闻同步配置说明。
- 任务管理方式。
- 禁用任务的显示逻辑。
十四、统计数据
提交统计
| 指标 | 数量 |
|---|---|
| 总提交数 | 25 个 |
| 新增文件 | 5 个 |
| 修改文件 | 20+ 个 |
| 删除错误数据 | 82,631 条 |
代码变更
| 类型 | 内容 |
|---|---|
| 后端新增 | 约 1500 行,包含 WebSocket 路由、连接管理器、文档 |
| 前端新增 | 约 200 行,包含 WebSocket 客户端、自动重连 |
| 配置优化 | Nginx、环境变量、Docker |
| 数据修复 | 清理 AKShare 错误 trade_date 数据 |
问题修复清单
- Redis 连接泄漏:彻底解决。
- AKShare 数据格式错误:清理 82,631 条错误记录。
- 配置管理混乱:优化配置优先级与验证提示。
- UI 导航错误:修复报告下单跳转路径。
- 新闻同步任务不可见:修复任务注册逻辑。
十五、技术细节
1. WebSocket 与 SSE 对比
| 维度 | SSE | WebSocket |
|---|---|---|
| 协议 | HTTP | WebSocket,基于 HTTP 升级 |
| 通信方式 | 单向 | 双向 |
| 自动重连 | 浏览器自动 | 需要手动实现 |
| 消息格式 | 文本 Event Stream | 文本或二进制 |
| 代理支持 | 较好 | 需要特殊配置 |
| 实时性 | 较好 | 更好 |
2. WebSocket 连接生命周期
WebSocket 连接流程如下:
客户端发起连接
↓
HTTP 握手
↓
101 Switching Protocols
↓
协议升级到 WebSocket
↓
连接建立成功
↓
双向通信与心跳
↓
连接关闭
↓
客户端自动重连
3. 心跳机制设计
后端每 30 秒发送一次心跳:
while True:
await asyncio.sleep(30)
await websocket.send_json({"type": "heartbeat"})
前端收到心跳后无需响应。
Nginx 配置:
proxy_read_timeout: 3600s
后端心跳间隔:
30s
两者配合可以确保连接不会因为长时间空闲被代理关闭。
十六、影响总结
1. 系统可靠性提升
本次更新后:
- Redis 连接泄漏 问题彻底解决。
- 通知系统更加稳定。
- 数据质量显著提升。
- 错误数据被清理。
- 配置管理更加清晰。
2. 用户体验提升
用户侧体验改善包括:
- 实时通知更及时。
- WebSocket 连接更加稳定。
- 配置验证更加友好。
- 仪表台市场快讯不再空白。
- 报告下单跳转更准确。
3. 性能提升
系统性能层面:
- 不再依赖 Redis PubSub,减少 Redis 压力。
- WebSocket 通信延迟更低。
- 连接数可控。
- 不会因 PubSub 连接堆积导致 Redis 报错。
4. 开发体验提升
开发和运维侧收益包括:
- 监控工具更完善。
- 日志和错误提示更清晰。
- WebSocket 与 Redis 状态更容易排查。
- 数据质量问题有清理脚本和验证脚本支持。
十七、经验总结
1. 架构选择影响长期维护成本
SSE + Redis PubSub 看似简单,但在用户数增加和页面频繁刷新时容易暴露连接管理问题。
WebSocket 虽然需要手动实现重连和心跳,但长期来看更可靠。
2. 数据质量会直接影响系统可用性
82,631 条错误数据 导致分析任务无法正常获取 daily 数据。
这说明数据格式问题不是小问题,它会直接影响分析流程和用户体验。
3. 排查问题要追到根因
Redis 连接泄漏经历了多次修复尝试,最终确认问题来自架构本身。
这类问题不能只靠调大参数解决,需要定位底层机制。
4. 平滑迁移很重要
本次迁移采用:
WebSocket 优先,SSE 降级
既解决了新架构问题,也保留了旧客户端兼容性,降低了升级风险。
十八、总结
本次 TradingAgents-CN v1.0.0-preview 更新,是一次兼具架构升级和数据修复的重要版本。
核心成果包括:
- 完成从 SSE + Redis PubSub 到 WebSocket 的通知系统迁移。
- 彻底解决 Redis 连接泄漏 问题。
- 修复 AKShare trade_date 字段格式错误。
- 清理 82,631 条错误数据。
- 新增 SiliconFlow 硅基流动大模型支持。
- 优化 配置验证、API Key 管理、新闻同步任务和 UI 导航。
- 增加 WebSocket 监控与 Redis 连接监控工具。
- 保留 SSE 降级能力,保证平滑迁移。
整体来看,这次更新让 TradingAgents-CN 的实时通知系统更加稳定,数据质量更加可靠,也为后续更多用户同时在线和更复杂的分析任务打下了更稳的基础。
✅ 官方唯一渠道:📦 GitHub 仓库:https://github.com/hsliuping/TradingAgents-CN
Aekor AI-API 中转站,让全球顶尖 AI 大模型“触手可及”!你是否曾为这些烦恼头疼?
🔹 人在国内,却总被海外官网 API 的高延迟、掉线、甚至无法访问困扰?
🔹 想用最强的 GPT、Claude 等模型,却卡在海外信用卡、支付审核等重重阻碍?
🔹 官方 API 太贵?Aekor 为你打通“网络-支付-成本”的任督二脉!
💡 Aekor 核心价值:好用、便宜、快 💡
🚀 高速稳定,告别掉线国内专线加速,API 响应低延迟,告别「转圈圈」的焦虑,开发效率瞬间拉满!
🧠 顶尖模型,随需而调涵盖 GPT 系列、Claude 系列等全球主流大厂模型,一次接入,轻松调用!
🎁 免费白嫖,诚意拉满!
注册即送 20 美元体验额度,够你狠狠测试一轮模型质量与线路稳定性了!
🚪 入口:https://api.aekor.com
⚠️ 温馨提示:API 中转市场虽多但良莠不齐(甚至有些会偷工减料换小模型糊弄事儿)。Aekor 坚持提供正版稳定的服务,但还是建议:先用免费的 20 刀测试是否契合自身需求,满意了再小额充值上车,理性消费不盲目。
文章评论