Files
leaudit-platform-backend/docs/规则编辑/原生AuditCtx接入重构方案.md

647 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 原生 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。
---
## 10.5 为什么仍然需要 Bridge 适配层
即使当前方向已经明确为:
- 使用原生 `AuditCtx`
- 使用原生 `AuditService.audit(ctx)`
- 不再由平台自己手写 7 阶段主流程编排
也仍然必须保留 Bridge,原因是:
1. 平台世界和引擎世界不是同一套对象
- 平台关心:`document_id``rule_set_id``rule_version_id``oss_url``run_id`
- 引擎关心:`file_path``RulesFile``AuditServices``AuditConfig``AuditCtx`
2. 平台层还要处理 OSS 下载、run 状态、结果落库、前端查询
3. 如果 Controller / Service 直接 import `leaudit.services.*`,边界会失守,后续升级会把改动扩散到整个平台
因此正确结构不是:
```text
平台层 -> 直接调用 leaudit AuditCtx / AuditService
```
而是:
```text
平台层 -> Bridge 适配层 -> leaudit AuditCtx / AuditService
```
Bridge 的固定职责应保持为:
- 输入适配:文档真源、规则版本、OSS 下载、本地临时路径
- 运行装配:`AuditServices``AuditConfig``AuditCtx`
- 输出适配:把 `ctx` 结果写回 `leaudit_*`
- 边界保护:只有 `leaudit_bridge/` 感知原生 `leaudit` 类型
所以这次重构真正要取消的,是“平台自己编排评查主流程”,不是 Bridge 本身。
---
## 11. 与现有文档的关系
本方案是以下文档的进一步收敛:
- `docs/规则编辑/统一OSS与规则管理实施计划.md`
它的核心新增点是:
- 不再停留在“Bridge 保留 / 自编排废弃”的原则层
- 而是把“如何迁移到原生 AuditCtx 模式”具体化
---
## 12. 最终结论
当前项目的 Bridge 应该正式转向下面这条路线:
- **平台负责 run / OSS / DB / 权限 / API**
- **Bridge 负责 AuditCtx 接入适配**
- **`leaudit` 原生服务层负责评查执行**
所以重构的最终方向不是“继续完善旧 pipeline”,而是:
> **用原生 `AuditCtx + AuditService.audit(ctx)` 替代平台自编排主链,并把 Bridge 重塑为适配层。**
这是当前项目最稳、最可维护、最符合 `leaudit` 演进方向的接入方案。