3EYES 发票工作流插件 → SKILL2026-08-22

插件退役与 SKILL 接管方案

基于客户真实 ledger(1354 条)、六个 SKILL 全文与四张飞书表的实际字段核对得出。含数据迁移、能力补齐、表结构、仪表盘切换与实施步骤。

528已进 ApprovalMax
560已处理未推送
221仅下载未处理
41拆分母文件
4无 SHA 需人工
1350可确定性迁移
  1. 01结论摘要
  2. 02数据盘点
  3. 03SKILL 覆盖核对
  4. 04必须补的能力
  5. 05数据表方案
  6. 06迁移方案
  7. 07仪表盘切换
  8. 08实施步骤
  9. 09待决策项

01结论摘要

好消息

迁移在身份层面是无损的。新表主键 Invoice ID = SHA256("outlook:"+attachmentSha256)[:24] 可从旧记录确定性推算,1354 条里 1350 条有 attachmentSha256。迁移脚本可重复执行、不会产生重复行、与新表已有的 187 行测试数据天然去重。

三个硬阻塞 —— 不解决不能停插件

① 催办邮件完全没有实现。六个 SKILL 里没有任何发信能力。插件一停,当天起没有任何催办。

② 审批看板的核心字段无处产出。当前审批人 / 当前节点 / 已等待 / 是否超时 / 催办次数 / 风险等级,SKILL 侧全无。这些是本地推断出来的,不是 ApprovalMax 返回的。

③ ApprovalMax 授权仍寄生在插件里。两个 SKILL 直接 require 插件的 config.jsapprovalmax.js,插件一删立刻失效。

此外 3eyes-commercial-done-reconcile(商业票 QS 回收)整体建立在插件 CLI 之上,调用 5 个插件命令,需重写为独立 SKILL。

新 Invoice Tracking 表没有任何公式字段,而旧表有 24 个公式字段在驱动仪表盘。图表数据源无法直接重指 —— 必须先把公式补上。

02数据盘点

客户 ledger 共 1354 条,时间跨度 2026-06-18 至 2026-08-21。

判定依据条数处置风险
C 已进 ApprovalMaxrequestId528必须迁移最高
B 已处理未推送其余已提取560迁事实不迁结论
A 仅下载未处理outlook_downloaded_to_folder221不迁,重跑
S 拆分母文件multi_invoice_split_parent41父子一起迁
X 无 SHAattachmentSha2564人工建行需人工
C 类的判定键不是状态,是 requestId

approvalmax_submitted 只有 491 条,但带 requestId 的有 528 条。多出的 37 条是 approvalmax_duplicate_remote / superseded已认领远端 bill 的记录,同样不能重推。按状态筛会漏掉它们。

414 条「阻塞」其实不是积压

按主要标记拆开后,337 条本就是不可推送的文档类型,分类是正确的:

标记条数性质
Non Invoice175正确分类,非积压
Statement118
Quote14
Credit Note12
Payment Claim / Confirmation / Schedule17
Purchase Order1
Project Not Matched · No PO · PO Missing · Site Address Missing 等~77真正待人工处理

地块分布

未识别 467 · Commercial 344 · Residential 340 · Hotel 121 · Office 82。未识别占 34%,其中大部分是 A 类(尚未提取)与非发票文档。

03SKILL 覆盖核对

把 wiki 文档 14 章 + 附录逐条与六个 SKILL(118KB 文档 + 220KB 代码)比对:

规则章节覆盖承载 SKILL缺口 / 需调整
1 收件与导入完整outlook-intake-filter仅 Outlook;旧 manual_folder 来源无对应
2 多发票拆分完整outlook-intake-filter
3 字段提取完整outlook-intake-filter
3.4 业务指纹查重需确认intake + enrichment「指纹相同文档类别相同」这条未在文档中明写
4 文档类型判定完整outlook-intake-filter含 Watercare 双标题例外 ✓
5 地块识别完整intake + enrichment五个水电网账号规则齐全 ✓
6 PO 匹配合并完整project-po-enrichment
7 归档目录与命名完整enrichment (archive-policy)
8 异常票月份分层完整intake (business-rules)urgentCarryBack 已实现 ✓
8.6 异常票 DONE 回收完整exception-invoice-intake[please cancel] 覆盖 DONE ✓
9 QS 审核与回收依赖插件commercial-done-reconcile整体调用 5 个插件 CLI 命令,需重写
10 审批链与阈值完整enrichment (approval-chain)
11 推送 ApprovalMax部分依赖approvalmax-dispatch授权借用插件
12 参考数据完整dispatch (live-reference)改为实时拉取,不再依赖本地镜像
13.1 看板字段缺失当前审批人/节点/等待/超时/风险全无
13.2 红标类别完整intake七类一致 ✓
13.3 催办缺失无任何发信能力
13.4 审批意见分类部分comment-recoveryV1 只做 code 更正;hold / info-missing 仅记录不执行,暂停催办未实现
13.5 Cross-month缺失建议改为公式字段 + 视图
14 实现注意事项完整各 SKILL 分散体现
附录 A cost code完整dispatch (history-code)覆盖住宅/酒店/免 PO 商业 ✓

