# 原生 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` 演进方向的接入方案。
