FinRobot Desktop V2 全解析:投资银行级股权研究的桌面 AI 工作站架构与实战指南

原创2026-10-01 17:01:441,444 阅读
文章标签:人工智能AI Agent金融科技AI 应用大模型RAG

FinRobot Desktop V2 全解析:投资银行级股权研究的桌面 AI 工作站架构与实战指南

FinRobot Desktop 是开源 AI 金融平台 FinRobot 的 V2 生产版本:一个把「确定性财务数字计算」与「LLM 叙事」结合在一起的桌面端股权研究工作台,每次 research 运行产出一份含 13 章的完整研究报告,且报告中每个数字都能追溯到一次函数调用。本文将以 finrobot_desktop/README.md 为主线,结合 finrobot_desktop/ 下的源码、配置与测试,完整讲解其双进程架构、Agent/Pipeline/计算引擎/数据层设计、macOS 安装、CLI 与浏览器模式、源码构建及开发测试全流程,帮助读者在真机上把 FinRobot Desktop 跑起来并读懂它的底层实现。

FinRobot Desktop V2 是什么:版本谱系中的「生产版」

FinRobot Desktop 定位为面向分析师、量化研究员与活跃投资者的开源股权研究工作台。一次 research 运行即可产出 13 章研究报告:Cover、Investment Thesis、Company Overview、Financial Analysis、Valuation、Recent News、Sensitivity、Catalysts、Technical & Advanced、Competitive Landscape、Financial Data、Ownership & Governance、Disclaimer。

它的核心设计赌注是:

数字由代码计算,判断由 LLM 提供(numbers are computed by code, judgment is supplied by the LLM)。 模型永远不会输出一个无法追溯到 finrobot/engine/compute/ 中函数调用的数字。

在 FinRobot 版本谱系 中,Desktop 是 V2 世代,另外两代各有分工:

  • finrobot_autogen/(V0):面向教学的 AutoGen 库,对应白皮书中的经典实现;
  • finrobot_equity/(V1):自托管 Web 应用,支持浏览器端报告生成;
  • finrobot_desktop/(V2,本文主题):用于真实工作负载的生产系统。

README 给出了「为什么它是生产系统而非更大 Demo」的四个量化理由,均在仓库源码中可印证:

  1. 32 个确定性算子用纯 Python 计算所有财务数字——LLM 只负责叙述,不负责生产数字(对应 operators 目录 下的 26 个计算算子与 6 个审计算子);
  2. 6 个审计算子在报告发布前把叙事与数字回查比对,标记偏离(对应 audit 子目录);
  3. 类型化 Pipeline 运行时,每步带校验器与重试,而非开放式的 Agent 对话(对应 pipelines 目录);
  4. 7 个带故障转移的数据 Provider,响应校验在到达计算层之前就拒绝畸形负载(对应 providers 目录)。

此外,README 声明项目拥有 237 个测试文件、基于属性的测试,并在 Python 3.11/3.12 与 Node 22/24 上通过 CI。

研究驾驶舱:输入一个股票代码即可打开全可追溯的 AI 研究台

双进程架构:Tauri 外壳 + FastAPI 后端

应用以单个 .app 发布,内含两个通过本地 HTTP 通信的二进制:

  • finrobot-desktop:Tauri 外壳——原生窗口、菜单栏与 React UI;
  • finrobot-server:自包含的 Python 后端(FastAPI + PydanticAI + 确定性计算引擎),随应用启动自动在 127.0.0.1:8321 后台拉起。

用户不需要手动启动任何东西:打开 App 即启动后端,退出即关闭后端,目标机器上无需预装 Python、uv 或源码树。README 中的架构图可概括为:

                       Tauri shell (Rust) ── React 19 UI
                                 │  HTTP  127.0.0.1:8321
                       FastAPI routes ── PydanticAI agents
                                 │
          ┌──────────────────────┼──────────────────────┐
     pipelines              compute engine          data layer
  (7 workflows)      (32 pure-Python operators)   (7 providers,
                                                  with failover)
                                 │
                          artifact store (SQLite)

