在差旅报销填表成功后删除 __pycache__,避免运行脚本产生的缓存目录残留在项目根目录。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| .gitignore | ||
| README.md | ||
| 差旅报销.py | ||
| 差旅费明细表.xlsx | ||
差旅报销自动化流程
给大模型的自动执行入口
当用户把本 README.md 提供给大模型,或让大模型在本目录处理差旅报销时,大模型应直接按下面流程开始执行,不需要用户再复制额外提示词:
-
先阅读本说明,确认当前目录是报销目录。
-
检查当前目录是否存在基础文件:
差旅报销.py、差旅费明细表.xlsx,以及用户放在根目录的本次报销 PDF/图片附件。 -
自动运行
extract,脚本会先把根目录的 PDF/JPG/JPEG/PNG 移动到处理当天目录(如20260529/),再提取 PDF 原文:python3 差旅报销.py extractWindows 环境可改用:
py 差旅报销.py extract -
读取本次处理目录中的
报销输出/pdf_raw_text.txt,按本文的 JSON Schema 分析出差人、日期、事由、费用分类、金额和证据。 -
如果同一笔滴滴/打车费用同时有行程单和电子发票,金额只按电子发票计入一次;行程单只作为附件和行程明细使用,不要与发票重复计入金额。
-
如果出差人、出差事由、出发地、目的地、日期、金额等关键信息无法从原文可靠判断,必须先问用户;不要编造。
-
将最终 JSON 保存为本次处理目录中的
报销输出/analyzed_result.json。 -
确认
questions为空、金额合计一致、附件张数一致后,自动运行:python3 差旅报销.py fill 20260529/报销输出/analyzed_result.jsonWindows 环境可改用:
py 差旅报销.py fill 20260529/报销输出/analyzed_result.json -
最后告诉用户生成的 Excel 文件路径和本次归档目录,以及是否有任何需要人工复核的证据或假设;如果存在行程单和电子发票对应同一笔费用,必须明确提醒用户:行程单未重复计入金额,只作为附件和行程明细使用。
简单说:看到本 README 后,大模型应自动完成“提取 PDF 原文 → 分析生成 JSON → 填写 Excel”,只有在信息不确定时才暂停询问用户。
核心思想
这个流程分成三段:
- Python 提取原文:只把 PDF 内容提取成 TXT,不理解、不计算、不猜测。
- 大模型分析:读取 TXT,判断出差人、日期、事由、费用分类和金额,输出标准 JSON。
- 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 包:
pdfplumber # 提取 PDF 文本
openpyxl # 复制和填写 xlsx 模板
不再依赖 LibreOffice,也不需要系统命令做 .xls / .xlsx 转换。
如果你手里只有 .xls 模板,请先用 Excel、WPS 或 LibreOffice 手动另存为:
差旅费明细表.xlsx
依赖安装
Windows
py -m pip install pdfplumber openpyxl
运行时使用:
py 差旅报销.py extract
py 差旅报销.py fill 20260529/报销输出/analyzed_result.json
Linux / macOS
python3 -m pip install pdfplumber openpyxl
运行时使用:
python3 差旅报销.py extract
python3 差旅报销.py fill 20260529/报销输出/analyzed_result.json
标准目录结构
每次开始处理前,根目录只需要保留基础文件,并把本次报销附件直接放到根目录:
报销目录/
├── 差旅费明细表.xlsx # 必需:模板
├── 差旅报销.py # 必需:脚本
├── README.md # 必需:说明
├── 住宿.pdf # 本次报销 PDF,运行 extract 后会被移动
├── 机票*.pdf # 本次报销 PDF,运行 extract 后会被移动
├── 滴滴出行行程报销单*.pdf # 本次报销 PDF,运行 extract 后会被移动
├── 滴滴电子发票*.pdf # 本次报销 PDF,运行 extract 后会被移动
└── 打车截图.jpg # 本次报销图片附件,运行 extract 后会被移动
运行 extract 后会先形成处理当天目录,例如:
报销目录/
└── 20260529/
├── 住宿.pdf
├── 机票*.pdf
├── 滴滴出行行程报销单*.pdf
├── 滴滴电子发票*.pdf
├── 打车截图.jpg
└── 报销输出/
└── pdf_raw_text.txt
运行 fill 成功后,会把当天目录重命名为本次归档目录,并保留 报销输出/ 结构:
报销目录/
└── 20260520-20260522_北京-广州_售后维修/
├── 住宿.pdf
├── 机票*.pdf
├── 滴滴出行行程报销单*.pdf
├── 滴滴电子发票*.pdf
├── 打车截图.jpg
└── 报销输出/
├── pdf_raw_text.txt
├── analyzed_result.json
└── 差旅费明细表_xxxx_20260520_广州_售后维修.xlsx
使用流程
Step 1:提取 PDF 原文
python3 差旅报销.py extract
Windows 可用:
py 差旅报销.py extract
运行时会把根目录的 PDF/JPG/JPEG/PNG 移动到处理当天目录,例如 20260529/。如果当天目录已存在且已有附件,脚本会询问是否合并;不合并会创建 20260529-1/、20260529-2/ 这样的新目录。
输出位于本次处理目录中,例如:
20260529/报销输出/pdf_raw_text.txt
这个文件按 PDF 文件分块保存原始文本,例如:
============================================================
文件: 20260529/住宿.pdf
============================================================
电子发票(增值税专用发票)
...
============================================================
文件: 20260529/滴滴出行行程报销单A.pdf
============================================================
滴滴出行-行程单
...
注意:这一步不会提取金额、不会判断类型、不会计算总额。
Step 2:让大模型分析 TXT 并输出 JSON
把本次处理目录中的 报销输出/pdf_raw_text.txt 交给大模型,让大模型生成同目录下的 JSON,例如:
20260529/报销输出/analyzed_result.json
可以先查看 JSON 模板:
python3 差旅报销.py schema
也可以直接输出模板文件:
python3 差旅报销.py schema --output
输出:
报销输出/schema_example.json
给大模型的推荐提示词
请根据本 README 自动处理当前目录的差旅报销。
你的任务:
1. 先运行 `python3 差旅报销.py extract`,脚本会把根目录 PDF/JPG/JPEG/PNG 移动到本次处理目录,并提取 PDF 原文。
2. 读取 extract 输出提示中的 `报销输出/pdf_raw_text.txt`,从 PDF 原文中识别出差人、出差事由、出发日期、结束日期、出发地、目的地、出行方式、出差天数。
3. 将费用分为:住宿、交通、饮食、其他。
4. 每个费用类别都要给出金额、单据张数、说明。
5. 计算附件总张数、报销总额、预借差旅费、补领金额、退还金额。
6. 每个金额必须写入 evidence,说明来自哪个文件、依据是哪段文本。
7. 如果同一笔滴滴/打车费用同时有行程单和电子发票,金额只按电子发票计入一次;行程单只作为附件和行程明细使用,不要与发票重复计入金额。
8. 如果某个字段无法可靠判断,必须先问用户,不要编造;在未确认前不要填表。
9. 将最终 JSON 保存到本次处理目录中的 `报销输出/analyzed_result.json`,JSON 必须符合 README 中的 schema。
10. 确认 `questions` 为空、金额合计一致、附件张数一致后,运行 `python3 差旅报销.py fill 本次处理目录/报销输出/analyzed_result.json` 生成最终 Excel,并把本次目录重命名为 `出发日期-结束日期_出发地-目的地_事由简写` 格式。
11. 最后告诉用户生成的 Excel 文件路径和本次归档目录,并列出需要人工复核的证据或假设;如果存在行程单和电子发票对应同一笔费用,必须明确提醒用户:行程单未重复计入金额,只作为附件和行程明细使用。
JSON Schema
大模型必须输出如下结构:
{
"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 并填表
python3 差旅报销.py fill 20260529/报销输出/analyzed_result.json
Windows 可用:
py 差旅报销.py fill 20260529/报销输出/analyzed_result.json
脚本会:
- 校验 JSON 字段是否完整
- 校验金额合计是否一致
- 校验附件张数是否一致
- 复制
差旅费明细表.xlsx - 用 openpyxl 填入固定单元格
- 输出最终 XLSX 到本次处理目录的
报销输出/ - 将本次处理目录重命名为
出发日期-结束日期_出发地-目的地_事由简写格式
输出文件命名规则:
差旅费明细表_[出差人]_[出发日期YYYYMMDD]_[目的地]_[事由简写].xlsx
示例:
20260520-20260522_北京-广州_售后维修/报销输出/差旅费明细表_xxxx_20260520_广州_售后维修.xlsx
Excel 填写位置
脚本只填写这些单元格:
| 单元格 | 内容 |
|---|---|
E3 |
出差事由 |
A6 |
出发日期-结束日期 |
B6 |
出发地-目的地 |
C6 |
出差天数 |
D6 |
出行方式 |
H6 / I6 |
住宿单据张数 / 住宿金额 |
H7 / I7 |
饮食单据张数 / 饮食金额 |
H8 / I8 |
交通单据张数 / 交通金额 |
H9 / I9 |
其他单据张数 / 其他金额 |
脚本不会覆盖公式单元格,例如:
| 单元格 | 公式 |
|---|---|
H2 |
附件张数合计 |
F9 |
天数合计 |
C10 |
报销总额 |
H10 |
补领金额 |
H11 |
退还金额 |
校验规则
fill 命令会检查:
summary.报销总额 = 住宿金额 + 交通金额 + 饮食金额 + 其他金额
summary.附件总张数 = 住宿单据张数 + 交通单据张数 + 饮食单据张数 + 其他单据张数
如果不一致,脚本会停止,不会写 Excel。
本次数据示例
{
"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
确认当前目录存在:
差旅费明细表.xlsx
如果只有 .xls,请先用 Excel / WPS / LibreOffice 另存为 .xlsx。
2. 报销总额不一致
说明 JSON 里的总额和分类费用合计不一致,检查:
住宿 + 交通 + 饮食 + 其他
3. 附件总张数不一致
说明 JSON 里的附件总数和分类单据张数合计不一致。
4. 缺少 Python 库
Windows:
py -m pip install pdfplumber openpyxl
Linux / macOS:
python3 -m pip install pdfplumber openpyxl