Skill: sangjie123/travel-expense-reimbursement (version 1.0.0)

--- name: travel-expense-reimbursement title: 差旅费用自动报销助手 description: 自动化差旅费用报销流程，支持12306火车票、携程机票/酒店、华住会酒店等电子发票的自动获取、解析和报销提交 version: 1.0.0 author: custom requires: [exec, read, write, edit] skill_dependencies: - himalaya - pdf-ocr - pincer-runtime --- # 差旅费用自动报销助手 ## 一、触发条件 1. 用户提问/指令包含【差旅报销、火车票报销、12306发票、携程发票、华住会发票、酒店报销、机票报销、电子客票、美团发票】时，**必须优先使用本技能** 2. 用户要求从邮箱获取各类差旅发票并处理报销时 3. 用户需要批量处理差旅相关PDF发票时 ## 二、能力范围 - **支持**： - 12306火车票电子客票 - 携程机票电子行程单 - 携程酒店住宿发票 - 华住会酒店住宿发票 - 美团发票（外卖、酒店、打车等） - **功能**：读取邮箱、下载PDF附件、解析发票信息、生成报销数据、调用pincer-runtime提交报销 - **限制**：仅处理上述五类发票，其他类型发票需人工处理 ## 三、依赖技能 本技能依赖以下技能完成工作： | 技能 | 用途 | |------|------| | **himalaya** | 邮件访问、搜索12306邮件、下载PDF附件 | | **pdf-ocr** | PDF/图片OCR文字提取、发票信息解析 | | **pincer-runtime** | 报销系统自动化提交（底层使用 browser 工具实现浏览器自动化操作）| ## 四、执行步骤（严格按顺序执行） ### 步骤1：初始化工作目录 1. 设定工作目录为 `/by/.uiagent/files/` 2. 创建子目录结构： ``` /by/.uiagent/files/ ├── inbox/ # 存放从邮箱下载的原始PDF │ ├── railway/ # 12306火车票 │ ├── flight/ # 携程机票 │ ├── hotel_ctrip/ # 携程酒店 │ ├── hotel_huazhu/ # 华住会酒店 │ └── meituan/ # 美团发票 ├── processed/ # 存放已解析的发票数据 ├── output/ # 存放生成的报销文件 └── config.json # 配置文件（邮箱、报销系统账号等） ``` 3. 如果目录已存在，保持现有结构，仅清空 `inbox/` 各子目录 ### 步骤2：读取配置信息 1. 从 `config.json` 读取以下配置： - `email_account`: 邮箱账号配置（供himalaya使用） - `reimbursement_system`: 报销系统配置（供pincer-runtime使用） - `company_info`: 购买方信息（统一社会信用代码、公司名称） 2. 如果 `config.json` 不存在或配置不完整： - 提示用户补充配置 - 提供配置模板示例 - 等待用户确认后继续 ### 步骤3：调用 himalaya 获取各类差旅邮件 #### 3.1 获取12306火车票邮件 1. 使用 himalaya 搜索发件人为 "12306" 的邮件： ```bash himalaya envelope list "from 12306" ``` 2. 对于每封找到的邮件： - 获取邮件ID - 使用 `himalaya attachment download <message_id>` 下载附件 - 仅保留PDF格式的附件，删除其他格式（如OFD、ZIP等） - 将PDF文件移动到 `inbox/railway/` 目录 - 记录邮件信息到处理日志 #### 3.2 获取携程机票邮件 1. 使用 himalaya 搜索发件人含 "携程" 且主题含 "机票" 或 "行程单" 的邮件： ```bash himalaya envelope list "from 携程" ``` 然后过滤主题包含"机票"、"行程单"、"航班"的邮件 2. 对于每封符合条件的邮件： - 获取邮件ID - 使用 `himalaya attachment download <message_id>` 下载附件 - 仅保留PDF格式的附件 - 将PDF文件移动到 `inbox/flight/` 目录 - 记录邮件信息到处理日志 #### 3.3 获取携程酒店邮件 1. 使用 himalaya 搜索发件人含 "携程" 且主题含 "酒店" 或 "住宿" 的邮件： ```bash himalaya envelope list "from 携程" ``` 然后过滤主题包含"酒店"、"住宿"、"订单确认"的邮件 2. 对于每封符合条件的邮件： - 获取邮件ID - 使用 `himalaya attachment download <message_id>` 下载附件 - 仅保留PDF格式的附件（排除"行程单"、"确认单"等非发票文件） - 将PDF文件移动到 `inbox/hotel_ctrip/` 目录 - 记录邮件信息到处理日志 #### 3.4 获取华住会酒店邮件 1. 使用 himalaya 搜索发件人含 "华住" 或 "华住会" 的邮件： ```bash himalaya envelope list "from 华住" ``` 2. 对于每封找到的邮件： - 获取邮件ID - 使用 `himalaya attachment download <message_id>` 下载附件 - 仅保留PDF格式的附件 - 将PDF文件移动到 `inbox/hotel_huazhu/` 目录 - 记录邮件信息到处理日志 #### 3.5 获取美团发票邮件 1. 使用 himalaya 搜索发件人含 "美团" 的邮件： ```bash himalaya envelope list "from 美团" ``` 2. 对于每封符合条件的邮件： - 获取邮件ID - 使用 `himalaya attachment download <message_id>` 下载附件 - 仅保留PDF格式的附件 - 将PDF文件移动到 `inbox/meituan/` 目录 - 记录邮件信息到处理日志 3. 如果某类邮件未找到： - 提示用户 "未在邮箱中找到[某类]发票邮件" - 询问是否手动上传PDF到对应 `inbox/` 子目录 ### 步骤4：调用 pdf-processor 解析各类发票 #### 4.1 解析12306火车票 1. 遍历 `inbox/railway/` 目录中的所有PDF文件 2. 对每个PDF文件，调用pdf-ocr提取文字内容，提取关键字段： - 发票号码 - 开票日期 - 出发站、到达站 - 车次 - 乘车日期、发车时间 - 座位信息（车厢、座位号、席别） - 票价金额 - 乘客姓名、身份证号（脱敏） - 购买方名称、统一社会信用代码 3. 保存为JSON格式到 `processed/` 目录： ```json { "type": "railway", "invoice_number": "26329116804005647551", "issue_date": "2026-05-26", "departure": "南京南", "arrival": "北京南", "transport_number": "G50", "travel_date": "2026-04-01", "departure_time": "20:06", "seat_info": "02车05A号 二等座", "amount": 464.00, "passenger_name": "马天澍", "passenger_id": "3212011996****0216", "buyer_name": "浩鲸云计算科技股份有限公司", "buyer_tax_id": "91320100745379000T", "pdf_path": "inbox/railway/26329116804005647551.pdf" } ``` #### 4.2 解析携程机票 1. 遍历 `inbox/flight/` 目录中的所有PDF文件 2. 对每个PDF文件，调用pdf-ocr提取文字内容，提取关键字段： - 发票号码/行程单号 - 开票日期 - 出发城市、到达城市 - 航班号 - 起飞日期、起飞时间 - 舱位等级 - 票价金额（含税） - 乘客姓名 - 购买方名称、统一社会信用代码 3. 保存为JSON格式到 `processed/` 目录： ```json { "type": "flight", "invoice_number": "2026052600123456", "issue_date": "2026-05-26", "departure": "南京", "arrival": "北京", "transport_number": "MU2811", "travel_date": "2026-05-26", "departure_time": "08:30", "seat_info": "经济舱", "amount": 850.00, "passenger_name": "张三", "passenger_id": "3201021990****1234", "buyer_name": "浩鲸云计算科技股份有限公司", "buyer_tax_id": "91320100745379000T", "pdf_path": "inbox/flight/2026052600123456.pdf" } ``` #### 4.3 解析携程酒店 1. 遍历 `inbox/hotel_ctrip/` 目录中的所有PDF文件 2. 对每个PDF文件，调用pdf-ocr提取文字内容，提取关键字段： - 发票号码 - 开票日期 - 酒店名称 - 入住日期、离店日期 - 房间数量、晚数 - 金额 - 入住人姓名 - 购买方名称、统一社会信用代码 3. 保存为JSON格式到 `processed/` 目录： ```json { "type": "hotel_ctrip", "invoice_number": "2026052600789012", "issue_date": "2026-05-26", "hotel_name": "南京金陵饭店", "check_in": "2026-05-25", "check_out": "2026-05-26", "nights": 1, "rooms": 1, "amount": 580.00, "guest_name": "张三", "buyer_name": "浩鲸云计算科技股份有限公司", "buyer_tax_id": "91320100745379000T", "pdf_path": "inbox/hotel_ctrip/2026052600789012.pdf" } ``` #### 4.4 解析华住会酒店 1. 遍历 `inbox/hotel_huazhu/` 目录中的所有PDF文件 2. 对每个PDF文件，调用pdf-ocr提取文字内容，提取关键字段： - 发票号码 - 开票日期 - 酒店名称（如汉庭、全季等） - 入住日期、离店日期 - 房间数量、晚数 - 金额 - 入住人姓名 - 购买方名称、统一社会信用代码 3. 保存为JSON格式到 `processed/` 目录： ```json { "type": "hotel_huazhu", "invoice_number": "263291167890123456", "issue_date": "2026-05-26", "hotel_name": "汉庭酒店南京新街口店", "check_in": "2026-05-25", "check_out": "2026-05-26", "nights": 1, "rooms": 1, "amount": 299.00, "guest_name": "张三", "buyer_name": "浩鲸云计算科技股份有限公司", "buyer_tax_id": "91320100745379000T", "pdf_path": "inbox/hotel_huazhu/263291167890123456.pdf" } ``` #### 4.5 解析美团发票 1. 遍历 `inbox/meituan/` 目录中的所有PDF文件 2. 对每个PDF文件，调用pdf-ocr提取文字内容，提取关键字段： - 发票号码 - 开票日期 - 服务类型（外卖、酒店、打车等） - 商家名称 - 消费日期 - 金额 - 购买方名称、统一社会信用代码 3. 保存为JSON格式到 `processed/` 目录： ```json { "type": "meituan", "invoice_number": "263291167890123789", "issue_date": "2026-05-26", "service_type": "外卖", "merchant_name": "美团外卖商家", "consume_date": "2026-05-25", "amount": 45.00, "buyer_name": "浩鲸云计算科技股份有限公司", "buyer_tax_id": "91320100745379000T", "pdf_path": "inbox/meituan/263291167890123789.pdf" } ``` 4. 按时间排序（火车票/机票按出发时间，酒店按入住时间，美团按消费日期），生成 `invoice-list.json` #### 4.6 发票类型识别说明 由于不同供应商的发票格式差异，解析时需要根据以下特征识别发票类型： | 发票类型 | 识别特征 | |---------|---------| | 12306火车票 | 包含"中国铁路"、"国家税务总局"、"电子客票"等字样 | | 携程机票 | 包含"携程"、"航空运输电子客票行程单"等字样 | | 携程酒店 | 包含"携程"、"住宿服务"、"酒店"等字样 | | 华住会酒店 | 包含"华住"、"汉庭"、"全季"、"桔子"等品牌名称 | | 美团发票 | 包含"美团"、"美团外卖"、"大众点评"等字样 | ### 步骤5：行程分析（核心步骤） **重要**：本步骤依据 `/references/行程分析指南.md` 进行，分为三个阶段完成。 #### 5.1 第一阶段：生成详细行程安排 1. 读取 `processed/invoice-list.json` 中的车票和住宿发票信息 2. 根据发票信息分析行程，遵循以下原则： - 行程是从出发地到目的地再返回的完整过程 - 短行程：仅一个目的地（如"南京 → 北京 → 南京"） - 长行程：多个目的地（如"南京 → 上海 → 杭州 → 北京 → 南京"） - 按时间顺序排列：车票按乘车日期+发车时间，住宿按入住日期 3. 生成分段行程信息，每个分段包含： - 交通信息：出发地、出差地、交通工具、出发日期、结束日期 - 停留信息：住宿情况、开始日期、结束日期、住宿天数、住宿方式 4. 处理特殊情况： - **当日往返**：根据发车时间早晚判断行程方向 - **车站名转城市名**：将"南京南站"转换为"南京" - **信息缺失**：标注为"待确认"，第二阶段补充 5. 输出结果到 `output/travel-schedule.md`，格式示例： ``` 行程1：南京 → 北京 → 南京（短行程、停留住宿） 分段1交通：南京 → 北京，交通工具：高铁，出发日期：2026年03月18日，结束日期：2026年03月18日，出发地：南京，出差地：北京； 分段1停留：北京，住宿：有，开始日期：2026年03月18日，结束日期：2026年03月20日，住宿方式：宾馆，住宿天数：2； 分段1行程总天数：3天 ------------------------------------ 分段2交通：北京 → 南京，交通工具：高铁，出发日期：2026年03月20日，结束日期：2026年03月20日，出发地：北京，出差地：南京 分段2行程总天数：0天 ``` #### 5.2 第二阶段：完善行程信息 1. 读取 `output/travel-schedule.md` 2. 针对包含"待确认"内容的行程，通过对话补充信息： - 缺交通信息：询问交通工具、起始地、出发日期 - 缺住宿信息：询问是否住宿、入住协议酒店/宿舍/自行安排、入住时间 3. 根据用户回复更新 `travel-schedule.md`： - 协议酒店 → 住宿方式填写"协议宾馆" - 住宿舍 → 住宿方式填写"宿舍" - 自行安排 → 住宿方式填写"自行安排" 4. 如用户表示某行程暂不处理： - 将对应发票PDF移动到 `inbox/pending/` 目录 - 从 `travel-schedule.md` 中删除该行程 5. **重要**：确认 `travel-schedule.md` 中无"待确认"内容后，才进入第三阶段 #### 5.3 第三阶段：生成报销表单数据 1. 读取 `travel-schedule.md` 2. 根据 `/references/行程分析指南.md` 第三阶段的规则，逐行程处理： **短行程（往返交通工具相同）**： ``` ---------------------- 行程：填写本行程名称 出发日期：填写分段1交通的出发日期 结束日期：填写分段2交通的结束日期 是否结束：填写是 总天数：填写分段1行程总天数加分段2行程总天数 出发地：填写分段1交通的出发地 出差地：填写分段1交通的出差地 交通：填写分段1交通的交通工具 是否享受补贴：填写是 宾馆天数：根据住宿方式填写 宿舍天数：根据住宿方式填写 是否入住协议酒店：根据住宿方式填写 ``` **短行程（往返交通工具不同）**：分为两条记录 **长行程**：每个分段生成一条记录（除最后分段），最后分段单独处理 3. 输出到 `output/travel-detail.md`，严格遵循格式要求 4. 执行检查： - 前一分段结束日期不晚于后一分段出发日期 - 除最后分段，其他分段"是否结束"为"否" - 各分段通过"----------------------"分割 - 所有字段无缺失 5. **重要**：确认 `travel-schedule.md` 和 `travel-detail.md` 都已生成后，才进入下一步骤 **参考文档**：详细的行程分析规则请参见 `/references/行程分析指南.md` ### 步骤6：调用 pincer-runtime 提交报销 **前置条件**：必须已生成 `travel-detail.md` 文件 1. 检查 `output/travel-detail.md` 是否存在，如不存在则报错退出 2. 检查 `config.json` 中的报销系统配置是否完整 3. 调用 pincer-runtime 技能： - 传入 `output/travel-detail.md` 作为输入数据源 - 执行报销系统自动化提交流程 - 监控提交状态 4. 记录提交结果到 `output/submission-log.json` ### 步骤7：输出结果 1. 向用户展示处理结果： - 成功处理的发票数量 - 总金额 - 行程汇总 - 报销提交状态 2. 提供文件位置说明，方便用户查看详细数据 ## 五、输出规范 1. **结果分段展示**，关键信息加粗 2. **命令执行、文件内容完整保留**，不随意删减 3. **执行失败时**，主动说明报错原因 + 简易修复建议 4. **发票信息表格化展示**，便于核对 ## 六、安全约束 1. 所有涉及外部系统（邮箱、报销系统）的操作必须基于用户提供的配置 2. 敏感信息（密码、Token）不得明文存储，使用环境变量或加密存储 3. 删除操作前必须确认，禁止自动删除原始邮件或文件 4. PDF解析失败时保留原始文件，便于人工复核 ## 七、配置模板 `config.json` 示例： ```json { "email_account": { "name": "default", "type": "imap" }, "reimbursement_system": { "type": "fol", "url": "https://fol.example.com", "credentials": { "username": "${FOL_USERNAME}", "password": "${FOL_PASSWORD}" } }, "company_info": { "name": "浩鲸云计算科技股份有限公司", "tax_id": "91320100745379000T" } } ``` ## 八、错误处理 | 错误场景 | 处理方式 | |---------|---------| | himalaya未配置 | 提示用户配置邮箱账号 | | 未找到某类邮件 | 提示该类发票未找到，询问是否手动上传PDF到对应inbox子目录 | | PDF解析失败 | 保留文件，记录错误，继续处理其他文件 | | 发票类型识别失败 | 提示无法识别发票类型，记录到待处理列表 | | pincer-runtime提交失败 | 保存数据，提示用户手动提交 | | 配置信息缺失 | 生成配置模板，引导用户补充 | ## 九、支持的邮箱发件人 | 发票类型 | 发件人特征 | 邮件主题特征 | |---------|-----------|-------------| | 12306火车票 | [REDACTED_SECRET] | "电子发票通知" | | 携程机票 | 含"携程" | "机票"、"行程单"、"航班" | | 携程酒店 | 含"携程" | "酒店"、"住宿"、"订单确认" | | 华住会酒店 | 含"华住"、"华住会" | "发票"、"电子发票" | | 美团发票 | 含"美团" | "发票"、"电子发票" | ## 十、相关技能 - **himalaya**: 邮件管理CLI工具，用于访问邮箱、搜索邮件、下载附件 - **pdf-ocr**: PDF/图片OCR文字提取和解析工具，用于从发票中提取结构化数据 - **pincer-runtime**: 报销系统自动化工具，用于完成最终的报销提交 --- *本技能支持12306火车票、携程机票/酒店、华住会酒店、美团发票五类差旅发票的自动化报销处理。*


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal:  | SkillSpector: suspicious (CRITICAL)