21 KiB
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_jsonclauses_jsonrisk_items_jsonrisk_summary_jsonconfidence_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 已有:
contractsapproval_timeline_itemscontract_filesreview_taskscontract_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_configsprompt_templatesknowledge_documentsknowledge_import_batchesknowledge_chunksai_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_versionai_platform_alembic_version
隔离迁移历史。
这适合本地开发,但最终生产建议改为:
yuqei_contract:合同业务数据库。yuqei_ai:AI 中台数据库。yuqei_ops:可选,任务、运行状态、监控、队列补偿等运行数据。
这样数据库边界和服务边界一致,避免 AI 数据膨胀拖慢合同主流程。
5. V2 数据库最终目标
5.1 总原则
- PostgreSQL 16 作为唯一生产关系数据库。
- 合同平台和 AI 中台逻辑拆库,避免数据增长互相拖累。
- 所有核心业务表都有
tenant_id。 - 所有核心表都有
created_at、updated_at、created_by、updated_by。 - 用户可删除/撤回的数据使用
deleted_at软删除。 - 审计和 AI Trace 使用 append-only 模型,不允许物理更新业务含义。
- 文件内容不进入数据库,只存对象存储元数据、hash、size、mime、object_key。
- 高频时间序列表按月分区。
- 搜索字段使用 PostgreSQL FTS,向量字段使用 pgvector。
- 数据库迁移必须可以从空库一键执行。
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
用于企业、部门、用户、角色。
核心表:
tenantsorganizationsdepartmentsuserstenant_membershipsrolesrole_bindingspermission_policies
关键字段:
tenant_id
organization_id
department_id
user_id
role_code
status
created_at
updated_at
deleted_at
关键约束:
users:(tenant_id, email)uniquedepartments:(tenant_id, parent_department_id, name)indexrole_bindings:(tenant_id, user_id, role_code, scope_type, scope_id)unique
6.2 contract schema
用于合同主数据和合同房间。
核心表:
contractscontract_partiescontract_versionscontract_sectionscontract_commentscontract_status_eventscontract_obligationscontract_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_instancesapproval_stepsapproval_actionsapproval_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
文件仓库只存元数据,不存文件正文。
核心表:
filesfile_linksfile_versionsfile_text_extractionsfile_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_eventsaudit_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_configsai_provider_config_versionsai_model_routesai_runsai_run_stepsai_run_costsai_quality_labelsai_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_templatesprompt_template_versionsprompt_releasesprompt_change_audits
原则:
- Prompt 内容按版本不可变。
- 发布记录独立于草稿记录。
- AI Run 必须记录命中的 prompt name + version。
7.3 knowledge schema
核心表:
knowledge_basesknowledge_documentsknowledge_document_versionsknowledge_import_batchesknowledge_chunksknowledge_chunk_embeddingsretrieval_tracesretrieval_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_eventsappend-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:
- 新增
yuqei-infra/compose中的双数据库配置:yuqei_contractyuqei_ai
- 新增 Contract API 最终 baseline migration 草案。
- 新增 AI Platform 最终 baseline migration 草案。
- 把当前 MVP migration 标记为重构前临时迁移,不继续作为最终生产基线。
- 写 Postgres 集成测试策略,减少对 SQLite 的关键路径依赖。
这一步完成后,V2 后面的合同主流程、AI 中台、知识库、审计和权限都会建立在干净数据库地基上,不会把 V1 的历史包袱带进新版本。