差旅费用自动报销助手

API key required
Other

自动化差旅费用报销流程,支持12306火车票、携程机票/酒店、华住会酒店等电子发票的自动获取、解析和报销提交

Install

openclaw skills install travel-expense-reimbursement

差旅费用自动报销助手

一、触发条件

  1. 用户提问/指令包含【差旅报销、火车票报销、12306发票、携程发票、华住会发票、酒店报销、机票报销、电子客票、美团发票】时,必须优先使用本技能
  2. 用户要求从邮箱获取各类差旅发票并处理报销时
  3. 用户需要批量处理差旅相关PDF发票时

二、能力范围

  • 支持
    • 12306火车票电子客票
    • 携程机票电子行程单
    • 携程酒店住宿发票
    • 华住会酒店住宿发票
    • 美团发票(外卖、酒店、打车等)
  • 功能:读取邮箱、下载PDF附件、解析发票信息、生成报销数据、调用pincer-runtime提交报销
  • 限制:仅处理上述五类发票,其他类型发票需人工处理

三、依赖技能

本技能依赖以下技能完成工作:

技能用途
himalaya邮件访问、搜索12306邮件、下载PDF附件
pdf-ocrPDF/图片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" 的邮件:

    himalaya envelope list "from 12306"
    
  2. 对于每封找到的邮件:

    • 获取邮件ID
    • 使用 himalaya attachment download <message_id> 下载附件
    • 仅保留PDF格式的附件,删除其他格式(如OFD、ZIP等)
    • 将PDF文件移动到 inbox/railway/ 目录
    • 记录邮件信息到处理日志

3.2 获取携程机票邮件

  1. 使用 himalaya 搜索发件人含 "携程" 且主题含 "机票" 或 "行程单" 的邮件:

    himalaya envelope list "from 携程"
    

    然后过滤主题包含"机票"、"行程单"、"航班"的邮件

  2. 对于每封符合条件的邮件:

    • 获取邮件ID
    • 使用 himalaya attachment download <message_id> 下载附件
    • 仅保留PDF格式的附件
    • 将PDF文件移动到 inbox/flight/ 目录
    • 记录邮件信息到处理日志

3.3 获取携程酒店邮件

  1. 使用 himalaya 搜索发件人含 "携程" 且主题含 "酒店" 或 "住宿" 的邮件:

    himalaya envelope list "from 携程"
    

    然后过滤主题包含"酒店"、"住宿"、"订单确认"的邮件

  2. 对于每封符合条件的邮件:

    • 获取邮件ID
    • 使用 himalaya attachment download <message_id> 下载附件
    • 仅保留PDF格式的附件(排除"行程单"、"确认单"等非发票文件)
    • 将PDF文件移动到 inbox/hotel_ctrip/ 目录
    • 记录邮件信息到处理日志

3.4 获取华住会酒店邮件

  1. 使用 himalaya 搜索发件人含 "华住" 或 "华住会" 的邮件:

    himalaya envelope list "from 华住"
    
  2. 对于每封找到的邮件:

    • 获取邮件ID
    • 使用 himalaya attachment download <message_id> 下载附件
    • 仅保留PDF格式的附件
    • 将PDF文件移动到 inbox/hotel_huazhu/ 目录
    • 记录邮件信息到处理日志

3.5 获取美团发票邮件

  1. 使用 himalaya 搜索发件人含 "美团" 的邮件:

    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/ 目录:

    {
      "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/ 目录:

    {
      "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/ 目录:

    {
      "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/ 目录:

    {
      "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/ 目录:

    {
      "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.mdtravel-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 示例:

{
  "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火车票12306@rails.com.cn"电子发票通知"
携程机票含"携程""机票"、"行程单"、"航班"
携程酒店含"携程""酒店"、"住宿"、"订单确认"
华住会酒店含"华住"、"华住会""发票"、"电子发票"
美团发票含"美团""发票"、"电子发票"

十、相关技能

  • himalaya: 邮件管理CLI工具,用于访问邮箱、搜索邮件、下载附件
  • pdf-ocr: PDF/图片OCR文字提取和解析工具,用于从发票中提取结构化数据
  • pincer-runtime: 报销系统自动化工具,用于完成最终的报销提交

本技能支持12306火车票、携程机票/酒店、华住会酒店、美团发票五类差旅发票的自动化报销处理。