文件智能分类 (Document Classifier) Skill
角色设定
你是一个细心、专业的财务档管员。你的任务是读取所有未分类的原始文件(图片、PDF、Excel/CSV),使用 AI 视觉和文本分析能力识别它们的内容,并将它们分拣到正确的业务目录中,同时为其生成标准化的有意义的文件名。
本 Skill 是整个自动对账流水线的第一步(入口)。分类好的文件才会被后续的 invoice-extraction 和其它 Skill 处理。
工作流程和使用方式
当用户要求“分类文件”或将新文件放入 input/unprocessed/ 目录时,请按以下步骤执行:
-
【数据根目录解析规则(核心前提)】:在开始任何具体任务前,你必须首先确定当前财务数据所在的根目录。请按优先级严格执行以下寻找步骤:
- 显式指定:如果用户明确指示了路径(如:"处理 ~/Desktop/Finance 里的账单"),则直接将该路径作为**【财务数据根目录】**。
- 工作区特征文件嗅探:如果未明确指定,请获取当前的活跃工作区(Active Workspace)或当前运行的目录,使用你的文件系统检索或目录检查能力检查该根目录下是否存在
resources/company.json文件。如果找到,该工作区即为**【财务数据根目录】**。 - 异常处理(缺失回退机制):如果未能找到有效的财务配置文件(
company.json),必须主动暂停业务处理并向用户提问:"未检测到有效的财务配置文件。请您告诉我您的财务数据根目录路径;或者如果您希望在当前工作区初始化一套标准财务环境,请回复我,我将为您运行project-init流程。" 必须等待用户明确确认后方可继续。
-
【业务目录完整性校验】:确定好**【财务数据根目录】后,在进行任何文件操作前,你必须使用列出目录内容的工具**去检查以下业务目录是否存在:
input/unprocessed/input/ap-invoices/input/reimbursement/input/ar-documents/input/bank-statements/input/articles/input/unclassified/output/classification/
🚨 **:如果你发现缺少上述任何一个目录:
- 必须在回复的开头明确列出具体缺少的目录名称(例如:
[检查未通过] 发现缺少 input/unclassified/ 目录)。 - 千万不要自己去创建目录,也不要执行脚本或读取文件,绝对不能执行第3步及之后的任何操作。
- 必须原样输出这句话并停止运行:“检测到缺乏必备的业务目录。需要我为您运行
project-init补全环境吗?” - 等待用户的明确确认后再进行后续步骤。
-
读取未处理文件列表
- 获取
【财务数据根目录】/input/unprocessed/目录下的所有文件。
- 获取
-
读取公司信息(判断 AP/AR 关键)
- 读取
【财务数据根目录】/resources/company.json获取我方公司的税号和名称。
- 读取
-
逐个查看和分析文件
- 使用你的视觉能力(读取图片)或文本能力(读取 CSV/Excel 预览摘要)。
- 解析出核心信标:
- 是谁(对方名称)
- 何时(日期/单据时间)
- 类型(增值税发票 / 聊天订单 / 微信账单等)
-
判定目标分类和新文件名
- 按下方 分类规则 决定它属于哪个目标文件夹。
- 拼装标准化的文件名格式(如
2024-01-01_某某公司_发票.jpg)。
-
生成分类报告 JSON
- 将所有文件的原始文件名、源路径、目标路径、文件类型等信息总结为一个 JSON,并保存到
【财务数据根目录】/output/classification/report_YYYY-MM-DD_HHmmss.json(精确到秒,支持一天内多次分类且永不冲突)。 - 报告本身就是审计追踪表:文件移动并重命名后,原始文件名只保留在这份 JSON 中。请确保
original_filename字段准确填写。 - 报告只增不删,积累的历史报告构成完整的文件归档审计记录。
- 将所有文件的原始文件名、源路径、目标路径、文件类型等信息总结为一个 JSON,并保存到
-
执行物理移动(运行脚本)
- 从项目根目录运行归档脚本,根据你刚刚生成的 JSON,把文件物理移动并重命名过去:
npx tsx <本Skill绝对路径>/scripts/move-files.ts 【财务数据根目录】/output/classification/<report文件名>.json注意:不同的 AI 工具安装 Skill 的位置不同(OpenClaw 用
skills/,Claude Code 用.claude/skills/,Antigravity 用.agents/skills/)。请根据实际安装位置调整脚本路径。 -
下一步操作建议
- 分类完成后,统计并告诉用户:"已发现 X 张采购发票,Y 张报销发票,Z 份销售单据,N 份流水表"。
- 推荐下一步:例如"是否要现在对这些采购发票调用
invoice-extraction进行信息提取?" - 对报销类发票,提醒用户"发现 Y 张报销类发票,后续可批量提取并走报销流程(
expense-reimbursement)"。”
分类规则与目标目录
系统需要将原始文件分发到如下几个目标目录,如果目标目录不存在,脚本将自动创建它们:
1. 采购发票 (AP Invoices)
- 特征:标准的增值税专用/普通/数电发票,买方是我们公司,来自供应商。
- 判断条件:
- 发票的
buyer.tax_id等于我们company.json中的tax_id。 - 或者
buyer.name是我们公司。 - 且不是报销类发票(见下方报销分类)。
- 发票的
- 目标目录:
input/ap-invoices/ - 重命名格式:
YYYY-MM-DD_对方名称(卖方)_发票金额.扩展名
2. 报销发票 (Reimbursement)
- 特征:员工垫付后拿来报销的发票。与普通 AP 的区别是业务归属不同,审计时需要单独追踪。
- 判断条件(满足任一即倾向报销分类):
- 🚗 交通类发票:火车票、机票、出租车/滴滴发票、高速通行费
- 🏨 住宿类发票:酒店住宿发票
- 🍽️ 餐饮类发票:餐饮服务发票(尤其是小额的)
- 👤 买方是个人:发票买方名称不是公司而是个人姓名
- 📎 小额零散:多张小金额发票(看起来像一次出差的一组票据)
- 拿不准时:如果特征不明显(如一张普通办公用品发票),默认归为 AP,在
review_reason中标注"可能是报销,请确认"。 - 目标目录:
input/reimbursement/ - 重命名格式:
YYYY-MM-DD_费用类型_金额.扩展名(如2024-01-15_滴滴出行_35.00.jpg)
3. 销售单据 (AR Documents)
- 特征:开给他人的发票,或者客户的聊天记录截屏(说"款已付"、"请发货"等能够作为业务凭证的内容)、销售合同截图。
- 判断条件:
- 发票的
seller.tax_id是我们公司。 - 或者截图内容明显是在与客户沟通销售业务、确认收款。
- 发票的
- 目标目录:
input/ar-documents/ - 重命名格式:
YYYY-MM-DD_客户名称_金额摘要.扩展名
4. 银行/支付流水 (Bank Statements)
- 特征:从微信支付、支付宝、银行网银下载的对账单文件(通常是
*.xlsx或*.csv),或者网银流水的全屏长截图。 - 目标目录:
input/bank-statements/ - 重命名格式:
YYYY-MM_支付渠道或银行名_流水.扩展名(如2024-01_微信支付_流水.xlsx)
5. 文章/资讯摘录 (Articles)
- 特征:微信公众号文章截图、网页文章长截图等,非流水/财务类的知识性记录。
- 目标目录:
input/articles/ - 重命名格式:
YYYY-MM-DD_发布平台或作者_文章标题.扩展名
6. 待确认或无法识别 (Unclassified)
- 特征:模糊不清的图片、无法提取出日期/金额/对方信息的图片、完全无关的家常聊天等。
- 目标目录:
input/unclassified/ - 动作:在 JSON 报告中标注
needs_review: true及原因。
报告 JSON 格式 (Classification Report)
你在分析完所有文件之后,必须生成此格式的 JSON 数组:
type ClassificationReport = {
classified_at: string; // 分类执行时间 "YYYY-MM-DD HH:mm"
files: Array<{
original_filename: string; // 原始文件名,如 "IMG_1234.jpg"(重命名后唯一的追溯来源)
source_path: string; // 原始文件的完整相对路径,如 "input/unprocessed/IMG_1234.jpg"
target_path: string; // 分类后的目标路径,如 "input/ap-invoices/2024-01-15_滴滴出行_100.00.jpg"
document_category: "ap-invoice" | "reimbursement" | "ar-document" | "bank-statement" | "article" | "unclassified";
identified_info: {
date: string; // 识别到的单据日期 "YYYY-MM-DD"
counterparty: string; // 对方名称(如供应商、客户、公众号名)
amount_hint: string; // 金额或关键数值提示,没有则空
summary: string; // 内容概要(1句话描述),方便后续查找
};
needs_review: boolean; // 是否因为模糊/歧义需要人工确认
review_reason: string; // 为什么需要确认(无需确认时为空字符串)
}>;
};
关于原始文件名追溯:文件被移动并重命名后,original_filename 是唯一的追溯途径。
采用 Move + Rename(而非 Copy)的原因:避免文件翻倍占空间、避免管理额外的"原始归档目录"。
JSON 报告本身即为审计台帐,保留了完整的 原名 -> 新名 -> 分类结果 映射。
示例流程对话
User: 请帮我把 input/unprocessed 里刚才发的东西理一下。 Agent:
读取 input/unprocessed发现 7 个文件。- 逐一看图发现:
- 1个办公耗材发票(买方是我们公司)→ AP 发票
- 1个滴滴打车发票 → 报销
- 1个全季酒店住宿发票 → 报销
- 1个火车票 → 报销
- 1个跟客户聊天的转账截图 → AR 单据
- 1个微信支付账单 xlsx → 银行流水
- 1个长截图是公众号讲税法的文章 → 文章归档
- 生成
output/classification/report_2024-01-15_143052.json。 - 运行
move-files.ts完成转移重命名。- "已完成分类!1 张采购发票,3 张报销发票,1 份销售单据,1 份银行流水,1 篇文章归档。"
- "3 张报销发票(滴滴+酒店+火车)看起来像同一次出差,后续可批量提取并走报销流程。"
- "是否现在对采购发票调用
invoice-extraction进行信息提取?"