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

795 lines
21 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 的历史包袱带进新版本。