FinAgent 单股分析耗时优化:并行分析师、指标批量拉取与进度LRU扩容


摘要:本文基于单股分析时间慢的问题,分析耗时原因,并实现三类改动缩短单股分析时间:技术指标批量拉取同一截OHLCV进程内LRU扩容与四分析师子图并行


一、耗时因素分析

从任务进入到分析师阶段产出四份分析师报告(market_report等)为止,耗时过长的决定因素包括:LLM 与工具的往返次数、行情/新闻等I/O、分析师分析过程是否串行叠加,以及云厂商或本机 Ollama 的限流与队列


二、为什么“换更快模型”不够

本人从ollama将模型换成deepseek-v4,但是分析师阶段是典型的多专科 × 多轮 ReAct:模型bind_tools后反复推理 → 调工具 → 再看结果,总耗时更接近:

                阶段数×工具往返×(网络延迟+LLM 延迟)

当工具轮次已经较多时,Flash/Pro这类单次提速对耗时优化的贡献就容易被忽略,因此本人尝试后感觉时间优化上效果不大。

因此工程上选择了三类不改变下游仍读四个字符串报告的优化方向:

  1. 减少重复行情拉取(同一轮工具调用里多指标合并);
  2. 无相互依赖的四专科从串行改为并行(墙钟由四段相加变为近似最慢一段);
  3. 细粒度进度字段 + 轮询签名改善体感,避免误判「无进展」而导致轮询越来越稀。

三、Market 侧:get_indicators批量路径与 LRU

3.1 Market 分析师会调哪些工具、慢在哪

技术面分析师阶段里,模型通常会用到两类与行情相关的工具:

  • get_stock_data:按日期区间拉日线OHLCV,给模型一段原始 K 线文本,用来写走势、支撑压力等叙述。
  • get_indicators:在当前日期等时间或天数限制下,拉足够长的 OHLCV,在服务端算出RSI、MACD、布林带等技术指标,再格式化成Markdown还给模型。

耗时往往不在算MACD公式,而在反复访问AkShare/网络:每调用一次 fetch_stock_ohlcv都可能是一次真实拉数或至少一次缓存查找前的准备。如果一轮对话里模型要6~8个指标,而底层实现成每个指标各自拉一遍全量 OHLCV,就等于同一标的、同一段时间窗的数据被重复下载多遍,耗时就会增大。

本节针对的就是:减少无效重复拉网(批内合并)和提高命中进程内缓存的概率(LRU 容量);不改变算出来的指标含义和返回给模型的文本格式。

3.1 批量路径在干什么

改造前(逻辑上等价于):模型在一次思考里需要多个指标时,若底层多次走「单指标 get_indicators」,每次内部都会 fetch_stock_ohlcv 一次,再算一个指标、输出一段 Markdown;最后把多段用空行拼起来。

改造后:模型仍在一次工具调用里用**indicator="rsi,macd,boll,..."(英文逗号分隔)表达我要一串指标。工具层识别到逗号后,只路由一次get_indicators_batch→ AkShare 的get_indicators_akshare_batch,就是说整条链路只调用一次fetch_stock_ohlcv 拿到一块 OHLCV DataFrame,然后在内存里循环调用同一个格式化函数,生成多段Markdown,再用\n\n拼成最终字符串。

这样做的直接效果是:一次用户可见的工具往返里,网下行情次数从指标个数降为 1,返回内容和以前多次单指标再拼接也是对齐的。

_format_indicator_report_from_ohlcv 为什么要抽出来:单指标路径 get_indicators_akshare 与批量路径共用同一套从OHLCV 算指标→写 Markdown的实现。否则很容易出现线上批量路径修了 bug、单指标路径没修的错误或反之,维护成本高。

3.2 LRU

fetch_stock_ohlcv带了一个lru_cache,就是说同样的股票代码+起始日+结束日,第二次再来问,直接用内存里缓存的答案,不必再打一遍外部数据源。

缓存只能记有限条数。以前是32条,分析任务里如果出现多个标的、多个时间窗,很容易把旧条目挤掉;下一轮又问到同一窗口,就得重新拉网。改成128 条,同一进程里能多记一些组合,重复命中的概率高一些。


四、分析师子图:四专科并行

4.1 为何可以并行

Market、舆情、新闻、基本面四个角色 互不依赖对方的对话上下文。串行时,墙钟近似 四段耗时相加*;并行后近似取四段中的最大值(外加线程池调度开销)。下游辩论等阶段仍读取四个*_report 字符串,接口契约不变。

4.2 实现要点

实现集中在 backend/app/graph/analyst_subgraph.py,核心可概括为下表:

组件 作用
_compile_single_role_graph(role) 单专科 ReAct:analyst → tools/retry,结束直达END;并行时不使用msg_clear(各子图轨迹隔离)。
analyst_parallel(内部 _parallel_analyst_node 对每个selected_analysts()分析师独立 invoke,共享初始化模板、state 互相独立
合并 按配置顺序合并四个*_reportanalyst_react_summariesmessages 前加分析师角色说明;工具轮次类字典update

4.3 注意

  • 云 API 限流或本机 Ollama 实质串行时,并行加速会打折扣。
  • 同一任务 四路同时打模型,RPM、TPM、费用及本机显存压力都会上升。
  • 文档层面预留 FINAGENT_ANALYST_PARALLEL=0 一类开关回退串行。

五、其它配套改动(简述)

  • 阶段进度 UI:只根据已结束阶段加权,避免假进度条(analysisProgress.tsSingleStockAnalysisPage.tsx与后端stage_log)。
    在这里插入图片描述

  • DeepSeek:thinking 模式与tool_calls易 400,默认关 thinking,环境变量可控(config.pyllm.py)。

  • Windows启动脚本:注意 FINAGENT_ENABLED_STAGESgoto 分支不要互相跳过。


六、总结

FinAgent把分析慢拆成了三件事——技术面get_indicators多指标合并成一次拉 OHLCV(避免同一次调用里重复打网)、fetch_stock_ohlcv 的LRU扩容(同一进程里多标的多窗口更容易命中缓存)、以及四个分析师LangGraph子图并行(由四段相加变为近似最慢一段,仍受API/Ollama限流约束)。

最终实现了从十多分钟到五分钟左右的耗时优化!!


其他进度报告
【山东大学项目实训FinAgent】本周进度报告:AKshare 数据接入与测试
【山东大学项目实训FinAgent】 周报(2026-04-19):Dataflow分层设计与FastAPI路由协作实践

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