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
This commit is contained in:
401
.agents/docs/guides/项目架构全景图.md
Normal file
401
.agents/docs/guides/项目架构全景图.md
Normal file
@@ -0,0 +1,401 @@
|
||||
# 项目架构全景图
|
||||
|
||||
> 最后更新: 2026-06-15
|
||||
> 用途: 理解项目整体结构、模块职责、依赖关系和数据流
|
||||
|
||||
---
|
||||
|
||||
## 一、分层架构总览
|
||||
|
||||
```
|
||||
src/
|
||||
├── agent/ Agent 调度层(协调提取-校验-修正循环,状态机管理)
|
||||
├── core/ 核心业务层(纯逻辑,零框架依赖)
|
||||
├── infra/ 基础设施层(浏览器、文档、LLM 提示词)
|
||||
├── web/ Web 界面层(Flask + SSE)
|
||||
├── pipeline.py CLI 流程编排
|
||||
├── pipeline_core.py CLI/Web 公共管道逻辑
|
||||
├── main.py CLI 入口
|
||||
├── config.py 配置加载
|
||||
└── exceptions.py 异常定义
|
||||
```
|
||||
|
||||
### 依赖方向
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
classDef entry fill:#e8eaf6,stroke:#3f51b5,color:#1a237e
|
||||
classDef orchestrate fill:#e0f2f1,stroke:#00897b,color:#004d40
|
||||
classDef agent fill:#fff8e1,stroke:#ff8f00,color:#3e2723
|
||||
classDef core fill:#e3f2fd,stroke:#1565c0,color:#0d47a1
|
||||
classDef infra fill:#e8f5e9,stroke:#2e7d32,color:#1b5e20
|
||||
|
||||
subgraph 入口层
|
||||
CLI["main.py"]:::entry
|
||||
WEB["web/app.py"]:::entry
|
||||
end
|
||||
|
||||
subgraph 编排层
|
||||
PIPE["pipeline.py"]:::orchestrate
|
||||
PIPE_WEB["web/pipeline_web.py"]:::orchestrate
|
||||
PIPE_CORE["pipeline_core.py"]:::orchestrate
|
||||
end
|
||||
|
||||
subgraph Agent调度层
|
||||
AGENT["agent/orchestrator.py"]:::agent
|
||||
SESSION["agent/session.py"]:::agent
|
||||
EVENTS["agent/events.py"]:::agent
|
||||
end
|
||||
|
||||
subgraph 核心业务层
|
||||
EXTRACT["core/extraction/"]:::core
|
||||
MATCH["core/matching/"]:::core
|
||||
VALID["core/validation/"]:::core
|
||||
end
|
||||
|
||||
subgraph 基础设施层
|
||||
BROWSER["infra/browser/"]:::infra
|
||||
DOCS["infra/documents/"]:::infra
|
||||
LLM["infra/llm/"]:::infra
|
||||
end
|
||||
|
||||
CLI --> PIPE
|
||||
WEB --> PIPE_WEB
|
||||
PIPE --> PIPE_CORE
|
||||
PIPE --> EXTRACT
|
||||
PIPE --> BROWSER
|
||||
PIPE_WEB --> AGENT
|
||||
PIPE_WEB --> PIPE_CORE
|
||||
PIPE_WEB --> EXTRACT
|
||||
AGENT --> EXTRACT
|
||||
AGENT --> VALID
|
||||
AGENT --> LLM
|
||||
AGENT --> PIPE_CORE
|
||||
EXTRACT --> MATCH
|
||||
EXTRACT --> DOCS
|
||||
EXTRACT --> LLM
|
||||
MATCH --> DOCS
|
||||
BROWSER --> DOCS
|
||||
```
|
||||
|
||||
**关键约束**:
|
||||
- `infra` 不依赖 `core` 和 `agent`,只提供工具能力
|
||||
- `core` 零外部依赖,不依赖 Flask、Playwright 等框架
|
||||
- `agent` 依赖 `core` 和 `infra`,作为调度中枢编排各模块
|
||||
- 所有跨层调用均通过 `__init__.py` 导出的稳定接口
|
||||
|
||||
---
|
||||
|
||||
## 二、模块清单
|
||||
|
||||
### 2.1 Agent 调度层 (`src/agent/`)
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `coordinator.py` | 核心协调逻辑:提取-校验-修正循环(最多 3 次重试)、用户补充处理、强制提交 |
|
||||
| `session.py` | 会话状态:`AgentState` 枚举、`AgentSession` 数据类、状态持久化(原子写入) |
|
||||
| `events.py` | SSE 事件发射:事件去重、事件日志追加、事件读取 |
|
||||
| `orchestrator.py` | 兼容层:从子模块重新导出所有符号,保持旧导入路径可用 |
|
||||
|
||||
**对外接口**:`AgentSession`, `AgentState`, `run_agent_round()`, `force_submit()`, `add_supplement()`, `process_user_text_supplement()`, `load_agent_state()`, `save_agent_state()`
|
||||
|
||||
### 2.2 核心业务层 (`src/core/`)
|
||||
|
||||
| 子模块 | 职责 | 对外接口 |
|
||||
|------|------|------|
|
||||
| `extraction/extractor.py` | 编排入口:扫描目录 → 逐文件提取 → 分类 → 金额匹配 | `extract_invoices()`, `extract_document()` |
|
||||
| `extraction/llm_extractor.py` | LLM 多模态提取核心:统一文档提取、差旅/普通信息提取、缓存管理、SSE 流式事件 | `llm_query_text()`, `extract_travel_info()`, `extract_normal_info()`, `load_cache()` |
|
||||
| `matching/matcher.py` | 发票与支付记录按金额匹配(一对一 / 一对多贪心,相对容差 3%) | `match_invoices_to_cards()` |
|
||||
| `validation/validator.py` | 声明式规则校验引擎,规则从 JSON 配置文件加载 | `validate_extracted_info()`, `ValidationReport` |
|
||||
|
||||
### 2.3 基础设施层 (`src/infra/`)
|
||||
|
||||
| 子模块 | 职责 | 对外接口 |
|
||||
|------|------|------|
|
||||
| `browser/base.py` | `BaseBot` 基类:Playwright 浏览器生命周期、登录、导航、截图 | 内部基类 |
|
||||
| `browser/travel.py` | 差旅报销填报:基本信息 → 明细 → 支付 → 补助 → 附件上传 | 内部流程 |
|
||||
| `browser/normal.py` | 普通报销填报:基本信息 → 总明细 → 支付 → 附件上传 | 内部流程 |
|
||||
| `browser/__init__.py` | 浏览器入口:类型路由和流程调度 | `run_bot()`, `run_bot_web()` |
|
||||
| `documents/invoice.py` | 发票数据模型、CSV/JSON 读写、发票分类 | `load_csv()`, `save_csv()`, `save_invoice_csv()`, `classify_invoice_batch()` |
|
||||
| `documents/pdf.py` | PDF 渲染为图片(PyMuPDF) | `render_pdf_to_images()` |
|
||||
| `documents/consumable.py` | 易耗品出库单填写:CSV → Word 模板 | `fill_consumable_doc()` |
|
||||
| `llm/prompt.py` | LLM 提示词加载 | `build_invoice_system_prompt()`, `build_travel_info_system_prompt()`, `build_normal_info_system_prompt()` |
|
||||
|
||||
### 2.4 Web 界面层 (`src/web/`)
|
||||
|
||||
| 文件/目录 | 职责 |
|
||||
|------|------|
|
||||
| `app.py` | Flask 应用入口,注册蓝图和模板 |
|
||||
| `routes.py` | 路由定义:会话管理、文件上传、配置、SSE 日志流、Agent 交互 API |
|
||||
| `pipeline_web.py` | Web 管道逻辑:发票提取 + 出库单生成 + 财务提交 |
|
||||
| `sse_handler.py` | SSE 日志收集器、日志转义、文件轮询 |
|
||||
| `templates/` | `index.html`(PC 端主界面)、`mobile_upload.html`(移动端上传) |
|
||||
| `static/js/` | 前端逻辑(按加载顺序):`state.js` → `utils.js` → `chat.js` → `upload.js` → `config.js` → `process.js` → `sync.js` → `index.js` |
|
||||
|
||||
---
|
||||
|
||||
## 三、CLI 模式数据流
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
CLI_ENTRY["main.py --step all"] --> PIPE["pipeline.py run_pipeline()"]
|
||||
|
||||
subgraph Step1["Step 1: 发票提取"]
|
||||
PIPE --> EXT["core/extraction/extractor.py extract_invoices()"]
|
||||
EXT --> DOC["逐文件提取"]
|
||||
DOC --> LLM["LLM 多模态识别 (infra/llm)"]
|
||||
LLM --> CLASS["分类: train/hotel/general/payment/application"]
|
||||
CLASS --> MATCH["core/matching/matcher.py 金额匹配"]
|
||||
MATCH --> SAVE["infra/documents/ CSV/JSON 保存"]
|
||||
end
|
||||
|
||||
subgraph Step2["Step 2: 信息提取"]
|
||||
SAVE --> TYPE{"判断报销类型"}
|
||||
TYPE -->|差旅| TRAVEL["提取差旅信息 → travel_info.json"]
|
||||
TYPE -->|普通| NORMAL["提取普通发票信息 → normal_info.json"]
|
||||
end
|
||||
|
||||
subgraph Step3["Step 3: 浏览器填报"]
|
||||
TRAVEL --> BOT["infra/browser/ 填报"]
|
||||
NORMAL --> BOT
|
||||
BOT -->|差旅| BOT_T["browser/travel.py"]
|
||||
BOT -->|普通| BOT_N["browser/normal.py"]
|
||||
end
|
||||
```
|
||||
|
||||
**关键文件输出**:
|
||||
|
||||
| 文件 | 来源 | 说明 |
|
||||
|------|------|------|
|
||||
| `payment_records.csv` | Step 1 | 支付记录级别(每笔刷卡记录一行) |
|
||||
| `invoice_summary.csv` | Step 1 | 发票级别(每张发票一行) |
|
||||
| `travel_applications.json` | Step 1 | 出差事前申请单 |
|
||||
| `invoice_groups.json` | Step 1 | 发票分类结果 |
|
||||
| `travel_info.json` | Step 2 | 差旅信息:交通/住宿明细、补贴、附件清单 |
|
||||
| `normal_info.json` | Step 2 | 普通发票信息:报销说明、发票总数、总金额、附件清单 |
|
||||
|
||||
---
|
||||
|
||||
## 四、Web 模式数据流
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant F as 前端 (浏览器)
|
||||
participant API as routes.py
|
||||
participant PW as pipeline_web.py
|
||||
participant AG as agent/orchestrator.py
|
||||
participant EX as core/extraction/
|
||||
participant VA as core/validation/
|
||||
participant SSE as SSE 轮询
|
||||
|
||||
F->>API: POST /api/session → 创建 session
|
||||
F->>API: POST /api/upload/:sid → 上传文件
|
||||
F->>API: POST /api/agent/process/:sid
|
||||
API-->>F: {status: "started"}
|
||||
F->>SSE: GET /api/logs/:sid (SSE 长连接)
|
||||
|
||||
Note over API: 后台 daemon 线程启动
|
||||
|
||||
API->>PW: extract_invoices(session_dir)
|
||||
PW->>EX: 发票提取 + 分类 + 匹配
|
||||
EX-->>PW: payment_records, applications, groups
|
||||
|
||||
API->>AG: run_agent_round(session_dir, session)
|
||||
loop 校验-修正循环 (最多 3 次)
|
||||
AG->>EX: llm_query_text() 提取信息
|
||||
AG->>VA: validate_extracted_info() 规则校验
|
||||
alt 校验失败
|
||||
AG->>AG: 构建修正提示
|
||||
end
|
||||
end
|
||||
AG-->>API: session (READY 或 AWAITING_SUPPLEMENT)
|
||||
|
||||
SSE-->>F: file_progress, llm_stream, agent_state_change, agent_ready/agent_request_supplement
|
||||
|
||||
API->>API: 写入 result.json
|
||||
SSE-->>F: done (携带 result)
|
||||
F->>F: 关闭 SSE, 展示结果
|
||||
```
|
||||
|
||||
### Web 模式特有的 Agent 调度
|
||||
|
||||
CLI 模式中 `pipeline.py` 直接调用 `extract_invoices()` → `infra/browser/`,不经过 Agent 层。
|
||||
|
||||
Web 模式中 `routes.py` 启动后台线程,调用 `agent/orchestrator.py` 作为调度中枢:
|
||||
|
||||
```
|
||||
run_agent_round()
|
||||
├── 1. load_cache() — 检查缓存
|
||||
├── 2. _do_extraction_with_validation() — 提取-校验-修正循环
|
||||
│ ├── llm_query_text() — LLM 提取结构化信息
|
||||
│ ├── validate_extracted_info() — 规则校验
|
||||
│ └── 校验失败 → 构建修正提示 → 再次调用 LLM (最多 3 次)
|
||||
├── 3. 判断 can_submit 字段
|
||||
│ ├── true → READY → 自动触发财务提交
|
||||
│ └── false → AWAITING_SUPPLEMENT → 等待用户补充
|
||||
├── 4. 用户补充处理
|
||||
│ ├── add_supplement() — 记录补充文件
|
||||
│ └── process_user_text_supplement() — LLM 解析文字补充
|
||||
└── 5. save_agent_state() — 持久化状态
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、Agent 状态机
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> IDLE: 会话创建
|
||||
|
||||
IDLE --> EXTRACTING: POST /api/agent/process
|
||||
IDLE --> EXTRACTING: POST /api/agent/supplement
|
||||
IDLE --> EXTRACTING: POST /api/agent/user-supplement
|
||||
|
||||
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 校验
|
||||
最多 3 次重试
|
||||
end note
|
||||
```
|
||||
|
||||
### 终态保护
|
||||
|
||||
以下状态为终态,再次触发 `run_agent_round()` 会被跳过:
|
||||
- `DONE` — 提交完成
|
||||
- `SUBMITTING` — 提交中
|
||||
- `READY` — 准备提交
|
||||
|
||||
### 轮次保护
|
||||
|
||||
默认最多 5 轮(`AgentSession.max_rounds`),超限后进入 `ERROR` 状态,用户可选择强制提交。
|
||||
|
||||
---
|
||||
|
||||
## 六、SSE 事件通信机制
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph 后端写入
|
||||
AGENT[agent/orchestrator.py] -->|追加写入| AE[agent_events.log]
|
||||
LLM[LLM 回调] -->|追加写入| LS[llm_stream.log]
|
||||
PW[pipeline_web.py] -->|追加写入| FE[file_events.log]
|
||||
SH[sse_handler.py] -->|追加写入| SL[session.log]
|
||||
RT[_run_agent_task] -->|finally 原子写入| RJ[result.json]
|
||||
end
|
||||
|
||||
subgraph SSE 轮询 (0.5s)
|
||||
POLL[SSE 端点] -->|读取| AE
|
||||
POLL -->|读取| LS
|
||||
POLL -->|读取| FE
|
||||
POLL -->|读取| SL
|
||||
POLL -->|检测| RJ
|
||||
end
|
||||
|
||||
POLL -->|event: agent_*| FRONT[前端 agent.js]
|
||||
POLL -->|event: llm_stream| FRONT
|
||||
POLL -->|event: file_progress| FRONT
|
||||
POLL -->|event: done| FRONT
|
||||
```
|
||||
|
||||
### 信号文件生命周期
|
||||
|
||||
| 阶段 | `result.json` | `llm_stream.log` | `agent_events.log` | `file_events.log` | `session.log` |
|
||||
|------|:--:|:--:|:--:|:--:|:--:|
|
||||
| 会话创建 | 不存在 | 不存在 | 不存在 | 不存在 | 不存在 |
|
||||
| 后台线程启动 | 已删除 | 已删除 | 已删除 | 保持 | 保持 |
|
||||
| 文件提取中 | 不存在 | 不存在 | 不存在 | 持续追加 | 持续追加 |
|
||||
| LLM 提取中 | 不存在 | 持续追加 | 持续追加 | 保持 | 持续追加 |
|
||||
| 校验中 | 不存在 | 保持 | 持续追加 | 保持 | 持续追加 |
|
||||
| 任务完成 | 已写入 | 保持 | 保持 | 保持 | 保持 |
|
||||
| SSE done 事件 | 保持 | 保持 | 保持 | 保持 | 保持 |
|
||||
|
||||
---
|
||||
|
||||
## 七、发票类型路由
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
INPUT["上传文件 (PDF/图片)"] --> EXT["LLM 多模态识别"]
|
||||
EXT --> TYPE{"invoice_type?"}
|
||||
|
||||
TYPE -->|train| TRAVEL["差旅报销流程"]
|
||||
TYPE -->|hotel| TRAVEL
|
||||
TYPE -->|general| NORMAL["普通报销流程"]
|
||||
TYPE -->|payment| MATCH["参与金额匹配"]
|
||||
TYPE -->|application| APP["存储为 JSON"]
|
||||
|
||||
TRAVEL --> TRAVEL_INFO["提取差旅信息<br/>travel_info.json"]
|
||||
TRAVEL_INFO --> TRAVEL_BOT["browser/travel.py<br/>填报差旅报销单"]
|
||||
|
||||
NORMAL --> NORMAL_INFO["提取普通发票信息<br/>normal_info.json"]
|
||||
NORMAL_INFO --> NORMAL_BOT["browser/normal.py<br/>填报普通报销单"]
|
||||
NORMAL_INFO --> CONSUMABLE["生成易耗品出库单<br/>(仅普通报销)"]
|
||||
|
||||
MATCH --> MERGE["合并到对应发票组"]
|
||||
|
||||
style TRAVEL fill:#cfe2ff,stroke:#0d6efd
|
||||
style NORMAL fill:#f8d7da,stroke:#dc3545
|
||||
style MATCH fill:#d1e7dd,stroke:#198754
|
||||
style APP fill:#fff3cd,stroke:#ffc107
|
||||
```
|
||||
|
||||
| 发票类型 | `invoice_type` | 报销流程 | 生成出库单 |
|
||||
|----------|---------------|---------|:--:|
|
||||
| 高铁票/火车票 | `train` | 差旅报销 | 否 |
|
||||
| 酒店住宿 | `hotel` | 差旅报销 | 否 |
|
||||
| 普通发票 | `general` | 普通报销 | 是 |
|
||||
| 支付记录 | `payment` | 参与匹配 | 否 |
|
||||
| 出差申请单 | `application` | 单独存储 | 否 |
|
||||
|
||||
> 差旅发票和普通发票不支持混报,混合时系统按普通报销处理。
|
||||
|
||||
---
|
||||
|
||||
## 八、设计原则
|
||||
|
||||
| 原则 | 说明 |
|
||||
|------|------|
|
||||
| **Agent 是调度中枢** | 校验-修正循环由 Agent 编排,不内嵌在 `llm_extractor` 中 |
|
||||
| **模块职责单一** | `llm_extractor` 只管提取,`validator` 只管校验,Agent 负责编排 |
|
||||
| **core 零外部依赖** | 不依赖 Flask、Playwright 等框架 |
|
||||
| **infra 不依赖业务** | 基础设施层只提供工具能力,不包含业务逻辑 |
|
||||
| **缓存优先** | 信息提取优先读取 `.invoice_cache`,避免重复调用 LLM |
|
||||
| **轮次保护** | 默认 5 轮上限,校验-修正循环最多重试 3 次 |
|
||||
| **终态保护** | `DONE`/`SUBMITTING`/`READY` 状态下不再重复处理 |
|
||||
| **容错降级** | 规则校验 3 次重试后返回最佳结果,不阻断流程 |
|
||||
| **原子写入** | 状态文件先写 `.tmp` 再 `rename()`,防止读取不完整数据 |
|
||||
|
||||
---
|
||||
|
||||
## 九、关键文件索引
|
||||
|
||||
| 文件 | 职责 |
|
||||
|------|------|
|
||||
| `src/main.py` | CLI 入口 |
|
||||
| `src/web/app.py` | Web 入口 |
|
||||
| `src/pipeline.py` | CLI 流程编排 |
|
||||
| `src/pipeline_core.py` | CLI/Web 公共管道逻辑 |
|
||||
| `src/web/pipeline_web.py` | Web 管道逻辑 + 财务提交 |
|
||||
| `src/web/routes.py` | Web 路由 + 后台线程启动 |
|
||||
| `src/agent/coordinator.py` | Agent 核心协调逻辑 |
|
||||
| `src/agent/session.py` | 会话状态定义与持久化 |
|
||||
| `src/agent/events.py` | SSE 事件发射 |
|
||||
| `src/core/extraction/extractor.py` | 发票提取编排入口 |
|
||||
| `src/core/extraction/llm_extractor.py` | LLM 多模态提取核心 |
|
||||
| `src/core/matching/matcher.py` | 金额匹配 |
|
||||
| `src/core/validation/validator.py` | 声明式规则校验 |
|
||||
| `src/infra/browser/base.py` | 浏览器自动化基类 |
|
||||
| `src/infra/documents/invoice.py` | 发票数据模型 |
|
||||
| `src/web/sse_handler.py` | SSE 日志收集器 |
|
||||
| `src/web/static/js/process.js` | 前端主提交流程 |
|
||||
| `src/web/static/js/agent.js` | 前端 Agent 交互处理 |
|
||||
| `config.json` | 项目配置 |
|
||||
20
.agents/docs/plans/README.md
Normal file
20
.agents/docs/plans/README.md
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
last_reviewed: 2026-06-15
|
||||
---
|
||||
|
||||
# .agents/docs/plans — 实施方案与工作交接
|
||||
|
||||
存放项目实施方案、架构分析报告、重构计划等规划类文档。
|
||||
|
||||
## 文件
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `架构分析-2026-06-15.md` | 项目架构分析与重构建议(模块拆分、分层设计、接口契约) |
|
||||
|
||||
## 用途
|
||||
|
||||
- 架构决策记录
|
||||
- 重构实施方案
|
||||
- 工作交接说明
|
||||
- 技术选型论证
|
||||
225
.agents/docs/plans/架构分析-2026-06-15.md
Normal file
225
.agents/docs/plans/架构分析-2026-06-15.md
Normal file
@@ -0,0 +1,225 @@
|
||||
# 项目架构分析与重构建议
|
||||
|
||||
## 一、当前架构总览
|
||||
|
||||
```
|
||||
src/
|
||||
├── main.py # CLI 入口
|
||||
├── pipeline.py # CLI 管道编排
|
||||
├── pipeline_core.py # CLI/Web 公共管道逻辑
|
||||
├── config.py # 配置加载
|
||||
├── exceptions.py # 异常定义
|
||||
│
|
||||
├── doc/ # 文档处理模块(职责过重)
|
||||
│ ├── extractor.py # 发票提取编排
|
||||
│ ├── llm_extractor.py # LLM 提取核心
|
||||
│ ├── invoice.py # 发票数据模型 + CSV 工具
|
||||
│ ├── matcher.py # 发票匹配逻辑
|
||||
│ ├── validator.py # 信息校验规则
|
||||
│ ├── prompt.py # 提示词加载
|
||||
│ ├── pdf.py # PDF 渲染
|
||||
│ ├── fill_consumable_doc.py # 出库单填写
|
||||
│ └── prompts/ # LLM 提示词模板
|
||||
│
|
||||
├── agent/ # Agent 调度模块
|
||||
│ └── orchestrator.py # 校验-修正循环调度
|
||||
│
|
||||
├── bot/ # 浏览器自动化模块
|
||||
│ ├── base.py # 浏览器基类
|
||||
│ ├── travel.py # 差旅填报
|
||||
│ └── normal.py # 普通报销填报
|
||||
│
|
||||
└── web/ # Web 界面模块
|
||||
├── app.py # Flask 应用
|
||||
├── routes.py # 路由定义
|
||||
├── pipeline_web.py # Web 管道逻辑(与 pipeline_core 重复)
|
||||
├── sse_handler.py # SSE 日志流处理
|
||||
└── static/templates/ # 前端资源
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、问题分析
|
||||
|
||||
### 2.1 职责不清(高耦合)
|
||||
|
||||
| 问题 | 位置 | 说明 |
|
||||
|------|------|------|
|
||||
| **doc 模块职责过重** | `src/doc/` | 同时负责:提取、匹配、校验、提示词、PDF渲染、出库单填写、CSV操作 |
|
||||
| **Web 层重复逻辑** | `pipeline_web.py` vs `pipeline_core.py` | 两者的 `is_travel_invoice`、`extract_and_cache_*` 逻辑重复 |
|
||||
| **提示词与校验耦合** | `validator.py` | 校验规则直接引用提示词相关函数,缺乏分层 |
|
||||
| **bot 模块位置** | `src/bot/` | 浏览器自动化属于基础设施,却被放在 src 根目录而非独立模块 |
|
||||
|
||||
### 2.2 逻辑混乱
|
||||
|
||||
1. **`src/doc/validator.py`** 的问题:
|
||||
- 校验规则(`TRAVEL_VALIDATION_RULES`)硬编码在模块中,修改需改代码
|
||||
- `FieldRule` 和 `ArrayRule` 类与校验逻辑紧耦合
|
||||
- 数组元素字段支持简单格式和详细格式两种配置,增加了理解成本
|
||||
|
||||
2. **`src/doc/prompt.py`** 的问题:
|
||||
- 简单的文件读取包装,但调用方分散
|
||||
- `build_invoice_system_prompt()` 和 `build_travel_info_system_prompt()` 分别调用,但结构相似
|
||||
|
||||
3. **`src/agent/orchestrator.py`** 的问题:
|
||||
- 校验循环与提取逻辑混合在 `_do_extraction_with_validation`
|
||||
- SSE 事件发射逻辑(`_emit_agent_event`)与业务逻辑混杂
|
||||
- 状态机转换逻辑分散
|
||||
|
||||
### 2.3 分层不合理
|
||||
|
||||
```
|
||||
当前分层(按目录):
|
||||
main.py → pipeline.py → doc/ + bot/
|
||||
↓
|
||||
pipeline_web.py → web/
|
||||
|
||||
建议分层(按职责):
|
||||
应用层: main.py, pipeline.py, pipeline_web.py
|
||||
业务层: agent/orchestrator.py, doc/validator.py, doc/matcher.py
|
||||
提取层: doc/extractor.py, doc/llm_extractor.py
|
||||
基础设施层: bot/, web/, doc/pdf.py, doc/fill_consumable_doc.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、重构建议
|
||||
|
||||
### 3.1 目录重组
|
||||
|
||||
```
|
||||
src/
|
||||
├── main.py # CLI 入口
|
||||
├── config.py # 配置加载
|
||||
├── exceptions.py # 异常定义
|
||||
│
|
||||
├── apps/ # 应用层(管道编排)
|
||||
│ ├── cli/ # CLI 应用
|
||||
│ │ └── pipeline.py
|
||||
│ └── web/ # Web 应用
|
||||
│ ├── app.py
|
||||
│ ├── routes.py
|
||||
│ ├── pipeline.py # Web 专用管道
|
||||
│ └── sse.py
|
||||
│
|
||||
├── core/ # 核心业务逻辑
|
||||
│ ├── agent/ # Agent 调度
|
||||
│ │ ├── orchestrator.py
|
||||
│ │ └── session.py
|
||||
│ ├── validation/ # 校验模块
|
||||
│ │ ├── validator.py
|
||||
│ │ └── rules/ # 校验规则(可配置化)
|
||||
│ ├── matching/ # 匹配模块
|
||||
│ │ └── matcher.py
|
||||
│ └── extraction/ # 提取模块
|
||||
│ ├── extractor.py
|
||||
│ └── llm.py
|
||||
│
|
||||
├── infra/ # 基础设施层
|
||||
│ ├── browser/ # 浏览器自动化
|
||||
│ │ ├── base.py
|
||||
│ │ ├── travel.py
|
||||
│ │ └── normal.py
|
||||
│ ├── documents/ # 文档处理
|
||||
│ │ ├── invoice.py
|
||||
│ │ ├── pdf.py
|
||||
│ │ └── consumable.py
|
||||
│ └── llm/ # LLM 接口
|
||||
│ └── prompts/ # 提示词模板
|
||||
│
|
||||
└── shared/ # 共享工具
|
||||
├── logging.py
|
||||
└── cache.py
|
||||
```
|
||||
|
||||
### 3.2 关键重构点
|
||||
|
||||
#### 3.2.1 doc 模块拆分
|
||||
|
||||
| 职责 | 建议移动位置 |
|
||||
|------|-------------|
|
||||
| `validator.py` | `core/validation/` |
|
||||
| `matcher.py` | `core/matching/` |
|
||||
| `llm_extractor.py` | `core/extraction/` |
|
||||
| `extractor.py` | `core/extraction/` |
|
||||
| `invoice.py` | `infra/documents/` |
|
||||
| `pdf.py` | `infra/documents/` |
|
||||
| `fill_consumable_doc.py` | `infra/documents/` |
|
||||
| `prompt.py` + `prompts/` | `infra/llm/` |
|
||||
|
||||
#### 3.2.2 消除重复逻辑
|
||||
|
||||
**问题**: `pipeline_web.py` 和 `pipeline_core.py` 都有相似逻辑:
|
||||
- `is_travel_invoice()`
|
||||
- `extract_and_cache_travel_info()`
|
||||
- `extract_and_cache_normal_info()`
|
||||
|
||||
**建议**: 将这些公共逻辑统一到 `core/pipeline/` 目录,两个入口调用同一模块。
|
||||
|
||||
#### 3.2.3 Validator 重构
|
||||
|
||||
**当前问题**:
|
||||
- 校验规则硬编码
|
||||
- `FieldRule` 和 `ArrayRule` 类过于复杂
|
||||
|
||||
**建议**:
|
||||
- 将校验规则外部化为 JSON/YAML 配置文件
|
||||
- 简化 `FieldRule` 为单一数据结构
|
||||
- 统一顶层字段和数组元素字段的校验方式
|
||||
|
||||
#### 3.2.4 Agent 拆分
|
||||
|
||||
**当前问题**:
|
||||
- `orchestrator.py` 包含:状态机、SSE 事件、校验循环、提取逻辑
|
||||
|
||||
**建议**:
|
||||
```
|
||||
agent/
|
||||
├── session.py # 状态机定义 + 会话数据模型
|
||||
├── coordinator.py # 校验-修正循环
|
||||
├── events.py # SSE 事件发射
|
||||
└── orchestrator.py # 总调度入口
|
||||
```
|
||||
|
||||
### 3.3 接口契约强化
|
||||
|
||||
| 模块 | 依赖关系 | 接口契约 |
|
||||
|------|----------|----------|
|
||||
| `core/extraction` | 被 `apps/*` 调用 | 返回 `(payment_records, applications, groups)` |
|
||||
| `core/validation` | 被 `agent/*` 调用 | `validate(info, rules) -> ValidationReport` |
|
||||
| `core/matching` | 被 `extraction` 调用 | `match(invoices, cards) -> List[Dict]` |
|
||||
| `infra/browser` | 被 `apps/*` 调用 | `run(bot, info) -> None` |
|
||||
| `infra/llm` | 被 `core/extraction` 调用 | `extract_document(file) -> dict` |
|
||||
|
||||
---
|
||||
|
||||
## 四、优先重构顺序
|
||||
|
||||
### 第一阶段(降低耦合)
|
||||
1. 将 `doc/` 拆分为 `core/` + `infra/`
|
||||
2. 消除 `pipeline_web.py` 和 `pipeline_core.py` 的重复逻辑
|
||||
3. 将 `bot/` 移动到 `infra/browser/`
|
||||
|
||||
### 第二阶段(职责清晰化)
|
||||
4. 拆分 `agent/orchestrator.py` 为多个模块
|
||||
5. 外部化 `validator.py` 的校验规则为配置文件
|
||||
6. 统一 SSE 事件处理接口
|
||||
|
||||
### 第三阶段(可维护性)
|
||||
7. 完善 `__init__.py` 的接口导出
|
||||
8. 添加模块间依赖注入机制
|
||||
9. 建立跨模块调用规范
|
||||
|
||||
---
|
||||
|
||||
## 五、当前项目优点
|
||||
|
||||
1. **日志规范**: 统一的 `get_logger()` 方式,全局日志管理
|
||||
2. **异常体系**: 清晰的 `ReimbursementError` 异常层次
|
||||
3. **SSE 事件协议**: 良好的实时反馈机制
|
||||
4. **缓存设计**: `llm_extractor.py` 的缓存加载逻辑完善
|
||||
5. **声明式校验**: `validator.py` 的规则配置思路正确
|
||||
|
||||
---
|
||||
|
||||
*生成时间: 2026-06-15*
|
||||
Reference in New Issue
Block a user