能力令牌认证(Capability Token)

虽然服务只绑定回环地址,但回环接口对机器上所有进程是共享的,普通 HTTP 鉴权无法阻止本机其他进程直接 POST 到 127.0.0.1:8321 窃取密钥或消耗 LLM 配额。因此桌面壳在每次启动时生成随机令牌,通过 FINROBOT_CAPABILITY_TOKEN 环境变量交给 sidecar,同时通过 Tauri IPC 命令交给 WebView,WebView 将令牌附加到每个请求头。相关实现位于 auth.py:CapabilityAuthMiddleware 在配置了令牌时强制要求 Authorization: Bearer <token> 或 SSE 场景的 ?token= 查询参数(EventSource 无法设置请求头),/health 与 CORS 预检始终放行,其余请求无令牌一律 401。令牌未设置时中间件为空操作——这正是浏览器开发模式与测试能够免令牌运行的原因。

Agent 层:9 个智能体,纯 Markdown 指令驱动

九个 Agent 各自由一份位于 finrobot/engine/agents/instructions/ 的 Markdown 指令文件定义:

分组 Agent 职责
Pipeline data、analysis、modeling、synthesis、report 主研究链——采集、解读、建模、对账、成文
Debate bull、bear、judge 论证多头与空头观点,再裁决
Orchestration lead agent(factory.py、orchestrator.py) 路由请求并编排其余 Agent

指令即 Markdown 意味着改变 Agent 行为不需要触碰 Python 代码。值得留意的是 instructions.md 中的纪律性约定:报告叙事必须严格限定在所请求的 ticker 范围内,不得把 watchlist 动因或其它 ticker 织入报告正文;upside 存在「watchlist 口径(以现价做分母)」与「报告口径(以入场价做分母)」两种数值,不得混淆;数据完整性上「绝不编造数字」,每个数字必须标注来源与 as-of 时间。

Pipeline 层:7 条类型化工作流

finrobot/engine/pipelines/ 下存放七条工作流,每条都是带校验器与重试的 typed step 序列:

Pipeline CLI 输出
equity_research.py finrobot research 完整 13 章研究 artifact
dcf.py finrobot dcf 折现现金流估值
ddm.py finrobot ddm 股利折现模型
lbo.py finrobot lbo 杠杆收购模型
comps.py finrobot comps 可比公司分析
earnings_analysis.py finrobot earnings 盈利质量回顾
ic_memo.py finrobot ic-memo 投委会备忘录

特别地,finrobot dcf 会自动对股息模型更站得住脚的公司回退到 DDM,可传 --force-dcf 强制覆盖。这一机制在 cli.py 中可见:_should_use_ddm 先抓取财务数据拿到行业/板块,再通过 finrobot.engine.primitives.industry.is_bank 判断是否为银行/金融企业,判断失败则默认走 DCF(fail-open)。

从源码结构看,registry.py 是 pipeline 集合的唯一事实来源(single source of truth):_PIPELINE_SPECS 中登记了全部 7 条 spec(key、import_path、tool_name、tool_description),REST(routes/runs.py)、CLI(cli.py)、SDK(sdk.py)与编排器(orchestrator.py)四处调用点都从这里读取;新增一条 pipeline 只需改一行 spec。模块还刻意采用懒导入(PipelineSpec.factory 为 cached_property),避免 server→web→tasks→server 的循环导入。

equity_research 流水线的财务分析章节——从营收到净收入的利润瀑布与利润率

确定性计算引擎:LLM 无权发挥的部分

finrobot/engine/compute/ 是 README 明确标注「LLM 不得即兴发挥」的部分,全部为纯 Python、无模型参与,因此数字可复现、来源可核验:

  • 26 个计算算子:dcf、ddm、lbo、multiples、monte_carlo、sotp、residual_income、peer_screen、forward_estimates、fx_normalize、ownership、catalyst、signal 及其 seed 变体(对应 operators 目录 下的 dcf.py、monte_carlo.py、wacc.py、valuation_aggregator.py 等文件);
  • 6 个审计算子:currency_caliber、ev_bridge、narrative_divergence、narrative_numeric_grounding、sector_sign、ttm_period(对应 audit 子目录 中的 6 个模块)——它们把叙事与数字比对并标记偏离,是报告发布前的质量闸门;
  • 7 个协调器:从数据层组装算子输入(extractor、historical_extractor、segment_extractor、market、news、dcf_seed、technical_payload)。

