19 KiB
last_reviewed
| last_reviewed |
|---|
| 2026-06-13 |
财务报销自动化 — API 文档
基础地址:
http://localhost:5000
启动:uv run python src/web/app.py
总览
| # | 方法 | 路径 | 说明 |
|---|---|---|---|
| 1 | GET | / |
PC 端主页 |
| 2 | GET | /mobile/<session_id> |
移动端上传页面 |
| 3 | POST | /api/session |
创建会话 |
| 4 | POST | /api/upload/<session_id> |
上传文件(PDF/图片) |
| 5 | GET | /api/files/<session_id> |
列出会话目录中的文件 |
| 6 | GET | /api/download/<session_id>/<filename> |
下载生成的文件 |
| 7 | POST | /api/mobile-upload/<session_id> |
移动端上传图片 |
| 8 | GET | /api/config/<session_id> |
获取会话配置 |
| 9 | GET | /api/data/<session_id> |
获取发票数据(JSON) |
| 10 | POST | /api/save/<session_id> |
保存编辑后的发票数据 |
| 11 | POST | /api/process/<session_id> |
启动管道处理(仅发票提取,不自动提交) |
| 12 | GET | /api/logs/<session_id> |
SSE 日志流 |
| 13 | POST | /api/submit-financial/<session_id> |
提交到财务系统 |
| 14 | GET | /api/agent/state/<session_id> |
获取 Agent 会话状态 |
| 15 | POST | /api/agent/process/<session_id> |
启动 Agent 多轮处理(主入口) |
| 16 | POST | /api/agent/supplement/<session_id> |
补充文件后重新分析 |
| 17 | POST | /api/agent/user-supplement/<session_id> |
通过文字补充信息 |
| 18 | POST | /api/agent/force-submit/<session_id> |
强制提交,跳过校验 |
加粗条目为 Agent 多轮校验流程新增接口。
入口选择建议
- 推荐使用
/api/agent/process:完整流程,包含发票提取、LLM 信息校验、自动提交财务系统。 - 仅发票提取
/api/process:跳过 Agent 校验,只做文档解析和发票分类。适合调试发票提取本身,或仅需导出 CSV 的场景。
会话与目录
- 调用
POST /api/session获得session_id - 该会话下所有文件存放在
src/web/uploads/<session_id>/ - 典型产物:
invoice_summary.csv、payment_records.csv、易耗品、出库单.doc、config.json、session.log、result.json、agent_state.json、agent_events.log、file_events.log、llm_stream.log
接口详情
1. 创建会话
POST /api/session
响应:
{ "session_id": "a1b2c3d4e5f6" }
2. 上传文件(PDF/图片)
POST /api/upload/<session_id>
Content-Type: multipart/form-data
| 字段 | 类型 | 说明 |
|---|---|---|
| file | File | PDF 发票或支付截图 |
响应(成功):
{ "ok": true, "filename": "1. 电容一批.pdf" }
响应(失败):
{ "error": "未选择文件" }
HTTP 400
3. 列出会话文件
GET /api/files/<session_id>
响应:
{
"files": [
{ "name": "1. 电容一批.pdf", "type": "pdf", "size": 12345 },
{ "name": "payment_01.jpg", "type": "image", "size": 67890 }
],
"pdfs": ["1. 电容一批.pdf"],
"images": ["payment_01.jpg"]
}
images 包含扩展名:.png、.jpg、.jpeg、.bmp、.webp。
4. 下载文件
GET /api/download/<session_id>/<filename>
响应: 文件二进制流,带 Content-Disposition: attachment 与 UTF-8 文件名。
| 扩展名 | Content-Type |
|---|---|
.csv |
text/csv; charset=utf-8 |
.doc |
application/msword |
| 其它 | application/octet-stream |
常见文件名:
| 文件名 | 说明 |
|---|---|
invoice_summary.csv |
发票汇总 |
payment_records.csv |
支付记录 |
易耗品、出库单.doc |
自动填写的出库单 |
travel_applications.json |
差旅申请信息 |
result.json |
处理结果 |
agent_state.json |
Agent 会话状态 |
错误:
{ "error": "文件不存在" }
HTTP 404。filename 仅允许会话目录内的文件名(防止路径穿越)。
5. 移动端上传页面
GET /mobile/<session_id>
返回移动端 HTML 页面。
6. 移动端上传图片
POST /api/mobile-upload/<session_id>
Content-Type: multipart/form-data
| 字段 | 类型 | 说明 |
|---|---|---|
| file | File | 图片文件 |
逻辑与 POST /api/upload/<session_id> 相同。
7. 获取会话配置
GET /api/config/<session_id>
获取当前会话的配置,供前端回填表单。优先读取会话目录下的 config.json,未找到则使用项目全局配置。
响应:
{
"username": "",
"password": "",
"default_name": "",
"default_card_no": "",
"default_person_id": "",
"consumable_storage": ""
}
注意:password 字段始终返回空字符串。
8. 获取发票数据
GET /api/data/<session_id>
响应:
{
"csv_filename": "payment_records.csv",
"fields": [
"序号", "发票号码", "开票日期", "项目名称", "规格型号",
"价税合计", "销售方名称", "人员姓名", "刷卡日期",
"公务卡号", "刷卡金额", "备注", "工号"
],
"data": [
{
"__row": 0,
"序号": "1",
"发票号码": "26442000005432755951",
"价税合计": "2900.00"
}
]
}
读取优先级:payment_records.csv → invoice_summary.csv → 任意 .csv 文件。
fields:列顺序data[].__row:内部行索引(保存时不需要提交,服务端按数组顺序写回)
错误: 404 未找到 CSV;500 读取失败。
9. 保存编辑后的发票数据
POST /api/save/<session_id>
Content-Type: application/json
请求体:
{
"csv_filename": "invoice_summary.csv",
"data": [
{
"序号": "1",
"发票号码": "26442000005432755951",
"开票日期": "2026/05/18",
"项目名称": "...",
"规格型号": "...",
"价税合计": "2900.00",
"销售方名称": "...",
"人员姓名": "",
"刷卡日期": "2026/04/28",
"公务卡号": "",
"刷卡金额": "2850.00",
"备注": "",
"工号": "202407021"
}
]
}
响应(成功):
{
"ok": true,
"doc_ok": true,
"doc_url": "/api/download/<session_id>/%E6%98%93%E8%80%97%E5%93%81%E3%80%81%E5%87%BA%E5%BA%93%E5%8D%95.doc"
}
保存后会根据最新 CSV 重新生成 出库单 Word(与会话 config.json 中的 consumable_storage 等配置一致)。
纯差旅发票跳过出库单:
{
"ok": true,
"doc_ok": null,
"doc_skipped": true
}
出库单生成失败:
{
"ok": true,
"doc_ok": false,
"doc_error": "出库单模板不存在,请将模板放在项目根目录"
}
10. 启动管道处理(仅发票提取)
POST /api/process/<session_id>
Content-Type: application/json
此接口仅执行发票提取和 LLM 识别,不会触发 Agent 多轮校验,也不会自动提交到财务系统。如需完整的 Agent 校验流程,请使用
/api/agent/process。
请求体:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| username | string | 否 | 财务系统工号 |
| password | string | 否 | 登录密码 |
| default_name | string | 否 | 默认报销人姓名 |
| default_card_no | string | 否 | 默认公务卡号 |
| default_person_id | string | 否 | 默认人员编号 |
| consumable_storage | string | 否 | 出库单存放地点 |
处理内容:
- 从会话目录 PDF 提取发票信息 →
invoice_summary.csv - 对支付截图多模态 LLM 识别,回填刷卡字段
- 根据发票类型自动分类:差旅发票(高铁票/酒店住宿)不生成出库单;普通发票从模板复制并自动填写
配置会写入 src/web/uploads/<session_id>/config.json。
响应(立即):
{ "status": "started" }
处理在后台线程执行,进度与结果通过 GET /api/logs/<session_id>(SSE)获取。
SSE 完成时 result 示例(成功):
{
"ok": true,
"elapsed": "45.2s",
"invoice_count": 4,
"csv_url": "/api/download/<session_id>/invoice_summary.csv",
"travel_count": 2,
"general_count": 2,
"doc_url": "/api/download/<session_id>/%E6%98%93%E8%80%97%E5%93%81%E3%80%81%E5%87%BA%E5%BA%93%E5%8D%95.doc",
"doc_ok": true
}
11. SSE 日志流
GET /api/logs/<session_id>
Accept: text/event-stream
每 0.5 秒轮询 4 个日志文件,通过文件 size 增量检测新内容:
| 文件 | 内容 |
|---|---|
session.log |
普通日志(extractor、llm_extractor、matcher、pipeline、bot、agent、validator 等模块) |
file_events.log |
文件处理进度事件 |
llm_stream.log |
LLM 流式输出 |
agent_events.log |
Agent 调度事件 |
检测到 result.json 存在时,读取后发送 done 事件并断开连接。
SSE 超时: 600 秒。
12. 提交到财务系统
POST /api/submit-financial/<session_id>
前置条件:
- 会话目录存在
config.json,否则返回400 - 存在可用的发票 CSV(通常为
invoice_summary.csv或payment_records.csv)
说明:
- 前端一般在提交前调用
/api/save保存表格修改 - 根据发票类型选择填报模式:纯差旅发票走差旅报销流程,含普通发票走普通报销流程
响应(立即):
{ "status": "started" }
SSE 完成示例:
{
"type": "done",
"result": {
"ok": true,
"submit_ok": true
}
}
失败时 submit_ok: false,submit_error 为错误描述。
Agent 多轮校验流程
Agent 是系统的调度中枢,负责编排信息提取、规则校验、补充材料请求的完整流程。推荐使用 /api/agent/process 作为主入口。
Agent 状态机
stateDiagram-v2
[*] --> IDLE
IDLE --> EXTRACTING: 启动处理
EXTRACTING --> READY: can_submit == true
EXTRACTING --> AWAITING_SUPPLEMENT: can_submit == false
EXTRACTING --> ERROR: 异常 / 轮次超限
READY --> SUBMITTING: _emit_ready_and_submit()
SUBMITTING --> DONE: 财务提交完成
AWAITING_SUPPLEMENT --> EXTRACTING: 用户补充文件/文字
AWAITING_SUPPLEMENT --> READY: 用户强制提交
note right of EXTRACTING
LLM 提取 + validator 校验\n最多 3 次重试
end note
13. 获取 Agent 会话状态
GET /api/agent/state/<session_id>
响应:
{
"session_id": "a1b2c3d4e5f6",
"state": "extracting",
"rounds": 1,
"max_rounds": 5,
"invoice_type": "travel",
"extracted_info": { ... },
"validation_reports": [ ... ],
"user_supplements": [ ... ],
"error_message": ""
}
状态值:
| 状态 | 含义 |
|---|---|
idle |
初始状态 |
extracting |
LLM 正在分析文件 |
awaiting_supplement |
信息不完整,等待用户补充 |
ready |
信息完整,可以提交 |
submitting |
正在提交到财务系统 |
done |
流程结束 |
error |
出错 |
14. 启动 Agent 多轮处理(主入口)
POST /api/agent/process/<session_id>
Content-Type: application/json
请求体:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| username | string | 否 | 财务系统工号 |
| password | string | 否 | 登录密码 |
| default_name | string | 否 | 默认报销人姓名 |
| default_card_no | string | 否 | 默认公务卡号 |
| default_person_id | string | 否 | 默认人员编号 |
| consumable_storage | string | 否 | 出库单存放地点 |
处理流程:
- 发票提取(同
/api/process) - Agent 调度 LLM 分析提取结果
- validator 规则校验(最多 3 次校验-修正循环)
- LLM 语义判断信息完整性(
can_submit字段) - 校验通过 → 自动提交到财务系统
- 校验未通过 → 等待用户补充材料
响应(立即):
{ "status": "started" }
SSE done 事件 - 信息完整(成功提交):
{
"type": "done",
"result": {
"ok": true,
"agent_ready": true,
"submit_ok": true,
"round": 1,
"message": "信息完整,已自动提交到财务系统"
}
}
SSE done 事件 - 信息完整但提交失败:
{
"type": "done",
"result": {
"ok": true,
"agent_ready": true,
"submit_ok": false,
"submit_error": "提交失败原因",
"round": 1,
"message": "校验通过但提交失败"
}
}
SSE done 事件 - 需补充材料:
{
"type": "done",
"result": {
"ok": true,
"agent_ready": false,
"agent_state": "awaiting_supplement",
"round": 1,
"waiting_for_supplement": true
}
}
SSE done 事件 - 处理失败:
{
"type": "done",
"result": {
"ok": false,
"error": "未提取到任何发票数据"
}
}
15. 补充文件后重新分析
POST /api/agent/supplement/<session_id>
Content-Type: application/json
在 Agent 请求补充材料后,用户上传新文件并调用此接口触发重新分析。
请求体:
{
"files": ["补充材料1.pdf", "补充材料2.jpg"]
}
处理流程:
- 记录补充文件
- 重新提取所有发票(包含新文件)
- 加载上一轮分析结果作为历史上下文
- 重新执行 Agent 校验
响应(立即):
{ "status": "started" }
SSE done 事件: 同上(可能仍需补充或校验通过自动提交)。
错误:
{ "error": "未找到 Agent 状态" }
HTTP 404(未先调用 /api/agent/process 或 Agent 状态已丢失)。
16. 通过文字补充信息
POST /api/agent/user-supplement/<session_id>
Content-Type: application/json
用户通过对话方式提供补充信息,LLM 解析后更新已提取的信息并重新校验。
请求体:
{
"text": "报销人是张三,公务卡号是 6228480402564890001"
}
处理流程:
- LLM 分析用户文字,提取需要更新的字段
- 合并到已提取的信息中
- 保存到缓存
- 重新执行 Agent 校验
响应(立即):
{ "status": "started" }
SSE done 事件: 同上(可能仍需补充或校验通过自动提交)。
错误:
{ "error": "请输入补充信息" }
HTTP 400(文本为空)。
17. 强制提交,跳过校验
POST /api/agent/force-submit/<session_id>
当 Agent 校验未通过或出错时,用户可选择强制提交,跳过所有校验直接提交到财务系统。
处理流程:
- 将 Agent 状态设为
READY - 执行财务提交
响应(立即):
{ "status": "started" }
SSE done 事件:
{
"type": "done",
"result": {
"ok": true,
"submit_ok": true,
"submit_error": null
}
}
SSE 事件类型
Agent 事件
通过 agent_events.log 轮询推送:
| 事件类型 | 数据结构 | 触发条件 |
|---|---|---|
agent_state_change |
{type, state, round, attempt, message} |
状态切换 |
agent_ready |
{type, round, message} |
双重校验通过 |
agent_request_supplement |
{type, round, missing_fields, missing_materials, semantic_issues, suggestion} |
校验未通过 |
agent_supplement_received |
{type, files} |
收到用户补充 |
agent_force_submit |
{type, message} |
用户强制提交 |
agent_error |
{type, message} |
提取失败 |
agent_max_rounds |
{type, message} |
达到最大轮次 |
文件进度事件
通过 file_events.log 轮询推送:
| 事件类型 | 数据结构 | 触发条件 |
|---|---|---|
file_progress |
{type, file, status, summary?, error?} |
文件处理状态变更 |
status 取值: processing / done / cached / error
LLM 流式事件
通过 llm_stream.log 轮询推送:
| 事件类型 | 数据结构 | 触发条件 |
|---|---|---|
llm_stream |
{type, phase, text?} |
LLM 输出流 |
phase 取值: start / reasoning / chunk / end / error
完成事件
SSE 检测到 result.json 后直接发送:
{
"type": "done",
"result": { ... }
}
错误码汇总
| HTTP | 场景 |
|---|---|
| 400 | 参数缺失、未找到配置、文本为空等 |
| 404 | session_id 不存在、文件不存在、Agent 状态丢失 |
| 500 | CSV 读取失败、服务未初始化等内部错误 |
统一错误体:
{ "error": "错误描述" }
发票类型分类
系统自动将发票分为两类,影响出库单生成和后续报销流程:
| 类型 | 判断依据 | 出库单 | 报销流程 |
|---|---|---|---|
| 差旅发票 | 高铁票、酒店住宿等 | 不生成 | 差旅报销 |
| 普通发票 | 其他(办公用品、耗材等) | 自动生成 | 普通报销 |
/api/process 和 /api/save 的响应中 travel_count / general_count 即为分类统计。
端到端流程
sequenceDiagram
participant F as 前端
participant S as SSE连接
participant B as 后端线程
participant A as Agent调度器
F->>B: POST /api/session
B-->>F: session_id
F->>B: POST /api/upload/{sid} (多次)
F->>B: POST /api/agent/process/{sid}
B-->>F: {status: "started"}
F->>S: GET /api/logs/{sid}
Note over B,A: 后台线程启动
B->>A: extract_invoices()
S-->>F: file_progress (processing/done)
S-->>F: llm_stream (start/chunk/end)
S-->>F: agent_state_change (extracting)
Note over A: LLM 提取 + validator 校验<br/>最多 3 次重试
alt 信息完整
A->>A: state → READY
A->>A: _emit_ready_and_submit()
S-->>F: agent_ready
S-->>F: done (submit_ok=true)
F->>F: es.close()
else 信息不完整
A->>A: state → AWAITING_SUPPLEMENT
S-->>F: agent_request_supplement
S-->>F: done (waiting_for_supplement=true)
F->>F: es.close()
F->>B: 上传补充文件或输入文字
alt 文件补充
F->>B: POST /api/agent/supplement/{sid}
else 文字补充
F->>B: POST /api/agent/user-supplement/{sid}
end
B-->>F: {status: "started"}
F->>S: GET /api/logs/{sid}
Note over A: 重新分析 + 校验
alt 仍不完整
S-->>F: agent_request_supplement
S-->>F: done (waiting=true)
F->>F: es.close()
Note over F: 可继续补充或强制提交
else 完整
A->>A: state → READY
A->>A: _emit_ready_and_submit()
S-->>F: agent_ready
S-->>F: done (submit_ok=true)
F->>F: es.close()
end
alt 强制提交
F->>B: POST /api/agent/force-submit/{sid}
B-->>F: {status: "started"}
F->>S: GET /api/logs/{sid}
S-->>F: agent_force_submit
S-->>F: done
F->>F: es.close()
end
end
F->>B: GET /api/data/{sid}
B-->>F: fields + data
F->>B: POST /api/save/{sid}
Note over B: 更新 CSV,重新生成出库单
B-->>F: doc_url
F->>B: GET /api/download/{sid}/易耗品、出库单.doc
相关 CLI
不经过 Web、在本地直接填写出库单:
uv run python -m src.doc.fill_consumable_doc --csv invoice_summary.csv --doc "易耗品、出库单.doc"
详见 README.md。