docs: add fix-double-finalize-and-bindings-api implementation plan

This commit is contained in:
wren
2026-04-28 11:44:31 +08:00
parent 1b4e0ec00a
commit be9fc4856b
15 changed files with 5733 additions and 0 deletions
@@ -0,0 +1,610 @@
# 原生 AuditCtx 接入重构方案
## 1. 目标
基于对 `/home/wren-dev/Porject/leaudit/src` 源码的核对,确认当前 `leaudit` 的正式执行模型应视为:
- `AuditCtx`
- `AuditServices`
- `AuditConfig`
- `AuditService.audit(ctx)`
因此,`leaudit-platform` 后续不应继续由平台自己手写主流程编排,而应重构为:
```text
平台层
→ Bridge 适配层
→ build AuditServices
→ build AuditConfig
→ build AuditCtx
→ call AuditService.audit(ctx)
→ persist ctx outputs
→ 返回 run / result
```
本文档用于说明:
- 为什么要重构
- 重构后的目标架构
- 现有文件怎么迁移
- 建议新增哪些文件
- 如何分阶段实施,避免一次性推翻现有代码
---
## 2. 关键结论
## 2.1 必须使用原生 CTX
这一点已经可以下明确结论:
- 后续应使用 `leaudit.services.audit_ctx.AuditCtx`
- 后续应通过 `leaudit.services.audit_service.AuditService.audit(ctx)` 执行主流程
- 不建议平台继续手工串接:
- OCR
- Extract
- Phase
- Evaluate
- Rescue
- Persist
## 2.2 但 Bridge 不能取消
Bridge 仍然是必须保留的正式边界层。
Bridge 的职责应改为:
- 平台对象 -> 原生 `AuditCtx`
- 原生 `ctx` -> 平台数据库结果
也就是说:
- **不要自己编排**
- **但要自己适配**
---
## 3. 当前实现与目标实现的差异
## 3.1 当前实现(过渡态)
当前 `fastapi_modules/fastapi_leaudit/leaudit_bridge/pipeline.py` 的角色,是平台侧自编排器:
```text
file_path + rules_file
→ OCR
→ Extraction
→ Phase detection
→ Evaluation
→ Save results
```
特点:
- 直接调用 engine / extraction / evaluation 低层函数
- 部分阶段逻辑由平台维护
- rescue / finalize 语义未完整对齐原生服务层
- 结果写入与运行编排耦合较高
## 3.2 目标实现(正式态)
目标应改成:
```text
document + file + rule version
→ build AuditServices
→ build AuditConfig
→ build AuditCtx
→ await AuditService.audit(ctx)
→ persist final ctx
```
特点:
- 主流程交给 `leaudit` 原生编排器
- Bridge 只负责适配与持久化
- 平台不再复制 `leaudit` 的阶段编排语义
- 后续升级风险更小
---
## 4. 目标架构图
```text
┌────────────────────────────────────────────┐
│ 平台层 │
│ Controller / Service / ORM / API / DB │
│ 文档上传、规则管理、任务触发、结果查询 │
└──────────────────┬─────────────────────────┘
┌────────────────────────────────────────────┐
│ Bridge 适配层 │
│ │
│ 1. resolve document file │
│ 2. resolve rule version │
│ 3. download doc / yaml to local temp │
│ 4. build AuditServices / AuditConfig │
│ 5. build AuditCtx │
│ 6. call AuditService.audit(ctx) │
│ 7. persist ctx outputs to leaudit_* │
└──────────────────┬─────────────────────────┘
┌────────────────────────────────────────────┐
│ leaudit 原生服务层 │
│ AuditCtx / AuditServices / AuditService │
│ normalization / extraction / evaluation │
│ rescue / finalize │
└────────────────────────────────────────────┘
```
---
## 5. Bridge 重构后的职责边界
## 5.1 Bridge 负责的事情
- 从平台数据库找到本次执行的文档与文件版本
- 从平台规则表找到当前生效规则版本
- 从 OSS 下载文档文件与规则文件到本地临时路径
- 构造 `AuditServices`
- 构造 `AuditConfig`
- 构造原生 `AuditCtx`
- 调用 `AuditService.audit(ctx)`
- 将最终 `ctx` 中的结果持久化到 `leaudit_*`
### 5.1.1 当前已落地到代码的链路
当前项目已经完成第一批骨架接入:
- 文档文件:
- `auditServiceImpl.py` 中从 `LeauditDocumentFile` 解析文件来源
- `fileSourceResolver.py` 已支持 `localPath``ossUrl`
- `tasks.py` 执行前统一写入本地临时文档文件
- 规则文件:
- `auditServiceImpl.py` 创建 `LeauditAuditRun` 时锁定 `ruleVersionId` / `ruleSourceOssUrl`
- `ruleVersionResolver.py``run_id` 解析规则版本来源
- `tasks.py` 已支持 `OSS URL -> 本地临时 YAML -> RulesLoader -> NativeRunner`
因此当前 bridge 的真实职责已经不只是“理论适配层”,而是运行期真正负责把平台侧 DB/OSS 语义转换成 `leaudit` 原生可消费的本地文件输入。
## 5.2 Bridge 不再负责的事情
- 不再手工定义 7 个 stage 的顺序
- 不再自己负责 phase 判定与 rescue 调度的业务语义
- 不再自己维护与原生服务层重复的一套编排逻辑
## 5.3 平台层不应直接做的事情
- 不直接构造 `AuditCtx`
- 不直接调用 `AuditService.audit(ctx)`
- 不直接 import `leaudit.services.*`
---
## 6. 建议新增的核心文件
## 6.1 `audit_ctx_builder.py`
建议新增:
- `fastapi_modules/fastapi_leaudit/leaudit_bridge/audit_ctx_builder.py`
职责:
- 把平台运行对象组装成原生 `AuditCtx`
建议输入:
- `run_id`
- `document_id`
- `document_file_id`
- `rule_version_id`
- `local_file_path`
- `rules_file`
- `services`
- `audit_config`
建议输出:
- `AuditCtx`
建议职责细分:
- `build_services(...)`
- `build_config(...)`
- `build_ctx(...)`
---
## 6.2 `audit_service_factory.py`
建议新增:
- `fastapi_modules/fastapi_leaudit/leaudit_bridge/audit_service_factory.py`
职责:
- 构造原生 `AuditService` 及其依赖服务
建议内部负责:
- `DocNormalizationService`
- `ExtractionService`
- `EvaluationService`
- `RescueService`
- `AuditServices`
- `AuditService`
目标是让平台不直接感知 `leaudit` 服务装配细节。
---
## 6.3 `file_source_resolver.py`
建议新增:
- `fastapi_modules/fastapi_leaudit/leaudit_bridge/file_source_resolver.py`
职责:
- 根据 `document_file_id` 找到文件真源
- 必要时从 OSS 下载到本地临时路径
说明:
当前 `ctx_builder.py` 有一部分类似职责,但建议拆得更明确:
- 文档文件解析
- 规则文件解析
- 原生 ctx 组装
不要混在一个“万能 builder”里。
---
## 6.4 `rule_version_resolver.py`
建议新增:
- `fastapi_modules/fastapi_leaudit/leaudit_bridge/rule_version_resolver.py`
职责:
- 根据 `document.type_id` / `binding` 找到当前生效规则版本
- 从 OSS 下载 `rules.yaml`
- 解析为 `RulesFile`
说明:
当前 `rules_loader.py` 偏向本地路径加载器;后续建议保留它做“底层 YAML 解析器”,但把“版本解析 + 规则定位”职责上移到 resolver。
---
## 7. 现有文件的重构策略
## 7.1 `pipeline.py`
文件:
- `fastapi_modules/fastapi_leaudit/leaudit_bridge/pipeline.py`
### 当前问题
- 现在是平台自编排器
- 直接串低层 stage
### 目标定位
重构后应退化为“薄包装器”。
建议最终形态:
```python
ctx = build_audit_ctx(...)
ctx = await audit_service.audit(ctx)
return ctx
```
也就是说:
- 保留 `pipeline.py` 文件名可以
- 但不再保留其“自定义主编排器”角色
### 建议处理方式
- 第一阶段:保留现有 `LauditPipeline`,但新增原生 `AuditCtxPipeline`
- 第二阶段:调用方切换到新实现
- 第三阶段:删除或降级旧自编排逻辑
---
## 7.2 `tasks.py`
文件:
- `fastapi_modules/fastapi_leaudit/leaudit_bridge/tasks.py`
### 当前问题
- 现在既负责任务分发,又负责规则路径解析,又直接触发旧 pipeline
- 仍依赖 `LEAUDIT_RULES_DIR``_TYPE_ID_RULES_MAP`
### 重构目标
把它变成真正的“任务入口层”:
- 创建执行上下文
- 调 bridge 执行服务
- 更新 run 状态
### 建议改造
- `dispatch_leaudit_task()` 只做任务分发
- `leaudit_process_document()` 只做:
- resolve run inputs
- call bridge runner
- update run status
不要在这里继续放太多规则解析与 pipeline 内部细节。
---
## 7.3 `ctx_builder.py`
文件:
- `fastapi_modules/fastapi_leaudit/leaudit_bridge/ctx_builder.py`
### 当前问题
- 当前 `ExecutionContext` 不是原生 `AuditCtx`
- 更像平台内部轻量执行输入对象
### 重构目标
有两种方案:
#### 方案 A:保留并改名
- 保留文件,但改成“平台预上下文 builder”
- 只负责收集文档/文件/规则/本地路径
#### 方案 B:拆分
推荐拆分成:
- `file_source_resolver.py`
- `rule_version_resolver.py`
- `audit_ctx_builder.py`
我更建议方案 B,更清晰。
---
## 7.4 `rules_loader.py`
文件:
- `fastapi_modules/fastapi_leaudit/leaudit_bridge/rules_loader.py`
### 当前问题
- 更偏“本地 YAML 加载器”
- 还没有真正承担“规则版本解析器”职责
### 重构目标
让它只做一件事:
- 输入本地路径 / YAML 文本
- 输出 `RulesFile`
而以下职责交给 resolver
- 查绑定表
- 查规则版本
- 查 OSS 路径
- 下载本地临时文件
---
## 7.5 `storage_adapter.py`
文件:
- `fastapi_modules/fastapi_leaudit/leaudit_bridge/storage_adapter.py`
### 当前问题
- 结果写入还依赖“按 `document_id` 查最新 run”
- 与当前平台自编排写法耦合较深
### 重构目标
让它变成“最终 ctx 持久化器”。
建议接口风格:
- `persist_run_start(...)`
- `persist_ctx_outputs(run_id, ctx, meta)`
- `persist_failure(run_id, err)`
其中 `persist_ctx_outputs()` 从原生 `ctx` 中读取:
- `normalized_doc`
- `extraction`
- `phase`
- `evaluation`
- `fallback_tasks`
- `timing`
统一落表。
---
## 8. 目标执行链路
重构后的理想执行链如下:
```text
AuditServiceImpl.Run()
→ 创建 leaudit_audit_runs
→ dispatch_leaudit_task(run_id, ...)
→ bridge runner
→ resolve document file
→ resolve rule version
→ build AuditServices
→ build AuditConfig
→ build AuditCtx
→ await AuditService.audit(ctx)
→ persist ctx outputs
→ 返回 run_id
```
这里的关键点是:
- 平台仍然围绕 run 管理
- 引擎仍然围绕 ctx 管理
- bridge 负责把两者接起来
---
## 9. 推荐实施步骤
## 阶段 1:并行引入原生 CTX 路线
目标:
- 不马上删旧 `pipeline.py`
- 先把原生接入链做出来
建议动作:
- 新增 `audit_ctx_builder.py`
- 新增 `audit_service_factory.py`
- 新增一个新的 bridge runner
例如:
- `fastapi_modules/fastapi_leaudit/leaudit_bridge/native_runner.py`
职责:
- 一次性跑通原生 CTX 执行
---
## 阶段 2:让任务入口切到 native runner
目标:
- `tasks.py` 不再调用旧自编排 pipeline
- 改成调用原生 ctx 路线
建议动作:
-`dispatch_leaudit_task()`
-`leaudit_process_document()`
- 保留旧 pipeline 作为 fallback 一小段时间
---
## 阶段 3:让结果持久化围绕最终 ctx 收口
目标:
- `storage_adapter.py` 从“阶段中途写”转成“最终 ctx 聚合写”
建议动作:
- 显式传 `run_id`
- 从 ctx 中统一提取产物与汇总
---
## 阶段 4:删除旧自编排主链
目标:
- 彻底避免出现两套主流程语义
建议动作:
- 删除旧 `LauditPipeline.run()` 中的核心编排
- 或保留文件但只做代理包装
---
## 10. 风险与注意事项
## 10.1 不要一次性重写全部文件
建议采用“双轨过渡”:
- 旧 pipeline 先保留
- 新 native runner 并行引入
- 验证结果一致后再切换
## 10.2 不要把平台字段直接塞进原生 CTX
例如:
- `run_id`
- `trigger_user_id`
- `biz_document_id`
这些属于平台字段,不应该污染 `AuditCtx` 本身。
建议放在:
- bridge 层本地元数据对象
- 或持久化上下文对象
## 10.3 不要让平台业务代码直接 import `leaudit.services.*`
这条边界必须守住。
即使未来 100% 使用原生 `AuditCtx`,也应该只允许 `leaudit_bridge/` 感知这些类型。
## 10.4 规则与文件都要先落成本地路径
原生 `AuditCtx` 依旧是文件路径驱动:
- 文档文件要落地成本地路径
- 规则文件也要落地成本地 YAML 路径
不要试图让 `leaudit` 直接理解 OSS / DB。
---
## 11. 与现有文档的关系
本方案是以下文档的进一步收敛:
- `docs/规则编辑/yaml规则在线编辑设计.md`
- `docs/规则编辑/跑通全流程所需准备项.md`
- `docs/规则编辑/开发任务拆解清单.md`
- `docs/规则编辑/为什么仍然需要Bridge适配层.md`
它的核心新增点是:
- 不再停留在“Bridge 保留 / 自编排废弃”的原则层
- 而是把“如何迁移到原生 AuditCtx 模式”具体化
---
## 12. 最终结论
当前项目的 Bridge 应该正式转向下面这条路线:
- **平台负责 run / OSS / DB / 权限 / API**
- **Bridge 负责 AuditCtx 接入适配**
- **`leaudit` 原生服务层负责评查执行**
所以重构的最终方向不是“继续完善旧 pipeline”,而是:
> **用原生 `AuditCtx + AuditService.audit(ctx)` 替代平台自编排主链,并把 Bridge 重塑为适配层。**
这是当前项目最稳、最可维护、最符合 `leaudit` 演进方向的接入方案。