用AI Agent搞定繁琐报销——小白也能懂的"自动填表"自动化方案

月底,你打开公司OA系统。

面前是47张发票,有餐饮、有交通、有住宿,还有几张皱皱巴巴的出租车票。你深吸一口气,开始逐张翻看——日期、金额、类目、备注,一行一行手动输入。第23张的时候,你把1,280元打成了12,800元,没注意。三天后财务把单子打回来,备注两个字:金额有误

你重新打开OA,一切从头再来。

这不是段子,这是中国几千万职场人每个月都在经历的"报销地狱"。有调研数据显示,职场人平均每月花在报销流程上的时间接近3小时——光是填表、对账、上传附件这些纯体力活,就能吃掉你大半个下午。

如果我告诉你,这整件事可以在你泡一杯咖啡的时间里自动完成呢?

今天这篇文章,我会手把手带你搭建一个真正能用的AI报销助手:拍照→识别→自动填表→生成报销单,全程不超过3分钟,不需要任何编程基础也能跟着做。

---

一、方案全景图:AI Agent自动报销的底层逻辑

在开始写代码之前,先花两分钟搞清楚这套方案的工作原理。整个流水线只有五个环节:

📷 拍照/截图

🔍 多模态大模型识别发票(OCR + 语义理解)

📋 结构化提取关键字段(JSON输出)

📊 自动写入Excel报销单(openpyxl)

✅ 人工确认 → 一键提交

每个环节用到的工具都很轻量:多模态大模型(比如GPT-4o)负责"看懂"发票,Python脚本负责把各环节串起来,Excel/飞书表格负责最终输出。

很多人第一反应是"直接用OCR不就行了",但传统OCR方案有个致命问题:它只认字,不理解语义。遇到格式稍有变化的发票(比如手写票、境外票、非标准版式),识别率会断崖式下跌,而且你还得自己写规则去解析识别结果。

来看一下两种方案的实际差距:

| 对比维度 | 传统OCR方案 | 大模型方案 | | 识别准确率 | 85-90%(标准发票)| 95%+(含非标准格式)| | 开发成本 | 高(需训练/调参)| 低(写Prompt即可)| | 格式适配难度 | 高(每种格式写规则)| 低(自然语言描述)| | 维护成本 | 高(格式变了要改代码)| 低(改Prompt即可)| | 单张处理费用 | 约0.01-0.05元 | 约0.03元 |

结论很清晰:对于报销这种"格式多变、字段语义复杂"的场景,大模型方案在准确率和开发效率上都完胜传统OCR。

---

二、实战搭建:从第一张发票到完整流水线

Step 1:调通API,让大模型"看懂"一张发票

首先安装依赖:

pip install openai openpyxl pillow

然后写第一段核心代码——让大模型识别单张发票:

import base64

import json

from openai import OpenAI

这里我用的是 api.884819.xyz 提供的中转API

一个key能调GPT-4o、Claude、Gemini,对国内用户非常友好

client = OpenAI(

api_key="你的API_KEY",

base_url="https://api.884819.xyz/v1"

)

def encode_image(image_path):

"""将图片转为base64编码"""

with open(image_path, "rb") as f:

return base64.b64encode(f.read()).decode("utf-8")

普通发票识别Prompt(精调版)

INVOICE_PROMPT = """

你是一个专业的财务票据识别助手。请仔细分析这张发票图片,提取以下字段并以JSON格式返回:

{

"invoice_type": "发票类型(增值税专票/增值税普票/出租车票/火车票/其他)",

"invoice_number": "发票号码",

"invoice_date": "开票日期(格式:YYYY-MM-DD)",

"seller_name": "销售方名称",

"buyer_name": "购买方名称",

"amount_without_tax": "不含税金额(数字,保留两位小数)",

"tax_amount": "税额(数字,保留两位小数,无则填0)",

"total_amount": "价税合计/总金额(数字,保留两位小数)",

"category": "费用类别(餐饮/交通/住宿/办公用品/其他)",

"description": "商品或服务描述(简短概括)",

"confidence": "识别置信度(0-1之间的小数)"

}

注意:

1. 如果某个字段无法识别,填null

2. 所有金额字段只填数字,不含货币符号

3. 如果图片模糊或无法识别,在confidence字段填0.5以下并说明原因

"""

def recognize_invoice(image_path):

"""识别单张发票,返回结构化数据"""

base64_image = encode_image(image_path)

response = client.chat.completions.create(

model="gpt-4o",

messages=[

{

"role": "user",

"content": [

{"type": "text", "text": INVOICE_PROMPT},

{

"type": "image_url",

"image_url": {

"url": f"data:image/jpeg;base64,{base64_image}",

"detail": "high"

}

}

]

}

],

response_format={"type": "json_object"},

max_tokens=1000

)

