# 差旅报销自动化流程 ## 给大模型的自动执行入口 当用户把本 `README.md` 提供给大模型,或让大模型在本目录处理差旅报销时,大模型应直接按下面流程开始执行,不需要用户再复制额外提示词: 1. 先阅读本说明,确认当前目录是报销目录。 2. 检查当前目录是否存在基础文件:`差旅报销.py`、`差旅费明细表.xlsx`,以及用户放在根目录的本次报销 PDF/图片附件。 3. 自动运行 `extract`,脚本会先把根目录的 PDF/JPG/JPEG/PNG 移动到处理当天目录(如 `20260529/`),再提取 PDF 原文: ```bash python3 差旅报销.py extract ``` Windows 环境可改用: ```powershell py 差旅报销.py extract ``` 4. 读取本次处理目录中的 `报销输出/pdf_raw_text.txt`,按本文的 JSON Schema 分析出差人、日期、事由、费用分类、金额和证据。 5. 如果同一笔滴滴/打车费用同时有行程单和电子发票,金额只按电子发票计入一次;行程单只作为附件和行程明细使用,不要与发票重复计入金额。 6. 如果出差人、出差事由、出发地、目的地、日期、金额等关键信息无法从原文可靠判断,必须先问用户;不要编造。 7. **复核**:重新读取 `pdf_raw_text.txt`,逐项核对生成的 JSON 是否与 PDF 原文中金额、日期、人员等信息一致;如果发现错误,修正后重新生成 JSON。 8. 将最终 JSON 保存为本次处理目录中的 `报销输出/analyzed_result.json`。 9. 确认 `questions` 为空、金额合计一致、附件张数一致后,自动运行: ```bash python3 差旅报销.py fill 20260529/报销输出/analyzed_result.json ``` Windows 环境可改用: ```powershell py 差旅报销.py fill 20260529/报销输出/analyzed_result.json ``` 9. 最后告诉用户生成的 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 ``` --- ## 依赖安装 ### 第一步:检测 Python 环境 在运行脚本前,应先确认 Python 和 pip 可用。以下是各平台的检测方法: #### Windows ```powershell # 检测 Python 是否可用 py --version py -m pip --version # 如果提示找不到 python,或版本异常,尝试: where python where pip ``` **注意**:Windows 应用商店的 Python(从 Microsoft Store 安装)可能有执行限制。如果 `py` 命令不可用或行为异常,请从 [python.org](https://www.python.org/downloads/) 或使用 conda 安装官方版本。 #### Linux / macOS ```bash python3 --version python3 -m pip --version # 如果提示找不到 python3,尝试: which python3 which pip3 ``` --- ### 第二步:安装依赖 确认 Python 可用后,安装所需的两个库: #### Windows ```powershell py -m pip install pdfplumber openpyxl ``` #### Linux / macOS ```bash python3 -m pip install pdfplumber openpyxl ``` --- ### 第三步:验证安装 ```bash python3 -c "import pdfplumber, openpyxl; print('依赖安装成功')" ``` 如果提示 `ModuleNotFoundError`,说明安装失败,需要重新执行上一步。 --- ### 常见问题 | 问题 | 解决方法 | |------|----------| | `py` 命令找不到 | 从 python.org 下载安装包,勾选 "Add Python to PATH" | | `python3` 命令找不到 | `sudo apt install python3`(Linux)或从 python.org 安装(macOS)| | 应用商店 Python 执行受限 | 卸载应用商店版本,安装官方 Python | | pip 安装失败 | 尝试 `python3 -m pip install --upgrade pip` 先升级 pip | --- ## 标准目录结构 每次开始处理前,根目录只需要保留基础文件,并把本次报销附件直接放到根目录: ```text 报销目录/ ├── 差旅费明细表.xlsx # 必需:模板 ├── 差旅报销.py # 必需:脚本 ├── README.md # 必需:说明 ├── 住宿.pdf # 本次报销 PDF,运行 extract 后会被移动 ├── 机票*.pdf # 本次报销 PDF,运行 extract 后会被移动 ├── 滴滴出行行程报销单*.pdf # 本次报销 PDF,运行 extract 后会被移动 ├── 滴滴电子发票*.pdf # 本次报销 PDF,运行 extract 后会被移动 └── 打车截图.jpg # 本次报销图片附件,运行 extract 后会被移动 ``` 运行 `extract` 后会先形成处理当天目录,例如: ```text 报销目录/ └── 20260529/ ├── 住宿.pdf ├── 机票*.pdf ├── 滴滴出行行程报销单*.pdf ├── 滴滴电子发票*.pdf ├── 打车截图.jpg └── 报销输出/ └── pdf_raw_text.txt ``` 运行 `fill` 成功后,会把当天目录重命名为本次归档目录,并保留 `报销输出/` 结构: ```text 报销目录/ └── 20260520-20260522_北京-广州_售后维修/ ├── 住宿.pdf ├── 机票*.pdf ├── 滴滴出行行程报销单*.pdf ├── 滴滴电子发票*.pdf ├── 打车截图.jpg └── 报销输出/ ├── pdf_raw_text.txt ├── analyzed_result.json └── 差旅费明细表_xxxx_20260520_广州_售后维修.xlsx ``` --- ## 使用流程 ### Step 1:提取 PDF 原文 ```bash python3 差旅报销.py extract ``` Windows 可用: ```powershell py 差旅报销.py extract ``` 运行时会把根目录的 PDF/JPG/JPEG/PNG 移动到处理当天目录,例如 `20260529/`。如果当天目录已存在且已有附件,脚本会询问是否合并;不合并会创建 `20260529-1/`、`20260529-2/` 这样的新目录。 输出位于本次处理目录中,例如: ```text 20260529/报销输出/pdf_raw_text.txt ``` 这个文件按 PDF 文件分块保存原始文本,例如: ```text ============================================================ 文件: 20260529/住宿.pdf ============================================================ 电子发票(增值税专用发票) ... ============================================================ 文件: 20260529/滴滴出行行程报销单A.pdf ============================================================ 滴滴出行-行程单 ... ``` 注意:这一步**不会**提取金额、不会判断类型、不会计算总额。 --- ### Step 2:让大模型分析 TXT 并输出 JSON 把本次处理目录中的 `报销输出/pdf_raw_text.txt` 交给大模型,让大模型生成同目录下的 JSON,例如: ```text 20260529/报销输出/analyzed_result.json ``` 可以先查看 JSON 模板: ```bash python3 差旅报销.py schema ``` 也可以直接输出模板文件: ```bash python3 差旅报销.py schema --output ``` 输出: ```text 报销输出/schema_example.json ``` ### Step 3(复核):核对 JSON 与 PDF 原文 **重要**:生成 JSON 后,必须重新读取 `pdf_raw_text.txt`,逐项核对: 1. **金额核对**:每个 evidence.金额 是否与 PDF 原文中该文件的实际金额一致(检查"价税合计"或"合计"字段)。 2. **人员核对**:出差人姓名是否与 PDF 原文中一致。 3. **日期核对**:出发/结束日期是否与行程单或机票日期一致。 4. **费用分类核对**:每笔费用是否归类正确(住宿/交通/饮食/其他)。 5. **行程单处理**:同一笔滴滴费用同时有行程单和电子发票时,确认金额只计入了电子发票,行程单不重复计入。 6. **单据张数核对**:附件总张数是否与 evidence 条目数一致。 如果发现错误,修正 JSON 后重新核对,直到完全一致后再进入 Step 4。 --- ### Step 4:读取 JSON 并填表 脚本会读取 JSON 并填入 Excel: ```bash python3 差旅报销.py fill 20260529/报销输出/analyzed_result.json ``` Windows 可用: ```powershell py 差旅报销.py fill 20260529/报销输出/analyzed_result.json ``` 脚本会: 1. 校验 JSON 字段是否完整 2. 校验金额合计是否一致 3. 校验附件张数是否一致 4. 复制 `差旅费明细表.xlsx` 5. 用 openpyxl 填入固定单元格 6. 输出最终 XLSX 到本次处理目录的 `报销输出/` 7. 将本次处理目录重命名为 `出发日期-结束日期_出发地-目的地_事由简写` 格式 输出文件命名规则: ```text 差旅费明细表_[出差人]_[出发日期YYYYMMDD]_[目的地]_[事由简写].xlsx ``` 示例: ```text 20260520-20260522_北京-广州_售后维修/报销输出/差旅费明细表_xxxx_20260520_广州_售后维修.xlsx ``` #### 给大模型的推荐提示词 ```text 请根据本 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. **复核**:重新读取 `pdf_raw_text.txt`,逐项核对生成的 JSON 是否与 PDF 原文中金额、日期、人员等信息一致;如果发现错误,修正后重新生成 JSON。 10. 将最终 JSON 保存到本次处理目录中的 `报销输出/analyzed_result.json`,JSON 必须符合 README 中的 schema。 11. 确认 `questions` 为空、金额合计一致、附件张数一致后,运行 `python3 差旅报销.py fill 本次处理目录/报销输出/analyzed_result.json` 生成最终 Excel,并把本次目录重命名为 `出发日期-结束日期_出发地-目的地_事由简写` 格式。 12. 最后告诉用户生成的 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` | 如果大模型无法判断,可以列出问题;最终填表前应为空 | --- ## 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 ```