1. 项目概述:当AI研究团队遇上股票分析

最近在折腾一个挺有意思的东西,我把它叫做“AI驱动的股票研究流水线”。核心想法很简单:能不能让几个AI智能体(Agent)像一支专业的研究团队一样,分工协作,自动完成从数据获取、图表分析到报告生成的全过程?听起来有点科幻,但用现有的工具链,比如CrewAI和几个成熟的图表库,完全能搭出一个可用的原型。

这个项目的核心价值在于,它试图解决一个传统量化或基本面分析中的痛点:信息过载与流程割裂。一个研究员每天要看无数财报、新闻、技术图表,不同来源的数据格式不一,分析视角也不同,整个过程耗时费力且容易遗漏关键信息。而Multi-Agent系统,恰恰擅长将复杂任务分解,让擅长不同领域的“AI专家”各司其职,串联成一个高效、自动化的分析流水线。它不是为了替代人类决策,而是作为一个强大的“副驾驶”或“研究助理”,提供更全面、更及时的数据洞察支持。

2. 核心架构与工具选型解析

2.1 为什么是CrewAI?

市面上做Agent编排的框架不少,比如LangChain的Agent、AutoGen等。我选择CrewAI作为核心编排框架,主要基于几个实际考量:

第一,它的“团队”和“任务”抽象非常直观。 在CrewAI里,你定义“角色”(Role)就像招聘员工,指定他的职责、目标和背景;然后创建“任务”(Task),描述具体要干什么、交付什么成果;最后把这些角色和任务组装成一个“团队”(Crew),并设定他们如何协作(是顺序执行,还是某个任务完成后触发下一个)。这种模型与我们组织一个股票研究团队(比如有首席分析师、数据工程师、图表专家)的思维方式是完全吻合的,开发起来心智负担小。

第二,对复杂工作流的原生支持好。 CrewAI内置了任务依赖和上下文传递机制。比如,我可以让“数据收集Agent”先运行,它产出的数据摘要会自动成为“技术分析Agent”的输入上下文。这避免了我们需要手动在各个Agent之间管道式地传递数据,让工作流的设计更清晰。

第三,与现有生态结合方便。 CrewAI底层可以兼容多种LLM(如OpenAI GPT、Anthropic Claude、本地部署的Ollama模型),同时也容易集成外部的工具(Tools)。这意味着我的股票研究Agents不仅能进行推理和文本生成,还能调用真实的金融数据API、执行计算等。

注意: 选择LLM时需要权衡成本、速度与能力。对于金融文本分析,需要模型有较强的推理和信息提取能力。GPT-4虽然贵但效果稳定;如果追求性价比或数据隐私,可以考虑Claude 3系列或本地部署的DeepSeek等开源模型。

2.2 图表库的选择:Plotly vs. Matplotlib vs. 专业金融库

可视化是股票研究的眼睛。选对图表库至关重要,它直接关系到最终报告的专业性和交互体验。

  1. Plotly/Dash:交互式分析的利器

    • 优势: 生成的是交互式图表(HTML),可以缩放、平移、查看数据点详情。这对于技术分析尤其有用,研究员可以动态查看特定时间点的价格、成交量。结合Dash框架,甚至可以构建一个完整的实时数据监控仪表盘。
    • 适用场景: 生成最终的研究报告HTML文件,或构建内部分析看板。让图表“活”起来。
    • 实操心得: Plotly的 plotly.graph_objects (go) 模块功能强大,但学习曲线稍陡。对于经典的K线图(Candlestick),直接使用 go.Candlestick 非常方便,而且可以轻松叠加移动平均线、布林带等指标。
  2. Matplotlib/Seaborn:静态报告的基石

    • 优势: 极其稳定、高度可定制,是Python绘图的“标准答案”。你可以控制图表的每一个像素,输出出版级质量的PNG、PDF图片。Seaborn基于Matplotlib,在统计图表方面更美观、更便捷。
    • 适用场景: 需要嵌入到PDF报告、PPT或学术论文中,对图表样式有极其精确要求的场景。
    • 踩过的坑: 中文显示问题是个老麻烦,需要额外设置字体。虽然也能做交互,但需要结合其他工具,不如Plotly原生支持那么顺畅。
  3. 专业金融库:mplfinance, ta-lib

    • mplfinance: 专门为金融图表而生,几行代码就能画出专业的K线图,并内置了大量技术指标的可视化(如MACD, RSI)。它是基于Matplotlib的,所以输出是静态图。
    • ta-lib: 严格来说它不是绘图库,而是技术指标计算的“圣经”。几乎所有常见指标(MACD, RSI, Bollinger Bands, ATR等)都有高效且准确的实现。 我的标准做法是:用ta-lib计算指标数据,然后用Plotly或mplfinance来画图。 避免自己重复造轮子,保证指标计算的正确性。