result = json.loads(response.choices[0].message.content)

return result

测试一张发票

result = recognize_invoice("invoice_001.jpg")

print(json.dumps(result, ensure_ascii=False, indent=2))

运行之后,你会看到类似这样的输出:

{

"invoice_type": "增值税普票",

"invoice_number": "12345678",

"invoice_date": "2025-01-15",

"seller_name": "北京某某餐饮有限公司",

"buyer_name": "XX科技有限公司",

"amount_without_tax": "188.68",

"tax_amount": "11.32",

"total_amount": "200.00",

"category": "餐饮",

"description": "工作餐",

"confidence": 0.97

}

第一步完成。大模型不只是"读"出了数字,还自动判断了费用类别——这是传统OCR永远做不到的事情。

---

Step 2:批量处理,一个文件夹的发票全部识别

单张能跑通,接下来批量处理整个文件夹:

import os

import time

from pathlib import Path

def process_invoice_folder(folder_path, output_json="results.json"):

"""批量处理文件夹中的所有发票图片"""

folder = Path(folder_path)

image_files = list(folder.glob(".jpg")) + list(folder.glob(".png"))

results = []

failed = []

print(f"📂 发现 {len(image_files)} 张发票,开始处理...\n")

for i, image_path in enumerate(image_files, 1):

print(f"[{i}/{len(image_files)}] 正在处理: {image_path.name}", end=" ")

try:

data = recognize_invoice(str(image_path))

data["file_name"] = image_path.name

# 置信度低于0.7时标记为需人工复核

if data.get("confidence", 1) < 0.7:

data["need_review"] = True

print(f"⚠️ 置信度低,建议人工复核")

else:

print(f"✅ 成功 | 金额: ¥{data.get('total_amount', 'N/A')}")

results.append(data)

except Exception as e:

print(f"❌ 失败: {e}")

failed.append(str(image_path.name))

# 避免触发API频率限制,每张间隔0.5秒

time.sleep(0.5)

# 保存结果

with open(output_json, "w", encoding="utf-8") as f:

json.dump(results, f, ensure_ascii=False, indent=2)

print(f"\n✨ 处理完成!成功 {len(results)} 张,失败 {len(failed)} 张")

if failed:

print(f"失败文件: {', '.join(failed)}")

return results

运行批量处理

all_results = process_invoice_folder("./invoices/")

---

Step 3:自动填表,生成标准报销Excel

最后一步,把识别结果写入Excel报销单:

import openpyxl

from openpyxl.styles import Font, Alignment, PatternFill, Border, Side

from datetime import datetime

def generate_expense_report(results, output_file="报销单.xlsx"):

"""根据识别结果生成标准报销Excel"""

wb = openpyxl.Workbook()

ws = wb.active

ws.title = "报销明细"

# 设置表头

headers = ["序号", "文件名", "发票类型", "开票日期", "销售方",

"费用类别", "描述", "金额(元)", "是否需复核"]

header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid")

header_font = Font(color="FFFFFF", bold=True, size=11)

for col, header in enumerate(headers, 1):

cell = ws.cell(row=1, column=col, value=header)

cell.fill = header_fill

cell.font = header_font

cell.alignment = Alignment(horizontal="center")

# 填入数据

total_amount = 0

for row_idx, item in enumerate(results, 2):

amount = float(item.get("total_amount") or 0)

total_amount += amount

row_data = [

row_idx - 1,

item.get("file_name", ""),

item.get("invoice_type", ""),

item.get("invoice_date", ""),

item.get("seller_name", ""),

item.get("category", ""),

item.get("description", ""),

amount,

"是" if item.get("need_review") else "否"

]

for col, value in enumerate(row_data, 1):

cell = ws.cell(row=row_idx, column=col, value=value)

cell.alignment = Alignment(horizontal="center")

# 需复核的行标红

if item.get("need_review"):

cell.fill = PatternFill(start_color="FFE0E0", end_color="FFE0E0", fill_type="solid")

# 添加合计行

total_row = len(results) + 2

ws.cell(row=total_row, column=7, value="合计").font = Font(bold=True)

ws.cell(row=total_row, column=8, value=round(total_amount, 2)).font = Font(bold=True, color="CC0000")

# 调整列宽

column_widths = [6, 20, 15, 12, 25, 10, 20, 12, 10]

for col, width in enumerate(column_widths, 1):

ws.column_dimensions[openpyxl.utils.get_column_letter(col)].width = width

wb.save(output_file)

print(f"📊 报销单已生成: {output_file}")

