# AI 合同平台 V2 数据库优化重构方案 日期:2026-06-24 ## 1. 结论 需要做数据库优化重构,而且应该在 V2 继续扩展业务功能前完成。 当前 V2 代码已经按 `yuqei-contract-platform`、`yuqei-ai-platform`、`yuqei-ai-sdk-python`、`yuqei-infra` 重新划分,但数据库层还处在 MVP 过渡状态: - Contract API 已有 Alembic 和 SQLAlchemy 仓储,但表结构主要服务“主流程跑通”,缺少企业级租户、权限、软删除、组合索引和最终状态建模。 - AI Platform 已有知识库、chunk、AI Trace、质量评测、pgvector 起点,但还没有形成生产级检索索引、租户隔离、分区、冷热归档和可审计配置发布模型。 - 当前 compose 为了本地联调,把 Contract API 和 AI Platform 放在同一个 PostgreSQL 数据库里,只用不同 Alembic version table 隔离迁移历史。这适合开发,不适合作为最终生产数据库边界。 因为项目尚未正式上线,建议不要继续背 V1 兼容包袱,也不要在现有 MVP 迁移上无限叠加。应该在 V2 进入更多业务功能前,执行一次“干净数据库基线”重构。 ## 2. 本方案范围 本方案覆盖: - V1 数据库问题归纳。 - 当前 V2 数据库现状判断。 - V2 最终数据库边界与核心表设计。 - 性能索引、全文检索、向量检索、分区策略。 - 安全、租户隔离、审计、备份恢复。 - 从当前 V2 MVP 到最终数据库基线的实施路径。 本方案不直接改代码,不直接迁移生产数据。下一轮开发可按本文拆分 DB 任务执行。 ## 3. V1 数据库主要问题 根据 `D:\yuqei-project\ai-contract-platform-v1` 的模型与迁移观察,V1 有以下典型问题。 ### 3.1 迁移数量多,功能演进痕迹重 V1 后端已有大量迁移,覆盖审查、合同生成、知识库、法条、治理、商业订单、AI 质量评测、模型参数、审批等领域。 问题不是“迁移多”本身,而是这些能力都堆在一个业务后端数据库里,导致: - 合同业务、AI 能力、知识库、治理、商业订单混在一起。 - 不同模块数据增长速度差异巨大,却共享同一套表空间、索引和备份策略。 - 后续性能问题难以定位:到底是合同列表慢、知识库检索慢,还是 AI Trace 膨胀拖慢。 V2 不能继续沿用这种“一个后端库不断追加功能表”的模式。 ### 3.2 租户字段存在但不统一 V1 多数核心表有 `tenant_id`,例如 users、review_tasks、files、knowledge_bases、legal_articles 等。 但问题是: - 并非所有业务表都有完整 `tenant_id`。 - 主要依赖应用层过滤,缺少数据库层最后防线。 - 多租户唯一约束不够标准,例如应该更多使用 `(tenant_id, code)`、`(tenant_id, username)` 这类唯一约束。 - 审批、审计、文件、知识库、AI Trace 在租户维度上的隔离策略不一致。 V2 必须从数据库层统一租户上下文。 ### 3.3 JSON 与大文本字段使用过重 V1 `review_results` 中大量结构化结果存为 Text JSON: - `basic_info_json` - `clauses_json` - `risk_items_json` - `risk_summary_json` - `confidence_summary` 这种做法短期开发快,但会带来: - 查询困难,无法稳定索引风险项、法条引用、风险等级等字段。 - 数据质量不可控,字段结构漂移后很难校验。 - 后续统计分析、质量评测、用户筛选都需要反复解析 JSON。 V2 可以保留 JSONB 作为扩展字段,但核心查询字段必须结构化。 ### 3.4 文件内容曾进入数据库 V1 `generated_contracts` 里存在 `file_content_base64`,这类设计不适合生产: - 数据库膨胀快。 - 备份和恢复变慢。 - 文件下载性能受数据库影响。 - 文件去重、对象存储生命周期、冷热归档难做。 V2 必须坚持:数据库只存文件元数据和对象存储指针,文件正文进入 MinIO/S3。 ### 3.5 AI 能力和合同业务耦合 V1 里模型设置、知识库、法条、AI 质量评测、Prompt 资产等都和合同后端存在较强耦合。 V2 已经拆出 `yuqei-ai-platform`,数据库也必须跟着拆清楚: - 合同业务库不直接保存模型 provider、Prompt、RAG 内部 chunk。 - AI 中台库不保存合同审批流、合同履约状态、合同房间业务 UI 状态。 - 两边通过 `ai_audit_run_id`、`contract_id`、`trace_id` 做弱关联,而不是跨库强耦合事务。 ## 4. 当前 V2 数据库现状 ### 4.1 Contract API 当前表 当前 `yuqei-contract-platform/services/contract-api` 已有: - `contracts` - `approval_timeline_items` - `contract_files` - `review_tasks` - `contract_audit_events` 优点: - 已从内存数据推进到 SQLAlchemy/Alembic。 - 文件、审查任务、合同审计已经独立成表。 - 附件内容已走文件存储路径,不再把文件内容直接塞入合同表。 不足: - 缺少 `tenant_id`、`organization_id`、`department_id`、`created_by`、`updated_by`、`deleted_at`。 - `status_label`、`status_tone`、`next_action`、`next_actions` 偏 UI 展示,不应作为核心持久化真相。 - `owner`、`requested_by`、`actor_name` 仍是字符串,不是正式用户外键或快照字段组合。 - 索引以单列索引为主,缺少工作台、合同列表、审批待办、文件仓库、审计时间线的组合索引。 - 审批流还只是 timeline 表,没有正式 `approval_instances`、`approval_steps`、`approval_actions`。 - 版本、履约、归档仍未形成正式数据库模型。 ### 4.2 AI Platform 当前表 当前 `yuqei-ai-platform/services/ai-platform-api` 已有: - `ai_provider_configs` - `prompt_templates` - `knowledge_documents` - `knowledge_import_batches` - `knowledge_chunks` - `ai_run_audits` - 质量规则、质量标签、改进项、配置变更、worker runtime 等后续表 优点: - AI 中台已经形成独立数据库迁移历史。 - 已有 `knowledge_chunks` 和 pgvector column 起点。 - AI Trace 已记录 provider、model、token、费用、检索命中、质量标签等方向。 不足: - 生产级向量索引还不完整,需要明确 HNSW/IVFFLAT 策略、维度、重建流程。 - 知识库、AI Trace、质量评测缺少统一 `tenant_id` 与访问边界。 - `knowledge_documents.content` 和 `knowledge_chunks.content` 均可能增长较快,需要全文索引、分区和冷热策略。 - `ai_run_audits` 会高频增长,应从一开始设计分区或归档。 - Prompt、模型配置应有“草稿、发布、回滚、审计、影响范围”完整版本模型。 ### 4.3 当前基础设施问题 当前本地 compose 使用一个 PostgreSQL 数据库 `yuqei_v2` 同时承载 Contract API 和 AI Platform,并通过: - `contract_api_alembic_version` - `ai_platform_alembic_version` 隔离迁移历史。 这适合本地开发,但最终生产建议改为: - `yuqei_contract`:合同业务数据库。 - `yuqei_ai`:AI 中台数据库。 - `yuqei_ops`:可选,任务、运行状态、监控、队列补偿等运行数据。 这样数据库边界和服务边界一致,避免 AI 数据膨胀拖慢合同主流程。 ## 5. V2 数据库最终目标 ### 5.1 总原则 1. PostgreSQL 16 作为唯一生产关系数据库。 2. 合同平台和 AI 中台逻辑拆库,避免数据增长互相拖累。 3. 所有核心业务表都有 `tenant_id`。 4. 所有核心表都有 `created_at`、`updated_at`、`created_by`、`updated_by`。 5. 用户可删除/撤回的数据使用 `deleted_at` 软删除。 6. 审计和 AI Trace 使用 append-only 模型,不允许物理更新业务含义。 7. 文件内容不进入数据库,只存对象存储元数据、hash、size、mime、object_key。 8. 高频时间序列表按月分区。 9. 搜索字段使用 PostgreSQL FTS,向量字段使用 pgvector。 10. 数据库迁移必须可以从空库一键执行。 ### 5.2 生产数据库边界 推荐生产结构: ```text PostgreSQL cluster yuqei_contract identity schema contract schema document schema audit schema yuqei_ai ai schema knowledge schema prompt schema audit schema yuqei_ops jobs schema runtime schema notification schema ``` 本地开发可以继续用一个数据库,但 schema 和环境变量要与生产边界保持一致。不要让本地便利破坏最终模型。 ## 6. Contract Platform 目标模型 ### 6.1 identity schema 用于企业、部门、用户、角色。 核心表: - `tenants` - `organizations` - `departments` - `users` - `tenant_memberships` - `roles` - `role_bindings` - `permission_policies` 关键字段: ```text tenant_id organization_id department_id user_id role_code status created_at updated_at deleted_at ``` 关键约束: - `users`: `(tenant_id, email)` unique - `departments`: `(tenant_id, parent_department_id, name)` index - `role_bindings`: `(tenant_id, user_id, role_code, scope_type, scope_id)` unique ### 6.2 contract schema 用于合同主数据和合同房间。 核心表: - `contracts` - `contract_parties` - `contract_versions` - `contract_sections` - `contract_comments` - `contract_status_events` - `contract_obligations` - `contract_reminders` `contracts` 不应保存 UI 展示字段。目标字段: ```text id tenant_id contract_no title contract_type counterparty_name amount_cents currency lifecycle_status review_status approval_status signing_status archive_status owner_user_id department_id current_version_id effective_at expires_at created_by updated_by created_at updated_at deleted_at ``` 说明: - `status_label`、`status_tone` 在前端或 API serializer 层生成。 - `next_action` 由后端根据状态、角色、任务计算,不作为长期真相字段。 - 合同正文进入 `contract_versions`,不要直接塞在 `contracts` 主表。 `contract_versions` 目标字段: ```text id tenant_id contract_id version_no source_type body_text body_hash created_by created_at change_summary ai_generated_run_id ``` ### 6.3 approval schema 审批流独立建模,不再只靠 timeline。 核心表: - `approval_instances` - `approval_steps` - `approval_actions` - `approval_delegations` 关键查询: - 待我审批 - 退回后重提 - 审批时间线 - 审批人处理统计 关键索引: ```sql CREATE INDEX idx_approval_steps_tenant_assignee_status_created ON approval_steps (tenant_id, assignee_user_id, status, created_at DESC); CREATE INDEX idx_approval_instances_tenant_contract_status ON approval_instances (tenant_id, contract_id, status); ``` ### 6.4 document schema 文件仓库只存元数据,不存文件正文。 核心表: - `files` - `file_links` - `file_versions` - `file_text_extractions` - `file_processing_jobs` `files` 目标字段: ```text id tenant_id bucket object_key file_name mime_type file_size sha256 file_type storage_status encryption_status uploaded_by uploaded_at deleted_at ``` 关键索引: ```sql CREATE INDEX idx_files_tenant_type_uploaded ON files (tenant_id, file_type, uploaded_at DESC) WHERE deleted_at IS NULL; CREATE INDEX idx_files_tenant_sha256 ON files (tenant_id, sha256) WHERE deleted_at IS NULL; CREATE INDEX idx_file_links_tenant_resource ON file_links (tenant_id, resource_type, resource_id, created_at DESC); ``` ### 6.5 audit schema 合同侧审计必须 append-only。 核心表: - `audit_events` - `audit_event_hash_chain` `audit_events` 目标字段: ```text id tenant_id actor_user_id actor_display_name event_type resource_type resource_id summary metadata_jsonb request_id trace_id ip_address user_agent created_at ``` 关键策略: - 禁止业务代码 update/delete 审计事件。 - 高频审计按月分区。 - 可选 hash chain:每条审计记录保存 previous_hash 与 event_hash。 ## 7. AI Platform 目标模型 ### 7.1 ai schema 核心表: - `ai_provider_configs` - `ai_provider_config_versions` - `ai_model_routes` - `ai_runs` - `ai_run_steps` - `ai_run_costs` - `ai_quality_labels` - `ai_quality_improvement_actions` `ai_runs` 目标字段: ```text id tenant_id operation caller_service caller_user_id request_id trace_id provider model prompt_template_id prompt_template_version retrieval_trace_id status latency_ms prompt_tokens completion_tokens total_tokens estimated_cost quality_status quality_score created_at ``` 关键索引: ```sql CREATE INDEX idx_ai_runs_tenant_operation_created ON ai_runs (tenant_id, operation, created_at DESC); CREATE INDEX idx_ai_runs_tenant_status_created ON ai_runs (tenant_id, status, created_at DESC); CREATE UNIQUE INDEX ux_ai_runs_request_id ON ai_runs (request_id) WHERE request_id IS NOT NULL; ``` ### 7.2 prompt schema 核心表: - `prompt_templates` - `prompt_template_versions` - `prompt_releases` - `prompt_change_audits` 原则: - Prompt 内容按版本不可变。 - 发布记录独立于草稿记录。 - AI Run 必须记录命中的 prompt name + version。 ### 7.3 knowledge schema 核心表: - `knowledge_bases` - `knowledge_documents` - `knowledge_document_versions` - `knowledge_import_batches` - `knowledge_chunks` - `knowledge_chunk_embeddings` - `retrieval_traces` - `retrieval_trace_hits` 推荐把 embedding 从 `knowledge_chunks` 中拆出: ```text knowledge_chunks id tenant_id knowledge_base_id document_id chunk_index locator content content_hash token_count index_status created_at knowledge_chunk_embeddings id tenant_id chunk_id provider model dimension embedding vector created_at ``` 这样同一个 chunk 可以支持多套 embedding 模型并行评测,避免未来换模型时覆盖历史向量。 关键索引: ```sql CREATE INDEX idx_knowledge_chunks_tenant_kb_status ON knowledge_chunks (tenant_id, knowledge_base_id, index_status, created_at DESC); CREATE INDEX idx_knowledge_chunks_fts ON knowledge_chunks USING GIN (to_tsvector('simple', content)); CREATE INDEX idx_chunk_embeddings_vector_hnsw ON knowledge_chunk_embeddings USING hnsw (embedding vector_cosine_ops); ``` 说明: - `simple` 分词只是基础方案;中文法规检索后续可评估 `zhparser`、外部搜索服务或应用层分词字段。 - pgvector 索引必须按实际维度和数据量压测后决定 HNSW 参数。 ## 8. 统一索引策略 ### 8.1 合同工作台 ```sql CREATE INDEX idx_contracts_tenant_owner_updated ON contracts (tenant_id, owner_user_id, updated_at DESC) WHERE deleted_at IS NULL; CREATE INDEX idx_contracts_tenant_lifecycle_updated ON contracts (tenant_id, lifecycle_status, updated_at DESC) WHERE deleted_at IS NULL; ``` ### 8.2 待办任务 建议独立 `work_items` 表,不要每个页面临时拼任务。 ```sql CREATE INDEX idx_work_items_tenant_assignee_status_due ON work_items (tenant_id, assignee_user_id, status, due_at ASC, updated_at DESC) WHERE deleted_at IS NULL; ``` ### 8.3 统一搜索 合同、文件、审查任务先用 PostgreSQL FTS: ```sql CREATE INDEX idx_contract_search_fts ON contract_search_documents USING GIN (search_vector); CREATE INDEX idx_contract_search_tenant_type_updated ON contract_search_documents (tenant_id, resource_type, updated_at DESC); ``` 建议建立 `contract_search_documents` 物化表或同步表,而不是每次跨 contracts/files/review_tasks 临时 OR 查询。 ### 8.4 审计时间线 ```sql CREATE INDEX idx_audit_tenant_resource_created ON audit_events (tenant_id, resource_type, resource_id, created_at DESC); CREATE INDEX idx_audit_tenant_actor_created ON audit_events (tenant_id, actor_user_id, created_at DESC); ``` 按月分区后,每个分区保留同类本地索引。 ## 9. 分区与归档策略 从 V2 正式基线开始,以下表建议设计分区能力: - `audit_events`:按月分区。 - `ai_runs`:按月分区。 - `ai_run_steps`:按月分区。 - `retrieval_traces`:按月分区。 - `file_processing_jobs`:按月分区。 - `worker_job_executions`:按月分区。 归档策略: - 热数据:最近 3 到 6 个月,在线查询。 - 温数据:6 到 24 个月,保留数据库分区但降频查询。 - 冷数据:超过 24 个月,导出到对象存储归档,保留索引摘要。 ## 10. 安全策略 ### 10.1 租户隔离 所有核心业务表必须有 `tenant_id NOT NULL`。 应用层: - 所有 repository 方法必须显式接收 tenant context。 - 禁止无 tenant context 的业务查询。 数据库层: - PostgreSQL 生产环境建议启用 RLS。 - 每个请求连接事务内设置 `app.current_tenant_id`。 示例: ```sql ALTER TABLE contracts ENABLE ROW LEVEL SECURITY; CREATE POLICY tenant_isolation_contracts ON contracts USING (tenant_id = current_setting('app.current_tenant_id')::text); ``` ### 10.2 权限与最小账号 推荐数据库账号: - `contract_app_rw`:合同服务读写合同库。 - `contract_app_ro`:合同服务只读报表。 - `ai_app_rw`:AI 中台读写 AI 库。 - `migration_owner`:只用于 Alembic migration。 - `backup_user`:只用于备份。 应用运行账号不应拥有 DDL 权限。 ### 10.3 敏感数据 敏感字段: - 用户手机号、邮箱。 - 合同正文。 - 文件名和对象 key。 - AI prompt 输入和回答。 - 模型 API key。 要求: - API key 只保存密文或引用 secret manager key。 - AI Trace 对管理员展示时支持脱敏。 - 合同正文导出和下载进入审计。 - 数据库备份加密。 ## 11. 备份与恢复 生产基线: - 每日全量备份。 - WAL 连续归档。 - MinIO/S3 对象存储独立备份。 - 每周恢复演练。 - 备份加密和异地保存。 验收目标: - RPO:不超过 15 分钟。 - RTO:核心合同查询 2 小时内恢复。 - 单租户误删:可按 tenant + 时间点恢复。 ## 12. 当前 V2 到最终数据库的实施路径 因为当前项目未正式上线,推荐采用干净基线,而不是从当前 MVP 迁移一直叠加。 ### DB-0:数据库现状冻结 产出: - 当前 V2 migration 清单。 - 当前 V1 有价值数据清单。 - 最终需要迁移的数据类型。 建议迁移的数据: - 法规、法条、制度、模板、标准条款。 - 有价值的 Prompt、审查规则、知识库文档。 - demo 用户和 demo 合同可重建,不建议从 V1 迁移历史业务数据。 ### DB-1:建立最终数据库工程 动作: - `yuqei_contract` 和 `yuqei_ai` 使用独立 DATABASE_URL。 - Contract API 和 AI Platform 使用独立 Alembic 目录和迁移。 - compose 改为两个数据库或两个 Postgres database。 - 生产禁用 auto create,只能 Alembic migration。 ### DB-2:Contract Platform 干净基线 动作: - 重建合同侧 `0001_v2_contract_baseline`。 - 引入 `tenants/users/roles` 基础模型。 - 重构 `contracts`,移除 UI 展示字段。 - 增加 `contract_versions`、`work_items`、`approval_instances/steps/actions`。 - 增加 `files/file_links/file_text_extractions`。 - 增加 `audit_events` append-only。 ### DB-3:AI Platform 干净基线 动作: - 重建 AI 侧 `0001_v2_ai_baseline`。 - 增加 provider config version/release。 - 增加 prompt version/release。 - 拆分 knowledge chunk 和 embedding。 - 增加 retrieval trace/hits。 - 增加 ai_runs/ai_run_steps/quality_labels。 - 明确 pgvector extension 和 HNSW 索引。 ### DB-4:Repository 全面 tenant 化 动作: - 所有 repository 方法增加 tenant context。 - 所有 API 入口解析 current tenant。 - 所有测试补 tenant 隔离场景。 - 禁止直接用字符串 actor_name 作为业务主体,保留 actor_display_name 作为快照字段。 ### DB-5:搜索与性能基线 动作: - 建立 `contract_search_documents`。 - 建立 FTS 索引。 - 建立核心组合索引。 - 为工作台、合同列表、审批待办、文件仓库、统一搜索写 EXPLAIN 验证。 ### DB-6:安全与备份基线 动作: - RLS 策略。 - 只读/读写/migration 数据库账号。 - 备份脚本。 - 恢复演练脚本。 - 审计表 append-only 限制。 ## 13. 性能验收指标 开发阶段建议使用以下目标压测: | 场景 | 数据量 | 目标 | |---|---:|---:| | 工作台待办 | 100 万 work_items | P95 < 150ms | | 合同列表 | 100 万 contracts | P95 < 200ms | | 合同详情 | 10 万 contracts + versions | P95 < 250ms | | 审批待办 | 100 万 approval_steps | P95 < 150ms | | 文件仓库 | 100 万 files | P95 < 200ms | | 审计时间线 | 1000 万 audit_events | P95 < 300ms | | 统一搜索 FTS | 100 万 search docs | P95 < 500ms | | 知识库向量检索 | 100 万 chunks | P95 < 800ms | | AI Trace 列表 | 1000 万 ai_runs | P95 < 300ms | ## 14. 当前最优下一步 下一轮建议直接进入 DB-0 / DB-1: 1. 新增 `yuqei-infra/compose` 中的双数据库配置: - `yuqei_contract` - `yuqei_ai` 2. 新增 Contract API 最终 baseline migration 草案。 3. 新增 AI Platform 最终 baseline migration 草案。 4. 把当前 MVP migration 标记为重构前临时迁移,不继续作为最终生产基线。 5. 写 Postgres 集成测试策略,减少对 SQLite 的关键路径依赖。 这一步完成后,V2 后面的合同主流程、AI 中台、知识库、审计和权限都会建立在干净数据库地基上,不会把 V1 的历史包袱带进新版本。