我的选型结论: 在这个Multi-Agent项目中,我倾向于 使用Plotly作为主要图表生成引擎 。因为Agent产出的最终报告,我更希望它是交互式的、便于探索的。同时,在Agent内部进行指标计算时,会 调用ta-lib库 确保专业性。Matplotlib/mplfinance作为备选,用于需要生成静态简报图片的场景。

3. 构建股票研究AI智能体团队

一个完整的研究流程需要多种角色。我为这个项目设计了四个核心AI智能体。

3.1 角色一:数据收集与整理Agent

这是流水线的起点。它的职责是获取原始数据并进行初步清洗和结构化。

  • 角色定义: “金融数据工程师”。背景设定为擅长从多种API和源头抓取、清洗和标准化金融时间序列数据。
  • 核心工具:
    • yfinance :免费获取股票历史价格、基本面数据的神器,非常方便。
    • pandas_datareader :另一个数据源,可以作为备选。
    • pandas :进行数据清洗、重采样(如将日线数据转为周线)、处理缺失值的核心。
  • 任务流程:
    1. 接收输入(例如股票代码 AAPL ,时间范围 2023-01-01 2024-05-01 )。
    2. 调用 yfinance.download() 获取OHLCV(开盘、最高、最低、收盘、成交量)数据。
    3. 检查数据完整性,处理节假日导致的缺失值(例如向前填充或直接删除)。
    4. 计算基础的衍生数据,如日收益率、简单移动平均线(SMA)。
    5. 将清洗后的 DataFrame 以及关键统计摘要(如时间段、数据点数、起止价格)整理成一份清晰的文本摘要,传递给下一个Agent。
  • 实操要点:

    注意数据频率对齐。 如果后续需要结合宏观经济数据(可能是月度),需要考虑如何将股票日线数据与之对齐。通常的做法是对股票数据进行重采样,计算月度的平均价格或月末收盘价。

3.2 角色二:技术分析Agent

这是图表生产的核心。它负责深入分析价格和成交量数据,识别图表形态和技术指标信号。

  • 角色定义: “资深技术分析师”。精通各种技术指标、价格形态识别和趋势判断。
  • 核心工具: ta-lib (指标计算), plotly.graph_objects (图表绘制)。
  • 任务流程:
    1. 从上游接收清洗后的价格 DataFrame
    2. 使用 ta-lib 计算一组预定义的关键技术指标。我的常备清单包括:
      • 趋势类:MACD(及信号线、柱状图)、双指数移动平均线(DEMA)。
      • 动量类:相对强弱指数(RSI)、随机震荡指标(Stochastic Oscillator)。
      • 波动率类:平均真实波幅(ATR)、布林带(Bollinger Bands)。
    3. 基于指标值,生成文本分析结论。例如:“RSI目前处于62,接近超买区域但尚未突破70,显示上涨动能仍存但需警惕回调。”“价格已突破布林带上轨,可能预示短期强势或即将回归。”
    4. 使用Plotly创建综合图表。 这是关键步骤。一个优秀的分析图应该分层清晰:
      • 主图区: K线图( go.Candlestick )叠加短期(如20日)和长期(如60日)移动平均线,以及布林带。
      • 副图区1: 使用 make_subplots 创建,放置成交量柱状图,通常用颜色区分涨跌日。
      • 副图区2: 放置MACD图(快线、慢线、柱状图)。
      • 副图区3: 放置RSI图,并画出超买(70)和超卖(30)水平线。
    5. 将分析结论文本和Plotly图表对象( fig )打包,传递给报告生成Agent。
  • 避坑技巧:

    指标参数不要死记硬背。 MACD的常用参数是(12,26,9),但针对不同波动率的股票(如比特币 vs. 公用事业股),可能需要调整。可以让Agent在分析中备注所使用的参数。另外, ta-lib函数对输入数据有要求 ,比如数据不能有NaN,长度必须足够(至少超过周期参数)。必须在计算前做好数据检查。

