财务审计 — 个人 CFO 报告
将原始对账单文件文件夹转换为一份个人 CFO 报告:已验证的数据加上一层叙述性内容,回答发生了什么?为什么?下一步该怎么做?
准确性优先:金额按原文读取,并与运行余额核对——绝不捏造任何数据。
与银行无关。 只要有交易列表,就能与任何银行或账户的导出文件配合使用。列、表头行和日期顺序(DMY/MDY/YMD)会自动检测;商户/对手方清洗器也无关支付方式(UPI、POS、ACH、SEPA、银行卡、电汇等)。不针对特定银行硬编码——你需要确认自动检测到的映射并调优类别关键词。
流水线(5 个阶段)
ingest -> parse -> categorize (loop to >=99%) -> analyze -> report (serve + hot reload)
脚本位于此技能目录中。在用户的审计文件夹(包含对账单文件的文件夹)中运行它们。首先设置两个变量:
SKILL="<此技能目录的绝对路径>" # 包含此 SKILL.md 的文件夹
ROOT="<审计文件夹的绝对路径>" # 对账单文件所在的文件夹
隐私 — 绝不提交真实数据
所有对账单文件、余额、叙述和生成输出都保留在 ROOT/ 的本地。技能仓库只提供模板、示例配置和模拟测试数据(assets/fixtures/sample_analysis.json)。
已加入 .gitignore(切勿推送): output/(包括 report.html、analysis.json、audit.db)、对账单文件以及实时 config/mapping.json / 覆盖文件。
config/mapping.json 中的可选标签:account_label 或 account_last4 用于侧边栏页脚(默认值为“Primary account”)。config/business.json 中的业务显示名称 → display_name(默认值为“Business”)。
假设对账单文件已下载到 ROOT 中(PDF / XLSX / XLS / CSV / TXT)。
选择运行时(用户偏好 Node.js):
- 使用 Node:当
node可用且输入中没有 PDF 时。 - 使用 Python:当任何输入是 PDF(Node 路径无法解析 PDF)或
node不可用时。
检测并安装依赖:
# Node 路径(首选)
command -v node && node --version # 需要 Node >= 22 以使用内置 sqlite
( cd "$ROOT" && npm install xlsx ) # 仅用于 .xls/.xlsx;CSV/TXT 不需要
# Python 路径(回退 / 解析 PDF 必需)
python3 --version
pip install openpyxl "xlrd==2.0.1" pdfplumber pandas # + pandas 用于分析阶段
定义如何调用某个阶段($STAGE = ingest|parse|categorize|analyze|report):
# Node: node "$SKILL/scripts/node/$STAGE.mjs" --root "$ROOT" [args]
# Python: python3 "$SKILL/scripts/python/$STAGE.py" --root "$ROOT" [args]
步骤 1 — 导入并检测
node "$SKILL/scripts/node/ingest.mjs" --root "$ROOT" # 或 python3 .../ingest.py --root "$ROOT"
这会预览每个文件,并写入 config/mapping.json,其中包含自动检测的表头行、列角色以及明确的 date_format。阅读预览并验证 mapping.json——每家银行都不同。如果某个条目包含 _date_note,说明日期顺序有歧义(例如 03/04/2024);与用户确认 date_format。每个文件条目需要:header_row、data_start_row、date_format(一个 strptime 模式,如 %d/%m/%Y,或 auto)、columns(角色 → 基于 0 的索引)以及 amount_mode:
debit_credit— 单独的支出 + 存款列(最常见)signed— 一个amount列,其符号指示方向amount_type— 一个amount列加上一个包含 DR/CR 的type列
在解析之前纠正任何错误的索引。
步骤 2 — 解析、规范化、去重、验证
node "$SKILL/scripts/node/parse.mjs" --root "$ROOT" # 或 .../parse.py
写入 output/audit.db(SQLite)。借/贷拆分是从有符号金额派生的,因此始终与余额链匹配(正确处理编码为负支出的退款)。要求完整性检查通过(如果没有余额列,则为 n/a)。如果失败:
- 重新检查
mapping.json的列 /data_start_row/date_format。 - 重新读取导入标准的输出预览(如果需要,重新运行导入);确认没有包含表头/页脚行。
- 每文件的
chain_issues > 0指向解析错误的精确行。
在解析核对通过之前,不要进入分类步骤。
步骤 3 — 分类(循环,直到按价值计 >= 99%)
cp "$SKILL/config/categories.example.json" "$ROOT/config/categories.json" # 仅首次运行
# 对于企业/自由职业者账户,还需要:
cp "$SKILL/config/business.example.json" "$ROOT/config/business.json" # 然后编辑它
node "$SKILL/scripts/node/categorize.mjs" --root "$ROOT" # 或 .../categorize.py
规则优先级:overrides.json > business.json > categories.json;最高 priority 获胜。分类是脚本优先的;像这样达到 99%:
- 运行分类。阅读覆盖率摘要和
output/uncategorized.csv(商户已分组,价值最大的在前)。 - 对于从叙述中明显可识别的商户,自己将规则添加到
config/overrides.json(见下方格式)——无需询问。 - 对于模糊的商户(不清楚是谁/什么,或个人与企业界线不清),向用户提出一个简洁的问题,然后添加规则。
- 重新运行分类。重复,直到价值覆盖率 ≥ 99%。
config/overrides.json 格式:
{ "rules": [
{ "keywords": ["ACME", "ACME CORP"], "primary": "Income", "sub": "Acme retainer", "when": "credit" },
{ "keywords": ["BIGRENT"], "primary": "Housing", "sub": "Rent", "is_business": 0 }
] }
目标是每笔交易都有一个 primary_cat 和 sub_cat。事实上的 P2P 对手方回退标签将转账标记出来,但计为“需要审查”——将重要的标签转换为真正的规则。
步骤 4 — 分析(构建 output/analysis.json)
python3 "$SKILL/scripts/python/analyze.py" --root "$ROOT"
从 output/audit.db 计算财务年度切片、类别报告、CFO 叙述、收入引擎、烧钱摘要、问题区域和企业复盘。写入 output/analysis.json(以及本地的可选 findings.md——切勿提交)。
需要 pandas。调整 config/business.json → display_name 用于企业部分标签(默认值为“Business”)。
步骤 5 — 生成报告(提供服务 + 热重载 + 自动打开)
python3 "$SKILL/scripts/python/report.py" --root "$ROOT" --serve
从 analysis.json + 内联嵌入的总账行构建 output/report.html。使用 --serve 时,打开 http://127.0.0.1:8765/,并在数据库/配置更改时热重载(重新运行分析 + 重新渲染)。对于一次性构建,使用 --refresh-analysis 强制重新分析。
报告界面(output/report.html)— Money.Audit 混合仪表盘
外壳: 圆角应用框,包含 FY 列表 + 章节导航(滚动追踪)的浅色侧边栏,全宽主列。标题:你的钱什么最重要。
外观: Fraunces 衬线字体 + Spline Sans UI 字体;暖奶油色背景(#f7f6f3),金色强调色。不是通用的灰色 SaaS。
章节(侧边栏): 概览、收入引擎、月度烧钱、问题区域、企业复盘、现金与流动、行动计划、所有类别、总账(搜索 + 筛选)。
数据内嵌: const ANALYSIS = … 和 const LEDGER = …——无需外部请求。侧边栏页脚显示映射中的 account_label(切勿在模板中硬编码账户号码)。
动效: emil-design-eng — :active scale(0.98),条形宽度过渡,支持减少动效。样式指南:references/ui_style_guide.md。
模板: assets/report_template.html。文档的模拟测试数据:assets/fixtures/sample_analysis.json。
更改报告界面时:编辑模板 + 样式指南;切勿提交真实的 output/report.html。通过 analyze → report 在本地重新生成。
报告内容
- 概览 — 健康评分、指标、CFO 引用的三件事。
- 收入 / 烧钱 / 问题 — 决策优先的章节,可下钻到类别。
- 企业审查 — 当存在
config/business.json时。 - 总账 — 可搜索的交易证据(显示前 350 条匹配结果)。
标志:--port N、--no-open、--refresh-analysis。没有 --serve 时,一次性写入 output/report.html(如果缺少 analysis.json,则首先运行 analyze)。
方法论:references/financial_frameworks.md。
注意: 混合 Money.Audit 界面使用 Python analyze + report 路径。Node report.mjs 仍为标准审计渲染旧版 CFO 模板,无需 analyze 阶段。
审计文件夹中生成的结构
ROOT/
├── <对账单文件> # 输入(PDF/XLSX/XLS/CSV/TXT)
├── config/
│ ├── mapping.json # 列映射(导入后验证)
│ ├── categories.json # 类别规则(来自示例)
│ ├── business.json # 可选:企业收入/工资/基础设施
│ └── overrides.json # 你在循环期间添加的标签
└── output/
├── audit.db # SQLite 数据真相源
├── analysis.json # 分析阶段(仅本地 — 含个人身份信息)
├── uncategorized.csv # 审查队列(每次分类运行重新生成)
└── report.html # Money.Audit 仪表盘(仅本地 — 含个人身份信息)
该技能只写入必要的内容:可编辑的配置、携带各阶段状态的 SQLite 数据库、审查队列以及最终报告。中间输出(导入预览、报告 JSON)打印或嵌入,不作为额外文件写入。mapping.json 和 audit.db 必须持久化在磁盘上,因为每个阶段作为单独进程运行。
注意事项
- 相同的
mapping.json/config/ SQLite 模式在两个运行时上都能工作;你可以在审计过程中切换运行时。 - 将对账单原始文件保留在版本控制之外(它们包含个人身份信息)。
- 有关完整模式、验证数学和配置参考,请参阅 reference.md。
- 有关分析框架(储蓄率、50/30/20、基准、推荐逻辑),请参阅 references/financial_frameworks.md。
此技能处理的常见请求
- “审计/分类我的银行对账单并生成报告” — 完整流水线。
- “我的 2024-25 财年的个人 CFO 报告是什么?” — 报告后使用期间选择器。
- “我的储蓄率是多少,我该如何提高?” — 执行摘要 + 行动计划。
- “我在哪里超支了?” — 支出智能 + 反事实分析。
- “我应该停止/开始/继续做什么?” — 行动计划章节。
- “询问我的 CFO 关于现金流/债务/投资” — 向你的代理使用“询问你的 CFO”提示。
- “改进报告界面 / 使其全宽 / 修复布局” — 根据 references/ui_style_guide.md 编辑
assets/report_template.html;在本地使用 analyze → report 重新生成。切勿提交output/report.html。