feat(db): ai_messages 表 + 迁移——作者↔AI 往复聊天记录 append-only 侧表(AC-1)

计划 §2 canonical DDL:AiMessage(UuidPk, CreatedAt, Base),project_id(FK CASCADE)/
chapter_no(nullable=项目级)/thread_id/seq(repo 批内定序)/kind/tool_key/role(CHECK)/
content/meta(JSONB '{}'),append-only 无 updated_at。复合索引 (project_id,chapter_no,
created_at DESC) 手写 sa.text DESC + FK 单列索引。迁移 ceabd3c2c00d(down=f6a7b8c9d0e1),
autogenerate 仅捕获 ai_messages 无杂散漂移。T0.2 后第一张新业务表;旁路侧记录非手稿真源,
assemble()/agent 永不读(守 #1/#3/#6)。契约 C2 扩已「稳定」。
This commit is contained in:
Yaojia Wang
2026-07-09 16:39:24 +02:00
parent 9da938d39b
commit 1a188e6e5b
4 changed files with 118 additions and 1 deletions

View File

@@ -80,6 +80,23 @@
- **迁移**`packages/db/migrations/versions/1f011c42bd4d_provider_credentials_oauth_columns.py`down_revision=`220ca2e3d53f`。up: add_column auth_type/oauth_enc + alter_column api_key_enc→nullabledown 逆向。`alembic check` 无漂移。
- **⚠️ K1.3 @backend 必读**`api_key_enc` 现为 `bytes | None``apps/api/ww_api/services/credentials.py``StoredCredential.api_key_enc: bytes`dataclass 字段 line 28与 store 的 `r.api_key_enc` 赋值line 75/97mypy 报 `arg-type``bytes | None``bytes`)。**K1.1 不动 @backend 文件(目录所有权)**K1.3 接 OAuth store 时须把 `StoredCredential.api_key_enc` 改成 `bytes | None`(并加 `auth_type`/`oauth_enc` 字段/读写路径),届时 mypy 即绿。当前全仓 mypy 唯余此 2 处错(都在 credentials.py其余门禁绿ruff/format/alembic check/pytest 400 passed
### C2 扩展AC-1, 2026-07-09· `ai_messages` 表——作者↔AI 往复聊天记录 append-only 侧表 owner @db 状态: 稳定
- 背景:`docs/design/ai-chat-history-plan.md §2`。持久化 refine/rewrite/clarify/continue/generator 五类往复交换供每章「AI 对话」抽屉重放。**T0.2 MVP schema 后第一张新业务表**(对话历史是 18 张表都无的能力,正交于 review/job/手稿,塞进 `jobs.result`/`chapter_reviews` 会污染其真源语义)。消费方:`@backend`(AC-2 repo+router据本条建勿自拟 DDL)、`@frontend`(AC-3 经 gen:api 消费码生成名)。
- **侧记录语义(守 #1/#3/#6AC-2/AC-3 必读)**`ai_messages` 是「说了什么」的真源,**不是手稿真源**。`assemble()` 与任何 agent/orchestrator 节点**永不读它**——不喂 prompt、不改正文、无记忆注入(#6)/裁决(#3/#4)权威。**append-only**:无 `updated_at`、无 PATCH/PUT、v1 不删。
- **模型(`packages/db/ww_db/models.py` `AiMessage``UuidPk, CreatedAt, Base`**——列与类型snake_case即 OpenAPI/码生成表面):
- `id` UuidPk / `created_at` TIMESTAMPTZ(`func.now()`)——来自 mixin。
- `project_id: uuid.UUID` FK `projects.id ON DELETE CASCADE`, NOT NULL, `index=True`(→ `ix_ai_messages_project_id`)。
- `chapter_no: int | None` `Integer` **nullable****NULL = 项目级/工具箱生成**;有意偏离 `chapter_reviews` 等 bare-int 的 NOT NULL 先例)。**无 FK**:改章号/删章会孤儿化,同 `chapter_reviews`**属设计,勿加 cascade「修」**。
- `thread_id: uuid.UUID` `Uuid` NOT NULL客户端每次开面板 `crypto.randomUUID()` 生成,归组一次多轮交换;无 threads 表)。
- `seq: int` `Integer` NOT NULL**repo 赋值 = append 批内 0 基位置**(无 Identity/无客户端值/无 server_default/无 UNIQUE。**Postgres `now()` 是事务起始时刻→同批共享 `created_at`,靠 `seq` 批内定序**;跨批 `created_at` 不同自然定序。定序键统一 `(created_at, seq)`——DB list 与前端展示都不许单靠 `created_at`
- `kind: str` `Text` NOT NULL**无 CHECK**(同 `jobs.kind`/`characters.role` 自由 Text边界由 Pydantic `Literal["refine","rewrite","clarify","continue","generator"]` 校验;加新 kind 零迁移)。
- `tool_key: str | None` `Text`continue→`"continue"`generator→其 keyrefine/rewrite/clarify→NULL`prompt_templates.tool_key` 无 FK/CHECK
- `role: str` `Text` NOT NULL + `CheckConstraint("role IN ('author','ai')", name="ck_ai_messages_role")`二元封闭枚举CHECK 兜底防御)。
- `content: str` `Text` NOT NULL人读 bubble 全文clarify ai 行存问题文本,结构化选项进 meta被拒候选无 chapters 行,须存全文不截断)。
- `meta: dict[str, Any]` `JSONB` NOT NULL `server_default '{}'`per-kind 形状见 §3.4AC-2 登记 C3 时随端点定;长文进 content 不进 meta
- **索引**(1) `ix_ai_messages_project_id`FK 单列,`index=True`)。(2) `ix_ai_messages_project_chapter = (project_id, chapter_no, created_at DESC)`——**迁移里手写 `sa.text("created_at DESC")`**autogenerate 无法可靠还原 DESC沿用 `chapter_reviews` 证明可过 `alembic check` 的形态)。服务抽屉 union 查询 + newest-first。**无 UNIQUE**。
- **迁移**`packages/db/migrations/versions/ceabd3c2c00d_add_ai_messages_table_chat_history.py`**revision=`ceabd3c2c00d`, down_revision=`f6a7b8c9d0e1`**)。仅建 `ai_messages` + 两索引 + role CHECK + FK CASCADE无其它漂移。DoD 全绿:`alembic upgrade head`**`alembic check` 无漂移** → **downgrade→upgrade 往复干净** → ruff/format/mypy(233 files)/pytest(**971 passed**) 绿。
## C3 · API / OpenAPI 端点 owner @backend 状态: 稳定M1 端点全落T1.7 settings + T1.4 projects/chapters, 2026-06-18
- 来源:`ARCHITECTURE.md §7.2` 端点清单(章节端点统一 `/projects/:id/chapters/:no/...`;含 `PUT .../draft`(自动保存)、`/refine``/jobs/:id``/reviews`)。
- 消费方:`@frontend`(经 OpenAPI→TS 客户端)。**改端点/字段 → 前端必须 `pnpm gen:api` 重生成客户端。**