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