DCF 表格在 UI 中被明确标注为 code-computed——十年预测全部来自纯 Python 算子,旁边的段落则是 LLM 把这些数字读出来。下图展示的是估值三法(DCF、forward-P/E comps、EV/EBITDA)的足球场图:当三种方法偏离达 2.6 倍时,系统选择只发布区间目标价而不发布点目标,且每个假设都标注其依据来源——这正是「确定性数字 + 可追溯性」在结果呈现层面的体现。

DCF 预测表标注为 code-computed,与 LLM 叙事并列;底部为三种估值方法的足球场三角化

数据层:7 个 Provider 与故障转移

finrobot/engine/data/providers/ 下七个 Provider 共享统一接口,带健康追踪与自动故障转移:

yfinance · edgar(SEC)· fmp · finnhub · adanos(散户情绪)· news_aggregator · fx

配套组件包括:响应缓存、symbol 索引、quote 批量拉取、SEC 持仓同步,以及在畸形负载进入计算引擎之前就将其拒绝的校验器。每个 Provider 携带健康状态(always on / saved / optional / cooling down),故障转移据此决定源失效后的路由。这一「7 Provider 故障转移 + 前端健康面板」的设计可以从下图中直观看到。

数据源设置页:展示每个 Provider 的健康状态,即故障转移所读取的状态

Skills:56 本分析师手册

skills/ 以 SKILL.md 形式存放 56 本分析师剧本,按「工作台」分组:

类别 数量 类别 数量
financial-analysis 11 equity-research 9
private-equity 10 investment-banking 9
partner-lseg 8 wealth-management 6
partner-spglobal 3

可通过 CLI 浏览:finrobot skill list、finrobot skill search <query>(实现在 cli.py 中,底层为 SkillRegistry 的 list_all() 与 search())。第三方材料署名见 skills/ATTRIBUTION.md。

安装(macOS,Apple Silicon)

  1. 从 Releases 页下载 FinRobot_<version>_aarch64.dmg(或按下文「从源码构建」自行构建);
  2. 打开 .dmg,把 FinRobot 拖入 Applications;
  3. 首次打开——应用尚未代码签名,Gatekeeper 会拦截普通双击。两种解法:右键 App → 打开 → 打开,或一次性清除隔离标记:
    xattr -dr com.apple.quarantine /Applications/FinRobot.app
    
  4. 首次运行——打开 Settings 粘贴自己的 LLM API Key(DeepSeek / OpenAI / Anthropic)。FinRobot 编排的是你自己的 LLM 账户,不随包附送 Key;未设置 Key 前 App 可正常打开,但分析请求会返回「请配置 API Key」提示而非执行。

运行分析需要联网(yfinance / SEC EDGAR / 可选 FMP 的市场数据,加上你的 LLM Provider);启动应用与浏览已有报告则不需要。README 明确说明:Intel Mac、Windows 与 Linux 不在当前发布范围内,这些平台请改用「从源码运行」。

命令行使用:独立于桌面壳的 finrobot CLI

安装 Python 包即可获得独立于桌面壳的 finrobot CLI(入口见 cli.py,在 pyproject.toml 中注册为 finrobot = "finrobot.cli:cli"):

uv sync                          # 或:pip install -e .
finrobot research AAPL           # 完整研究 artifact
finrobot dcf MSFT                # DCF(合适时自动切换 DDM)
finrobot comps NVDA --peers AMD,INTC
finrobot ic-memo TSLA
finrobot ask AAPL "How exposed is the gross margin to tariffs?"
finrobot backtest ...            # 需要 backtest extra
finrobot serve                   # 单独运行后端

大多数命令接受 --model 指定 LLM(如 anthropic:claude-sonnet-4-6)与 --lang en|zh 设置输出语言。

