expense-tool/README.md
copper-xw 13bf1a0146 保存差旅报销自动化基础文件
仅保存说明、脚本和 Excel 模板,示例中的个人和公司信息已匿名化。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-28 19:28:23 +08:00

468 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 差旅报销自动化流程
## 给大模型的自动执行入口
当用户把本 `README.md` 提供给大模型,或让大模型在本目录处理差旅报销时,大模型应直接按下面流程开始执行,不需要用户再复制额外提示词:
1. 先阅读本说明,确认当前目录是报销目录。
2. 检查当前目录是否存在基础文件:`差旅报销.py`、`差旅费明细表.xlsx`、本次报销 PDF。
3. 如果 `报销输出/pdf_raw_text.txt` 不存在,自动运行:
```bash
python3 差旅报销.py extract
```
Windows 环境可改用:
```powershell
py 差旅报销.py extract
```
4. 读取 `报销输出/pdf_raw_text.txt`,按本文的 JSON Schema 分析出差人、日期、事由、费用分类、金额和证据。
5. 如果出差人、出差事由、出发地、目的地、日期、金额等关键信息无法从原文可靠判断,必须先问用户;不要编造。
6. 将最终 JSON 保存为 `报销输出/analyzed_result.json`。
7. 确认 `questions` 为空、金额合计一致、附件张数一致后,自动运行:
```bash
python3 差旅报销.py fill 报销输出/analyzed_result.json
```
Windows 环境可改用:
```powershell
py 差旅报销.py fill 报销输出/analyzed_result.json
```
8. 最后告诉用户生成的 Excel 文件路径,以及是否有任何需要人工复核的证据或假设。
> 简单说:看到本 README 后,大模型应自动完成“提取 PDF 原文 → 分析生成 JSON → 填写 Excel”,只有在信息不确定时才暂停询问用户。
---
## 核心思想
这个流程分成三段:
1. **Python 提取原文**:只把 PDF 内容提取成 TXT,不理解、不计算、不猜测。
2. **大模型分析**:读取 TXT,判断出差人、日期、事由、费用分类和金额,输出标准 JSON。
3. **Python 填表**:只读取 JSON,把数据填入 Excel 模板,生成最终 XLSX。
也就是说:**Python 只做机械衔接,大模型负责理解发票内容。**
---
## 基础三件套
每次复用这个流程,只需要保留下面三个基础文件:
| 文件 | 作用 |
|------|------|
| `差旅费明细表.xlsx` | 原始 Excel 模板,脚本会复制它,不直接修改 |
| `差旅报销.py` | 唯一 Python 脚本,提供 `extract` / `schema` / `fill` 三个命令 |
| `README.md` | 本说明,给人和大模型看 |
PDF 发票、`pdf_raw_text.txt`、`analyzed_result.json`、最终 XLSX 都是每次报销产生的过程文件或结果文件。
---
## 为什么使用 XLSX
为了跨平台,模板统一使用 `.xlsx`。
这样脚本只依赖 Python 包:
```text
pdfplumber # 提取 PDF 文本
openpyxl # 复制和填写 xlsx 模板
```
不再依赖 LibreOffice,也不需要系统命令做 `.xls` / `.xlsx` 转换。
如果你手里只有 `.xls` 模板,请先用 Excel、WPS 或 LibreOffice 手动另存为:
```text
差旅费明细表.xlsx
```
---
## 依赖安装
### Windows
```powershell
py -m pip install pdfplumber openpyxl
```
运行时使用:
```powershell
py 差旅报销.py extract
py 差旅报销.py fill 报销输出/analyzed_result.json
```
### Linux / macOS
```bash
python3 -m pip install pdfplumber openpyxl
```
运行时使用:
```bash
python3 差旅报销.py extract
python3 差旅报销.py fill 报销输出/analyzed_result.json
```
---
## 标准目录结构
```text
报销目录/
├── 差旅费明细表.xlsx # 必需:模板
├── 差旅报销.py # 必需:脚本
├── README.md # 必需:说明
├── 住宿.pdf # 本次报销 PDF
├── 机票*.pdf # 本次报销 PDF
├── 滴滴出行行程报销单*.pdf # 本次报销 PDF
└── 滴滴电子发票*.pdf # 本次报销 PDF
```
脚本输出统一放到:
```text
报销输出/
```
运行后会形成:
```text
报销目录/
└── 报销输出/
├── pdf_raw_text.txt
├── analyzed_result.json
├── schema_example.json # 可选
└── 差旅费明细表_xxxx_20260520_广州_售后维修.xlsx
```
---
## 使用流程
### Step 1:提取 PDF 原文
```bash
python3 差旅报销.py extract
```
Windows 可用:
```powershell
py 差旅报销.py extract
```
输出:
```text
报销输出/pdf_raw_text.txt
```
这个文件按 PDF 文件分块保存原始文本,例如:
```text
============================================================
文件: 住宿.pdf
============================================================
电子发票(增值税专用发票)
...
============================================================
文件: 滴滴出行行程报销单A.pdf
============================================================
滴滴出行-行程单
...
```
注意:这一步**不会**提取金额、不会判断类型、不会计算总额。
---
### Step 2:让大模型分析 TXT 并输出 JSON
把 `报销输出/pdf_raw_text.txt` 交给大模型,让大模型生成:
```text
报销输出/analyzed_result.json
```
可以先查看 JSON 模板:
```bash
python3 差旅报销.py schema
```
也可以直接输出模板文件:
```bash
python3 差旅报销.py schema --output
```
输出:
```text
报销输出/schema_example.json
```
#### 给大模型的推荐提示词
```text
请根据本 README 自动处理当前目录的差旅报销。
你的任务:
1. 如果 `报销输出/pdf_raw_text.txt` 不存在,先运行 `python3 差旅报销.py extract` 提取 PDF 原文。
2. 读取 `报销输出/pdf_raw_text.txt`,从 PDF 原文中识别出差人、出差事由、出发日期、结束日期、出发地、目的地、出行方式、出差天数。
3. 将费用分为:住宿、交通、饮食、其他。
4. 每个费用类别都要给出金额、单据张数、说明。
5. 计算附件总张数、报销总额、预借差旅费、补领金额、退还金额。
6. 每个金额必须写入 evidence,说明来自哪个文件、依据是哪段文本。
7. 如果某个字段无法可靠判断,必须先问用户,不要编造;在未确认前不要填表。
8. 将最终 JSON 保存为 `报销输出/analyzed_result.json`,JSON 必须符合 README 中的 schema。
9. 确认 `questions` 为空、金额合计一致、附件张数一致后,运行 `python3 差旅报销.py fill 报销输出/analyzed_result.json` 生成最终 Excel。
10. 最后告诉用户生成的 Excel 文件路径,并列出需要人工复核的证据或假设。
```
---
## JSON Schema
大模型必须输出如下结构:
```json
{
"meta": {
"出差人": "xxxx",
"出差事由": "前往广州进行售后维修工作",
"出发日期": "2026/5/20",
"结束日期": "2026/5/22",
"出发地": "北京",
"目的地": "广州",
"出行方式": "飞机",
"出差天数": 3,
"事由简写": "售后维修"
},
"expenses": {
"住宿": {
"金额": 510.0,
"单据张数": 1,
"说明": "广州市番禺明乐宾馆住宿费"
},
"交通": {
"金额": 2013.77,
"单据张数": 5,
"说明": "机票1690.00 + 滴滴323.77"
},
"饮食": {
"金额": 0.0,
"单据张数": 0,
"说明": "未发现餐饮发票"
},
"其他": {
"金额": 0.0,
"单据张数": 0,
"说明": "无其他费用"
}
},
"summary": {
"附件总张数": 6,
"报销总额": 2523.77,
"预借差旅费": 0.0,
"补领金额": 2523.77,
"退还金额": 0.0
},
"evidence": [
{
"文件": "住宿.pdf",
"类别": "住宿",
"金额": 510.0,
"依据": "价税合计(小写)¥510.00"
},
{
"文件": "机票预订出行订单尾号87377629-电子普通发票.pdf",
"类别": "交通",
"金额": 1690.0,
"依据": "价税合计(小写)¥1690.00"
}
],
"questions": []
}
```
### 字段说明
| 字段 | 说明 |
|------|------|
| `meta.出差人` | 出差人姓名,不确定就问用户 |
| `meta.出差事由` | 出差目的,不确定就问用户 |
| `meta.出发日期` / `meta.结束日期` | 格式建议为 `YYYY/M/D` |
| `meta.出差天数` | 含首尾日期,例如 5/20 至 5/22 为 3 天 |
| `expenses.住宿` | 酒店住宿类费用 |
| `expenses.交通` | 机票、火车、打车等交通类费用 |
| `expenses.饮食` | 餐饮费用,没有就 0 |
| `expenses.其他` | 不属于前三类的费用,没有就 0 |
| `summary.附件总张数` | 四类费用单据张数之和 |
| `summary.报销总额` | 四类费用金额之和 |
| `evidence` | 每笔金额的来源证据,便于人工核对 |
| `questions` | 如果大模型无法判断,可以列出问题;最终填表前应为空 |
---
## Step 3:读取 JSON 并填表
```bash
python3 差旅报销.py fill 报销输出/analyzed_result.json
```
Windows 可用:
```powershell
py 差旅报销.py fill 报销输出/analyzed_result.json
```
脚本会:
1. 校验 JSON 字段是否完整
2. 校验金额合计是否一致
3. 校验附件张数是否一致
4. 复制 `差旅费明细表.xlsx`
5. 用 openpyxl 填入固定单元格
6. 输出最终 XLSX 到 `报销输出/`
输出文件命名规则:
```text
差旅费明细表_[出差人]_[出发日期YYYYMMDD]_[目的地]_[事由简写].xlsx
```
示例:
```text
报销输出/差旅费明细表_xxxx_20260520_广州_售后维修.xlsx
```
---
## Excel 填写位置
脚本只填写这些单元格:
| 单元格 | 内容 |
|--------|------|
| `E3` | 出差事由 |
| `A6` | 出发日期-结束日期 |
| `B6` | 出发地-目的地 |
| `C6` | 出差天数 |
| `D6` | 出行方式 |
| `H6` / `I6` | 住宿单据张数 / 住宿金额 |
| `H7` / `I7` | 饮食单据张数 / 饮食金额 |
| `H8` / `I8` | 交通单据张数 / 交通金额 |
| `H9` / `I9` | 其他单据张数 / 其他金额 |
脚本**不会覆盖公式单元格**,例如:
| 单元格 | 公式 |
|--------|------|
| `H2` | 附件张数合计 |
| `F9` | 天数合计 |
| `C10` | 报销总额 |
| `H10` | 补领金额 |
| `H11` | 退还金额 |
---
## 校验规则
`fill` 命令会检查:
```text
summary.报销总额 = 住宿金额 + 交通金额 + 饮食金额 + 其他金额
summary.附件总张数 = 住宿单据张数 + 交通单据张数 + 饮食单据张数 + 其他单据张数
```
如果不一致,脚本会停止,不会写 Excel。
---
## 本次数据示例
```json
{
"meta": {
"出差人": "xxxx",
"出差事由": "前往广州进行售后维修工作",
"出发日期": "2026/5/20",
"结束日期": "2026/5/22",
"出发地": "北京",
"目的地": "广州",
"出行方式": "飞机",
"出差天数": 3,
"事由简写": "售后维修"
},
"expenses": {
"住宿": {"金额": 510.0, "单据张数": 1, "说明": "住宿费"},
"交通": {"金额": 2013.77, "单据张数": 5, "说明": "机票1690.00 + 滴滴323.77"},
"饮食": {"金额": 0.0, "单据张数": 0, "说明": "无餐饮发票"},
"其他": {"金额": 0.0, "单据张数": 0, "说明": "无其他费用"}
},
"summary": {
"附件总张数": 6,
"报销总额": 2523.77,
"预借差旅费": 0.0,
"补领金额": 2523.77,
"退还金额": 0.0
},
"evidence": [],
"questions": []
}
```
---
## 常见错误
### 1. `找不到模板文件:差旅费明细表.xlsx`
确认当前目录存在:
```text
差旅费明细表.xlsx
```
如果只有 `.xls`,请先用 Excel / WPS / LibreOffice 另存为 `.xlsx`。
### 2. `报销总额不一致`
说明 JSON 里的总额和分类费用合计不一致,检查:
```text
住宿 + 交通 + 饮食 + 其他
```
### 3. `附件总张数不一致`
说明 JSON 里的附件总数和分类单据张数合计不一致。
### 4. 缺少 Python 库
Windows:
```powershell
py -m pip install pdfplumber openpyxl
```
Linux / macOS:
```bash
python3 -m pip install pdfplumber openpyxl
```