Files
ai-contract-platform/project-docs/V2-database-optimization-refactor-plan-2026-06-24.md
yuqei a3bb78e06b
Security Quality Gate / npm audit, Bandit, pip-audit, detect-secrets (pull_request) Failing after 16m35s
release: freeze V2 release candidate
2026-07-12 15:32:24 +08:00

21 KiB
Raw Permalink Blame History

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 生产数据库边界

推荐生产结构:

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

关键字段:

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 展示字段。目标字段:

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 目标字段:

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

关键查询:

  • 待我审批
  • 退回后重提
  • 审批时间线
  • 审批人处理统计

关键索引:

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 目标字段:

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

关键索引:

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 目标字段:

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 目标字段:

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

关键索引:

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 中拆出:

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 模型并行评测,避免未来换模型时覆盖历史向量。

关键索引:

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 合同工作台

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 表,不要每个页面临时拼任务。

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:

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 审计时间线

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。

示例:

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 的历史包袱带进新版本。