结合源码,几个值得注意的 CLI 细节(均为 cli.py 中的实际实现):

  • finrobot research 直接调用 pipeline.execute(),不依赖 LLM 工具选择,因此无论使用何种模型都会确定性地跑完 5 个步骤;CLI 会打印实时进度([n/total] Step... done (xs)),步骤在重试后仍失败则显示 degraded 而非伪装成功;
  • finrobot comps --peers 接受 3–10 个逗号分隔的同行代码,并在 CLI 入口用共享的 validate_ticker 校验格式与数量(拒绝 CJK、乱码符号),避免在约 30 秒的数据采集步骤之后才暴露错误;
  • finrobot dcf 的银行检测与 pipeline 执行必须在同一个事件循环内完成(源码注释标注 BUG-082),否则共享 DataCache 的 aiosqlite 连接会被孤儿线程挂死;
  • finrobot backtest 支持 --strategy(默认 sma_crossover)、--start/--end(必填)、--cash(默认 100000.0)、--params(JSON)、--save-chart、以及 --auto(LLM 迭代式选参);默认仅内置策略,加载自定义 module:ClassName 策略需先在白名单 FINROBOT_BACKTEST_STRATEGY_MODULE_PREFIXES 中放行前缀;
  • finrobot ask 从 SEC EDGAR 拉取最新 10-K,经 BM25 检索后带来源引用作答,默认 --top-k 5;
  • finrobot serve 可选 --host(默认 127.0.0.1)、--port(默认 8321)、--reload(开发热重载)、--log-level,以及 --parent-pid——桌面 sidecar 用它实现父进程死亡看门狗,防止退出 App 后 127.0.0.1:8321 残留孤儿进程(详见 _start_parent_death_watchdog)。

另有四本 notebook(tutorials/)以交互方式覆盖同样主题:股权研究、DCF 估值、回测与 RAG Q&A。

浏览器模式:无 .dmg、无 Rust 的本地 Web 服务

同一套后端与 UI 也可以作为本地 Web 服务运行,无需 .dmg 与 Rust 工具链——适用于没有桌面构建的 Intel Mac、Linux 与 Windows。

前置条件: uv、Node 20+、Python 3.11+。

cd finrobot_desktop
uv sync                        # 后端依赖
(cd desktop && npm install)    # 前端依赖——只需一次

./dev.sh                       # → 打开 http://localhost:5173

uv sync 会安装基础依赖并移除环境中已有的 extras,若此前使用过 --extra dev 或 --extra package,同步时应带上这些标志:

uv sync --extra dev            # 保留 pytest / ruff / mypy

dev.sh 启动两个进程并把它们接好:

  Vite dev server  :5173   ← 在浏览器中打开它
        │  代理 /api, /chat, /health, /openapi.json
        ▼
  finrobot serve   :8321   ← FastAPI 后端,从源码运行

脚本会等待后端 /openapi.json 响应后才启动前端;若启动失败则打印后端日志路径(/tmp/finrobot-backend.log)。Ctrl+C 同时停止两者。后端改动需要重启脚本,前端改动热重载。LLM API Key 的配置方式与桌面应用完全一致——在浏览器 UI 的 Settings 页配置,Key 存储在**操作系统钥匙串(keychain)**而非 .env 文件中。

运行前有两点须知:

  • dev.sh 首先会杀掉占用 :8321 与 :5173 的任何进程。 如果 FinRobot.app 正开着,其中包括它捆绑的后端。请先退出 App。
  • 浏览器模式下本地 API 无鉴权。 桌面壳会铸一枚每次启动有效的能力令牌并由后端强制校验;而纯浏览器没有 Tauri IPC 无法读取该令牌,因此后端以 auth 中间件空操作的方式启动(见 auth.py)。它仍只绑定回环,但运行期间机器上任何其他进程都能访问 :8321——包括持有密钥的 /api/settings。共享机器上请优先使用桌面应用。

