Back to home@988hj7tczd-oss

dsh-invoice-tools

DSH native tools: parse Chinese e-invoice PDFs into structured JSON with amount cross-check, and generate expense reports (Markdown / xlsx)

Stars
0
Language
TypeScript
Created
Aug 24, 2026
Updated
Aug 24, 2026

Introduction

dsh-invoice-tools — 发票解析 / 报销单生成工具

[!IMPORTANT] 依赖前置:相邻 dsh-src 检出(link: 依赖) 本项目在开发形态下使用 link: 依赖指向相邻的 DeepSeek Harness 源码检出(dsh-src), 与当前仓库保持同一父目录布局(<parent>/dsh-src)。克隆本仓库后:

  1. 先把官方 deepseek-ai/deepseek-harness 检出到与本仓库同级的 dsh-src/ 目录,并执行其 pnpm install && pnpm run build
  2. 再按下方「安装」一节执行本仓库的 pnpm install --offline && pnpm build 与测试。 发布到 npm 的版本会尽量把 link: 依赖替换为 registry 真实版本;无法替换的内部包保持 link:,见各包 README 说明。

DSH(DeepSeek Harness)原生工具插件:读取工作区内的增值税电子发票文件(数电票/全电发票 PDF、图片), 结构化为 JSON(发票代码/号码/金额/税额/购买方/销售方/开票日期),多张汇总生成报销单(Markdown / xlsx)。 全程只读输入、写入输出文件;发票数据仅在本机处理,不上传任何查验接口;解析缓存按会话隔离 (工具无法取得会话上下文时回退为进程级缓存,详见「安全与隐私」)。

2026-08 调研结论:dsh invoice payment 报销 发票 实时搜索 0 结果,发票/报销方向无人做,本插件填补该空白。

功能一览

工具作用参数
invoice_parse单张/批量发票解析(文件或目录)path(必填)
invoice_summary汇总结算 + 报销单生成files? / out? / categories?

解析路径(invoice_parse)

  1. XML 附件优先:读取 PDF 内嵌的结构化 XML(/EmbeddedFiles 命名树),按中英双语候选标签 白名单抽取:发票号码/代码、金额(不含税)、税额、价税合计、购买方、销售方、开票日期、查验平台网址。
  2. 文本层兜底:无 XML 时以文本正则抽取——票号正则 \d{8,20} + 标签定位 (发票号码:金额(大写)…(小写)¥x开票日期:购买方名称: 等)。
  3. 勾稽校验价税合计 = 金额 + 税额(容差 0.01),不符时核对表标
  4. 置信度标注:XML 直读 = 高(high);正则 = 中(medium);关键字段缺失 = 低(low)并列出缺失清单。

汇总(invoice_summary)

  • 合计/税额/价税合计、按类别小计categories 映射,键 = 票号或文件名);
  • 重复票检测:同票号 + 同金额(保留首张,其余建议剔除);
  • 生成 报销单-<日期>.md(默认)或 xlsxout 参数)到工作区;
  • files 缺省时复用 invoice_parse解析缓存;缓存 key 含会话维度 (<会话id>:<绝对路径>),工具拿不到会话 id 时回退 global: 前缀——即进程级缓存,见「安全与隐私」。

目录结构

dsh-invoice-tools/
├── cordis.yml                  # 装配清单(生产以裸包名 dsh-invoice-tools 挂载)
├── package.json                # dsh.bundle.patch 声明 + 构建/测试脚本
├── tsconfig.json
├── src/
│   ├── index.ts                # 装配 + 注册 2 工具
│   ├── tools/invoice-parse.ts  # 单张/批量解析(fs 编排 + 核对表渲染 + 会话作用域缓存)
│   ├── tools/invoice-summary.ts# 汇总 + 报销单生成(版本守卫写入)
│   ├── pdf-extract.ts          # 自写最小 PDF 解析器:文本层 + XML 附件 + 加密检测
│   ├── invoice-model.ts        # 字段模型 + XML/正则抽取 + 金额勾稽校验
│   └── summary.ts              # 报销单 md/xlsx 渲染 + 极小 zip 打包器
├── tests/smoke.e2e.ts          # 离线冒烟测试(fixture PDF 内嵌生成)
└── README.md

装配方式

依赖:宿主已提供 tools(工具注册表)与 fs(文件系统服务)。两种挂载模式:

  1. 生产(推荐):dsh plugin add 安装后以裸包名挂载package.jsondsh.bundle.patch 声明随包发布;main 指向编译产物 lib/index.js(发布前 npm run build 产出)。cordis.yml 中的 name: 'dsh-invoice-tools' 由 loader 按 Node 模块解析(import('dsh-invoice-tools')),不要使用相对路径——loader 对 . 开头的 name 按 bundle/profile 根目录解析,包内相对源码路径(./src/index.ts) 必然解析失败并使启动 fail-loud。

    - insert:
        - id: invoice-tools
          name: 'dsh-invoice-tools'
    
  2. 开发直挂:绝对路径(同 dsh-src/scratch-plugin 的做法):把 insert 行并入宿主 cordis.yml,name 写源码入口的绝对路径:

    - insert:
        - id: invoice-tools
          name: '/absolute/path/to/dsh-invoice-tools/src/index.ts'
    