插件依赖清单

invoice-approvalmax-dispatch/scripts/approvalmax-live-reference.js:3
  require(".../plugins/3eyes-invoice/src/config.js")        ← 配置解析
  require(".../plugins/3eyes-invoice/src/approvalmax.js")   ← loadApprovalMaxToken / approvalMaxRequest

invoice-approvalmax-comment-recovery/SKILL.md
  "Read ApprovalMax only through the existing plugin/OpenClaw ApprovalMax tools"

3eyes-commercial-done-reconcile/*
  bin/3eyes-invoice.js  commercial-done-scan | commercial-done-apply
                        commercial-done-note | approver-history | approver-names

04必须补的能力

能力优先级形态建议要点
ApprovalMax 授权与请求层P0公共脚本,六个 SKILL 共用loadApprovalMaxToken / refreshApprovalMaxToken / tokenStatus / approvalMaxRequest + 配置解析;token 文件一并迁移
催办 SKILLP0新建 invoice-approval-reminderPM 按人汇总每 3 天;QS/Manager 超期次日一次;Graph sendMail;暂停规则;状态去重
审批看板同步 SKILLP0新建 invoice-approval-dashboard轮询状态 + 本地推断当前审批人/节点 + 写 Approval Dashboard 表
商业票 QS 回收重写P0重写现有 SKILL文件定位四层、done 门槛、code/PM/QS 取值、历史 bill 回溯,全部脱插件
审批意见暂停催办P1扩 comment-recoveryhold_payment / hold_no_reject / info_missing 三类需真正影响催办
Cross-month 视图P1公式字段 + 视图不需要独立表
文件夹收件P2扩 intake 或不做取决于是否还保留手工投递入口
授权迁移的红线

ApprovalMax 用 OAuth2 refresh token 轮换 —— 每次刷新作废旧 token。任何并发刷新都会打废授权,需要人重新走一遍完整授权流程,期间整条推送链停摆。

所以:所有取 token 的路径必须走同一个入口函数,批量提交必须串行,不加并发 worker。迁移时 customer-3eyes-token.json(含 refresh_token)必须原样带过去,不要重新授权。

05数据表方案

四张表同在 base DAbBb7uJnakKiMstSE5jvUM3p0d,无跨库问题。

字段公式去向
tblmCudpsvnmHVil 旧 Invoice Processing6316退役
tbl68CNMwGC57HJD 旧 Approval Dashboard468退役
tblNLHNj5N5sK0iD 新 Invoice Tracking540主表,需补公式
新建 Approval Dashboard~308需新建

Invoice Tracking 需补的公式字段

可直接照搬(只依赖 Received At,新表已有):

字段作用
Dashboard MonthTEXT([Received At],"YYYY-MM")
Dashboard Current Month自然月
Dashboard Recent 6 Months近 6 个月
Dashboard Current Live Cycle — Received26–25 周期,当前
Dashboard Previous Closed Cycle — Received26–25 周期,上一期

必须重写(旧公式依赖新表没有的字段):

字段旧依赖新写法
Is InvoiceCurrent State · Review Flags · Missing / Blockers 的 14 段字符串匹配改读 Category,一行即可:
IF(OR([Category]="Invoice",[Category]="Payment Claim"),"true","false")
StageCurrent State · Is Invoice · Review Flags改读 Workflow Status + ApprovalMax Status,映射见下
Stage 值新判定条件
已提交ApprovalMax Status ∈ Submitted / Approved
待自动提交Workflow Status = ApprovalMax Ready
重复或作废Workflow Status = Duplicate
多发票文件Category = Split Source
非发票Is Invoice = false
非 3EYES 发票Review Label 含 Not 3EYES Construction
等 QSWorkflow Status = Project Matched 且 Review Label 非空
待人工处理Workflow Status = Manual Review
待补信息ApprovalMax Status = Blocked
处理中其余
好消息

依赖 Stage 的 5 个统计公式(上周期已推送 / 待人工 / 待补信息 / 重复非发票 / 完成率)在 Stage 重写后原样可用,不需要改。

为什么 Approval Dashboard 要独立建表

不能只扩展 Invoice Tracking。QS 手工传进 ApprovalMax 的 bill 在 Tracking 里没有行,并进单表会丢掉这部分。独立表按 Request ID 建行,外部创建的 bill 也能出现在看板上。

需要的字段:Current Approval Node · Current Approver · Current Approver Email · Approval Deadline · Overdue · Overdue Rule · Blocked Person · Age Days · Reminder Count · Last Reminder At · Reminder Due · Risk Level · Risk Reason · Dashboard State · Action Suggestion · Last Comment · Comment Category · Comment Intent Flags · Last Actor · Submitted By · Escalate Ray · Suggested Recipient + 8 个公式字段。

公式移植时的一个坑

旧公式判 [Approval Status]="on-approval" / "on-review",而新表 ApprovalMax Status 的取值是 Not Ready / Ready / Created / Submitted / Approved / Rejected / Blocked —— 没有 on-approval。新 Approval Dashboard 表要么单独保留 ApprovalMax 原始状态词,要么把公式改判 Submitted

催办状态:建议落表

插件现在存在 reminder-state.json这个文件不迁移,切换后所有待批的人会被重新催一遍。建议改成小表(分组键 / 角色 / 收件人 / firstPendingAt / lastReminderAt / reminderCount),可迁移可审计。

顺带修插件那个缺陷:firstPendingAt 按人粘住不清零,导致清空队列后的新账单第一次运行就被判超期。新实现应每轮从当前待批行重算,状态只作兜底。

06迁移方案

C 类 · 528 条 —— 先做,风险最高

Invoice ID               ← SHA256("outlook:"+attachmentSha256)[:24]
ApprovalMax Request ID   ← approvalMax.requestId
ApprovalMax Status       ← Submitted / Approved / Rejected
ApprovalMax Phase        ← submitted 或 terminal
ApprovalMax Submitted At ← approvalMax.submittedAt
Workflow Status          ← ApprovalMax Submitted / Approved / Rejected
File Path                ← finalPath
Destination Folder       ← Project Folder

ApprovalMax Phase 最关键 —— dispatch 的相位单调递增(none → preview_ready → preparing → created → attachment_uploaded → submitted → terminal),写成 submitted 后不会回头创建。

不能依赖第二道网

dispatch 在 create 前会查远端重复,同供应商 + 同发票号 + 同金额判重。但金额不同时它会判为「修订」去更新那张旧 bill —— 那比重复建单更糟。迁移必须完整,不能靠查重兜底。

B 类 · 560 条 —— 只迁事实,不迁结论

不迁
SHA · Message ID · Sender · Subject · 原文件名旧 review flags(词表不同)
Received / Downloaded / Invoice 三个时间currentState(状态机不同)
Supplier · Invoice No · Total · Currency · GST旧 Manual Review 子目录判断
Site / PO Number / Cost Code / PM / QS
File Path

Workflow Status 统一写 Classified,让 SKILL 从项目/PO 环节重判。理由:旧插件的判断里有已知误判(101 条 No PO 里 97 条是错判),把结论迁过去等于把 bug 迁过去。

其中 337 条非发票文档可直接按 Category 落位、Workflow Status = Manual Review,不需要重判。

A 类 · 221 条 —— 不迁,让新 intake 重跑

这批只承载「附件下载过」一个事实,没有分类、没有地块。新 intake 的分类能力强得多(标题证据、拆分、timesheet 合并、紧急类判定),重跑能拿到旧插件从未产出的结果。

唯一要处理的是重复下载:新 intake 按 state.attachments[sha] 去重,直接跑会再下一遍。建议重下,落到新 stage 目录,旧 Original Files 作为历史归档保留不再使用。

S 类 · 41 条拆分母文件

Category = Split Source,Workflow Status = Split Required,Destination Folder = Manual Review/Split Source Files。子记录写 Parent Invoice ID = 母记录的新 ID、Source = Split Child。父子必须一起迁,否则子记录成孤儿。

X 类 · 4 条

attachmentSha256,其中一条来源是 approvalmax_manual_upload。无法算出主键,人工建行。

07仪表盘切换

排版可复用已复制的 blkmqotmvLkdtqwY,但每个图表的数据源都要重指,且有严格的先后依赖:

1. Invoice Tracking 补 5 个照搬公式 + 重写 Is Invoice / Stage
2. 新建 Approval Dashboard 表 + 22 个字段 + 8 个公式
3. 数据迁移(C → S → B → A)
4. 逐个图表重指数据源
5. 新旧仪表盘并行对账 1–2 个周期
6. 停插件定时任务,退役旧表
顺序不能颠倒

第 1、2 步没做完,第 4 步无从下手 —— 图表读的就是那些公式字段。旧仪表盘的 16 + 8 个公式字段是它全部指标卡与图表的数据来源。

08实施步骤

  1. 抽出 ApprovalMax 公共层

    把授权、token 刷新、统一请求、配置解析搬进 SKILL 侧公共脚本。customer-3eyes-token.json 原样迁移,不要重新授权

    六个 SKILL 全文 grep 不到 plugins/3eyes-invoice;拉一次参考数据成功
  2. 重写商业票 QS 回收 SKILL

    脱插件实现:文件定位四层、done 门槛、搜索范围、多候选取舍、code/PM/QS 取值与冲突裁决、历史 bill 回溯。

    对当前 11 条 final_ready_for_qs 只读跑通,定位结果与插件一致
  3. 建 Invoice Tracking 公式字段

    5 个照搬 + Is Invoice / Stage 重写。先在测试视图上验证 187 行现有数据的 Stage 分布合理。

    187 行全部落到预期 Stage,无「处理中」兜底堆积
  4. 迁移 C 类 528 条

    预演脚本先出清单,核对 requestId 与 Phase 后再写入。分批 50 条,每批读回校验。

    528 行 ApprovalMax Request IDPhase 全部非空且与 ledger 一致
  5. 迁移 S 类 41 条及其子记录

    父子成对写入,校验 Parent Invoice ID 指向存在的行。

    无孤儿子记录
  6. 迁移 B 类 560 条

    337 条非发票直接落位,其余 Workflow Status = Classified 交 SKILL 重判。

    重判后 Project Not Matched 数量不高于迁移前
  7. A 类重跑

    新 intake 扫描邮箱,221 个附件重新下载分类。

    新表总行数 ≈ 1350 + 187 测试行,无重复 Invoice ID
  8. 建 Approval Dashboard 表 + 看板同步 SKILL

    轮询状态、本地推断当前审批人/节点、计算超期与风险等级、写表。

    528 条已推送记录在新看板上状态与 ApprovalMax 一致
  9. 建催办 SKILL + 迁移催办状态

    reminder-state.json 转表。先以 dryRun 跑一轮,确认不会对已催过的人重复发信。

    dryRun 输出的待发清单与插件当前状态一致,无历史重发
  10. 仪表盘重指数据源

    逐个图表切换,新旧并行对账。

    同一周期的关键指标新旧两边数值一致
  11. 停插件、退役旧表

    先停四个定时任务观察一个完整周期,再删插件目录。旧表保留只读。

    一个 26–25 周期内无重复推送、无重复催办、无漏处理

09待决策项

决策选项建议
A 类 221 条重新下载 / 播种 state 复用旧文件重下 —— 干净,旧目录整体归档
B 类 560 条统一 Classified 重判 / 保留旧结论重判 —— 旧结论含已知误判
Approval Dashboard独立建表 / 并进 Invoice Tracking独立 —— 否则丢掉 QS 手工传的 bill
催办状态落表 / 继续用 JSON 文件落表 —— 可迁移可审计
Cross-month建表 / 公式字段 + 视图公式 + 视图 —— 只是一个日期筛选
文件夹收件补实现 / 不再支持看是否还保留手工投递入口
QS/Manager 期限基准发票日期 / 邮件接收日需业务确认 —— 现状会让 QS 期限早于 PM
PD 无催办补 / 维持需业务确认
建议的第一步

先做不动数据的迁移预演脚本:读 ledger → 算出每条的新 Invoice ID → 输出四类清单与完整字段映射 → 检测与新表现有 187 行的 ID 冲突 → 不写任何数据。跑完拿到清单再决定上面这些选项。