
1. 多 Agent 炒股这件事到底在解决什么问题第一次看到“多 Agent 炒股”这个说法很多人脑子里冒出来的画面大概是几个机器人围在一张桌子前盯盘、吵架、下单。这个想象不算离谱但真正落到工程实现上它要解决的核心问题其实非常朴素单一大模型做交易决策时视角太单一、容易拍脑袋、缺乏交叉验证。我自己最早用单个 LLM 跑行情分析的时候最大的感受就是“它什么都敢说”。你给它一段 K 线数据它能给你编出一套看起来逻辑自洽的买入理由但换一天同样的数据它可能给出完全相反的结论。这不是模型笨而是单 Agent 架构天然缺少制衡机制——没有人和它唱反调它就没有动力去审视自己的假设。多 Agent 编排要解决的就是把这个“一个人拍板”的过程拆成“一群人各司其职、互相质疑、最后投票或汇总”的过程。TradingAgents 这类项目之所以能冲到十万级 Star本质上是因为它踩中了一个真实痛点大家想让 AI 真的下地干活而不是只在聊天框里纸上谈兵。这篇文章我会围绕 TradingAgents 这个项目把多 Agent 炒股系统的整体设计思路、LangGraph 编排的核心机制、CLI 交互方式、回测验证方法以及我在实际搭建过程中踩过的坑完整地拆一遍。适合两类人看一类是想理解多 Agent 编排到底怎么落地的开发者另一类是想把 AI 用到量化交易场景里、但不知道从哪下手的朋友。哪怕你之前没接触过 LangGraph跟着思路走也能明白个七八成。2. 整体架构设计为什么是“多 Agent LangGraph CLI”这套组合2.1 单 Agent 为什么不够用先说说单 Agent 方案的死穴。假设你让一个 Agent 同时负责“看财报、读新闻、分析技术指标、判断情绪、决定买卖”它会面临几个问题。第一是上下文污染。财报数据、新闻标题、技术指标混在一个 prompt 里模型很容易被最近读到的信息带偏。比如刚读完一条利空新闻它可能就忽略了技术面上明显的支撑位。第二是角色冲突。一个 Agent 既要做多又要做空既要激进又要保守最后往往变成“和稀泥”给出的建议模棱两可。第三是无法追溯。决策错了你根本不知道是哪一步推理出了问题因为所有思考都揉在一段输出里。多 Agent 的思路就是把这些问题拆开让不同的 Agent 承担不同的角色各自有独立的上下文和明确的职责边界最后通过一个协调机制汇总。这就像一家投资公司里研究员、交易员、风控各干各的谁也别想一个人说了算。2.2 LangGraph 在中间扮演什么角色多 Agent 说起来简单但“谁先跑、谁后跑、谁的结果传给谁、出现分歧怎么办”这些问题需要一个编排框架来管。LangGraph 就是干这个的。它把整个流程建模成一张有向图节点是 Agent 或工具调用边是数据流向和条件分支。相比传统的链式调用LangGraph 最大的好处是支持循环和条件跳转。比如“看多 Agent”和“看空 Agent”辩论三轮如果还没达成一致就交给“裁判 Agent”拍板——这种带循环的逻辑用普通 Chain 很难写用 LangGraph 就是一个带条件的回边。我选 LangGraph 而不是自己手写状态机主要看中三点状态管理是内置的多个 Agent 共享一个 State 对象不用自己维护全局变量支持检查点跑一半崩了能恢复和 LangChain 生态无缝衔接工具调用、模型切换都很顺。2.3 CLI 为什么比 Web 界面更适合这个场景很多人第一反应是做个网页界面多好看。但真正跑过交易分析的人会告诉你CLI 才是效率最高的交互方式。原因很实际交易分析往往是批量、重复、需要脚本化的。你今天想跑 50 只股票的多 Agent 分析用网页得点 50 次用 CLI 一行命令加个循环就搞定了。而且 CLI 的输出可以直接重定向到文件、管道给下一个工具方便做后续处理。TradingAgents 的 CLI 设计得比较克制核心就是几个命令指定股票代码、选择分析深度、指定时间范围、输出格式。这种“少即是多”的设计反而让它更容易被集成进已有的工作流。2.4 整体数据流长什么样把上面几块拼起来一个典型的多 Agent 炒股流程是这样的用户通过 CLI 输入股票代码和分析参数数据层拉取行情、财报、新闻等原始数据多个分析 Agent 并行或串行处理不同维度的信息多空双方 Agent 基于分析结果展开辩论裁判 Agent 综合辩论内容给出最终决策决策结果连同推理过程一起输出可选写入回测系统这个流程里LangGraph 负责把 3 到 5 步串起来State 对象在节点之间传递每个 Agent 读取自己需要的字段、写入自己的结论。下面这张表能帮你快速理解各角色的分工角色职责输入输出数据 Agent拉取并清洗原始数据股票代码、时间范围结构化行情/财报/新闻技术分析 Agent计算指标、识别形态行情数据技术面多空信号基本面 Agent解读财报、估值财报数据基本面评分情绪 Agent分析新闻与舆情新闻文本情绪倾向多头 Agent构建看多论证上述分析结果买入理由空头 Agent构建看空论证上述分析结果卖出理由裁判 Agent综合辩论给结论多空论证最终决策与置信度3. 核心细节拆解LangGraph 编排与 Agent 通信的实操要点3.1 State 设计多 Agent 共享的“白板”LangGraph 里最关键的抽象是 State。你可以把它理解成一块白板所有 Agent 都能往上写、也能从上面读。设计得好Agent 之间通信就很顺设计得烂就会出现字段冲突、数据覆盖。我一般会把 State 设计成几个区块原始数据区、分析结果区、辩论记录区、最终决策区。每个 Agent 只写自己负责的区块读的时候按需读取。这样职责清晰调试的时候也容易定位问题。from typing import TypedDict, Annotated from operator import add class TradingState(TypedDict): ticker: str market_data: dict technical_signal: str fundamental_score: float sentiment: str bull_arguments: Annotated[list, add] bear_arguments: Annotated[list, add] debate_round: int final_decision: str confidence: float注意bull_arguments和bear_arguments用了Annotated[list, add]这是告诉 LangGraph 这两个字段用“追加”而不是“覆盖”的方式更新。辩论是多轮的每轮都要保留记录如果用默认覆盖第二轮就会把第一轮的内容冲掉。这个坑我踩过当时调试了半天才发现是 State 更新策略的问题。3.2 节点与边的编排逻辑节点就是一个个 Agent 函数边决定执行顺序。TradingAgents 这类系统的编排通常分三段并行分析段、辩论段、决策段。并行分析段里技术、基本面、情绪三个 Agent 可以同时跑因为它们互不依赖。LangGraph 支持从同一个节点发散出多条边实现并行。辩论段则是一个循环结构多头发言、空头发言、判断是否继续如果轮次没到上限且分歧还大就回到多头继续。from langgraph.graph import StateGraph, END graph StateGraph(TradingState) graph.add_node(fetch_data, fetch_data_node) graph.add_node(technical, technical_node) graph.add_node(fundamental, fundamental_node) graph.add_node(sentiment, sentiment_node) graph.add_node(bull, bull_node) graph.add_node(bear, bear_node) graph.add_node(judge, judge_node) graph.set_entry_point(fetch_data) graph.add_edge(fetch_data, technical) graph.add_edge(fetch_data, fundamental) graph.add_edge(fetch_data, sentiment) graph.add_edge([technical, fundamental, sentiment], bull) graph.add_edge(bull, bear) graph.add_conditional_edges(bear, should_continue_debate, { continue: bull, judge: judge }) graph.add_edge(judge, END)add_edge的第一个参数传一个列表表示要等这几个节点都跑完才进入下一个节点。这是 LangGraph 做“扇入”的标准写法。should_continue_debate是一个普通函数根据debate_round和分歧程度返回continue或judge。3.3 工具调用让 Agent 真的能查数据Agent 光会说话没用得能调工具。LangGraph 里工具调用一般走 LangChain 的 Tool 接口。比如给技术分析 Agent 配一个计算 MACD 的工具、给情绪 Agent 配一个新闻搜索工具。这里有个经验工具的描述要写得非常具体。模型决定调不调工具、调哪个工具全靠工具描述。我见过有人把工具描述写成“获取数据”结果模型经常调错。改成“根据股票代码获取最近 30 天的日线行情返回开盘价、收盘价、成交量”调用准确率立刻上去了。from langchain_core.tools import tool tool def get_stock_price(ticker: str, days: int 30) - dict: 根据股票代码获取最近 N 天的日线行情数据。 返回包含 date, open, close, high, low, volume 的列表。 days 默认 30最大 365。 # 实际实现略 return {ticker: ticker, data: [...]}3.4 辩论机制怎么让多空双方真的吵起来辩论是多 Agent 炒股最有意思的部分也是最容易做砸的部分。如果两个 Agent 的 prompt 写得太像它们会互相附和辩论就变成了走过场。我的做法是给多空双方设定对立的人格和约束。多头 Agent 的 prompt 里明确要求“你必须找出至少三条支持买入的理由并反驳空头的每一条论点”空头 Agent 则相反。同时给它们不同的信息侧重多头多看增长数据空头多看风险指标。还有一个技巧是限制发言长度。不限制的话Agent 会写出一大段废话既费 token 又稀释重点。我一般限制在 200 字以内逼它说重点。3.5 裁判 Agent 的决策逻辑裁判 Agent 不是简单投票而是要做加权综合。它需要读取多空双方的论证评估哪方的证据更扎实然后给出决策和置信度。这里有个细节置信度不能让它随便给。我试过直接问“你有多确信”模型经常给 0.9 这种虚高的数字。后来改成让它先列出支持决策的关键证据数量、反驳成功的论点数量再基于这些计数给出置信度数字就靠谱多了。4. 从零跑通一个多 Agent 分析完整实操流程4.1 环境准备与依赖安装先把环境搭起来。Python 3.10 以上主要依赖是 langgraph、langchain、langchain-openai或你用的其他模型 SDK、pandas、以及数据源相关的库。python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install langgraph langchain langchain-openai pandas yfinance模型 API Key 通过环境变量注入不要硬编码在代码里。这一点是基本安全习惯尤其是你要把代码传到公开仓库的时候。export OPENAI_API_KEYyour-key-here4.2 数据层搭建行情、财报、新闻三路数据数据是整个系统的地基。行情数据我用 yfinance 快速起步财报数据可以用现成接口新闻数据可以接搜索工具。关键是统一数据格式让下游 Agent 不用关心数据从哪来。我一般会定义一个fetch_all_data(ticker)函数返回一个字典包含price_df、financials、news_list三个字段。这样数据层和 Agent 层就解耦了以后换数据源只改这一个函数。4.3 逐个 Agent 的实现与调试不要一上来就把所有 Agent 写完再跑那样出问题很难定位。我的习惯是一次只加一个 Agent跑通再加下一个。先写数据 Agent确认能拿到正确数据。再加技术分析 Agent单独调用它看输出是否合理。然后加基本面、情绪。等这三个都稳了再加多空辩论。最后加裁判。每加一个 Agent都用同一只股票、同一时间段测试这样能对比出新增 Agent 带来的变化。调试单个 Agent 的时候可以直接调用它的节点函数不用跑整张图省时间。4.4 组装 LangGraph 并首次运行所有节点写好后按第 3 节的编排逻辑组装图。首次运行建议开debugTrueLangGraph 会打印每个节点的输入输出方便你观察数据流转。app graph.compile() result app.invoke({ ticker: AAPL, debate_round: 0, bull_arguments: [], bear_arguments: [] }) print(result[final_decision]) print(result[confidence])第一次跑大概率会遇到字段缺失、类型不匹配的问题。别慌看 debug 输出定位到具体节点检查它的输入字段是不是上游没写进去。4.5 CLI 封装让分析可以批量跑核心逻辑跑通后用 argparse 或 click 包一层 CLI。我推荐 click写起来更清爽。import click click.command() click.option(--ticker, requiredTrue, help股票代码) click.option(--rounds, default3, help辩论轮次) click.option(--output, defaultresult.json, help输出文件) def analyze(ticker, rounds, output): 对指定股票运行多 Agent 分析 result app.invoke({ ticker: ticker, debate_round: 0, bull_arguments: [], bear_arguments: [] }) with open(output, w) as f: json.dump(result, f, ensure_asciiFalse, indent2) click.echo(f决策: {result[final_decision]}, 置信度: {result[confidence]})封装好之后批量分析就是一行 shell 循环的事for code in AAPL MSFT GOOG; do python analyze.py --ticker $code --output ${code}_result.json done4.6 回测验证用 backtrader 检验决策质量Agent 说买不代表真的该买。必须回测。我用 backtrader 做多股回测思路是把多 Agent 的决策信号转成买卖信号喂给 backtrader 的策略类。关键点在于避免未来函数。多 Agent 分析用到的数据必须严格限制在决策时点之前。我见过有人图省事把整段历史数据一次性喂给 Agent结果回测收益高得离谱实盘一跑就亏——因为 Agent “偷看”了未来。回测时还要注意交易成本。A 股有印花税和佣金美股有佣金和滑点。不扣成本的回测结果都是耍流氓。我一般按单边 0.1% 到 0.2% 估算具体看标的。回测参数建议值说明初始资金100000便于计算收益率单边成本0.1%-0.2%含佣金与滑点持仓上限单票不超过 20%控制集中度风险回测区间至少 2 年覆盖不同市场环境基准沪深300 或标普500对比超额收益5. 常见问题与排查技巧实录5.1 Agent 输出格式不稳定怎么办这是最高频的问题。模型有时候返回 JSON有时候返回带 markdown 代码块的 JSON有时候干脆返回一段散文。解决办法有三层第一在 prompt 里明确要求“只返回 JSON不要任何其他文字”第二用 Pydantic 定义输出结构配合模型的 structured output 功能第三加一层解析容错用正则先把 JSON 抠出来再解析。我实测下来structured output 加解析容错双保险最稳。纯靠 prompt 约束十次里总有一两次翻车。5.2 辩论陷入死循环怎么破如果多空双方谁也不服谁should_continue_debate一直返回 continue图就跑不完了。必须设硬性上限比如最多 5 轮到点强制进裁判。另外可以在分歧度计算里加个阈值双方论点重合度高于某个值就认为“没必要再吵了”直接进裁判。5.3 工具调用失败或超时数据源接口不稳定是常态。我的做法是给每个工具调用加重试和降级。重试三次还失败就返回一个“数据不可用”的标记让 Agent 知道这块信息缺失而不是直接崩掉整个流程。Agent 拿到缺失标记后可以在论证里说明“因数据缺失此维度未纳入考量”反而更诚实。5.4 回测结果好得不像真的先别高兴大概率是未来函数或者幸存者偏差。检查三件事Agent 用到的数据时间戳是否都早于决策时点股票池是否包含了已退市的票交易成本是否扣了。这三项都确认没问题再谈策略有效性。5.5 常见问题速查表问题现象可能原因排查方向图跑不完辩论无终止条件检查 should_continue 逻辑字段丢失State 更新策略错误检查 Annotated 配置决策反复横跳温度参数过高降低 temperature 到 0.2 以下工具调用错乱工具描述模糊细化工具 docstring回测虚高未来函数/未扣成本核对时间戳与成本设置输出解析失败格式约束不足加 structured output5.6 几个我踩过的坑第一个坑是并行节点的 State 写入冲突。技术、基本面、情绪三个 Agent 并行跑如果它们都往同一个字段写后写的会覆盖先写的。解决办法是给每个 Agent 分配独立字段或者用Annotated指定合并策略。第二个坑是模型温度设太高。做交易决策温度必须低我一般设 0.1 到 0.2。温度高了同样的输入每次输出都不一样回测根本没法复现。第三个坑是忽略 token 成本。多 Agent 多轮辩论token 消耗是单 Agent 的好几倍。跑批量分析前先用一两只股票估算单次成本心里有数再铺开。我一开始没算跑了一晚上账单出来吓了一跳。6. 多 Agent 炒股系统的边界与我的实际体会说了这么多技术细节最后想聊聊这类系统的边界。多 Agent 炒股不是印钞机它解决的是“决策过程的结构化和可追溯”不是“预测涨跌”。市场里充满随机性再好的分析框架也不能保证盈利。我自己的用法是把它当成一个辅助研究工具让它帮我快速梳理一只股票的多空逻辑把散落在各处的信息整合成结构化报告然后我自己做最终判断。它最大的价值是省时间、减少遗漏而不是替我做决定。另外这套多 Agent 编排的思路其实不限于炒股。任何需要多视角交叉验证的决策场景——比如选品、投资尽调、方案评审——都可以套用同样的架构多个角色独立分析、结构化辩论、裁判综合。LangGraph 提供的编排能力是通用的炒股只是它一个比较抓眼球的应用。如果你打算动手我的建议是从最小可用版本开始先跑通“数据 单分析 Agent 裁判”的三节点流程确认整条链路通了再逐步加角色、加辩论、加回测。一上来就追求完整的多 Agent 系统很容易在调试地狱里放弃。先把一个 Agent 调明白比同时调七个 Agent 高效得多。