3.3 角色三:基本面与舆情摘要Agent

技术面与基本面结合,分析才更立体。这个Agent负责获取并提炼非价格信息。

  • 角色定义: “基本面与市场情报研究员”。擅长阅读和总结财务报告、新闻标题,提取关键事件和情绪。
  • 核心工具: LLM的文本总结能力,可能结合新闻API(如Google News RSS, 公开的财经新闻API)。
  • 任务流程:
    1. 给定股票代码,通过网络搜索或预置的新闻源,获取近期(如过去一个月)的相关财经新闻标题和摘要。
    2. 调用LLM(如GPT-4),对这批新闻进行要点总结、情感分析(正面/负面/中性)和主题归类(如“财报发布”、“产品上市”、“监管动态”、“分析师评级”)。
    3. yfinance 或其它数据源获取最新的关键基本面数据快照,如市盈率(P/E)、市净率(P/B)、股息率、最近一期财报的营收和利润同比变化。
    4. 将舆情摘要和基本面快照整合成一段简洁的叙述,例如:“近期舆情偏正面,主要围绕其新产品发布预期。当前P/E为28,略高于行业平均,显示市场给予成长溢价。”
  • 实操心得:

    这个Agent最依赖LLM的能力,也最容易产生“幻觉”(编造信息)。 必须严格限制其信息源 ,最好只让它总结你提供的、确定的新闻文本,而不是让它自己去“想象”或“回忆”新闻。提示词(Prompt)要清晰,例如:“你是一名研究员,请严格基于以下提供的新闻标题列表,总结出最多三个核心主题,并判断整体市场情绪是积极、消极还是中性。不要添加列表之外的知识。”

3.4 角色四:报告生成与合成Agent

这是团队的“主编”,负责整合所有发现,输出最终成果。

  • 角色定义: “投资研究报告编辑”。擅长整合多源信息,撰写结构清晰、论据充分的综合性报告。
  • 核心任务:
    1. 接收来自前三个Agent的所有输出:数据摘要、技术分析文本与图表对象、基本面舆情摘要。
    2. 利用LLM的强大写作和逻辑组织能力,将这些材料整合成一份格式规范的研究报告。报告结构可以包括:
      • 执行摘要(Overall Assessment)
      • 基本面概览(Fundamentals Snapshot)
      • 技术分析(Technical Analysis):这里直接嵌入技术分析Agent生成的文本结论。
      • 图表部分(Charts):这里需要处理Plotly图表对象。CrewAI Agent本身输出文本,但我们可以让这个Agent生成一个包含 fig.to_html() 的HTML字符串,或者生成一个保存图表的指令。
      • 风险提示(Risks and Considerations)
    3. 最终输出一份完整的HTML报告(包含交互式图表),或一份Markdown报告(包含图表保存路径)。
  • 关键技术点: 如何让文本型的Agent处理并“引用”图表?这里有个实用技巧: 将Plotly图表转换为静态图片或HTML片段,然后以“资源标识符”的形式放入Agent的上下文中。 例如,技术分析Agent在生成文本结论后,可以额外执行 fig.write_image(“tech_chart.png”) 将图表保存为文件,然后在文本中注明“技术图表见附件: tech_chart.png”。报告生成Agent在撰写时,就知道需要引用这个文件。如果输出HTML,则可以直接将 fig.to_html(full_html=False) 得到的 div 字符串插入到报告模板的对应位置。

