news 2026/9/11 23:44:21

TradingAgents-CN 进度跟踪系统修复实战:从 LangGraph 节点映射到多阶段进度计算的完整方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TradingAgents-CN 进度跟踪系统修复实战:从 LangGraph 节点映射到多阶段进度计算的完整方案

TradingAgents-CN 进度跟踪系统修复实战:从 LangGraph 节点映射到多阶段进度计算的完整方案

【免费下载链接】TradingAgents-CN基于多智能体LLM的中文金融交易框架 - TradingAgents中文增强版项目地址: https://gitcode.com/GitHub_Trending/tr/TradingAgents-CN

导读

本文基于 TradingAgents-CN 开源仓库中的进度跟踪修复文档(docs/fixes/performance/progress-tracking-fix.md),系统剖析多智能体 LLM 金融分析中"前端进度条卡死、分析完成后直接跳到 100%"这一经典问题。你将掌握 LangGraph 节点名称与中文映射表的对齐方法、按阶段权重计算进度的完整逻辑、以及从图执行到 Redis/文件持久化的整条进度链路原理,可直接复用于任何基于 LangGraph 的流式任务进度展示场景。

一、问题现象与根因分析

1.1 核心问题

在 TradingAgents-CN 的多智能体分析流程中,前端进度条无法实时更新,尤其是在"研究辩论"阶段(60%–85%)会长时间卡住,直到整个分析完成后才直接跳到 100%。用户看到的进度体验是:10% → 60% → [长时间卡住] → 100%

1.2 三个根本原因

通过对源码的深入排查,问题由三层因素叠加导致:

原因一:节点名称不匹配(致命错误)

LangGraph 图实际注册的节点名称是带空格的英文短语(见 tradingagents/graph/setup.py),而进度映射表却使用了下划线风格的 Python 变量命名,导致回调函数无法识别任何节点,进度更新完全失效。

LangGraph 实际节点名称(来自tradingagents/graph/setup.pyworkflow.add_node(...)的调用):

# 分析师节点 "Market Analyst" # 由 f"{analyst_type.capitalize()} Analyst" 生成 "Fundamentals Analyst" "News Analyst" "Social Analyst" # 工具节点(预置 ToolNode) "tools_market" "tools_fundamentals" "tools_news" "tools_social" # 消息清理节点(AgentState 消息裁剪) "Msg Clear Market" "Msg Clear Fundamentals" "Msg Clear News" "Msg Clear Social" # 研究员节点 "Bull Researcher" "Bear Researcher" "Research Manager" # 交易员节点 "Trader" # 风险评估节点 "Risky Analyst" "Safe Analyst" "Neutral Analyst" "Risk Judge"

而旧代码中的错误映射形如:

