241 lines
11 KiB
Markdown
241 lines
11 KiB
Markdown
---
|
||
reviewStatus: pending
|
||
reviewedAt: null
|
||
replacedBy: null
|
||
---
|
||
|
||
# 第一阶段工程基线数据库设计
|
||
|
||
## 1. 设计边界
|
||
|
||
本文档定义第一阶段用户资料和 Agent 运行审计数据的逻辑结构、约束、索引、迁移与回滚策略。开发和自动化测试使用 SQLite,集成与生产使用 PostgreSQL。
|
||
|
||
项目尚未确认 PostgreSQL 精确版本,因此本文档不采用版本专属字段、索引方法、表达式索引或方言 SQL。实现以 Django 6.0.8 ORM 和迁移能力表达;版本专属优化须在 PostgreSQL 版本确认后另行评审。
|
||
|
||
关联设计:
|
||
|
||
- [后端落地设计](backend-design.md)
|
||
- [前端落地设计](frontend-design.md)
|
||
- [接口与页面契约设计](interface-design.md)
|
||
|
||
## 2. 设计原则
|
||
|
||
- 用户私有数据必须具有明确的用户归属。
|
||
- 状态转换由服务层执行,数据库约束负责守住唯一性和基础合法性。
|
||
- 运行事件采用追加模式,保留审计轨迹。
|
||
- JSON 字段只保存经过脱敏和大小限制的摘要。
|
||
- 时间统一存储为支持时区的 Django 时间值,显示时转换到用户时区。
|
||
- 金额、token 和耗时使用确定类型,避免浮点误差。
|
||
- 表名、约束名和索引名在迁移中显式稳定,避免不同数据库自动命名差异影响排查。
|
||
|
||
## 3. 实体关系
|
||
|
||
```mermaid
|
||
erDiagram
|
||
AUTH_USER ||--o| USER_PROFILE : has
|
||
AUTH_USER ||--o{ AGENT_RUN : owns
|
||
AUTH_USER ||--o{ MODEL_PROVIDER_CONFIG : owns
|
||
AGENT_RUN ||--o{ AGENT_RUN_EVENT : contains
|
||
AGENT_RUN ||--o{ TOOL_CALL : contains
|
||
AGENT_RUN ||--o{ HUMAN_APPROVAL : requests
|
||
AUTH_USER ||--o{ HUMAN_APPROVAL : resolves
|
||
```
|
||
|
||
## 4. 表结构草案
|
||
|
||
### 4.1 `accounts_user_profile`
|
||
|
||
| 字段 | Django 类型建议 | 空值 | 约束与说明 |
|
||
| --- | --- | --- | --- |
|
||
| `id` | `BigAutoField` | 否 | 主键 |
|
||
| `user_id` | `OneToOneField` | 否 | 唯一,关联 Django 用户 |
|
||
| `display_name` | `CharField` | 是 | 空字符串表示未设置,长度上限实现时确认 |
|
||
| `timezone` | `CharField` | 否 | 默认 `Asia/Shanghai`,必须来自受控候选 |
|
||
| `created_at` | `DateTimeField` | 否 | 创建时写入 |
|
||
| `updated_at` | `DateTimeField` | 否 | 修改时更新 |
|
||
|
||
删除策略:用户尚无审计数据时资料可随用户删除;用户存在 Agent Run 时由运行记录的保护关系阻止用户物理删除。
|
||
|
||
### 4.2 `agent_runtime_agent_run`
|
||
|
||
| 字段组 | 字段 | 类型建议 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| 标识 | `id` | `UUIDField` | 主键,避免在页面暴露连续编号 |
|
||
| 归属 | `owner_id` | `ForeignKey(PROTECT)` | 用户私有归属 |
|
||
| 展示 | `title` | `CharField` | 简短标题 |
|
||
| 状态 | `status` | `CharField` | 使用代码枚举和数据库检查约束 |
|
||
| 并发 | `lock_version` | `PositiveBigIntegerField` | 每次状态写入递增 |
|
||
| 摘要 | `input_summary` | `JSONField` | 脱敏、限长输入摘要 |
|
||
| 摘要 | `output_summary` | `JSONField` | 可空,脱敏输出摘要 |
|
||
| 版本 | `agent_name` | `CharField` | Agent 稳定标识 |
|
||
| 版本 | `agent_version` | `CharField` | 可空 |
|
||
| 版本 | `instruction_version` | `CharField` | 可空 |
|
||
| 版本 | `toolset_version` | `CharField` | 可空 |
|
||
| 模型 | `model_name` | `CharField` | 第一阶段允许为空 |
|
||
| 追踪 | `trace_id` | `CharField` | 可空,非秘密标识 |
|
||
| 时间 | `started_at` | `DateTimeField` | 可空 |
|
||
| 时间 | `finished_at` | `DateTimeField` | 可空 |
|
||
| 耗时 | `duration_ms` | `PositiveBigIntegerField` | 可空 |
|
||
| 用量 | `input_tokens` | `PositiveBigIntegerField` | 可空 |
|
||
| 用量 | `output_tokens` | `PositiveBigIntegerField` | 可空 |
|
||
| 费用 | `estimated_cost` | `DecimalField` | 可空,精度实现前确认 |
|
||
| 失败 | `error_code` | `CharField` | 可空、稳定机器码 |
|
||
| 失败 | `error_summary` | `TextField` | 可空、脱敏且限长 |
|
||
| 审计 | `created_at` | `DateTimeField` | 否 |
|
||
| 审计 | `updated_at` | `DateTimeField` | 否 |
|
||
|
||
检查约束至少保证:
|
||
|
||
- `status` 属于已定义枚举。
|
||
- `lock_version >= 0`。
|
||
- token、耗时和费用非负。
|
||
|
||
### 4.2.1 `agent_runtime_model_provider_config`
|
||
|
||
| 字段 | 类型建议 | 说明 |
|
||
| --- | --- | --- |
|
||
| `owner_id` | `ForeignKey(PROTECT)` | 用户私有归属 |
|
||
| `provider_code` | `CharField` | 对应代码中的服务商稳定编码 |
|
||
| `encrypted_api_key` | `TextField` | Fernet 密文,禁止通过页面或 Admin 展示 |
|
||
| `key_hint` | `CharField` | 仅保存末四位提示 |
|
||
| `status`、`enabled`、`is_default` | 状态字段 | 连接状态和当前默认选择 |
|
||
| `default_model_id` | `CharField` | 用户选择或预设推荐模型 |
|
||
| `available_models` | `JSONField` | 最近一次验证取得的模型标识快照 |
|
||
| `last_verified_at`、`last_error_code` | 审计字段 | 验证时间和稳定错误码 |
|
||
|
||
唯一约束为 `(owner_id, provider_code)`。API Key 加密主密钥只从部署环境读取,不进入数据库;主密钥轮换必须先完成密文重加密,不能直接替换后重启。
|
||
|
||
跨字段状态不变量仍由服务层验证,因为 SQLite 与 PostgreSQL 对复杂检查约束和时间语义的行为需要保持一致。
|
||
|
||
### 4.3 `agent_runtime_agent_run_event`
|
||
|
||
| 字段 | 类型建议 | 说明 |
|
||
| --- | --- | --- |
|
||
| `id` | `BigAutoField` | 主键 |
|
||
| `run_id` | `ForeignKey(PROTECT)` | 关联 Run,审计事件不可级联丢失 |
|
||
| `sequence` | `PositiveBigIntegerField` | Run 内递增序号 |
|
||
| `event_type` | `CharField` | 事件枚举 |
|
||
| `summary` | `CharField` | 短摘要 |
|
||
| `payload_summary` | `JSONField` | 脱敏事件数据 |
|
||
| `occurred_at` | `DateTimeField` | 业务发生时间 |
|
||
| `created_at` | `DateTimeField` | 数据库记录时间 |
|
||
|
||
唯一约束:`(run_id, sequence)`。
|
||
|
||
索引:`(run_id, sequence)` 唯一索引已覆盖详情页时间线读取,无需重复创建同前缀普通索引。
|
||
|
||
### 4.4 `agent_runtime_tool_call`
|
||
|
||
| 字段组 | 字段 | 类型建议 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| 标识 | `id` | `BigAutoField` | 主键 |
|
||
| 归属 | `run_id` | `ForeignKey(PROTECT)` | 关联 Run |
|
||
| 调用 | `call_id` | `CharField` | Run 内调用标识 |
|
||
| 工具 | `tool_name` | `CharField` | 工具稳定名称 |
|
||
| 工具 | `tool_version` | `CharField` | 可空 |
|
||
| 幂等 | `idempotency_key` | `CharField` | 可空,作用域见约束 |
|
||
| 状态 | `status` | `CharField` | `started/succeeded/failed` |
|
||
| 摘要 | `arguments_summary` | `JSONField` | 脱敏、限长 |
|
||
| 摘要 | `result_summary` | `JSONField` | 可空、脱敏、限长 |
|
||
| 失败 | `error_code` | `CharField` | 可空 |
|
||
| 失败 | `error_summary` | `TextField` | 可空、限长 |
|
||
| 时间 | `started_at` | `DateTimeField` | 否 |
|
||
| 时间 | `finished_at` | `DateTimeField` | 可空 |
|
||
| 耗时 | `duration_ms` | `PositiveBigIntegerField` | 可空 |
|
||
| 审计 | `created_at`、`updated_at` | `DateTimeField` | 否 |
|
||
|
||
唯一约束:
|
||
|
||
- `(run_id, call_id)` 唯一。
|
||
- 非空幂等键的作用域在真实工具模型确定前采用 `(run_id, idempotency_key)`,后续如需跨 Run 幂等必须重新设计业务作用域。
|
||
|
||
### 4.5 `agent_runtime_human_approval`
|
||
|
||
| 字段 | 类型建议 | 说明 |
|
||
| --- | --- | --- |
|
||
| `id` | `UUIDField` | 主键 |
|
||
| `run_id` | `ForeignKey(PROTECT)` | 关联 Run |
|
||
| `request_key` | `CharField` | Run 内稳定请求键 |
|
||
| `approval_type` | `CharField` | 确认类型枚举 |
|
||
| `status` | `CharField` | 确认状态枚举 |
|
||
| `request_summary` | `JSONField` | 脱敏请求摘要 |
|
||
| `decision_summary` | `JSONField` | 可空、脱敏处理摘要 |
|
||
| `requested_at` | `DateTimeField` | 请求时间 |
|
||
| `resolved_at` | `DateTimeField` | 可空 |
|
||
| `resolved_by_id` | `ForeignKey(PROTECT)` | 可空,处理人 |
|
||
| `created_at`、`updated_at` | `DateTimeField` | 审计时间 |
|
||
|
||
唯一约束:`(run_id, request_key)`。
|
||
|
||
索引:`(status, requested_at)`,支持 Admin 查询待处理确认。
|
||
|
||
## 5. 主要访问模式与索引
|
||
|
||
| 访问模式 | 过滤与排序 | 索引建议 |
|
||
| --- | --- | --- |
|
||
| 当前用户 Run 列表 | `owner_id`,`created_at DESC` | `(owner_id, created_at)` |
|
||
| 当前用户按状态查询 | `owner_id`、`status`、`created_at DESC` | 第一阶段无筛选需求,暂不新增 |
|
||
| Run 事件时间线 | `run_id`、`sequence ASC` | 唯一索引 `(run_id, sequence)` |
|
||
| Run 工具调用列表 | `run_id`、`started_at ASC` | `(run_id, started_at)` |
|
||
| 待处理人工确认 | `status`、`requested_at ASC` | `(status, requested_at)` |
|
||
| trace 定位运行 | `trace_id` | 有真实查询需求后再决定唯一性和索引 |
|
||
|
||
索引以第一阶段真实页面和 Admin 查询为依据,不为未来假设查询提前增加索引。
|
||
|
||
## 6. 事务与并发
|
||
|
||
- Run 状态变更、状态事件和关联确认记录在同一事务内完成。
|
||
- 更新 Run 前锁定目标记录,并校验 `lock_version`。
|
||
- 事件序号在锁定 Run 后读取最大值并递增。
|
||
- 唯一约束捕获遗漏的并发冲突,并转换为领域冲突错误。
|
||
- 工具外部调用不持有数据库事务;开始和结束分别使用短事务。
|
||
- SQLite 不提供等价的行锁验证,PostgreSQL 集成测试必须覆盖双并发更新。
|
||
|
||
## 7. 数据生命周期
|
||
|
||
- 用户资料可修改,保留最新值即可。
|
||
- Agent Run、事件、工具调用和人工确认属于审计数据,第一阶段不提供删除入口。
|
||
- 用户存在运行记录时不得物理删除,改为停用账户。
|
||
- 第一阶段不自动清理日志或审计记录。
|
||
- 后续建立保留周期时必须区分运行摘要、完整原始证据和应用日志,不能使用同一删除策略。
|
||
|
||
## 8. 迁移顺序
|
||
|
||
1. 创建 `common` 基础抽象代码,不生成抽象表。
|
||
2. 创建 `accounts` 与 `UserProfile` 迁移。
|
||
3. 创建 `agent_runtime` 核心表和基础约束。
|
||
4. 单独迁移补充经验证的组合索引和检查约束。
|
||
5. 在空 SQLite 数据库执行完整迁移。
|
||
6. 确认 PostgreSQL 精确版本和隔离环境。
|
||
7. 在空 PostgreSQL 数据库执行完整迁移和核心约束测试。
|
||
|
||
迁移发布后不得直接修改历史迁移;修正通过新迁移完成。
|
||
|
||
## 9. 回滚策略
|
||
|
||
- 第一阶段没有存量业务数据转换,回滚以撤销未发布迁移为主。
|
||
- 已发布环境回滚前必须确认表内是否已产生审计数据。
|
||
- 存在数据时禁止直接反向删除表,应先停止写入、导出验证数据并制定显式迁移。
|
||
- 应用代码回滚必须与数据库迁移兼容,不能先删除旧代码仍会读取的字段。
|
||
|
||
## 10. 验证清单
|
||
|
||
- Django 迁移无遗漏。
|
||
- 空 SQLite 数据库全量迁移和反向迁移测试。
|
||
- 空 PostgreSQL 数据库全量迁移。
|
||
- 用户一对一资料唯一约束。
|
||
- Run 状态、非负数值和用户保护约束。
|
||
- 事件序号唯一约束。
|
||
- 工具调用编号和幂等键冲突。
|
||
- 人工确认请求键唯一约束。
|
||
- 两个并发状态更新只有一个成功。
|
||
- 主要列表查询使用预期索引;数据量不足时不声称完成性能验收。
|
||
|
||
## 11. 待确认项
|
||
|
||
- PostgreSQL 精确版本、部署方式和连接池策略。
|
||
- 摘要字段的最大序列化字节数。
|
||
- 字符字段长度、费用精度和稳定约束名称。
|
||
- trace 标识是否需要全局唯一。
|
||
- PostgreSQL 版本确认后是否需要部分索引或其他专属优化。
|