print(f"💰 报销总金额: ¥{round(total_amount, 2)}")

生成报销单

generate_expense_report(all_results)

三段代码,整个流水线就跑通了。

💰 成本核算:按 [api.884819.xyz](https://api.884819.xyz) 当前定价,GPT-4o识别一张发票(含图片token)大约花费0.03元。100张发票=3元。一年下来也就36元——可能还不够你打一次车去财务部。

---

三、踩坑指南——我替你交过的"学费"

坑1:发票照片模糊,识别结果一塌糊涂

解法:在Prompt里加兜底逻辑,同时在代码里做置信度校验(上面的批量处理代码已经包含)。拍照时尽量保证光线充足、发票平铺,用手机"文档扫描"模式效果更好。

坑2:金额大小写转换出错

import re

def validate_amount(amount_str):

"""校验金额格式,防止大小写混淆导致的错误"""

if amount_str is None:

return 0.0

# 移除所有非数字和小数点的字符

cleaned = re.sub(r'[^\d.]', '', str(amount_str))

try:

value = float(cleaned)

# 金额合理性校验:单张发票超过5万触发警告

if value > 50000:

print(f"⚠️ 金额异常警告: ¥{value},请人工核实")

return round(value, 2)

except ValueError:

return 0.0

坑3:增值税专票字段更多,普票Prompt不够用

专票需要额外提取税率、税号、开户行等字段。解法是根据发票类型切换Prompt模板:

PROMPTS = {

"专票": "...(专票专用Prompt,包含税号、税率等字段)...",

"普票": INVOICE_PROMPT, # 复用上面的Prompt

"出租车票": "...(出租车票专用Prompt)..."

}

坑4:API调用频率限制导致报错

import time

from functools import wraps

def retry_on_failure(max_retries=3, delay=2):

"""自动重试装饰器"""

def decorator(func):

@wraps(func)

def wrapper(args, *kwargs):

for attempt in range(max_retries):

try:

return func(args, *kwargs)

except Exception as e:

if attempt < max_retries - 1:

print(f"第{attempt+1}次失败,{delay}秒后重试...")

time.sleep(delay * (attempt + 1)) # 指数退避

else:

raise e

return wrapper

return decorator

@retry_on_failure(max_retries=3, delay=2)

def recognize_invoice(image_path):

# ... 原来的识别代码 ...

坑5:发票含公司敏感信息,不敢传云端

这是很多人的顾虑,给出两个建议:

1. 本地部署方案:用Ollama跑本地多模态模型(如LLaVA),完全离线处理

2. 脱敏后再传:用PIL对发票图片做局部遮罩,只保留金额、日期等必要字段区域

---

四、举一反三——报销只是开始

搞清楚这套"拍照→识别→结构化→填表"的Agent模式,你会发现它可以平移到几乎所有"重复性信息录入"场景:

  • 合同关键条款提取:上传合同PDF,自动提取甲乙方、金额、交付日期、违约条款
  • 简历批量筛选:100份简历丢进去,自动按技能标签分类排序
  • 体检报告整理:把体检单拍照,自动生成健康数据趋势表
  • 银行流水分类:导出流水CSV,自动按消费类别汇总统计

这些场景的代码骨架几乎一模一样,只需要换一个Prompt模板。

如果你想试试不同模型的效果——比如用Claude处理合同(逻辑推理更强)、用Gemini分析体检报告(多模态能力出色)——不需要分别注册三个平台。在 [api.884819.xyz](https://api.884819.xyz) 上切换一下model参数就行,其他代码一行不用改。

---

这套方案的本质,其实是一种思维方式的转变:把大模型当成一个永不疲倦的"信息理解引擎",把Python脚本当成连接各个环节的"胶水"。两者结合,你就拥有了一个可以处理任何重复性认知工作的自动化框架。

报销只是最容易被量化的那个切口。真正有趣的,是当你开始用这套框架审视自己工作中所有"重复、低价值、消耗注意力"的环节,然后一个一个地把它们交给AI代劳。

那些省下来的时间,才是你真正的竞争力。

---

🔜 下一篇预告

>

今天我们搞定了"拍照→识别→填表",但这个流程还差最后一公里——你还得手动把Excel上传到OA系统

>

下一篇,我们来做一个真正闭环的方案:让AI Agent自动登录你的公司OA系统,把报销单直接提交上去。我会用到Browser Use(浏览器自动化)+ 大模型决策的组合技——AI不只是帮你填表,而是帮你"办事"。这是2025年AI Agent最火的落地方向,也是从"自动化工具"进化为"数字员工"的关键一跳。

>

关注我,别错过。

---

本文由8848AI原创,转载请注明出处。