Files
Auto-Finance/docs/API.md
wandering 1b35f07fd7 refactor: 架构重组 — doc/bot → core/infra,新增 Agent 调度模块
- src/doc/ 拆分为 src/core/extraction/, matching/, validation/(核心业务逻辑)
- src/bot/ 重命名为 src/infra/browser/(浏览器自动化基础设施)
- fill_consumable_doc.py → src/infra/documents/consumable.py
- 新增 Agent 调度模块:coordinator.py, events.py, session.py,重构 orchestrator.py
- 更新 AGENTS.md、README.md 及所有子目录 README
2026-07-02 18:36:19 +08:00

19 KiB
Raw Permalink Blame History

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.csvpayment_records.csv易耗品、出库单.docconfig.jsonsession.logresult.jsonagent_state.jsonagent_events.logfile_events.logllm_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 404filename 仅允许会话目录内的文件名(防止路径穿越)。


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.csvinvoice_summary.csv → 任意 .csv 文件。

  • fields:列顺序
  • data[].__row:内部行索引(保存时不需要提交,服务端按数组顺序写回)

错误: 404 未找到 CSV500 读取失败。


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 出库单存放地点

处理内容:

  1. 从会话目录 PDF 提取发票信息 → invoice_summary.csv
  2. 对支付截图多模态 LLM 识别,回填刷卡字段
  3. 根据发票类型自动分类:差旅发票(高铁票/酒店住宿)不生成出库单;普通发票从模板复制并自动填写

配置会写入 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 超时: 900 秒。


12. 提交到财务系统

POST /api/submit-financial/<session_id>

前置条件:

  • 会话目录存在 config.json,否则返回 400
  • 存在可用的发票 CSV通常为 invoice_summary.csvpayment_records.csv

说明:

  • 前端一般在提交前调用 /api/save 保存表格修改
  • 根据发票类型选择填报模式:纯差旅发票走差旅报销流程,含普通发票走普通报销流程

响应(立即):

{ "status": "started" }

SSE 完成示例:

{
  "type": "done",
  "result": {
    "ok": true,
    "submit_ok": true
  }
}

失败时 submit_ok: falsesubmit_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 出库单存放地点

处理流程:

  1. 发票提取(同 /api/process
  2. Agent 调度 LLM 分析提取结果
  3. validator 规则校验(最多 3 次校验-修正循环)
  4. LLM 语义判断信息完整性(can_submit 字段)
  5. 校验通过 → 自动提交到财务系统
  6. 校验未通过 → 等待用户补充材料

响应(立即):

{ "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"]
}

处理流程:

  1. 记录补充文件
  2. 重新提取所有发票(包含新文件)
  3. 加载上一轮分析结果作为历史上下文
  4. 重新执行 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"
}

处理流程:

  1. LLM 分析用户文字,提取需要更新的字段
  2. 合并到已提取的信息中
  3. 保存到缓存
  4. 重新执行 Agent 校验

响应(立即):

{ "status": "started" }

SSE done 事件: 同上(可能仍需补充或校验通过自动提交)。

错误:

{ "error": "请输入补充信息" }

HTTP 400(文本为空)。


17. 强制提交,跳过校验

POST /api/agent/force-submit/<session_id>

当 Agent 校验未通过或出错时,用户可选择强制提交,跳过所有校验直接提交到财务系统。

处理流程:

  1. 将 Agent 状态设为 READY
  2. 执行财务提交

响应(立即):

{ "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_extract_status {type, state, round, attempt, 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.infra.documents.consumable --csv invoice_summary.csv --doc "易耗品、出库单.doc"

详见 README.md