mirror of
https://git.openapi.site/https://github.com/desirecore/market.git
synced 2026-09-05 17:43:49 +08:00
1f9e0b0a03f2e99b41b0b802c3f52c5815a6b079
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 1f9e0b0a03 |
chore(invoice-organizer): 1.0.1 —— 重新 pin 到已合并 commit / repin to merged commit (#121)
## 背景 / Background 接 [#120](https://github.com/desirecore/market/pull/120)。那个 PR 只改了技能内容,**刻意没动版本号与 pin**——`contentSource.ref` 必须指向**已合并的 commit**,而那个 SHA 在合并前并不存在。先 bump 版本会让目录声称 1.0.1、却仍按指向 1.0.0 内容的 pin 去取文件,比不 bump 更糟。这是 #117/#118 那次两步发布的同一条约束。 Follow-up to #120, which deliberately left the version and pin untouched: `contentSource.ref` must point at an already-merged commit, which did not exist until #120 landed. ## 改动 / Changes - `agent.json#contentSource.ref`、sidecar 的 `provenance.content.ref` 与 `governance.compliance.reviewedRef` **三处同步** pin 到 `7560a58` - 版本 `1.0.0` → `1.0.1`(`agent.json` 与 sidecar `release.version` 两处一致) - 补 1.0.1 changelog(中英双语) ## 1.0.1 修了什么 / What 1.0.1 fixes 三条都是真机整理实测撞出来的 Agent 侧规格洞,不是模型不听话: | 缺陷 | 实测现象 | 根因 | | --- | --- | --- | | 归档路径 | 五轮里有两轮归成 `<工作目录>/2022/11/增值税电子普通发票/2022-11-18_….pdf`——同时违反「缺 `归档/` 首层」「多插一层 `<发票类型>/`」「日期用 `YYYY-MM-DD` 而非八位连写」三条 | 原文只用一句散文说了路径,上下文压缩后凭印象重建就会漂 | | 报告文件名 | 三轮全部写出 `报告/整理报告-<运行日期>.md` | 技能只定义了 `报告/<YYYY-MM>.md`,多月场景无从填写 | | 台账表数量 | 只有 1 张发票时产出三张表(省了「按销售方汇总」) | 技能写了「四张表,顺序固定」但没说「即使为空也必须存在」 | 前两条的共同点:**只跑一遍、或只跑成功的那遍,都看不见**。归档路径在早期几轮全对,是在同一会话被压缩之后才走样的。 All three are spec gaps found by repeated real-machine runs, not model disobedience — and the first two are only visible across multiple runs in the same (compacted) conversation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) |
|||
| 7560a58642 |
fix(invoice-organizer): 统一报告文件名约定 / unify report file naming (#120)
## 问题 / Problem 真机三轮整理实测:Agent 全部写出 `报告/整理报告-<运行日期>.md`,而技能里只定义了 `报告/<YYYY-MM>.md`。 **原因不是模型不听话。** 测试范围跨了 2020-08 到 2024-09 共十几个月,`<YYYY-MM>` 在多月场景下无从填写——技能没给出这种情况的文件名,Agent 只能自己发明一个。 **后果不是难看,而是会攒垃圾。** `invoice-automation` 的定时任务每月重建的是 `报告/<上月 YYYY-MM>.md`;与按运行日期命名的手工报告永远不会互相覆盖,用户目录里会留下一堆内容重叠、无从分辨新旧的报告。 Three real-machine runs all produced `报告/整理报告-<run date>.md`, while the skills only define `报告/<YYYY-MM>.md`. The runs spanned 2020-08 through 2024-09, so `<YYYY-MM>` was unfillable and the agent improvised. Because the scheduled monthly job rebuilds `报告/<last month YYYY-MM>.md`, the two naming schemes never overwrite each other and the user's directory accumulates overlapping reports with no way to tell which is current. ## 改动 / Changes - `invoice-workflow` 第 7 步补一张表,把两种文件名钉死: | 本次覆盖 | 文件名 | 例 | | --- | --- | --- | | 恰好一个自然月 | `报告/<YYYY-MM>.md` | `报告/2024-08.md` | | 跨多个月 | `报告/<起始 YYYY-MM>_<结束 YYYY-MM>.md` | `报告/2020-08_2024-09.md` | 并写明月份按**开票日期**归属(与台账同口径)、同名直接覆盖(报告是从 `.index/ledger.json` 全量重建的派生产物,不像台账需要先备份)、以及**为什么不许用运行日期命名** - `invoice-ledger` 与 `references/月度报告模板.md` 同步该约定 - 目录布局示例与中英 USAGE 一并标注跨月形态 ## 为什么不在本 PR 里 bump 版本 / Why no version bump here `contentSource` 的 `ref` 必须 pin 到**已合并的 commit**,本 PR 合并前拿不到那个 SHA。先 bump 版本会让目录声称 1.0.1、却仍按指向 1.0.0 内容的 pin 去取文件——比不 bump 更糟。版本号、pin 与 changelog 留到后续 PR 一起改。 The `contentSource` ref must pin to an already-merged commit, which does not exist until this PR lands. Bumping the version now would advertise 1.0.1 while still serving 1.0.0 content. Version, pin and changelog follow in a second PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- ## 追加:台账四张表必须全建 / Also: all four ledger sheets must always exist 真机压测(只有 1 张发票)产出的 `台账.xlsx` **只有三张表**——Agent 判定「按销售方汇总」在单一销售方下冗余而略过: ``` 发票测试(23 张) → ['明细', '月度汇总', '按销售方汇总', '异常'] 发票压测(1 张) → ['明细', '月度汇总', '异常'] ← 少一张 ``` 技能写的是「四张表,顺序固定」,但没说「即使为空也必须存在」。台账是要被重复重建、被用户自己写公式引用、被下一次整理覆盖的产物;表的位置一旦随数据量浮动,「第 3 张是按销售方汇总」这类引用就会在下个月悄悄指错。已在 `invoice-ledger` 里把这条钉死。 A stress run with a single invoice produced only three sheets — the agent judged the per-seller summary redundant. The skill said "four sheets, fixed order" but never said "even when empty". Sheet positions that float with row count silently break user formulas and rebuilds; now pinned. |
|||
| 6c303ef21f |
feat(invoice-organizer): 声明为可安装并 pin 到已合并 commit / make Invoice Organizer installable (#118)
接 #117。那个 PR 只登记了条目本身(`availability: listing-only`),用户在市场里**看得到、装不了**。 本 PR 补齐可安装所需的证据链。 Follow-up to #117, which only listed the entry (`listing-only`) — visible in the marketplace but not installable. This PR supplies the evidence chain required for installation. ## 改动 / Changes - `agent.json` 新增 `contentSource`,pin 到 #117 的合并 commit `42a29e9` - sidecar 的 `provenance.content` 与之**归一化后逐字一致** (两侧字段名按各自 schema 写:agent.json 侧是 `repoUrl`(`marketPointerSchema` 的必填字段名), sidecar 侧是 `url`;`normalizeContentSource` 里 `input.url ?? input.repoUrl` 把两者统一成 `url`, 比对发生在归一化之后,取的是 `{kind, url, path, ref, sha256}` 五元组) - `governance.compliance.reviewedRef` 绑定同一 commit;`reviewedAt` 与 `timestamps.reviewedAt` 取同一天 - `governance.availability` 翻成 `installable` - **移除** sidecar 的 `stewardship: "official"` 与 `branding.relationship: "official"` ## 为什么移除 `stewardship: official`(这一条值得单独看) `normalize/sidecar.ts` 只允许 `bundled-builtin` / `system-agent` / `official-pointer` 三种受信来源自称 official。**inline agent 不在其中**,命中即 `catalog.sidecar-forbidden-provider-claim` —— **整份 sidecar 被丢弃**, 静默回落到 `agent.json` 派生的 legacy 默认值。 在 dev 实例上实测确认(同步到 `42a29e9` 之后): ``` 客户端拿到的 governance: {"availability":"listing-only","license":{"state":"unknown"}, "redistribution":"verify-package-terms","listingMaintainer":{...}} sidecar 里写的却是: license: {state:"known", value:"MIT", evidencePath:"LICENSE"}, redistribution: "allowed" catalogDiagnostics: null ``` 也就是说:sidecar 被整份丢弃,而**维护者没有任何反馈**。 `dingtalk-workspace` 也有同样的 `stewardship: "official"`,它的 sidecar **同样一直在被静默丢弃** (实测它的客户端 governance 与本条目改之前完全一样)。这条不在本 PR 范围内, 建议单独处理;DesireCore 主仓库那边我另外记了一条「sidecar 被拒的诊断没有透出到任何 API」的缺口。 ## 验证 / Verification 直接调用客户端的真实代码路径(不是照着文档推断): ``` hydrateCatalogSidecar success=true diagnostics=[] compareMarketCatalogConsistency consistent=true mismatches=[] evaluateCatalogAcquisitionEligibility allowed=true reasons=[] legacy provenance.content: {"kind":"git","url":"https://github.com/desirecore/market.git", "path":"agents/invoice-organizer","ref":"42a29e99a0…"} sidecar provenance.content: {同上,逐字一致} ``` `compareMarketCatalogConsistency` 对 inline agent 会**逐字比对** `provenance.content`; 不一致会让整份 sidecar 静默降级回 listing-only,而 Python 校验器对 inline 条目 直接跳过这项比对(`_validate_content_consistency` 对 inline 早退),抓不到。 所以这一项只能靠上面这条客户端侧的验证兜住。 ## 校验 / Validation ``` validate_catalog_metadata.py --require-complete 0 error, 129 warning, agents=4, sidecars=74 validate-i18n.py 0 error, 129 warning translate.py --check ok gen-collection-children.py --check ok ``` 129 条 warning 全部来自 `skills/*` 的存量条目,`invoice-organizer` **零命中**。 |
|||
| 42a29e99a0 |
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。
|