7 分析耗时治理

日期:2026-06-04
项目:FinAgent

单股分析跑通之后,遇到最多的问题不是报告不对,而是怎么这么久。这篇把我在项目里为压耗时做过、且当前仓库仍生效的措施整理成一篇,方便日后对照 .env 和代码复盘。

文中路径均相对于 FinAgent 仓库根目录。

关键代码入口:

  • server/finagent/core/config.py:各类 FINAGENT_* 默认值与 clamp
  • server/finagent/services/analysis_prepare.py:分析前 prepare_stock_data
  • server/finagent/dataflows/interface.pydataflows/cache/local_cache.py:vendor 回退与磁盘缓存
  • server/finagent/graph/analyst_setup.py:分析师子图(默认并行 + 串行回退)
  • server/finagent/graph/runner.py:主图、stage_log / analyst_progress 落盘
  • server/finagent/api/routes/analysis.py:任务后台线程、首页 TTL 缓存

一、墙钟 vs 体感

演示时分析太慢有两层含义:

(1)墙钟(wall-clock)
从创建任务到 status=succeeded,服务器真实经过的时间。决定因素:外网拉数、LLM 往返次数、各阶段是否并行、云端 API 是否排队。

(2)体感 / 感知延迟
后台其实在跑,但 stage_loganalyst_progress 长时间不变,前端轮询退避后像「卡死」。这和 GPU/模型是否还在算是两回事。

后面每条优化都标一下主要影响哪一层,避免把页面刷新快了误说成模型算得更快。


二、耗时大致花在哪(按阶段)

结合 data/processed/langgraph_states/ 里几次 000001 任务的 started_at / finished_at,我目前的经验分布大致是:

阶段 典型占比 主要瓶颈
数据 prepare(若开启) 首跑 5分钟以上分钟;命中缓存后很短 AkShare 新闻多源、宏观快讯;行情空数据时多一次 GM 回退
analyst 常占全链路 20%–40% 3 专科 × ReAct + LLM;并行后≈最慢专科
research_debate 约 2–4 分钟 牛熊 + 经理,长 Prompt
trader 约 0.5 分钟 单次 quick LLM
risk_review 波动最大(2 分钟~异常时 10 分钟+) 3 辩手 + Judge,四份报告全文塞进 Prompt
portfolio_manager 约 0.5–1 分钟 deep LLM
build_report 秒级 本地拼装

风控阶段莫名其妙很久多半不是死循环,而是 4 次大上下文 LLM + API 超时重试stage_log 又只在整段开始/结束更新,中间无子步骤,体感尤其差。这块暂时还没有改动,因为出现频率较低,下文只记现状。


三、数据层:少打重复外网

1)分析前 prepare

主图 execute_analysis_langgraph_taskgraph.invoke 之前调用 prepare_analysis_data(默认开,FINAGENT_ANALYSIS_PREPARE=0 可关)。

prepare_stock_dataThreadPoolExecutor(max_workers=4) 并行预拉:

  • get_stock_data
  • get_indicators_batch
  • get_news
  • get_global_news

结果写入 route_to_vendor 使用的 vendor 磁盘缓存data/processed/vendor_cache/),后续分析师工具命中缓存时可零网络重复。

取舍:首跑会在数据准备文案下集中等待最慢的数据项(往往是新闻/宏观快讯);第二次同标的、同窗口会快很多。

2)vendor 回退链(AkShare → GM → …)

.env 里我常用:

FINAGENT_DATA_VENDOR_CORE_STOCK=akshare,gm
FINAGENT_GM_TOKEN=...

AkShare 返回空 OHLCV 时,route_to_vendor 会继续试掘金,避免任务死在「行情证据为空」。细节见之前的博客。

3)指标 batch + 进程内 LRU

  • get_indicators_batch:一次工具调用多个指标只拉一趟 OHLCV。
  • fetch_stock_ohlcv@lru_cache(maxsize=512):同进程重复窗口命中更快。

4)新闻条数与摘要截断

akshare_vendor.get_news_akshare 里:个股新闻约 20 条上限,单条摘要 800 字符截断,控制 ReAct 喂给 LLM 的体积。


四、分析师阶段:当前最大的墙钟优化

1)三专科默认并行

TradingAgents-CN 原版 setup.pyselected_analysts 严格串行(Market → … → Fundamentals,中间 Msg Clear)。FinAgent 在 TA-CN 语义之上加了 并行调度

  • 每个专科仍是独立子图:Analyst ↔ tools → Msg Clear → finalize
  • 外层 parallel_analysts 节点用 ThreadPoolExecutor 同时 invoke 三个子图
  • 墙钟由「三段相加」变为 ≈ max(market, news, fundamentals)
  • FINAGENT_ANALYST_PARALLEL=0 时退回与 TA-CN 一致的串行链

