Invoice Check — 发票查验
Overview
通过 scripts/check_invoice.py 直连查验服务端点(JSON-RPC over HTTP,等价于调用 MCP 工具 check_invoice/check_invoices,无需安装配置 MCP server,无需设置任何环境变量),连接税局查验通道验证发票真伪。查验七要素取自票面,结果以中文结论呈现并出卡片。服务地址与 token 已内置在脚本顶部(DEFAULT_URL / DEFAULT_TOKEN),开箱即用。
When to use this skill
触发:
- "帮我查这张发票 / 验一下这票 / 这票是真的吗"
- "这张票是不是作废了 / 红冲了 / 失控发票"
- 多张票批量核验
不触发:发票图片 OCR 识别(另有的能力)、报销整理、开票咨询。
Workflow
第 1 步:收集查验要素(必问清单 + 选择题规则)
硬性规则(不可跳过):执行脚本前必须逐项核对下表要素。任何必填要素缺失或口径不明,必须先向用户提问,等回答后再执行——禁止直接跑脚本靠报错发现缺参,禁止用占位值/猜测值凑参数。 只有当用户一次说全了所有必填项(号码、日期、金额,8 位老票还有代码,04/10/11/14 老式普票还有校验码后6位)才允许跳过提问直接执行。同样禁止反向过度提问:该票种不需要的要素(如数电票的代码/校验码)不要问。
提问形式:票种判断用选择题(念票头选类型);号码/日期/金额/校验码一律填空,不做"选项套填空"。首轮一次性并列问完所有缺失项(不要挤牙膏式逐个追问)。用户答完立即执行,不要重复确认已有信息。
| # | 要素 | 工具参数 | 问法 |
|---|---|---|---|
| 1 | 发票号码 | invoiceNumber | 填空:"请念发票右上角的发票号码"(老票 8 位数字;全电/数电票 20 位) |
| 2 | 发票代码 | invoiceCode | 老票(8位号码)必填:"发票代码(号码上方那行 10 或 12 位数字)";20 位全电票没有代码,不要再问 |
| 3 | 开票日期 | invoiceDate | 填空:"开票日期"(格式 YYYY-MM-DD;超 5 年的票查不了,如实告知) |
| 4 | 金额 | invoiceAmount | 填空 + 口径确认:"票面金额是【价税合计】还是【不含税金额】?念对应数字"(口径规则见下) |
| 5 | 校验码 | checkCode | 仅老式普票类要:增值税普通发票(04)/增值税电子普通发票(10)/卷式(11)/通行费(14)。数电/全电票(票头"电子发票(普通发票)"或"电子发票(增值税专用发票)",20位号码)没有校验码,不要问;专票类也不问。用户主动提供则填 |
| 6 | 销方税号/购方名称 | salesTaxNo / purchaserName | 区块链/通用机打票才问(深圳/云南/浙江/江苏票) |
金额口径规则(按票种,详见 references/field_rules.md):
- 专票类(增值税专用/机动车销售/电子专票/全电纸质专票/数电机动车)→ 填不含税金额
- 二手车(纸质/电子/数电)→ 填车价合计
- 电子普票/电子专票/铁路客票/航空行程单/数电通行费 → 填价税合计
- 拿不准票种 → 让用户念票头文字,按映射表翻译("增值税专用发票"→01、"增值税电子普通发票"→10、"电子发票(普通发票)"→90 全电普票等)
首轮提问示例(用户只说了"帮我查张票"时,一次发下面这组问题;选项工具可用时用选项 UI):
- 问题1(填空):请念发票右上角的发票号码(老票 8 位数字;全电/数电票 20 位)
- 问题2(填空,8 位号码才需要):发票代码(号码上方那行 10 或 12 位数字)
- 问题3(填空):开票日期(票面上的年月日)
- 问题4(填空+口径):金额——是票面的"价税合计"还是"不含税金额"?念对应数字
- 问题5(填空,仅老式普票 04/10/11/14 需要;数电/全电票没有校验码,跳过此问):校验码后 6 位(票面右下角长串数字的最后 6 位)
- 用户已说清的项目从清单中划掉,只问缺的
第 2 步:执行查验脚本(禁止自行判断真伪)
单张:
python3 scripts/check_invoice.py \
--number 12345678 --code 3300191130 \
--date 2025-06-15 --amount 113.00 [--check-code 123456]
多张(≤20):把要素整理成 JSON 数组文件后 --batch:
python3 scripts/check_invoice.py --batch /tmp/invoices.json
参数映射:号码→--number、代码→--code(20 位全电票不传)、日期→--date、金额→--amount、校验码后6位→--check-code、销方税号→--sales-tax-no、购方名称→--purchaser-name。
脚本输出即查验结果中文文本(stdout),业务错误(限流/配置/缺要素)会打印原因到 stderr 并非零退出——原样转述,修正后重跑。任何"这票看起来真不真"的判断必须来自脚本返回,禁止凭票面信息下结论。
> 兼容说明:若宿主已把该服务配置为 MCP server(工具 check_invoice / check_invoices 可用),二选一即可,结果等价;脚本方式无需任何 MCP 配置。
第 3 步:结果解读(结论先行)
工具返回每票一段中文结果,按此结构转述:
- 结论:"✅ 查验一致,票面真实,状态正常" / "❌ 查验未通过" / "⚠️ 查验一致但已红冲"
- 票面回显:号码、代码、日期、类型、购销方、价税合计
- 状态:正常/作废/红冲/失控(红冲要提示对应的红字发票号码)
- 查验次数与查询时间
错误码处理:
| checkResultCode | 含义 | 给用户的话 |
|---|---|---|
| 0 | 查验成功 | 直接给结论 |
| 4001 | 查无此票 | "税局查无此票。请核对号码/代码/日期/金额/校验码是否与票面一致,特别是金额口径(价税合计 vs 不含税)" |
| 5001 | 参数出错 | 转述具体错误(多为要素格式问题),修正后重试 |
| 6001 | 税局服务不可用 | "税局服务暂时不可用,建议 30 分钟后再试" |
| 7001 | 请求不规范 | 超次/超量/超 5 年,如实说明 |
| 8001 / -9999 | 系统异常 | "查验服务异常,请稍后重试" |
额度用完提示:"今日查验额度已用完(x/y 次/天),请明天再试"——原样告知,不要重试。
第 4 步:生成结果卡片(单票必出图)
出图规则(与 pension/income-tax 一致的激进式):
- 单票查验 → 必须出图。卡片是标准交付物:结论徽标(绿"查验一致·正常"/橙"查验一致·红冲/作废"/红"查验未通过")+ 票面信息 + 查验信息
- 查验未通过(4001 查无此票等)同样必须出图——红色"查验未通过"卡片就是给用户看的最终结论,不要因为业务失败/脚本报错而跳过出图。只有脚本网络/参数层面的错误(没查成)才不出图,改为修正后重跑
- 多票批量 → 默认出一张汇总卡(每票一行结论);用户要单票详情卡时再逐张出
- 重查/纠错中间过程不出图,最终结果再出
- 用户明确说"不用图"→ 尊重,纯文本
- Pillow 缺失时回退纯文本,提示可 pip install pillow 后重试,不要中断
执行方式:把查验结果整理成 JSON,通过 stdin 传给渲染脚本(第一个参数是输出 PNG 路径):
echo '{"invoiceNumber":"12345678", ...}' | python3 scripts/render_card.py /tmp/invoice_check_<号码>.png
JSON 字段模板(从工具返回文本+用户提供要素整理;没有的字段留空/省略):
| 字段 | 来源 |
|---|---|
| invoiceCode / invoiceNumber / invoiceDate | 用户提供的查验要素 |
| invoiceTypeName | 票头文字翻译(field_rules.md 映射表) |
| salesName / salesTaxNo / purchaserName / purchaserTaxNo / amountTax | 脚本返回文本中的票面信息(销方/购方后括号内为税号) |
| state / checkResultCode / checkResultMessage / checkTimes / queryDate | 脚本返回文本 |
| redwordNumbers | 脚本返回的红字发票号码数组 |
多票时传 {"results": [ {...}, {...} ]}。注意 checkResultCode=="0" 且 state=="1" 才显示绿色"查验一致·正常";红冲/作废/失控显示橙色警示。
生成后:宿主有 present_files 类工具必须用它展示;没有的宿主把图片路径告诉用户。
Examples
例 1 — 数电普票
用户:"帮我查张票,号码 25317000000123456789,2026-08-01 开的,358 块"
→ 20 位号码 = 数电票,无代码无校验码。若金额口径不明追问一次"价税合计还是不含税"(数电普票是价税合计),要素齐即执行:
→ python3 scripts/check_invoice.py --number 25317000000123456789 --date 2026-08-01 --amount 358.00(不要问校验码——数电票票面没有)
例 2 — 老式专票
用户:"专票,代码 3300191130,号码 12345678,2024-03-15,不含税 11300"
→ 要素齐了:python3 scripts/check_invoice.py --code 3300191130 --number 12345678 --date 2024-03-15 --amount 11300
例 3 — 批量
多张票让用户逐张念要素,凑成 JSON 数组写临时文件,--batch 一次查完。
Resources
scripts/
check_invoice.py— 查验直连客户端(纯标准库,零第三方依赖)。单票--number/--date/--amount;批量--batch <json文件>。服务地址与 token 写在脚本顶部DEFAULT_URL/DEFAULT_TOKEN(环境变量INVOICE_CHECK_URL/INVOICE_CHECK_TOKEN可覆盖)。业务错误非零退出。render_card.py— 结果卡片渲染。用法:python3 render_card.py <输出.png> < result.json(JSON 走 stdin)。需 Pillow;跨平台中文字体 fallback。单结果渲染大卡片,{"results":[...]}渲染汇总卡。卡片不带 logo。
references/
field_rules.md— 权威口径:票种全表(票头文字→代码映射)、金额口径矩阵、校验码规则、区块链地区特例、硬性校验规则api_contract.md— 服务端点契约:直连脚本与 MCP 两种消费方式、参数 schema、结果码全表、限流说明(排障用)