# LeAudit Platform — 系统现状总览 > 最后更新:2026-04-28 ## 一、目标架构 补充文档: - 并发与重试参数:`docs/leaudit/并发与重试参数说明.md` ``` ┌─ API ───────────────────────────────────────────────────────────┐ │ AuditController (/audit) RuleController (/rule-sets) │ │ DocumentController (/upload) │ │ POST /run 触发评查 GET / 规则集列表 │ │ POST /upload 上传建档/可选自动评查 │ │ GET /run/:id 查询状态 GET /{type}/versions 版本列表 │ │ GET /result/:id 查询结果 GET /versions/:id/content 正文│ │ POST /{type}/validate 校验 │ │ POST /{type}/versions 创建 │ │ POST /{type}/publish 发布 │ │ POST /{type}/rollback 回滚 │ │ GET /bindings 绑定列表 │ │ POST /{type}/bindings 创建绑定│ │ PUT /bindings/{id} 更新绑定│ │ DELETE /bindings/{id} 删除绑定│ ├─ Service ───────────────────────────────────────────────────────┤ │ AuditServiceImpl RuleServiceImpl + OssServiceImpl│ ├─ Bridge ────────────────────────────────────────────────────────┤ │ FileSourceResolver RuleVersionResolver RulesLoader │ │ AuditCtxBuilder AuditServiceFactory NativeRunner │ │ StorageAdapter ResultAdapter RuleValidator │ │ tasks.py (dispatch_leaudit_task) │ ├─ leaudit (不改) ────────────────────────────────────────────────│ │ AuditCtx → AuditService.audit() → OCR/Normalize/Extract/ │ │ Evaluate/Rescue → 填充 ctx.normalized_doc/extraction/ │ │ evaluation/fallback_tasks/timing/extraction_errors │ ├─ 存储 ──────────────────────────────────────────────────────────┤ │ OSS (MinIO) PostgreSQL │ │ bdocs/{region}/{type}/{doc_id}/ leaudit_documents │ │ rules/{rule_type}/{version_no}/ leaudit_document_files │ │ artifacts/{region}/{run_id}/ leaudit_audit_runs │ │ leaudit_rule_sets │ │ leaudit_rule_versions │ │ leaudit_rule_type_bindings │ │ leaudit_rule_results │ │ leaudit_field_results │ │ leaudit_run_metrics │ │ leaudit_run_errors │ │ leaudit_rescue_outcomes │ │ leaudit_artifacts │ └─────────────────────────────────────────────────────────────────┘ ``` ## 二、两条核心数据流 ### 流 A:规则生命周期 ``` 编辑 YAML → POST /rule-sets/{type}/validate → RuleValidator (YAML语法 + DSL语义) → POST /rule-sets/{type}/versions → 上传 OSS + INSERT leaudit_rule_versions → POST /rule-sets/{type}/publish → UPDATE leaudit_rule_sets.current_version_id → POST /rule-sets/{type}/bindings → INSERT leaudit_rule_type_bindings → 新 run 自动绑定新版本到对应文档类型 ``` ### 流 B:评查执行 ``` POST /audit/run { documentId } → AuditServiceImpl.Run() 1. 查 leaudit_documents + leaudit_document_files 2. 查 leaudit_rule_type_bindings → rule_set_id + rule_version_id 3. INSERT leaudit_audit_runs (rule_version_id, rule_source_oss_url, sha256) 4. FileSourceResolver → 下载文档 bytes 5. dispatch_leaudit_task() ┌─ RuleVersionResolver → OSS 下载规则 YAML → SHA256 校验 ├─ RulesLoader → RulesFile ├─ NativeRunner.run() → AuditCtx → AuditService.audit() └─ NativeRunner.persist_result() ├─ save_ocr_result() → leaudit_artifacts ├─ save_extraction_result() → leaudit_field_results ├─ save_evaluation_results() → leaudit_rule_results + 分数 ├─ save_run_errors() → leaudit_run_errors ├─ save_rescue_outcomes() → leaudit_rescue_outcomes ├─ save_run_metrics() → leaudit_run_metrics └─ finalize_run() → 终态 (result_status/finished_at) ``` ## 三、模块完成度 ### M1:OSS 基础设施 — 100% | 文件 | 状态 | 说明 | |---|---|---| | `fastapi_common/fastapi_common_storage/oss_client.py` | 已完成 | 上传/下载/Presign/ObjectExists | | `fastapi_common/fastapi_common_storage/oss_path_utils.py` | 已完成 | BuildBusinessDocKey/ArtifactKey/RuleYamlKey | | `fastapi_admin/config/_settings.py` | 已完成 | OSS_ENDPOINT/ACCESS_KEY/SECRET_KEY/BUCKET/REGION/USE_SSL/PRESIGN_EXPIRE | | `fastapi_admin/config/__init__.pyi` | 已完成 | 类型声明 | ### M2:规则管理后端 — 100% | 文件 | 状态 | 说明 | |---|---|---| | `controllers/ruleController.py` | 已完成 | 11 个端点(含绑定管理) | | `services/ruleService.py` | 已完成 | IRuleService 接口 | | `services/impl/ruleServiceImpl.py` | 已完成 | 规则 CRUD + 绑定 CRUD | | `leaudit_bridge/ruleValidator.py` | 已完成 | YAML 语法 + DSL 语义校验 | | `domian/Dto/ruleVersionCreateDto.py` | 已完成 | 创建版本 DTO | | `domian/Dto/ruleValidateDto.py` | 已完成 | 校验 DTO | | `domian/Dto/rulePublishDto.py` | 已完成 | 发布/回滚 DTO | | `domian/Dto/ruleBindingDto.py` | 已完成 | 绑定 DTO | | `domian/vo/ruleVo.py` | 已完成 | RuleSetVO/VersionVO/ContentVO/ValidationVO/BindingVO | ### M3:执行链与持久化 — ~95% | 文件 | 状态 | 说明 | |---|---|---| | `leaudit_bridge/auditCtxBuilder.py` | 已完成 | 构建原生 AuditCtx | | `leaudit_bridge/auditServiceFactory.py` | 已完成 | 创建 AuditService | | `leaudit_bridge/nativeRunner.py` | 已完成 | 原生执行入口 + persist_result | | `leaudit_bridge/fileSourceResolver.py` | 已完成 | 文档来源解析(接 OSS) | | `leaudit_bridge/ruleVersionResolver.py` | 已完成 | 规则版本解析(含 SHA256) | | `leaudit_bridge/storage_adapter.py` | 已完成 | 全部持久化方法(已修双重 finalize) | | `leaudit_bridge/tasks.py` | 已完成 | 任务入口 + 失败处理 | | `leaudit_bridge/pipeline.py` | 待退役 | 旧管线(已被 nativeRunner 替代) | | `leaudit_bridge/client_factory.py` | 90% | OCR/LLM/VLM 客户端工厂 | ### M4:全流程联调 — ~25% | 项目 | 状态 | |---|---| | 上传入口梳理 | 已完成 | | 上传后自动触发评查 | 已完成最小链路(`POST /upload` + `autoRun`) | | 结果查询展示 | 已补运行/规则/字段/errors/rescue/metrics/artifacts 查询 | | 联调样例准备 | 待做 | | E2E 验证 | 待做 | ## 四、持久化执行顺序与终态 `persist_result()` 严格按以下顺序执行,`finalize_run` 在最后是唯一的终态写入者: ``` save_ocr_result() save_extraction_result() save_evaluation_results() ← 只写分数/计数,不写 finished_at save_run_errors() save_rescue_outcomes() save_run_metrics() finalize_run() ← 唯一写 result_status / finished_at / rescue_applied / phase ``` **终态来源(直接从 AuditCtx 读取,不另存平行状态):** | 平台表 | 来源 | |---|---| | `leaudit_run_metrics` | `ctx.timing` | | `leaudit_run_errors` | `ctx.extraction_errors` + `ctx.extraction.all_errors` | | `leaudit_rescue_outcomes` | `ctx.fallback_tasks` | | `leaudit_audit_runs.result_status` | 综合 `ctx.fallback_tasks` + `ctx.evaluation` 推导 | | `leaudit_audit_runs.finished_at` | `finalize_run` 写入 now() | | `leaudit_audit_runs.rescue_applied` | `bool(ctx.fallback_tasks)` | ## 四点五、最新补充:上传建档入口 当前仓库已补最小上传入口: ```text POST /upload (multipart/form-data) file typeId / typeCode region=default fileRole=primary createdBy? autoRun=false speed=normal|urgent ``` 执行链: ```text Upload -> DocumentServiceImpl.Upload() -> create leaudit_documents -> 旧 active 文件失效 -> 上传原始文件到 OSS: bdocs/{region}/{type_code}/{document_id}/v{n}/{file_role}.{ext} -> INSERT leaudit_document_files -> autoRun=true 时直接调用 AuditServiceImpl.Run() ``` 说明: - `leaudit_documents` 现阶段是平台内部文档主表,不再依赖旧系统 `documents.id` - 每次前端上传都会新建一条 `leaudit_documents` - `speed` - `normal` -> `leaudit.normal` - `urgent` -> `leaudit.urgent` ## 四点六、最新补充:结果查询视图 当前 `GetRunStatus()` / `GetResult()` 已不再只返回 run 主表摘要。 - `GetRunStatus()` 当前已返回: - `documentFileId` - `resultStatus` - `ruleSetId` / `ruleVersionId` / `ruleTypeId` - `rescueApplied` - `skippedCount` - `GetResult()` 当前已可聚合查询: - `leaudit_rule_results` - `leaudit_field_results` - `leaudit_run_errors` - `leaudit_rescue_outcomes` - `leaudit_run_metrics` - `leaudit_artifacts` 建议联调方式: - 上传后从 `run.runId` 轮询 `GET /audit/run/{runId}` - 完成后调用 `GET /audit/result/{runId}` - worker 日志里会明确打印: - 已投递到哪个队列 - worker 实际消费的是 `urgent` 还是 `normal` ## 四点七、关键关系图:DocumentType -> Binding -> RuleSet -> RuleVersion 这一段是当前规则执行链里最关键的一层路由关系。 很多时候容易把这四张表混在一起,但它们职责其实完全不同: - `leaudit_document_types`:定义“系统里有哪些文档类型” - `leaudit_rule_type_bindings`:定义“某个文档类型该用哪套规则集” - `leaudit_rule_sets`:定义“某类规则集合本身” - `leaudit_rule_versions`:定义“某套规则的具体版本快照” ### 关系图 ```text ┌────────────────────────────┐ │ leaudit_document_types │ │----------------------------│ │ id │ │ code │ <- 文档类型编码,如 contract.entrust │ name │ │ extraction_mode │ │ is_enabled │ └──────────────┬─────────────┘ │ 1:N │ ▼ ┌────────────────────────────┐ │ leaudit_rule_type_bindings │ │----------------------------│ │ id │ │ doc_type_id │ -> leaudit_document_types.id │ doc_type_code │ -> 冗余编码,便于快速排查 │ rule_set_id │ -> leaudit_rule_sets.id │ binding_mode │ -> explicit / wildcard / fallback │ priority │ -> 多条命中时按优先级选 │ is_active │ │ region │ -> default / mz / yf / ... └──────────────┬─────────────┘ │ N:1 │ ▼ ┌────────────────────────────┐ │ leaudit_rule_sets │ │----------------------------│ │ id │ │ rule_type │ <- 与 DSL metadata.type_id 对齐 │ rule_name │ │ current_version_id │ -> 当前生效版本 │ status │ └──────────────┬─────────────┘ │ 1:N │ ▼ ┌────────────────────────────┐ │ leaudit_rule_versions │ │----------------------------│ │ id │ │ rule_set_id │ -> leaudit_rule_sets.id │ version_no │ │ status │ │ oss_url │ -> rules/.../rules.yaml │ file_sha256 │ │ metadata_type_id │ │ published_at │ └────────────────────────────┘ ``` ### 执行时怎么走 真正运行评查时,不是直接“拿一个 rule_type 去找 YAML”,而是按下面这条链走: ```text leaudit_documents.type_id -> leaudit_document_types.id -> leaudit_rule_type_bindings.doc_type_id -> leaudit_rule_type_bindings.rule_set_id -> leaudit_rule_sets.current_version_id -> leaudit_rule_versions.id -> leaudit_rule_versions.oss_url -> 下载对应 rules.yaml -> NativeRunner / AuditService.audit(ctx) ``` ### 每张表到底解决什么问题 #### 1. `leaudit_document_types` 它解决的是: ```text “这份文档属于什么类型?” ``` 例如: ```text id = 2 code = contract.entrust name = 通用委托合同 ``` 那一条 `leaudit_documents` 记录里如果: ```text type_id = 2 ``` 就表示这份文档被平台认定为“通用委托合同”。 它本质上是平台的“文档分类字典表”。 #### 2. `leaudit_rule_type_bindings` 它解决的是: ```text “这种文档类型,应该使用哪套规则集?” ``` 例如: ```text doc_type_id = 2 rule_set_id = 22 region = default binding_mode = explicit priority = 100 ``` 意思就是: - 文档类型 `2`(通用委托合同) - 在 `default` 区域 - 显式绑定到规则集 `22` 这张表本质上是“规则路由表”。 它不能省略,因为后续扩展会依赖它支持: - 同一种文档类型在不同地区走不同规则 - 同一种文档类型绑定多套规则并通过优先级决策 - fallback / wildcard 兜底规则 #### 3. `leaudit_rule_sets` 它解决的是: ```text “系统里存在什么规则集合?” ``` 例如: ```text id = 22 rule_type = contract.entrust rule_name = 通用委托合同 current_version_id = 2 ``` 这表示: - 系统里有一套“通用委托合同”规则集 - 当前生效版本是 `2` 注意: - `rule_set` 表示“规则集合”的稳定身份 - 它不是某个具体 YAML 版本文件 #### 4. `leaudit_rule_versions` 它解决的是: ```text “这套规则当前/历史具体长什么样?” ``` 例如: ```text id = 2 rule_set_id = 22 version_no = 2.0 oss_url = rules/contract.entrust/2.0/rules.yaml status = published ``` 这表示: - 规则集 `22` - 的 `2.0` 版本 - 对应 OSS 上某个 `rules.yaml` 这层是规则版本治理的核心,支持: - 发布 - 回滚 - 历史追溯 - 运行时精确复盘 ### 为什么一定要拆成四层 如果不拆,后面会遇到很多问题: - 文档类型和规则类型耦合死,无法独立演进 - 没法优雅支持一类文档多套规则 - 没法支持地区差异 - 没法做发布 / 回滚 / 历史 run 溯源 拆成四层以后,结构会更稳定: - `document_type` 负责“分类” - `binding` 负责“路由” - `rule_set` 负责“规则身份” - `rule_version` 负责“规则版本快照” ### 当前初始化策略 基于当前数据库里已有的 `leaudit_rule_sets`,当前最合理的初始化策略是: ```text rule_set.rule_type -> document_type.code rule_set.rule_name -> document_type.name document_type.id -> binding.doc_type_id rule_set.id -> binding.rule_set_id rule_set.current_version_id -> 当前执行版本 ``` 也就是说,先用现有 `rule_set` 反向补齐: - `leaudit_document_types` - `leaudit_rule_type_bindings` 这是当前最稳、也最方便后续扩展的做法。 ## 五、数据库表关系 ``` leaudit_document_types └── leaudit_rule_type_bindings (doc_type_id → rule_set_id) └── leaudit_rule_sets (current_version_id) └── leaudit_rule_versions (oss_url, sha256) └── leaudit_audit_runs (rule_version_id, rule_source_oss_url) ├── leaudit_rule_results ├── leaudit_field_results ├── leaudit_run_metrics ├── leaudit_run_errors ├── leaudit_rescue_outcomes └── leaudit_artifacts ``` ## 六、关键设计原则 1. **leaudit 核心不改** — 所有定制在 bridge 层,leaudit 保持纯 Python 包 2. **只读 AuditCtx** — 执行完只从 ctx 上读取,不自己模拟 stage 3. **规则版本溯源** — 每个 run 绑定具体 `rule_version_id`,老 run 不受新发布影响 4. **OSS 真源 + DB 索引** — YAML 正文存 OSS,元数据索引存 PostgreSQL 5. **终态单点写入** — `finalize_run` 是 run 主表终态的唯一写入者 6. **独立 session 提交** — 每个 `save_*` 独立会话,按"先子表后主表"顺序保证最终一致性 ## 七、已知待补项 | 缺口 | 优先级 | |---|---| | `run_metrics.llm_call_count` / `vlm_call_count` 仍为空 | P1(可从 RescueTask 汇总) | | `ctx.timing` 缺 normalize/rescue 独立 key | P2(等 leaudit 原生补) | | 文档状态更新逻辑分散在两处 | P1(应收敛到 StorageAdapter) | | Celery 异步化 | P2(当前同步可跑通,生产需异步) | | `_TYPE_ID_RULES_MAP` 硬编码兜底 | P2(等 bindings 全覆盖后移除) | | M4 E2E 联调 | P0(下个里程碑) |