docs(engineering-baseline): 补充第一阶段落地设计

This commit is contained in:
bruce
2026-09-09 13:01:36 +08:00
parent cbc1392feb
commit 57a20ff477
4 changed files with 1148 additions and 0 deletions
@@ -0,0 +1,224 @@
---
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
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、耗时和费用非负。
跨字段状态不变量仍由服务层验证,因为 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 版本确认后是否需要部分索引或其他专属优化。