使用示例

# 解析单文件 / 整目录
invoice_parse path='invoices/电子发票_2024.pdf'
invoice_parse path='invoices/'                      # 目录递归

# 汇总(缺省用上次 parse 缓存)→ 报销单-<日期>.md
invoice_summary
invoice_summary out='both' categories='{"发票号码123...":"差旅","文件B.pdf":"办公"}'

# 指定文件汇总(须先 parse)
invoice_summary files='["invoices/A.pdf","invoices/B.pdf"]' out='md'

输出示例(核对表):

| 源文件 | 发票号码 | 开票日期 | 金额 | 税额 | 价税合计 | 来源 | 置信度 | 勾稽 |
| 电子发票_2024.pdf | 24412000000012345678 | 2024-01-05 | 100.00 | 13.00 | 113.00 | XML | 高(XML) | ✓ |

安全与隐私

  • 不读取任何凭证/密钥/证书/账号配置;仅处理用户指定的发票文件,读取遵循宿主 fs 的 read 策略与沙箱,输出写入工作区。
  • 不上传任何查验接口:查验(verify)为显式 opt-in 能力,默认关闭,当前版本不实现任何网络请求。
  • 文件读写通过主机的 ctx.fs 服务完成;观察策略由宿主 fs 事件管道(fs/write-intent / fs/observed,宿主 fs-observation-policy 只挂事件、不注册服务)统一处理。本插件直接调用 ctx.fs 读写,不做额外工作区包含校验——插件自身不声称额外安全边界,信任边界完全落在 宿主 fs 沙箱与观察策略之上。
  • 写盘编排(只影响插件自身行为,并非策略闸门):目标已存在时先 read,再以版本守卫 (replaceIfVersion)原子写入;不存在时 createIfAbsent
  • 隐私边界如实说明:解析缓存按会话隔离(key 为 <会话id>:<绝对路径>,会话 id 取自工具 执行上下文的 agent.id);工具无法取得会话上下文时(非代理循环驱动等场景),缓存回退为 进程级——同进程内其它会话理论上可读到该缓存的解析结果,请勿向缓存中放入敏感发票数据。
  • OCR 不是首版依赖:纯扫描件(图片 / 无文本层 PDF)返回明确"跳过原因",不中断批量; 后续可显式 opt-in 对接社区 vision 插件(如 dsh-vision-guardvision_analyze)。

xlsx 落盘说明

DSH 文件系统服务提供 UTF-8 文本信道(无二进制写 API),故 invoice_summary out='xlsx' 生成的 xlsx(标准 OOXML ZIP,可由 Excel/WPS/openpyxl 打开)以 base64 文本落盘为 报销单-<日期>.xlsx.b64

base64 -d 报销单-20260823.xlsx.b64 > 报销单-20260823.xlsx   # macOS/Linux

工具返回值同时携带 xlsxBase64,会话内可自行解码。文件头 PK\x03\x04、中央目录与 xl/worksheets/sheet1.xml 结构均由离线测试做结构断言;openpyxl 交叉验证在环境可用时执行, 缺失时测试显式打印 -- SKIP openpyxl 交叉验证(python3/openpyxl 缺失) 并计入 skipped (node:test skip 语义,不再静默跳过)。

实现说明与已知限制

  • PDF 解析为自写最小解析器(零第三方依赖,仅 Node 内置 zlib): 顺序扫描对象并精确跳过 stream;支持 FlateDecode/未压缩流、Tj/TJ 文本操作符、 latin1 与 UTF-16BE(hex)字符串、/EmbeddedFiles 附件树。不依赖 xref 表,对 老式 xref 与 xref 流均鲁棒。局限:不支持对象流内的间接引用流、JPEG2000 图像、页面渲染; 生产环境如需完整 PDF 语义,可替换为调用 pdfplumber/pypdf 的外部路径(调研已核实可用)。
  • XML 附件抽取依赖中英双语候选标签白名单(invoice-model.ts);覆盖面外的 Schema 将导致该文件被跳过(reason=unparsable),不会生成错误数据。
  • 金额仅解析阿拉伯数字(¥//千分位);中文大写金额无法解析时字段缺失并降低置信度。
  • 目录扫描递归深度上限 3;单文件读取上限 16MB。

开发与测试

npm install          # 拉取类型依赖(@deepseek-ai/cordis / dsh-tools / typescript)
npm run typecheck    # tsc --noEmit
npm run build        # tsc → lib/
npm run test         # build + node --test
npm run test:offline # 纯离线冒烟(Node ≥ 22.19,原生运行 .ts,不需要 npm install)

冒烟测试(tests/smoke.e2e.ts)内嵌 fixture 生成器,覆盖验收标准: ① 含 XML 附件的样例解析字段完整 + 勾稽通过 + confidence=high; ② 无 XML 文本型样例走正则路径 + confidence=medium; ③ 3 张样例汇总:合计正确 + 重复检测命中构造样本; ④ xlsx 结构断言(ZIP + 表头/行数据);openpyxl 交叉验证在环境可用时执行、缺失时显式标记跳过; ⑤ 异常文件(加密 PDF / 纯图片)返回明确跳过原因且不中断批量; ⑥ 勾稽不符样本标红提示。

License

MIT