4. 使用CrewAI编排完整研究流水线

有了定义好的角色和任务,现在用CrewAI把他们串联起来。

4.1 定义角色与任务

以下是使用CrewAI框架的伪代码式逻辑,展示了如何具体定义:

from crewai import Agent, Task, Crew
from langchain_openai import ChatOpenAI # 示例使用OpenAI

# 1. 定义LLM
llm = ChatOpenAI(model=“gpt-4-turbo”, temperature=0.1) # 金融分析需要稳定性,temperature调低

# 2. 创建智能体
data_collector = Agent(
    role=“金融数据工程师”,
    goal=“准确、高效地获取并清洗指定股票的历史交易数据”,
    backstory=“你是一家顶级量化对冲基金的数据专家,对数据质量有极致追求。”,
    llm=llm,
    tools=[yfinance_tool] # 假设我们已将yfinance封装成一个Tool
)

technical_analyst = Agent(
    role=“资深技术分析师”,
    goal=“基于价格数据,计算关键技术指标并生成可视化图表与解读”,
    backstory=“你拥有20年技术分析经验,精通蜡烛图形态和各类指标。”,
    llm=llm,
    tools=[ta_lib_calculator, plotly_generator] # 自定义的工具
)

# 类似定义 fundamental_analyst 和 report_editor...

# 3. 创建任务
data_task = Task(
    description=“获取股票 {stock_symbol} 从 {start_date} 到 {end_date} 的日线级OHLCV数据。进行基础清洗,并计算5日、20日简单移动平均线。最后提供一份数据概要和清洗后的DataFrame。”,
    agent=data_collector,
    expected_output=“一份文本摘要,包含数据时间范围、数据点数量、起止价格、以及清洗后的pandas DataFrame对象。”
)

tech_task = Task(
    description=“基于数据工程师提供的数据,计算MACD(12,26,9)、RSI(14)、布林带(20,2)等技术指标。分析当前技术形态,指出关键支撑/阻力位和买卖信号。并生成一个包含K线、均线、成交量、MACD和RSI子图的交互式Plotly图表。”,
    agent=technical_analyst,
    context=[data_task], # 显式声明依赖data_task
    expected_output=“一段技术分析文字结论和一个Plotly图形对象(fig)。”
)

# 类似定义 fundamental_task 和 report_task...
# report_task 的 context 应包含 [data_task, tech_task, fundamental_task]

4.2 配置工作流与执行

# 4. 组建团队并配置流程
research_crew = Crew(
    agents=[data_collector, technical_analyst, fundamental_analyst, report_editor],
    tasks=[data_task, tech_task, fundamental_task, report_task],
    verbose=2, # 打印详细执行日志,便于调试
    process=“sequential” # 顺序执行,后一个任务依赖前一个任务的输出。也支持“hierarchical”等模式。
)

# 5. 启动研究流程
inputs = {
    “stock_symbol”: “AAPL”,
    “start_date”: “2024-01-01”,
    “end_date”: “2024-05-27”
}
result = research_crew.kickoff(inputs=inputs)

# result 包含了最终报告生成Agent的输出
print(result)

4.3 输出结果与集成

最终 result 会是报告生成Agent产出的内容。根据设计,这可以是一份Markdown字符串。我们可以将其写入文件:

with open(“AAPL_Research_Report.md”, “w”) as f:
    f.write(result)

如果报告中包含了图表文件路径或HTML片段,我们需要确保整个脚本能正确地保存图表文件,并将资源路径关联起来。一个更健壮的做法是使用一个 报告模板引擎 (如Jinja2),让报告生成Agent只产出结构化的数据和分析文本,再由一个外部的Python脚本负责将数据填入模板、渲染图表并生成最终HTML。

5. 实战中的挑战与优化策略

搭建原型容易,但要让它稳定、可靠地运行,并产生真正有参考价值的分析,还需要解决不少问题。