代码:server/finagent/graph/analyst_setup.py_compile_parallel_ta_cn_subgraph_compile_single_role_subgraph;开关在 config.pyanalyst_parallel_enabled() / analyst_parallel_workers()(默认 parallel=开,workers=3)。

2)专科裁剪

当前 .env

FINAGENT_SELECTED_ANALYSTS=market,news,fundamentals

不跑 social,少一段 LLM + 工具。排障时还可只留 market,fundamentals 做最小闭环。
(这里之前有social是学习别人项目的实现,但发现国内似乎没有社媒源,所以先取消。)

3)内联工具与补救

  • 模型发起 tool_calls 后,try_inline_tool_report 尽量 工具 → 二次 LLM → 报告,减少 ReAct 来回。
  • _remediate_force_tool_messages:模型不调工具时由代码代拉,避免空头报告。
  • FINAGENT_ANALYST_MAX_TOOL_ROUNDS 默认 3,防止单专科工具死循环。

4)共享新闻(可选)

prepare 已缓存 get_news 时,若 FINAGENT_ANALYST_SHARED_NEWS=1FINAGENT_ANALYSIS_SPEED=fast,news 专科可注入 ToolMessage,少一次 get_news 外网。默认 balanced 模式下未显式开启。


五、下游阶段与全链路开关

措施 默认值 / 我 .env 作用
FINAGENT_DEBATE_MAX_PAIRS 1(已写 .env 牛熊 1 对 + 经理,对齐 TA-CN max_debate_rounds=1
FINAGENT_RISK_MAX_CYCLES 1 风控 Risky→Safe→Neutral 一轮(日志里 ×33 次发言,不是 3 轮)
FINAGENT_ENABLED_STAGES 六阶段全开 排障可只开 analyst,build_report,演示从十几分钟压到几分钟
quick / deep 模型 .env 目前 quick、deep 同为 DeepSeek-V3.2;理想上辩手用小模型、Judge 用大模型会更省

六、工程与前端体感

措施 说明
POST /api/analysis/tasks + 后台 threading.Thread HTTP 立即返回 task_id,长任务不堵请求线程
stage_log + _persist_running_snapshot 各阶段 running/succeeded 落盘,供轮询
analyst_progress 并行分析师时 market/news/fundamentals 逐位 running/done,缓解「签名不变导致退避」
首页新闻 / 词云 TTL 实时新闻约 60s、词云约 30min 内存缓存,减轻重复打开首页的 LLM 压力
A 股简称表 @lru_cache 查代码、词云校验不反复读大表

七、常用 .env 对照

FINAGENT_DATA_VENDOR_CORE_STOCK=akshare,gm
FINAGENT_SELECTED_ANALYSTS=market,news,fundamentals
FINAGENT_DEBATE_MAX_PAIRS=1
# 并行分析师:默认即开,无需写;若要 TA-CN 串行:
# FINAGENT_ANALYST_PARALLEL=0
# FINAGENT_ANALYST_PARALLEL_WORKERS=2   # API 限流时可降到 2

未写但默认生效的:FINAGENT_ANALYSIS_PREPARE=1FINAGENT_ANALYST_PARALLEL=1FINAGENT_RISK_MAX_CYCLES=1FINAGENT_DATAFLOW_CACHE=1 等,完整列表见 server/finagent/core/config.py


八、小结

目前来说有效的:

  1. 分析师三专科并行 — 对 analyst 墙钟帮助最直接(FinAgent 扩展,非 TA-CN 原版)。
  2. prepare + vendor 磁盘缓存 + indicators batch — 少重复拉外网;首跑慢、二跑快。
  3. 专科裁剪 + 辩论/风控默认 1 轮 + 工具轮次上限 — 少 LLM 调用。
  4. AkShare→GM 回退 — 少因行情空数据失败重跑。
  5. 任务异步 + 细粒度进度 — 主要改善体感,不缩短模型本身算力时间。

待商榷的:

  • prepare 会把「新闻/宏观慢」集中到任务开头,前端长期显示「数据准备」并不等于后面 LLM 也在卡。
  • 风控阶段仍可能偶发很长,与 prepare/并行无关;根因是大 Prompt + 云端 API。
  • 并行会同时打 3 路 LLM,Sophnet 限流时宁可 FINAGENT_ANALYST_PARALLEL_WORKERS=2 或临时串行。

若后续还要压耗时,我认为的优先级是:风控/辩手 Prompt 摘要化 > quick/deep 模型分流 > GM 优先于 AkShare 减少空回退

Logo

脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

更多推荐