feat(invoice-organizer): 新增发票整理助手 Agent / add Invoice Organizer agent (#117)

新增官方 inline Agent「发票整理助手」(`invoice-organizer`)。

Adds an official inline Agent, **Invoice Organizer**, that turns
invoices scattered across
mailboxes and local folders into a reconcilable, reusable ledger.

## 它做什么 / What it does

七步固定流程,每一步幂等:**接入检查 → 收集 → 解析 → 去重 → 归档 → 台账 → 报告**。

- **收集**:从已接入的邮箱(Gmail / Outlook / IMAP)找出候选发票邮件,附件用 `MailOperations` 的
  `save_to` 直接落盘,base64 不进模型上下文
- **解析**:OFD(三个结构化来源分别处理)、PDF 文字层、扫描件走视觉;每个字段带 `extractedBy` 与置信度
- **去重**:发票号码为主键,跨格式识别同一张票(PDF 与照片、重复下载);近重复不自动合并
- **归档**:`归档/年/月/YYYYMMDD_销售方_金额_发票号码.ext`,原件一个不动
- **台账**:xlsx 四张表(明细 / 月度汇总 / 按销售方汇总 / 异常),发票号码强制文本格式;
  装不上 openpyxl 时降级为 UTF-8 BOM CSV
- **报告**:Markdown 月度报告,收尾固定报五个计数(范围 / 候选 / 成功 / 待复核 / 失败)
- **自动化**:邮件规则 `agent_handle` + `ManageSchedule` 定时出账

## 明确的能力边界 / Explicit boundaries

- **不做发票真伪查验**,也不暗示做过——只做形式校验与勾稽校验,给出官方查验平台入口让用户自己核验
- **不删除、不移动、不转发用户的邮件**
- **票面内容不外流**:不外发、不代发、不上传第三方接口或在线查验站点
- **不做汇率换算**;不承诺「已找全」,只报「在给定范围内找到 N 封候选、成功解析 M 张」

## 结构 / Structure

```
agents/invoice-organizer/
├── agent.json  persona.md  principles.md  LICENSE
├── USAGE.zh-CN.md  USAGE.en-US.md
├── catalog-metadata.v1.json      # availability: listing-only(可安装化见下)
├── assets/avatar.webp
└── skills/
    ├── invoice-workflow/         # 七步总纲、目录布局、落盘顺序、去重主键、幂等
    ├── invoice-extract/          # 三种载体的解析细则、置信度分档、特殊票据
    │   └── references/票面文本形态.md
    ├── invoice-ledger/           # 台账结构、人民币约定(覆盖 xlsx 技能的默认口径)
    │   └── references/月度报告模板.md
    └── invoice-automation/       # 邮件规则与定时调度的参数模板与排查
```

`manifest.json` 的 `stats.totalAgents` 3 → 4。

## 事实性核对 / Fact-checking

技能里引用的**每一个**端点 / 工具名 / 参数名 / 返回字段都对着 DesireCore 主仓库实现逐条核对过,
并经过一轮对抗式 review。review 抓到的、已修正的主要事实错误:

- OFD 一节原本只覆盖 2020 年式样;已改为按**三个来源**分述
  (内嵌附件 / 2024 数电票的 `Tags/CustomTag.xml` 标引 / `DocInfo/CustomDatas`),
  键名从 `Buyer/BuyerName` 改为真实的点号路径 `Buyer.BuyerName`,置信度按来源分档
- **`DocInfo/CustomDatas` 的「合计金额」是不含税金额**,误当 `totalAmount` 会让每张 2024
数电票少记税额
  ——已写成硬规则
- Gmail 本地缓存搜索的 `q` **实际只按主题过滤**(正文过滤在主题收窄之后才跑),
  原文写成「搜主题与正文」会导致静默漏邮件
- 「轮询只覆盖收件箱」只对 IMAP 成立,Gmail / Outlook 是整个邮箱
- `POST /rules/{id}/test` 走另一份内联实现、**没有 `matches_regex` 分支**,
  不能用它验证正则规则
- 邮件列表项里 Gmail / IMAP **是带** `attachments[]` 的,只有 Outlook 不带;
  `labelIds` 是 Gmail 专有

## 真机验证 / Verified on a live instance

在 dev 实例上以一句话指令处理 31 个混合文件(数电票 / 旧版票 / OFD / 扫描件 / 行程单,
外加重复、近重复、作废、零额与 4 个非发票负样本):

- 23 张归档,字段与夹具 ground truth **逐条吻合**
- 4 个非发票**全部正确拒绝**并写明理由(施工许可证 / 对账单 / 技术服务合同 / 邮件通知)
- 4 个跨格式重复(3 张扫描件 + 1 次重复下载)**全部靠发票号码主键识破**,隔离而非删除
- 近重复正确未合并;作废票归档但不计入合计;低置信度定额发票标为待复核
- 台账 xlsx 四张表、报告含五个计数、`SendUserMessage` 带附件交付
- `待整理/` 31 个原件一个未动

## 校验 / Validation

```
validate_catalog_metadata.py --require-complete   0 error, agents=4, sidecars=74   exit=0
validate-i18n.py                                  0 error                          exit=0
translate.py --check                                                               exit=0
gen-collection-children.py --check                                                 exit=0
```
129 条 warning 全部来自 `skills/*` 的存量条目,改前改后一字不差,`invoice-organizer` 零命中。
另核实:`agent.json` 过 `marketAgentSchema`(详情页)与收窄后过
`agentConfigSchema`(安装),
persona / principles 的 6 个 canonical key 用平台真实解析器全部解得出,全树敏感信息扫描通过
(所有公司名 / 税号 / 银行账号均为 `示例`/`示范`/`虚构`/`样例` 前缀的合成值)。

## 后续 / Follow-up

本 PR 为 `availability: listing-only`。可安装化需要把 `agent.json#contentSource` 与
sidecar 的 `provenance.content` **逐字一致地** pin 到本 PR 的合并 commit,
并补 `governance.compliance` 与 `timestamps.reviewedAt`——那是紧接着的第二个 PR。
This commit is contained in:
2026-09-04 07:28:03 -04:00
committed by GitHub
parent 61c82782c4
commit 42a29e99a0
15 changed files with 1889 additions and 2 deletions

View File

@@ -0,0 +1,74 @@
# 发票整理助手 · 使用说明
## 装之前先确认三件事
**1. 邮箱已接入。** 这是唯一需要你亲自完成的一步:在 DesireCore 的邮箱界面授权一个账户Gmail / Outlook / IMAP 都可以)。没有邮箱的话本 Agent 只能处理你手工放进工作目录的文件,收集那一步用不了。
**2. 有一个你找得到的工作目录。** 装好后 Agent 会自动拿到一个默认工作区,但那个路径在文件管理器里很难找。第一次对话时它会建议你登记一个可见目录(比如 `~/Documents/发票`)并设为首选——同意即可,产物会落在那里,并出现在 DesireCore 的「文件工作台」里。
**3. 它不做发票真伪查验。** 没有官方查验通道。它只做形式校验(字段齐全、金额勾稽、票面自洽),并把国家税务总局全国增值税发票查验平台的入口给你,让你用发票号码自行核验。如果你需要的是验真,这个 Agent 不是答案。
## 怎么用
装好之后直接说人话:
- 「帮我整理一下 8 月的发票」——跑完整流程:收集 → 解析 → 去重 → 归档 → 台账 → 报告
- 「这个月报销多少钱」——只汇总,不重新扫邮箱
- 「这张发票记过了吗」——查索引,秒回
- 「把 8 月的发票导成 Excel」——从索引重建台账不重新解析
- 「以后新发票自动入账」——配邮件规则与定时任务(会先跟你说清楚会发生什么)
「上个月」这类相对时间它会先换算成明确的起止日期说给你听,你确认了再开工——跨年时这一步能省掉不少麻烦。
## 你会拿到什么
工作目录下多出一个 `发票/` 目录:
```
发票/
├── 台账.xlsx依赖不可用时为 台账.csv
├── 报告/2024-08.md
├── 归档/2024/08/20240815_某某酒店管理有限公司_1959.98_24312000000000020002.pdf
├── _inbox/ 刚下载、尚未处理
├── _quarantine/ 解析失败或判定为非发票(每份都附一个 .reason.txt 说明原因)
└── .index/ 去重索引,别手工改
```
台账有四张表:明细、月度汇总、按销售方汇总、异常。金额按人民币口径(保留两位小数,零金额显示为 `¥0.00` 而不是隐藏),发票号码和税号按文本存放,不会被 Excel 变成 `2.4312E+19`
月度报告开头永远是一句话结论,后面才是明细;「需要你处理的」那一节里每条都带具体动作,不只是描述现象。
## 支持的票据
- **PDF**数电票2023 年后的全国统一电子发票,普票与专票)、旧版增值税电子普通发票、铁路电子客票报销凭证(新旧两种版式)、航空运输电子客票行程单、出租车与网约车票
- **OFD**读取包内的结构化发票数据。2020 年式样的票在包里附了一份完整的国标发票 XML字段由开票系统直接写出、不是版面猜测这条路径置信度给满分2024 数电票式样只带发票标引,拿不到发票代码和逐行明细,那部分会回到版面文本补齐,置信度相应下调。两者都没有时回落到版面文本
- **扫描件与图片**JPG / PNG / WebP以及没有文字层的扫描版 PDF交给视觉模型识别置信度相应下调
解析这些**不需要你安装任何东西**。只有生成 `.xlsx` 台账可能需要 Python 的 `openpyxl``pandas`;装不上时它会自动出一份带 UTF-8 BOM 的 CSVExcel 打开中文不乱码)并明确告诉你降级了。
## 关于审批(无人值守要看这一段)
发票整理会频繁调用需要确认的工具(读邮箱、下附件、写文件),默认的审批模式下每次都会弹审批卡。整理几十张发票时会连着弹很多张——这是设计如此,不是故障。
如果你要的是真正的无人值守(半夜定时出台账、新邮件自动入账),需要**你自己**在 Agent 设置里把执行审批模式改成「允许全部」。代价是之后本 Agent 的写文件与邮件调用都不再逐条询问。注意「总是允许」按钮对这些工具当前不生效,点了也还会弹。
## 它不会做的事
- 不删除、不移动、不转发你的邮件。要清理邮箱请你自己在邮箱里做
- 不做记账凭证、不做纳税申报、不做进项抵扣判断
- 不做汇率换算。外币结算的票面本身仍以人民币计价,原币金额与折算汇率原样抄进备注,不换算、也不并成第二套合计
- 不自动合并疑似重复(号码只差一位、其余字段全同),标出来交你确认
- 不编造票面字段。抽不到就留空、把原件隔离并说明卡在哪一步
- 不承诺「已全部找到」,只报「在给定范围内找到 N 封候选、成功解析 M 张」
## 已知限制
- **IMAP 账户的邮件规则只覆盖收件箱。** 如果你用的是 IMAP 且把发票邮件自动归档到了别的文件夹规则不会触发这种情况下用对话主动让它扫那个文件夹。Gmail 与 Outlook 的增量轮询覆盖整个邮箱,不受这条限制
- **本地缓存搜索只按主题匹配关键词。** 主题里不含「发票」而只在正文里提到的邮件「您有一份新的电子凭证」这类缓存搜索找不到。Gmail 账户它会改用 Gmail 服务端搜索来覆盖正文Outlook 与 IMAP 只能把邮件拉回本地逐封扫,范围给大时会慢。它会在收尾里说明这次是按什么匹配的
- **Outlook 与 IMAP 没有服务端搜索**,只能先把邮件拉到本地再筛。范围给大时会比 Gmail 慢不少Gmail 可以直接用原生搜索语法一次筛出来)
- **带口令的加密 PDF 打不开。** 部分开票平台会发带口令的 PDF口令通常写在邮件正文里。目前需要你自己解密后把文件放进 `_inbox/`,它会在下次整理时接上
- **作废判定依赖文本层。** 作废戳如果是纯图形,可能识别不出来。台账里作废票单独一栏,请顺手核一眼
## 隐私
发票里有公司抬头、纳税人识别号、开户行账号、行程信息。这些只在你本机的工作目录和台账里流转。本 Agent 没有对外发送的通道,也不会替你回复或转发邮件;你要把台账发给别人时,它把文件交回给你,由你决定发给谁。