另外,finrobot serve 单独运行只暴露 JSON API,不托管 UI,所以 Vite 服务器是浏览器版本能工作的关键;目前也没有面向浏览器的静态生产构建:npm run build 产出的是桌面捆绑包资源,npm run preview 未配置代理,其 /api 调用无法到达后端。

从源码构建:冻结 sidecar 的完整链路

前置条件:uv、Node 20+、Rust 工具链、Tauri CLI(cargo install tauri-cli → cargo tauri)、Python 3.11+。

uv sync --extra package            # 后端依赖 + pyinstaller(用于 sidecar)
bash desktop/src-tauri/sidecar/build.sh
                                   # 把 finrobot-server 冻结进 desktop/src-tauri/binaries/

cd desktop
cargo tauri dev                    # 运行桌面应用(debug)
cargo tauri build                  # 产出 FinRobot.app + .dmg 于 desktop/src-tauri/target/release/bundle/

build.sh 是薄包装,真正的跨平台冻结逻辑在 build_sidecar.py(macOS/Linux/Windows 单一事实来源);冻结产物是约 330 MB 的 PyInstaller 单目录 bundle。build.sh 把后端冻结成 PyInstaller sidecar,因此直接 cd desktop && cargo tauri dev 跑的是冻结后的二进制——后端改动要重新执行 build.sh 才生效。

需要「改后端源码 + 前端 HMR」的实时编辑循环(免冻结)时,使用 dev.sh:

./dev.sh            # 浏览器壳  → http://localhost:5173(见「浏览器模式」)
./dev.sh --app      # 桌面壳    → Tauri 原生窗口,实时后端

--app 模式设置 FINROBOT_DEV_LIVE_BACKEND=1,Tauri 壳跳过内置 sidecar 的启动,WebView 通过 Vite 代理与 dev.sh 启动的源码树后端通信(.py 改动立即生效)。也可以单独跑热重载后端:finrobot serve --reload。值得注意的一个细节:即使 dev.sh --app 跳过 sidecar 运行时,tauri-build 在编译期仍会校验 tauri.conf.json 中声明的 sidecar 资源路径存在,因此 dev.sh 会在缺失时注入一个「dev placeholder」占位脚本以满足编译期检查——该占位脚本在 live-backend 模式下永远不会被执行。

开发、测试与工程纪律

uv sync --extra dev
pytest tests/                                          # 237 个测试文件;联网测试默认跳过
pytest tests/ --cov=finrobot --cov-report=term-missing
ruff check . && mypy finrobot                          # 同时接入 .pre-commit-config.yaml

测试默认以 -m 'not integration' 跳过联网/LLM 测试(pyproject.toml 中的 addopts),保证 pytest tests/ 作为确定性离线闸门能跑完;联网测试用 -m integration 显式运行。依赖项中包含 hypothesis 用于属性测试(见 tests/unit/test_property_dcf.py),mypy --strict 全程启用。

目录布局(finrobot_desktop/ 下):

finrobot_desktop/
├── finrobot/              # Python 后端
│   ├── engine/            #   agents、pipelines、compute、data、skills
│   ├── routes/            #   FastAPI 端点(runs、artifacts、valuation、…)
│   ├── artifact/          #   报告存储、契约、语义 diff(SQLite)
│   ├── audit/ obs/        #   溯源检查与可观测性
│   ├── cli.py server.py   #   CLI 与 ASGI 入口
│   └── sdk.py             #   编程式 API
├── desktop/               # Tauri 壳 + React 19 / Vite 6 / Zustand / Recharts
├── skills/                # 56 本分析师剧本(SKILL.md)
├── tutorials/             # 4 本 notebook
├── tests/                 # pytest 套件,含 Hypothesis 属性测试
└── scripts/               # 维护与构建工具

CI 在 Python 3.11 / 3.12 上跑后端、在 Node 22 / 24 上跑前端。想进一步深入:finrobot/engine/instructions.md 描述引擎契约,finrobot/engine/agents/instructions/*.md 是每个 Agent 的任务简报,finrobot/sdk.py 是编程式入口。

许可证

Apache 2.0——见 finrobot_desktop/LICENSE。

登录后查看全文
FinRobot