node_mapping = { 'market_analyst': "📊 市场分析师", # ❌ LangGraph 中不存在该节点名 'fundamentals_analyst': "💼 基本面分析师", # ❌ 'bull_researcher': "🐂 看涨研究员", # ❌ # ... }

原因二:进度计算不完整

旧逻辑只在"辩论阶段"更新进度(通过判断消息内容中的"看涨/看跌"关键词),其余阶段——分析师阶段(10%–45%)、交易员阶段(70%–78%)、风险评估阶段(78%–93%)、最终阶段(93%–100%)——完全没有进度更新逻辑。

原因三:步骤权重与实际执行不同步

RedisProgressTracker在 app/services/progress/tracker.py 中定义了带有"准备阶段、环境检查"等虚拟步骤的动态步骤权重,但 LangGraph 实际只执行分析师、研究员、交易员、风险评估等真实节点,二者无法对齐,导致步骤状态与实际进度脱节。

二、完整修复方案

2.1 修复节点名称映射:_send_progress_update

修复集中在 tradingagents/graph/trading_graph.py 的_send_progress_update()方法。该方法在每个streamchunk 返回时被调用(updates模式下 chunk 格式为{node_name: state_update}),核心逻辑为:

def _send_progress_update(self, chunk, progress_callback): """发送进度更新到回调函数 LangGraph stream 返回的 chunk 格式:{node_name: {...}} """ try: if not isinstance(chunk, dict): return # 获取第一个非特殊键作为节点名称 node_name = None for key in chunk.keys(): if not key.startswith('__'): node_name = key break if not node_name: return # 检查是否为结束节点 if '__end__' in chunk: progress_callback("📊 生成报告") return # ✅ 正确的节点名称映射表(与 LangGraph 实际节点名完全一致) node_mapping = { # 分析师节点 'Market Analyst': "📊 市场分析师", 'Fundamentals Analyst': "💼 基本面分析师", 'News Analyst': "📰 新闻分析师", 'Social Analyst': "💬 社交媒体分析师", # 工具节点(跳过,避免重复推送进度) 'tools_market': None, 'tools_fundamentals': None, 'tools_news': None, 'tools_social': None, # 消息清理节点(跳过) 'Msg Clear Market': None, 'Msg Clear Fundamentals': None, 'Msg Clear News': None, 'Msg Clear Social': None, # 研究员节点 'Bull Researcher': "🐂 看涨研究员", 'Bear Researcher': "🐻 看跌研究员", 'Research Manager': "👔 研究经理", # 交易员节点 'Trader': "💼 交易员决策", # 风险评估节点 'Risky Analyst': "🔥 激进风险评估", 'Safe Analyst': "🛡️ 保守风险评估", 'Neutral Analyst': "⚖️ 中性风险评估", 'Risk Judge': "🎯 风险经理", } message = node_mapping.get(node_name) if message is None: # None 表示跳过(工具节点、消息清理节点) return if message: progress_callback(message) else: # 未知节点,回退为原始节点名 progress_callback(f"🔍 {node_name}") except Exception as e: logger.error(f"❌ 进度更新失败: {e}", exc_info=True)

关键设计点:

  • 跳过中间节点tools_*工具节点与Msg Clear *消息清理节点映射为None,避免进度消息在工具调用循环中被反复推送造成前端抖动;
  • 结束节点兜底:检测到'__end__'键时主动推送"📊 生成报告",保证收尾阶段有进度事件;
  • 未知节点兜底:映射不存在的节点回退为🔍 {node_name},保证消息不断流。

2.2 修复进度计算逻辑:graph_progress_callback

进度回调定义在 app/services/simple_analysis_service.py,通过一张"节点中文名 → 进度百分比"的静态映射表直接换算进度,各阶段百分比与RedisProgressTracker的步骤权重一一对应:

# ✅ 完整的节点进度映射表 node_progress_map = { # 分析师阶段 (10% → 45%) "📊 市场分析师": 27.5, # 10% + 17.5% "💼 基本面分析师": 45, # 10% + 35% "📰 新闻分析师": 27.5, "💬 社交媒体分析师": 27.5, # 研究辩论阶段 (45% → 70%) "🐂 看涨研究员": 51.25, # 45% + 6.25% "🐻 看跌研究员": 57.5, # 45% + 12.5% "👔 研究经理": 70, # 45% + 25% # 交易员阶段 (70% → 78%) "💼 交易员决策": 78, # 70% + 8% # 风险评估阶段 (78% → 93%) "🔥 激进风险评估": 81.75, # 78% + 3.75% "🛡️ 保守风险评估": 85.5, # 78% + 7.5% "⚖️ 中性风险评估": 89.25, # 78% + 11.25% "🎯 风险经理": 93, # 78% + 15% # 最终阶段 (93% → 100%) "📊 生成报告": 97, # 93% + 4% } def graph_progress_callback(message: str): """接收 LangGraph 的进度更新""" try: if not progress_tracker: return # 直接映射节点到进度百分比 progress_pct = node_progress_map.get(message) if progress_pct is not None: # 获取当前进度,只在进度增加时更新, # 避免覆盖 RedisProgressTracker 虚拟步骤的进度 current_progress = progress_tracker.progress_data.get('progress_percentage', 0) if int(progress_pct) > current_progress: progress_tracker.update_progress({ 'progress_percentage': int(progress_pct), 'last_message': message }) logger.info(f"📊 进度已更新: {current_progress}% → {int(progress_pct)}% - {message}") else: # 进度未增加,仅更新消息 progress_tracker.update_progress({'last_message': message}) else: # 未知节点,只更新消息 progress_tracker.update_progress({'last_message': message}) except Exception as e: logger.error(f"❌ 回调失败: {e}", exc_info=True)

这段实现中有一个值得借鉴的细节:单调递增保护。由于RedisProgressTracker的虚拟步骤(准备阶段、信号处理等)与 LangGraph 真实节点的进度并存,回调只有在int(progress_pct) > current_progress时才更新百分比,防止进度倒退或覆盖虚拟步骤已推进的进度。

三、进度链路源码纵深:从图执行到前端展示

3.1 图执行入口与 stream 模式切换

TradingGraph.propagate()(tradingagents/graph/trading_graph.py)根据是否传入进度回调自动选择 LangGraph 的 stream 模式:

  • 有回调时:使用stream_mode="updates",chunk 格式为{node_name: state_update},可精确获知当前执行到哪个节点;
  • 无回调时:使用默认values模式,仅打印消息并累积状态。

同时,propagate()内部还统计每个节点的执行耗时(node_timings),并生成performance_metrics写入最终状态,为后续性能分析提供数据支撑。

3.2 图拓扑与辩论/风险轮次控制

图的结构在 tradingagents/graph/setup.py 中定义,与分析阶段划分完全对应:

  1. 分析师阶段START → Market/Fundamentals/News/Social Analyst,每个分析师节点通过条件边在"工具节点 ↔ 分析师"间循环,最后由Msg Clear *清理消息后进入下一个分析师;
  2. 辩论阶段Bull Researcher ↔ Bear Researchershould_continue_debate(tradingagents/graph/conditional_logic.py)控制下交替发言,达到2 * max_debate_rounds后进入Research Manager
  3. 交易阶段Research Manager → Trader
  4. 风险阶段Trader → Risky Analyst → Safe Analyst → Neutral Analyst轮转,由should_continue_risk_analysis(tradingagents/graph/conditional_logic.py)控制,达到3 * max_risk_discuss_rounds后进入Risk Judge
  5. 收尾Risk Judge → END

这也解释了为什么进度映射表把"研究经理=70%、风险经理=93%"作为阶段终点——它们正是各阶段的条件出口。

3.3 步骤权重与动态步骤生成

RedisProgressTracker(app/services/progress/tracker.py)按以下权重动态生成分析步骤:

阶段权重说明
基础准备阶段10%(5 个虚拟步骤)准备阶段 3%、环境检查 2%、成本估算 1%、参数设置 2%、启动引擎 2%
分析师团队阶段35%0.35 / len(analysts),每个分析师均分
研究团队辩论阶段25%0.25 / (3 + rounds),含看涨、看跌、N 轮辩论、研究经理
交易团队阶段8%交易员决策
风险管理团队阶段15%0.15 / 4,激/保/中性风险 + 风险经理
最终决策阶段7%信号处理 4% + 生成报告 3%

其中辩论轮次rounds_get_debate_rounds()根据research_depth动态计算:快速=1标准=2、其他(深度/全面)=3。而update_progress()内部通过_update_steps_by_progress()依据累计权重把每个步骤标记为completed/current/pending,前端即可据此渲染步骤状态列表。

3.4 持久化双通道

_save_progress()(app/services/progress/tracker.py)支持两种存储:

  • Redis:环境变量REDIS_ENABLED=true时启用,键为progress:{task_id},并设置 3600 秒过期(expire);
  • 文件兜底:Redis 未启用或连接失败时,写入./data/progress/{task_id}.json

读取侧get_progress_by_id()(app/services/progress/tracker.py)会优先查 Redis,再依次尝试主文件路径与备用路径./data/progress_{task_id}.json,读取后调用_calculate_static_time_estimates()实时重算已用/剩余/预估总时长。

3.5 前端实时刷新链路

graph_progress_callback更新进度的同时,还会同步更新内存与 MongoDB(见 app/services/simple_analysis_service.py):优先通过asyncio.create_task异步更新analysis_tasks集合中的progress/current_step/message字段;若不在事件循环中,则新建同步 MongoDB 客户端完成写入。前端轮询/api/analysis/progress类接口即可拿到实时百分比与当前步骤名称。

四、修复效果对比

修复前 ❌

进度: 10% → 60% → [卡住很久] → 100% 步骤: 准备阶段 → 基本面分析师 → [卡住] → 完成

修复后 ✅

进度: 10% → 27.5% → 45% → 51.25% → 57.5% → 70% → 78% → 81.75% → 85.5% → 89.25% → 93% → 97% → 100% 步骤: 准备 → 市场分析师 → 基本面分析师 → 看涨研究员 → 看跌研究员 → 研究经理 → 交易员 → 激进风险 → 保守风险 → 中性风险 → 风险经理 → 生成报告 → 完成

五、验证与测试步骤

  1. 重启后端(在项目根目录执行):

    .\.venv\Scripts\python -m app

    或 Linux/macOS 环境使用:

    ./.venv/bin/python -m app
  2. 触发新的分析任务:在前端点击"开始分析"按钮,输入股票代码(如601398,工商银行)。

  3. 观察进度更新:前端进度条应平滑推进,步骤状态正确显示completed/current/pending,当前步骤名称实时变化。

  4. 检查日志(定位进度回调与图节点日志):

    Get-Content "logs\webapi.log" -Tail 1000 | Select-String "🎯🎯🎯|📊 \[Graph进度\]"

    其中🎯🎯🎯 [Graph进度回调被调用]是回调入口日志(app/services/simple_analysis_service.py),📊 [Graph进度] 进度已更新: X% → Y%是百分比推进日志,🔍 [Progress] 节点名称: xxx是图节点识别日志(tradingagents/graph/trading_graph.py)。

六、关键改进点总结

  1. 节点名称完全匹配:映射表键名与 tradingagents/graph/setup.py 中add_node注册的 LangGraph 实际节点名一一对应;
  2. 覆盖全部阶段:分析师、研究员、交易员、风险评估、最终阶段均有进度事件;
  3. 跳过中间节点tools_*Msg Clear *不触发进度更新,避免重复推送;
  4. 进度百分比准确:回调映射值与RedisProgressTracker的步骤权重逐段对应,两者不再相互覆盖;
  5. 错误处理完善:未知节点回退为🔍 {node_name},异常统一记录日志且不影响主流程。

七、后续优化建议

  1. 动态计算进度:目前分析师阶段的27.5/45是基于固定分析师数量的近似值,可根据实际selected_analysts数量动态计算每阶段占比;
  2. 辩论轮次支持:将node_progress_map中的辩论段百分比改为依据research_depth推导的轮次函数;
  3. 并行分析师适配:若后续分析师改为并行执行(setup.py 中分析师节点按selected_analysts顺序串行连接),需要将串行累加逻辑改为并行进度合并逻辑;
  4. 进度平滑过渡:前端可对百分比做动画插值,避免大跨度跳跃造成视觉突兀。

本文所述的修复思路具有通用性:任何基于 LangGraph 构建的多节点工作流,只要遵循"图节点名 ↔ 业务语义映射 ↔ 阶段权重百分比"三层对齐原则,配合单调递增保护与双通道持久化,即可构建出平滑、准确、可观测的实时进度系统。

【免费下载链接】TradingAgents-CN基于多智能体LLM的中文金融交易框架 - TradingAgents中文增强版项目地址: https://gitcode.com/GitHub_Trending/tr/TradingAgents-CN

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 23:38:51

基于动态放大器的Pipeline ADC设计要点与工程实践

这是一篇不够,但总开关必须严格禁用。我开始撰写这篇博文。我将直接输出正文,以资深模拟IC设计工程师的口吻,围绕“基于动态放大器的pipeline ADC”展开,融合热词中的工程经验(时钟抖动、PCB布局、前端RC滤波、校准、D…

作者头像 李华
网站建设 2026/9/11 23:37:59

狗狗表情识别实战:灰边填充+确定性增强的CNN训练全流程

简介:本资源是一套基于PyTorch实现的狗狗表情识别完整项目,面向深度学习初学者与计算机视觉实践者,解决宠物图像细粒度分类中的实际建模问题,适用于课程设计、AI兴趣实践及小型科研验证场景。压缩包共906个文件,主体为…

作者头像 李华
网站建设 2026/9/11 23:36:06

安当DBG:保险理赔系统怎么给证件号、银行卡与病历字段上字段级加密——客服坐席按角色最小可见、外包公估脱敏留证的落地路径

引言:理赔系统里最该保护、也最容易被看光的字段 保险业务的核心是"理赔",而理赔系统的核心数据,是三类高度敏感、又必须被反复查询的字段:被保人证件号、理赔收款银行卡号、以及出险病历与诊断结论。这三类字段有两个共…

作者头像 李华
网站建设 2026/9/11 23:35:27

Restormer自定义训练测试全流程:轻量Transformer图像复原实战

简介:本资源是一套面向深度学习初学者与图像恢复研究者的Restormer模型自定义训练与测试代码实现,聚焦Transformer架构在图像去雨、去模糊等低级视觉任务中的实践应用。代码复现完整,含训练、验证、推理全流程,注释详尽&#xff0…

作者头像 李华