5.1 数据质量与一致性保障

  • 问题: 数据源API可能不稳定,返回的数据格式可能变化,存在缺失值或异常值(如价格错误为0)。
  • 策略:
    1. 多源验证: 对于关键数据(如收盘价),可以同时用 yfinance pandas_datareader 抓取,进行交叉比对。
    2. 异常值检测: 在数据清洗环节加入规则,例如当日涨跌幅超过50%的数据需要标记复核。
    3. 数据快照与缓存: 频繁调用API可能被封禁。可以引入缓存机制(如使用 sqlite redis 存储历史数据),对于非实时分析,使用缓存数据能极大提高速度并降低风险。

5.2 Agent决策的透明性与可控性

  • 问题: LLM是“黑盒”,技术分析Agent给出的“看涨”或“看跌”结论是基于什么指标得出的?可能缺乏明确逻辑链。
  • 策略:
    1. 结构化输出: 强制要求Agent以特定JSON格式输出分析结果。例如:
      {
        “trend”: “bullish”,
        “confidence”: 0.7,
        “key_evidence”: [
          {“indicator”: “RSI”, “value”: 58, “interpretation”: “处于健康区间,无超买超卖压力”},
          {“indicator”: “Price vs MA20”, “value”: “above”, “interpretation”: “价格位于20日均线上方,短期趋势向上”}
        ],
        “chart_path”: “./output/tech_chart.html”
      }
      
    2. 提示词工程: 在给Agent的 description 中,明确要求其“先列出观察到的数据事实,再进行综合推理”,从而提高思考过程的可见度。

5.3 系统性能与成本考量

  • 问题: 多个Agent顺序调用LLM,特别是使用GPT-4,分析一只股票的成本和时间可能较高。
  • 策略:
    1. 模型分级: 对任务进行分级。数据整理、基础计算等逻辑性强的任务,可以使用更便宜、更快的模型(如GPT-3.5-Turbo)。而最终的报告综合撰写,再使用能力更强的GPT-4。
    2. 异步与并行: 如果任务间没有强依赖,可以设计并行流程。例如,技术分析Agent和基本面分析Agent可以同时运行,最后再汇总给报告Agent。CrewAI支持任务依赖图,可以配置非顺序流程。
    3. 本地模型部署: 对于对数据隐私要求高或需要大规模运行的情况,可以考虑使用本地部署的开源大模型(如通过Ollama部署Llama 3、Qwen等),虽然效果可能略有差距,但成本可控,且无数据外泄风险。

5.4 常见错误排查清单

  1. Agent卡住不执行: 首先检查 verbose=2 的日志输出,看是哪个环节出了问题。最常见的是Tool调用失败(API密钥错误、网络超时)或LLM返回的格式不符合预期,导致CrewAI解析失败。
  2. 图表无法显示: 如果生成的是HTML报告但图表不显示,检查Plotly的 fig.to_html() 是否包含了所有必要的JavaScript库( include_plotlyjs=‘cdn’ )。确保输出的是完整HTML或正确引用了CDN。
  3. 技术指标计算错误: 首先检查输入给 ta-lib 函数的数据长度是否大于指标周期。其次,检查数据中是否存在 NaN ta-lib 对此很敏感。使用 pandas .dropna() .fillna() 先行处理。
  4. LLM输出胡言乱语: 降低 temperature 参数(如设为0.1),使输出更确定。优化提示词,给出更明确的指令和输出格式示例(Few-Shot Prompting)。检查输入给LLM的上下文是否过长或包含了混乱的格式。

这个项目目前还是一个不断迭代的原型,但它清晰地展示了Multi-Agent系统在垂直领域自动化中的潜力。它不是一个“黑箱”预测机器,而是一个强大的信息整合与初步分析引擎,能将散乱的数据转化为结构化的洞察,让人类研究员能把精力更多集中在更高层的策略思考和最终决策上。下一步,我计划引入更多类型的数据源(如期权链数据、社交媒体情绪),并尝试让Agents之间进行简单的辩论,以生成更平衡、多角度的分析报告。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