ReportLens 全项目 Bug 复盘
这次跑通财报 Agent 的关键收获是:真正危险的不是“AI 不会写”,而是外部数据源、文件格式、状态一致性、上传路径、Prompt 约束和人工运维这些工程细节没有被系统性兜住。后续开发要把每一个环节都做成可验证、可回滚、可复盘的流水线。
一句话结论
ReportLens 的核心工程原则应该从“让 AI 自动生成报告”,升级为“让系统每一步都有证据链”。 对外部 ID 要核验,对下载文件要验真,对数字要复算,对 AI 判断要限定边界,对任务状态要对账,对前端操作要给反馈,对报告内容要做去重和质量门禁。
根因地图
12 个 Bug 背后,不是 12 个孤立错误,而是 6 类反复出现的系统性风险。
外部数据源假设错误
SEC 访问策略、CIK 公司 ID、EDGAR 文件格式都不能凭经验假设,必须接入官方接口和启动自检。
下载成功不等于内容正确
13 KB 索引页被当成年报、iXBRL 机器标签占满截断窗口,说明文件大小和内容特征必须校验。
任务状态缺少对账机制
服务重启、后台线程消亡、watchdog 字段错配,都说明持久化状态必须和真实进程对账。
生产环境路径与本地不同
本地测试通过,不代表服务器上传路径、SSH 认证和运行位置都正确,部署依赖必须在真实环境测试。
前端静默失败最伤信任
删除按钮无反馈、任务状态反复横跳,会让用户怀疑系统,而不是怀疑某个小功能。
Prompt 需要负向约束
AI 会为了凑页数重复洞察,也会在找不到数字时填符号。宁缺毋滥和禁止项同样重要。
12 个 Bug 复盘卡片
默认只展示摘要,点开后再看根因、解决方案和工程启发,避免信息一上来全部堆满。
01SEC EDGAR 返回 403,无法拉取美股财报列表美股公司提交分析后任务失败,Telegram 收到错误通知,美股全线不可用。P0
feedparser 默认 User-Agent 不符合 SEC 程序化访问要求,浏览器正常但程序请求被拒绝。
在 fetch_sec_filings() 中显式传入合规 User-Agent 与 Accept-Encoding。
第三方 API 的访问策略不能假设跟浏览器一致,必须用无浏览器环境测试。
025 家美股公司 CIK 填错,查到了别人家的文件任务可能失败,更严重的是静默下载错误公司的年报并生成错误报告。P0
CIK 来自搜索结果且未通过 EDGAR 官方 submissions API 二次核验。
通过 EDGAR submissions API 逐一验证公司名,替换 WATCHLIST 中错误 CIK。
外部 ID 类字段必须有二次校验,启动时应自动打印官方公司名供核对。
03下载了 13 KB 的目录索引页,而不是 5 MB 的年报Telegram 显示报告已生成,但内容全是空洞兜底话术,严重伤害用户信任。P1
原下载逻辑只认 PDF,遇到 SEC iXBRL inline 链接时把索引页当年报存盘。
download_pdf() 新增 ix?doc= 链接识别,正确下载 htm 年报正文。
下载成功不等于下载正确,兜底逻辑必须有文件大小与内容特征校验。
04年报文件下载对了,但提取出的全是 XBRL 机器标签报告成功生成但无真实财务数字,AI 判断数据严重缺失。P1
BeautifulSoup.get_text() 抽取了隐藏的 ix:header / context / unit 元数据,截断窗口被机器标签占满。
剔除 ix:header、ix:hidden、ix:resources 与 display:none,并用 EDGAR XBRL API 前置核心指标。
iXBRL 不是普通 HTML,结构化数据应作为主数据源,文本提取只能补充。
05Step 5 生成 HTML 后上传失败,返回空 URL最后一步失败,用户没有收到报告,前面 5-8 分钟分析时间白跑。P0
生产环境 pipeline 在目标服务器本机运行,却通过 SSH loopback 上传到自己,因 authorized_keys 未配置导致认证失败。
upload_to_server() 增加本地路径检测,目标目录可访问时直接 shutil.copy2()。
文件传输逻辑依赖部署位置,必须在真实服务器环境验证。
06服务重启后任务永久卡在“分析中”用户长时间看到分析中,不知道任务成功、失败还是系统挂了。P1
systemd 重启杀死后台线程,但 jobs.json 中 running 状态没有被清理。
服务启动时把 running 改为 error,并增加 5 分钟 watchdog 清理超时任务。
进程状态和持久化状态必须对账,启动时清理 stale running 是标准动作。
07运维操作误伤正在运行的用户任务用户短暂看到任务失败,几分钟后又变成功,状态翻转造成困惑。P2
手写脚本批量把 running 改为 error,误伤了真实正在运行的理想任务。
禁止运行期间手动批量修改 jobs.json,清理逻辑统一收进启动流程。
运维操作也有影响半径,修改生产数据必须先确认现场状态并最小化范围。
08Admin 后台报告排序逻辑反了,置顶反而排最后管理员置顶成功但刷新后排到最后,功能预期完全相反。P2
tuple 排序叠加 reverse=True,把 pinned 维度和发布时间维度一起反转。
改为两步稳定排序:先按发布时间倒序,再按 pinned 优先。
多维排序方向不同的时候,不要合并 tuple 再 reverse。
09pipeline.py CLI 缺少 --sec-cik 参数,手动测试直接报错无用户感知,仅影响开发者在服务器上手动触发 pipeline 测试。P3
函数签名新增 sec_cik / sec_accession,但 argparse CLI 没同步。
在 pipeline.py main() 中补全 CLI 参数并传入 run_pipeline()。
函数签名新增参数后,必须 grep 所有调用入口同步更新。
10Watchdog 时间戳字段名用错,实际上从不生效永久 running 的边缘任务无法被正确超时清理,生产流量下会再次卡住。P2
清理逻辑读取 started_at,但 jobs.json 实际字段是 created_at,字段名不一致。
统一使用 created_at,并增加注释和 fallback 说明。
共享数据结构要有 schema 或常量定义,字段名拼错会静默走错逻辑。
11管理后台删除按钮点击无效果管理员确认删除后弹窗关闭但列表不变,且没有任何错误提示。P1
closeModal() 在 fetch 前清空 _pendingDeleteIdx,导致请求发到 /api/admin/reports/null。
在关闭弹窗前先把待删索引复制到局部变量 idx,并处理非 ok 响应。
共享状态变量要先复制到局部变量,前端 API 问题第一步看 Network 请求 URL。
12AI 生成重复幻灯片,同一组数字被讲了两遍拼多多报告第 04 页和第 12 页重复,左侧 metric 出现“净>营业”,有凑页数观感。P2
Step 3 没有去重规则,Step 4 没有禁止比较符号作为 metric。
改为弹性 8-12 页,加入严格去重;metric 禁止 >、<、vs、→,选择更有冲击力的真实数字。
Prompt 必须有负向约束和宁缺毋滥原则,否则 AI 会走阻力最小的路。
下次开发必须遵守的规则
这部分从“问题复盘”提炼成“开发约束”,后续新任务可以直接作为验收清单。
遗留优化 Backlog
这些不是立即阻断项,但会影响系统长期稳定性和内容可信度。
给 Claude Code / Codex 的后续开发指令
这段是给后续开发任务直接复用的“系统提示词”。相比把所有复盘塞进正文,这里单独做成可复制的黑色代码块,便于喂给 AI。