AI 财报分析 Agent 完整工作流

最后更新:

真正可用的财报 Agent 不是把 PDF 全部塞给模型,而是把文件版本、表格、章节、计算和引用组织成可检查的证据链。模型负责归纳与质疑,原始财报和确定性计算负责事实。

结论:先建立证据卡,再让模型写结论

一条合格的财报分析链路至少包含原始文件、发布日期、解析结果、确定性指标、证据卡、模型审阅和最终引用。模型不应直接承担数字抽取、单位换算与同比计算,否则一个表格错位就会污染整份报告。 最小可用版本只选一家公司、两个连续报告期和三个问题:收入变化、现金流质量、重大风险。先证明每句判断能回到原文,再增加估值、同业比较和多 Agent 角色。

FinGPT 官方界面展示金融研究输入形态

这张官方截图说明金融研究工具通常需要标的与日期等明确输入。本文不把它当作本工作流已经运行的证据,而是用来说明:财报任务必须先锁定公司、报告期和当时可见资料。

FinGPT Forecaster 官方金融预测交互界面
项目官方截图:FinGPT Forecaster 接收股票与日期输入并组织金融研究结果。

完整数据流从文件版本开始

采集器保存原文件和响应头;解析器输出带页码或章节坐标的段落与表格;计算层生成指标;证据卡把数值、公式与原文绑定;模型只阅读证据卡并提出结论和反证。

AI 财报分析 Agent → 原始文件 -> 解析 -> 证据卡 -> 模型复核 → 财报原文 / 结构化指标 / 证据卡 / 带引用报告
原文与计算是事实层,模型是解释层;最终报告必须保留两层连接。

证据卡使用稳定字段而不是长文本拼接

每张卡只表达一个可检查事实,包含来源、定位、单位、期间、计算和状态。模型收到的是证据卡数组,不是无法定位的 PDF 大段文本。

evidence_card.py
from dataclasses import asdict, dataclass

@dataclass
class EvidenceCard:
    claim_id: str
    metric: str
    value: float
    unit: str
    period: str
    source_url: str
    source_locator: str
    published_at: str
    formula: str | None = None

card = EvidenceCard(
    claim_id="revenue-current",
    metric="revenue",
    value=123.4,
    unit="CNY million",
    period="2026-H1",
    source_url="https://issuer.example/report.pdf",
    source_locator="page 18, table 4",
    published_at="2026-08-01T08:00:00+08:00",
)
payload = asdict(card)

模型只基于卡片回答并返回引用 ID

请求中明确禁止引入卡片之外的公司事实,并要求每条判断附 claim_id。模型输出后由程序检查引用是否存在;不存在的句子退回重写,而不是直接展示。

review_cards.py
import json, os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["KKAIAPI_KEY"],
    base_url="https://api.kkaiapi.com/v1",
)
response = client.chat.completions.create(
    model=os.environ["KKAIAPI_MODEL"],
    temperature=0,
    messages=[
        {"role": "system", "content": "只使用证据卡。每条判断附 [claim_id];证据不足就写未知。"},
        {"role": "user", "content": json.dumps([payload], ensure_ascii=False)},
    ],
)
answer = response.choices[0].message.content or ""
assert "[revenue-current]" in answer

每个阶段都有明确通过条件

财报分析不是一次模型请求,而是一条证据生产链。阶段门禁能把下载、解析、计算和引用错误定位到具体步骤,同时保留可恢复的中间结果。

只有前一阶段通过,下一阶段才运行;失败不能被一段流畅文本覆盖。
阶段输入通过条件失败时保留
采集公司、报告期原文件哈希与发布日期可复现HTTP 状态与原始响应
解析PDF / HTML关键表格与章节有定位失败页码与解析日志
计算结构化字段单位、期间、公式明确缺失字段与异常值
模型复核证据卡每条判断引用存在请求 ID、原始错误与重试
发布带引用草稿链接可打开且数字一致未闭环条目

防止未来信息和口径错配

研究截止日必须早于或等于财报发布日期,不能按报告期末误判信息已经公开。同比计算要确认币种、单位、合并范围、会计准则和重述情况;现金流与利润的比较要使用相同期间。 若公司后来发布更正公告,应作为新版本加入,而不是覆盖旧文件。历史复跑使用当时可见版本,当前研究则明确采用最新修订。

成本、失败和可恢复重试

一次任务成本 = 输入 token × 输入单价 + 输出 token × 输出单价 + 缓存读写费用 + 外部数据费用。模型价格和实际 token 只从当前模型目录与控制台调用日志读取,不在文章里复制一份会过时的数字。 财报任务的主要浪费来自重复发送长原文。先在本地解析并压缩为证据卡,按章节缓存,再让模型阅读。下载失败可重试采集;解析失败只重跑对应页;模型 429 按服务端节奏退避;引用检查失败退回模型重写。每次重试沿用同一 job_id,并记录尝试编号。

故障是否可重试恢复点
原文件暂时不可用采集阶段
表格结构无法解析人工确认后单页或单表
模型超时 / 429同一证据卡请求
引用不存在模型复核阶段
原始数据矛盾不可自动决定人工审查

常见问题

为什么不直接把整份 PDF 发给模型?

整份 PDF 容易出现表格错位、定位困难和重复 token。先解析为带来源的证据卡,更容易验证和重试。

模型能负责同比和比率计算吗?

可以复核解释,但关键计算应由确定性代码完成,并把公式、单位和期间写入证据卡。

怎样防止使用未来信息?

保存文件发布日期与研究截止日,历史复跑只加载截止日前真实可见的文件版本。

引用检查通过就代表结论正确吗?

不代表。它证明结论有来源,还需要核对计算口径、数据完整性和模型推理。

财报 Agent 最先应该做哪三个问题?

收入变化、经营现金流质量和重大风险,范围足够窄,便于逐条核对原文。