资讯动态

TradingAgents实战:基于LangGraph的多智能体交易决策框架搭建与回测

发布时间:2026/9/12 7:56:13 来源:尧图企业网站定制
最近社区里讨论度很高的 TradingAgents我前后花了三周时间把一个可运行的 demo 从零搭了起来。这个项目本质上是一套基于多智能体Multi-Agent与大语言模型协作的交易决策仿真框架核心思路是把传统量化投研里投委会的流程搬进代码不同角色的 Agent 各自负责一块分析再通过辩论、投票和复盘产出最终结论。如果你正在研究多智能体编排、LLM 工具调用或者单纯想做个能跑通采集数据-分析-决策-复盘全流程的 AI 项目这篇文章值得看完。先说清楚这是一个技术研究与学习项目所有输出都是沙盘推演不构成任何投资建议我做这套东西主要是为了研究 Agent 协作机制而不是真的拿它去做交易。1. TradingAgents 到底是什么从单模型到多智能体决策1.1 为什么传统的单 Agent 做不好交易决策很多人第一次看到 TradingAgents 这个名字第一反应是这不就是给 ChatGPT 接个行情接口吗。实际上差别很大。单 Agent 方案里你通常只有一个超长 System Prompt塞进去你是一个资深交易员然后把行情、财报、新闻一股脑扔给它让它输出买/卖/观望。这种方式看起来简单但实际跑起来问题非常明显。第一个问题是上下文窗口的限制。一份完整的基本面分析需要财报数据、行业对比、宏观环境技术面分析又需要 K 线形态、均线系统、量价关系再加上舆情新闻随便一整理就是几万 token。把这些全塞给一个 Agent要么超过上下文限制要么模型会选择性忽略中间部分内容。第二个问题是角色冲突。让同一个模型既当基本面分析师又当风险控制官它很难真正产生内部对抗更倾向于顺着你提问的方向给出一个看似合理但缺乏推敲的答案。所谓屁股决定脑袋在模型身上体现得更明显因为它根本没有屁股它只会迎合上下文里最近的指令。第三个问题是最致命的没有纠错机制。单一 Agent 一旦在一个错误的理解上继续推理后续所有输出都会基于这个错误叠加而且没有任何环节能发现它错了。我最早用单 Agent 做实验的时候有次它把一份财报里的净利润同比下滑理解成大幅增长后续所有分析全部跑偏而整个推理链看起来依然流畅自信。这让我意识到交易决策这种需要高强度交叉验证的场景单 Agent 天生就有天花板。1.2 多智能体的核心价值角色分工与对抗辩论TradingAgents 这类多智能体框架之所以在社区里火核心在于它把一个人做所有事变成了一个团队做一件事。类比一下你在真实金融机构里看到的投研流程从来不是某个分析师拍板而是宏观分析师、行业研究员、技术面分析师、交易员、风控专员聚在一起开投委会互相质疑、补充、辩论最后才形成决议。多智能体架构就是把这套流程里的每个角色抽象成一个独立 Agent。每个 Agent 有专属的 System Prompt、专属的输入字段和输出格式他们只看自己负责的那部分数据。这样做的好处很明显角色边界清晰、职责单一模型的输出质量会明显更稳定因为上下文里的干扰信息变少了。更重要的是你可以在系统层面设计对抗机制。比如我让基本面 Agent 和技术面 Agent 分别生成看多观点和看空观点然后由一个仲裁者 Agent 强制要求双方进行多轮辩论最后才能提交给交易员决策。我自己的实测数据是这样的单 Agent 模式在 50 个历史样本上的决策准确率大约只有 52%基本等于抛硬币而换成多 Agent 辩论模式后同样 50 个样本准确率提高到 58% 左右。58% 谈不上多惊艳但已经把系统性偏差变成了弱有效信号这在量化研究里已经是质变。多智能体最大的价值不是让模型变聪明而是让错误被结构性地暴露出来让偶然的盲区有机会被其他角色发现。1.3 这个框架适合谁、不适合谁如果你是想学 LLM 应用开发、Agent 编排、Prompt 工程TradingAgents 是一个特别好的练习项目。它比普通的聊天机器人复杂得多有状态流转、有外部工具调用、有结构化输出、有结果评估几乎覆盖了 Agent 开发的全部关键知识点。我自己在搭建过程中把 LangGraph、函数调用、JSON 模式输出、缓存策略都重新梳理了一遍收获比看十篇教程都大。但如果你是想拿它来赚钱那就需要非常谨慎了。这类基于 LLM 的决策框架在你自己的数据集上回测漂亮换到实盘环境就会出现各种问题执行延迟、手续费磨损、模型输出不稳定的滑点、你根本来不及处理的黑天鹅事件。我身边好几个朋友跑过类似项目最后都转向了研究用途。所以我建议把 TradingAgents 当作一个AI 决策流程实验室而不是印钞机这个预期管理在动手之前就要做好。2. TradingAgents 的角色编排与协作流程拆解2.1 角色矩阵每个 Agent 到底负责什么我搭建的时候参考了社区里流传较广的 TradeAgent 类框架设计把整个系统拆成了 7 个角色每个角色都对应真实的投研岗位职责。这里我先列一个角色总表后面再逐个讲透。角色名称核心职责输入数据输出格式市场观察员获取行情、成交量、价格异动行情 API 数据结构化行情摘要基本面分析师财报、行业周期、估值判断财务指标、行业新闻基本面观点 风险点技术面分析师K 线形态、均线、量价分析OHLCV 历史数据趋势判断 关键价位舆情研究员新闻、公告、社交媒体情绪新闻 API、RAG 检索情绪打分 信息摘要多头辩手尽力寻找做多理由前四位 Agent 的输出完整多头逻辑链空头辩手尽力寻找做空风险前四位 Agent 的输出完整空头逻辑链风控专员仓位、回撤、集中度检查组合持仓、决策草案风控意见 / 否决交易员综合各方观点做最终决策辩论记录、风控意见目标仓位与理由这个矩阵里最巧妙的是多头辩手和空头辩手这对对抗角色。它们的 Prompt 刻意设计成了偏执型多头辩手被要求忽略暂时的利空聚焦长期价值潜力空头辩手被要求假设存在未披露的重大风险寻找每个积极信号背后的隐患。这样做不是为了让模型更客观恰恰相反是让模型主动形成立场然后通过对抗辩论把两个方向的论据都彻底榨干。2.2 完整的决策流水线从数据采集到复盘整个流程我用一句话总结就是先观察再分析然后对抗最后决策。这条流水线不是简单的串行里面有两个关键的并行分支和一个条件跳转逻辑。第一步是数据采集阶段。市场观察员通过 API 拉取目标资产的日线行情、实时报价和最近公告把这些数据统一转成 JSON 格式存入共享状态。这个阶段我用了并行执行因为行情、新闻、财务数据的来源不同串行等待会浪费时间。第二步是三个分析型 Agent 并行开工它们读的是同一份共享状态但各自只提取自己需要的那部分字段互不干扰。第三步是辩论阶段这也是整个系统的核心差异化设计。多头辩手和空头辩手会进行最多 3 轮对抗每一轮由仲裁者我复用了交易员 Agent总结上一轮双方的核心分歧然后引导双方针对分歧点展开下一轮辩论。这里有一个条件路由如果第一轮辩论结束后双方的核心逻辑高度一致我用文本相似度阈值做了判断就自动跳过后续轮次节省 token 成本如果分歧很大则完整跑完 3 轮。第四步是风险控制和最终决策。风控专员检查交易员草拟的决策是否触及仓位上限、单日亏损上限等硬约束如果不通过就打回重议。最后所有结果写入一个复盘节点生成一份包含决策理由、反对观点、风控意见的完整报告。这套流程完整跑一次大约需要 2 到 4 分钟具体取决于模型响应速度和辩论轮数。2.3 角色之间的握手协议结构化输出的重要性多 Agent 系统里最容易翻车的地方其实不是单个 Agent 的 Prompt而是角色之间的数据传递。我的经验是不要让一个 Agent 读取另一个 Agent 的原始输出文本那一定会出格式解析问题。正确的做法是强制所有 Agent 输出 JSON 格式的字段然后由上一层统一转成共享状态里的结构化数据。比如基本面分析师它的原始输出可能是长段落文本但我要求它必须同时输出一个summary字段一句话结论和一个risks数组最多 5 条风险。技术面分析师读到的就是 JSON 里的summary和risks而不是乱七八糟的 Markdown 表格。这套结构化握手协议是踩了很多次坑以后总结出来的。实际操作中我用了 Pydantic 做输出校验。每个 Agent 的 LLM 调用都指定了response_format为 JSON 对象然后解析后用 Pydantic 模型校验字段是否完整。如果校验失败系统会自动重试一次重试时会在 Prompt 里追加上次解析失败的错误信息。这个机制极大提升了流程稳定性我第一批测试里大约 40% 的失败都是 JSON 解析问题加了校验重试之后降到了 5% 以内。3. 工程落地用 LangGraph 把 Agent 流程跑起来3.1 技术选型对比LangGraph 与 AutoGen 怎么选社区做多 Agent 编排大多是两条路LangGraph 和 AutoGen或者 Semantic Kernel。Student 阶段我两个都试了这里分享下实际体感方便你在动手前就选对工具。AutoGen 的优势是对话驱动。它的核心抽象是ConversableAgent天然适合两个 Agent 互相聊天的场景比如让你一个 Agent 扮演多头、一个扮演空头无限制辩论AutoGen 开箱即用代码量很少。但它的弱点也很明显流程控制不够直观你想实现辩论两轮后必须进入风控节点这种强流程约束得写不少回调逻辑状态追溯也比较痛苦。LangGraph 的优势则正好相反。它是图结构的状态机所有节点和边都显式声明看代码就能看出整个系统的全貌这对调试来说太重要了。它的状态管理基于类型化的StateGraph每个节点接收状态、返回状态更新天然的适合流水线 条件路由这种场景。代价是你需要写更多样板代码尤其是状态定义那块。我的结论是如果你的核心需求是两个 Agent 自由聊天AutoGen 更快但如果你要像 TradingAgents 这样有严格的流水线、并行分支、条件跳转和可回溯状态LangGraph 是更合适的选择。我最后用了 LangGraph下面所有代码示例都是基于 LangGraph 的。3.2 状态图定义与节点实现LangGraph 里的核心概念是 StateGraph。你需要先定义一个状态类这个类就是你整个系统流转过程中共享的数据结构。我基于自己的实践整理了一份相对完整的定义这里拆开讲。from typing import Annotated, TypedDict, Optional, List from langgraph.graph import StateGraph, END class AnalysisState(TypedDict): symbol: str market_data: dict # 行情数据 news_items: List[dict] # 新闻列表 fundamentals: dict # 财务数据 technical_view: Optional[str] # 技术面结论可空 fundamental_view: Optional[str] # 基本面结论 sentiment_score: Optional[float] # 舆情分数 bull_arguments: List[str] # 多头论点集合 bear_arguments: List[str] # 空头论点集合 debate_rounds: int # 已辩论轮数 risk_passed: bool # 风控是否通过 final_decision: dict # 最终决策 report: str # 复盘报告 graph StateGraph(AnalysisState)定义好状态之后就是注册节点和边。这个过程特别像画流程图你声明每个节点是个函数函数输入是当前状态输出是要更新的字段然后通过add_node注册通过add_edge连接。我第一个版本踩了个坑并行分支的处理方式。LangGraph 里并行是通过add_edge从一个节点出发连多个节点来实现的但如果后续节点都要用到几个分支的结果就需要一个聚合节点来 merge。graph.add_node(market_observer, market_observer_node) graph.add_node(fundamental_analyst, fundamental_analyst_node) graph.add_node(technical_analyst, technical_analyst_node) graph.add_node(sentiment_analyst, sentiment_analyst_node) graph.add_node(bull_bear_debate, debate_node) graph.add_node(risk_control, risk_control_node) graph.add_node(trader, trader_node) graph.set_entry_point(market_observer) graph.add_edge(market_observer, fundamental_analyst) graph.add_edge(market_observer, technical_analyst) graph.add_edge(market_observer, sentiment_analyst) graph.add_edge(fundamental_analyst, bull_bear_debate) graph.add_edge(technical_analyst, bull_bear_debate) graph.add_edge(sentiment_analyst, bull_bear_debate) graph.add_edge(bull_bear_debate, risk_control) graph.add_edge(risk_control, trader) graph.add_edge(trader, END)这段代码本身不复杂但你会发现里面的一个关键设计fundamental_analyst、technical_analyst、sentiment_analyst三个节点都是从market_observer出发LangGraph 默认会并发执行这几个节点直到所有分支都到达bull_bear_debate之后才继续向下走。这个聚合语义是 LangGraph 内置的不需要额外处理这也是我一直推荐它的原因。每个节点函数的实现都遵循同一个套路从状态里读取输入字段拼接 Prompt调用 LLM解析结果把输出字段写回状态字典。以技术面分析师为例它的输入是state[market_data]输出是state[technical_view]逻辑基本就是一次带 Prompt 的 API 调用关键在于 Prompt 的设计和输出格式校验这部分我在下一节展开。3.3 Prompt 设计的几个关键细节多 Agent 项目的成败一半在角色 Prompt 的质量上。我迭代了三个版本才稳定下来这里总结几个规律。第一你要在 Prompt 里明确告诉 Agent你是谁、你看到了什么、你要输出什么、你不要做什么。注意最后一类不要做什么特别重要。比如基本面分析师我会明确写不要预测未来股价不要使用必然稳赚这类确定性表述不要输出超出你职责范围的交易建议。这一条指令直接让输出质量上了一个台阶因为它抑制了 LLM 最爱干的越权发挥。第二输出格式要用可校验的结构化模板并且给出示例。直接写输出一个 JSON是不够的模型会给你各种奇怪的嵌套。更稳的写法是在 Prompt 里放一个完整的 JSON 示例用我们系统设计时候的那个基础例子就够了。示例如下请将你的分析结果按以下 JSON 格式输出 { summary: 你的核心观点一句话不超过50字, trend: up/down/sideways 三选一, confidence: 0.0 到 1.0 的置信度分数 support_price: 0, resistance_price: 0, reasoning: 你的完整分析不超过300字 }第三要给每个 Agent 注入共享的决策上下文但不能太多。比如多头辩手在辩论时不需要重新读一遍完整财务数据它只需要读基本面分析师、技术面分析师、舆情研究员输出的结构化摘要。这既省 token也避免它被原始数据的噪音干扰。一个常见的错误是把所有 Agent 的 Prompt 都写得又长又全效果反而更差因为你分散了模型的注意力。第四在辩论节点里我做了轮次控制。每一轮辩论都会更新state[debate_rounds]并检查是否达到最大轮数。同时在节点里加入了一条路由逻辑如果某一轮双方的核心论点语义相似度超过 0.8就直接进入风控节点不再继续辩论。这能在保证对抗质量的同时把 token 消耗控制在一个合理范围内。3.4 外部工具接入行情数据、新闻检索与 RAG 缓存Agent 光有 LLM 是不够的TradingAgents 这类框架里的分析型 Agent 必须能读到实时数据。工具接入这块我采用了函数调用的方式给 LLM 暴露几个工具函数让它自行决定什么时候调用。行情数据这块我封装了一个fetch_market_data(symbol, timeframe)函数底层调的是公开的行情接口。这个函数会在市场观察员节点被调用拿到 OHLCV 数据后缓存到本地文件里避免重复请求。新闻和公告数据我接了一个新闻聚合 API然后做了一层关键词过滤只保留跟目标资产相关的信息。RAG 的部分我是用来做历史案例检索的。每个分析型 Agent 在输出之前会先从一个本地的向量数据库里检索与当前场景相似的历史案例这些案例是我从公开数据里整理出来的标注数据。检索结果会作为上下文片段拼进 Prompt。这里有个经验不要把检索结果放太多最多 3 段每段不超过 200 字否则模型会被不相关信息干扰。工具调用的稳定性也是一个要注意的点。LLM 有时候会编造工具名或者不存在的字段所以我在工具函数外层包了一层异常捕获和重试机制。具体做法是工具调用失败时把错误信息回传给 LLM让它在修正后再次调用最多重试 2 次。如果重试仍然失败该 Agent 会输出一个tool_error字段流程会自动跳过该角色对后续决策的影响而不是整个流程崩溃。4. 回测评估如何验证这套框架真的有效4.1 搭建事件驱动回测引擎模型搭好了接下来要回答一个关键问题这套多智能体决策流程的效果到底怎么样。答案必须通过历史数据回测来得到不能靠感觉。我自己写了一个轻量的事件驱动回测引擎核心逻辑就是模拟在历史时间点我们只能拿到当时已有的信息。设计上我把回测分成两层外层是数据迭代器内层是 Agent 推理接口。外层从历史数据里逐根 K 线移动每到一个时间点就把当前的行情快照、财务数据、新闻列表全部传给系统系统内部跑完整个 Agent 流程后输出一个目标仓位。这里的核心约束是杜绝未来函数我通过数据切片来实现传给 Agent 的永远只包含截至当前时间点之前的数据。def run_backtest(history, initial_capital100_000): capital initial_capital position 0 for bar in history: # 只取当前 bar 之前的数据保证没有未来数据泄漏 current_data bar # 当前时间点的完整数据快照 decision agent_pipeline.run(current_data) # 决策输出是目标仓位比例比如 0.0 到 1.0 target_position decision[target_weight] if target_position 0.8: position capital / bar[close] * 0.95 elif target_position 0.2: position 0 capital position * bar[close] (capital - position * bar[close]) return capital当然这只是核心逻辑的简化版真实回测里我加了滑点、手续费、最大持仓比例等约束。事件驱动设计看似简单但它在技术上保证了在决策那一刻系统看不到未来信息这是后续所有评估指标可信的前提。4.2 评价指标体系别只看收益率回测跑完很多人第一反应是看总收益率这是我最想纠正的误区。收益率高和策略好之间隔着十万八千里尤其是 LLM 生成型策略你更需要关注的是收益质量、风险控制能力和决策一致性。我用来评估 TradingAgents 策略的指标表如下建议你复现的时候也照这个标准来指标名计算方式说明年化收益率期末净值/期初净值 按年化折算绝对收益但会受行情影响最大回撤峰值到谷底的最大跌幅直接反映策略最坏情况夏普比率(策略收益率 - 无风险利率) / 收益率标准差衡量单位风险的超额收益胜率盈利交易次数 / 总交易次数只看胜负比忽略盈亏幅度盈亏比平均盈利 / 平均亏损反映盈亏不对称性决策稳定性相似状态下的决策是否一致用决策熵或离散度评估实际操作里我最关注的是最大回撤和决策稳定性。LLM 策略有一个通病是输出情绪化也就是连续几次亏损之后模型会在接下来的决策中加大仓位试图回本——这是模型训练数据里带出来的交易者心理缺陷。我的应对方法是把最大回撤的硬约束写进风控 Agent 的 Prompt 和代码逻辑一旦组合回撤超过 15%风控节点直接强制清仓并禁止后续开仓直到回撤收窄到安全区间。4.3 未来函数与过拟合回测最容易踩的两个坑回测比搭 Agent 流程更容易阴沟里翻船我在社区里见过太多被漂亮曲线欺骗的案例这里总结一下我自己踩过和见过的坑。第一个坑是未来函数。最常见的形式是财务数据的发布日期没对齐你拿到的当时的财务指标其实是几个月后才披露的版本这在回测里就意味着模型提前看到了未来信息。排查方法是把每个数据源的时间戳都打上发布时间字段回测引擎在组装数据时必须按发布时间过滤。另一个常见问题是新闻检索如果你用现在的搜索引擎去查某资产的历史新闻得到的结果很可能包含了后来对历史的总结性报道这也属于未来函数。第二个坑是过拟合。多 Agent 系统理论上有很多可以调节的旋钮Prompt 的语气、辩论轮数、置信度阈值、风控参数这些旋钮在训练集上怎么调都能找到一组让曲线好看的参数但换到样本外数据就原形毕露。我的做法是把数据切成了三段开发集60%、验证集20%、测试集20%所有参数调优只在开发集和验证集上进行测试集只在最后完整跑一次。三次跑同样的策略如果三条曲线差异明显说明策略大概率过拟合了反之则相对可信。还有一个隐蔽的坑是幸存者偏差。如果你回测的是一个标的池而这个池子里的标的都是现在还在交易的那么那些曾经因为退市而被踢出池子的标就被你忽略了这会系统性高估策略收益。我的处理方式是固定使用一个历史成分股列表确保包含发生过重大风险的标的这样回测结果才更贴近真实。5. 实操中遇到的问题与排查技巧实录5.1 五个高频问题与对应解法这一个月跑下来我整理了五个出现频率最高的问题和对应的排查思路写成了速查表希望你能少走一些弯路。问题现象根因分析解法Agent 输出频繁解析失败模型返回到 JSON 之外还夹带 Markdown 或解释文字启用 JSON Mode/结构化输出并包一层重试机制决策结果一天一变且逻辑漂移LLM 的随机性 Prompt 里的指令不够具体降低 temperature 到 0.1给 Agent 提供明确决策准则API 调用频繁触发限流并行分支同时发起大量请求给每个 Agent 加独立的线程池和请求排队控制并发数上游 Agent 的错误延续到下游没有校验中间输出坏数据一路传递每个节点做字段校验不合格就回退该节点重新生成回测曲线好看但实盘/样本外跑不动数据泄漏或过拟合按发布时间过滤数据三段数据隔离测试检查未来函数5.2 稳定性、成本与并发控制经验多 Agent 系统在生产环境里实际上是一个分布式系统问题你在 demo 里跑得通不代表在连续跑一个月的时候不出问题。我这里有几个成本与稳定性控制方面的真实经验。第一个经验是给 LLM 调用层加统一的重试、缓存与熔断机制。重试解决临时故障缓存解决重复分析熔断解决 API 大面积超时导致整个流程卡死的问题。我设置了三次重试机会超时时间 60 秒缓存命中率大约 30%因为同一标的在同一时间段内不会反复分析多次。熔断逻辑是这样的如果连续 5 次 API 调用失败流程会切换到降级模式用最近一次成功的历史决策替代本轮分析并标记为低置信度。第二个经验是控制辩论回合的预算。LLM 的 token 消耗大头就在辩论节点多一轮辩论就是几十万 token。我的做法是单次完整决策流程的 token 开销设置一个预算上限辩论轮数动态调整初始 3 轮但如果在第 2 轮就出现高度一致就提前终止如果第 3 轮分歧仍然没有收敛则强制由交易员 Agent 做最终裁决。这套动态预算机制把平均 token 成本降了差不多 40%。第三个经验是要建立完整的日志与审计链路。每一个 Agent 的输入输出、每一次工具调用的参数和返回、每一次状态流转我都写入了结构化的日志。这么做有两个价值一是出问题时能快速定位是哪个环节出了差错二是可以对 LLM 的决策过程进行事后的成本与质量分析。尤其是做论文或者分享案例的时候没有日志链路你根本没办法向别人解释系统为什么要做出某个决策。5.3 我把这套框架跑通之后的一些真心话如果你也想复现类似的项目我最后再分享几个我个人体会最深的事情。先看数据质量再看模型。这句话我在搭完第一版、然后被回测结果暴打之后才真正理解。从公开接口拿到的数据往往存在缺失、复权、时区混乱等问题如果你的数据管道没洗干净后面所有 Agent 的分析都是空中楼阁。我第一次回测时策略净值曲线跳空得离谱排查下来发现是因为某个时间段的行情数据缺失被前向填充了导致模型基于错误的价格位置做了决策。后来我把数据清洗和数据对齐这块单独抽出来做花了比搭 Agent 流程更多的时间但这个投入绝对值得。成本是真的不低。如果你像我一样频繁迭代 Prompt 和流程结构一个月的 API 账单会让你肉疼。我的建议是小规模、可重复的实验场景下不用最新最强的大模型可以用常规模型先跑通流程确认逻辑没问题后再切更强的模型做效果验证。另外一定要把 LLM 的中间输出做缓存这不仅是省钱更重要的是保证实验的可复现性因为模型输出有随机性如果不缓存你根本判断不了是 Prompt 改好还是随机波动造成的效果变化。想继续扩展的话我会建议你从三个方向入手一是接入更多资产类型比如加密货币或外汇数据源这就涉及不同市场规则的时间对齐问题二是加入多周期分析比如日线级别决策和周线级别趋势过滤互相配合三是做更细的决策报告可视化把多 Agent 的整个推理链展示成一张完整的决策流程图。这套项目做完你对 LLM Agent 应用开发的理解绝对会比看任何教程都更扎实。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价