后端重写提交
This commit is contained in:
306
docs/technical/M4_MODULE_AI_AXUM_FACADE_DESIGN_2026-04-22.md
Normal file
306
docs/technical/M4_MODULE_AI_AXUM_FACADE_DESIGN_2026-04-22.md
Normal file
@@ -0,0 +1,306 @@
|
||||
# M4 module-ai Axum facade 设计(2026-04-22)
|
||||
|
||||
更新时间:`2026-04-22`
|
||||
|
||||
## 0. 文档目标
|
||||
|
||||
本文件只冻结一件事:
|
||||
|
||||
**把已经在 `spacetime-module` 落地的 `module-ai` 任务真相表与最小 procedure / reducer,继续向上接到 `shared-contracts`、`spacetime-client` 与 `api-server`,形成可由 HTTP 直接调用的 AI task mutation facade。**
|
||||
|
||||
本轮只做最小同步 mutation 链,不扩到 SSE、真实模型供应商请求或前端订阅。
|
||||
|
||||
---
|
||||
|
||||
## 1. 本轮要解决的问题
|
||||
|
||||
当前仓库已经具备:
|
||||
|
||||
1. `module-ai`
|
||||
- 统一 `AiTaskKind / AiTaskStageKind / AiResultReferenceKind`
|
||||
- 统一任务、阶段、文本片段、结果引用领域模型
|
||||
2. `spacetime-module`
|
||||
- `ai_task / ai_task_stage / ai_text_chunk / ai_result_reference`
|
||||
- `create / append / complete / attach / fail / cancel` 最小 procedure
|
||||
- `start_ai_task / start_ai_task_stage` reducer
|
||||
3. `spacetime-client`
|
||||
- 已生成 AI 相关 Rust bindings
|
||||
|
||||
但当前仍缺三层:
|
||||
|
||||
1. `shared-contracts` 还没有 AI task HTTP DTO
|
||||
2. `spacetime-client` 还没有 AI facade 方法与 record 映射
|
||||
3. `api-server` 还没有 `/api/ai/tasks*` 路由
|
||||
|
||||
因此本轮只补下面三层:
|
||||
|
||||
1. `shared-contracts` AI DTO
|
||||
2. `spacetime-client` AI facade
|
||||
3. `api-server` AI tasks HTTP route
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前明确不做的事
|
||||
|
||||
本轮明确不做:
|
||||
|
||||
1. 不接入真实 `platform-llm` 流式回调
|
||||
2. 不提供 SSE 增量推送接口
|
||||
3. 不增加 AI task 查询 / 订阅 projection
|
||||
4. 不把 story / npc / quest / custom-world 旧入口自动迁到这组新接口
|
||||
5. 不修改 `spacetime-client/src/module_bindings/*` 生成文件
|
||||
|
||||
原因很直接:
|
||||
|
||||
1. 当前先把 AI task mutation 的最小 HTTP contract 固定下来
|
||||
2. SSE 与查询态必须等待后续订阅策略或 query procedure 冻结
|
||||
3. 业务编排入口切换应该在上层模块各自评估,不在本轮提前硬迁
|
||||
|
||||
---
|
||||
|
||||
## 3. 路由冻结
|
||||
|
||||
本轮首版新增以下路由:
|
||||
|
||||
1. `POST /api/ai/tasks`
|
||||
2. `POST /api/ai/tasks/{taskId}/start`
|
||||
3. `POST /api/ai/tasks/{taskId}/stages/{stageKind}/start`
|
||||
4. `POST /api/ai/tasks/{taskId}/chunks`
|
||||
5. `POST /api/ai/tasks/{taskId}/stages/{stageKind}/complete`
|
||||
6. `POST /api/ai/tasks/{taskId}/references`
|
||||
7. `POST /api/ai/tasks/{taskId}/complete`
|
||||
8. `POST /api/ai/tasks/{taskId}/fail`
|
||||
9. `POST /api/ai/tasks/{taskId}/cancel`
|
||||
|
||||
### 3.1 同步返回路由
|
||||
|
||||
当前下列路由走 `procedure`,成功时同步返回 `aiTask` 快照:
|
||||
|
||||
1. `POST /api/ai/tasks`
|
||||
2. `POST /api/ai/tasks/{taskId}/chunks`
|
||||
3. `POST /api/ai/tasks/{taskId}/stages/{stageKind}/complete`
|
||||
4. `POST /api/ai/tasks/{taskId}/references`
|
||||
5. `POST /api/ai/tasks/{taskId}/complete`
|
||||
6. `POST /api/ai/tasks/{taskId}/fail`
|
||||
7. `POST /api/ai/tasks/{taskId}/cancel`
|
||||
|
||||
其中:
|
||||
|
||||
1. `chunks` 额外返回 `aiTextChunk`
|
||||
2. 其他 mutation 当前只返回 `aiTask`
|
||||
|
||||
### 3.2 Accepted 路由
|
||||
|
||||
当前下列路由只接 `reducer`,不会同步返回快照:
|
||||
|
||||
1. `POST /api/ai/tasks/{taskId}/start`
|
||||
2. `POST /api/ai/tasks/{taskId}/stages/{stageKind}/start`
|
||||
|
||||
因此本轮明确冻结为:
|
||||
|
||||
1. HTTP 成功状态码返回 `202 Accepted`
|
||||
2. body 只返回:
|
||||
- `accepted`
|
||||
- `taskId`
|
||||
- `action`
|
||||
- `stageKind`(仅 stage start)
|
||||
3. 不伪装成“已经拿到最新任务快照”
|
||||
|
||||
后续如果要让这两条路由也同步返回快照,应先在 `spacetime-module` 增加对应 procedure。
|
||||
|
||||
---
|
||||
|
||||
## 4. 请求与响应 DTO 冻结
|
||||
|
||||
### 4.1 创建任务请求
|
||||
|
||||
`POST /api/ai/tasks` 请求体冻结为:
|
||||
|
||||
1. `taskKind`
|
||||
2. `requestLabel`
|
||||
3. `sourceModule`
|
||||
4. `sourceEntityId`
|
||||
5. `requestPayloadJson`
|
||||
6. `stageKinds`
|
||||
|
||||
其中:
|
||||
|
||||
1. `taskId` 不接受外部写入,由 Axum 使用 `generate_ai_task_id(nowMicros)` 生成
|
||||
2. `ownerUserId` 不接受外部写入,必须取自 Bearer token
|
||||
3. `stageKinds` 为空时,由 `module-ai` 根据 `taskKind.default_stage_blueprints()` 自动补齐默认阶段蓝图
|
||||
|
||||
### 4.2 追加文本片段请求
|
||||
|
||||
`POST /api/ai/tasks/{taskId}/chunks` 请求体冻结为:
|
||||
|
||||
1. `stageKind`
|
||||
2. `sequence`
|
||||
3. `deltaText`
|
||||
|
||||
### 4.3 完成阶段请求
|
||||
|
||||
`POST /api/ai/tasks/{taskId}/stages/{stageKind}/complete` 请求体冻结为:
|
||||
|
||||
1. `textOutput`
|
||||
2. `structuredPayloadJson`
|
||||
3. `warningMessages`
|
||||
|
||||
### 4.4 绑定结果引用请求
|
||||
|
||||
`POST /api/ai/tasks/{taskId}/references` 请求体冻结为:
|
||||
|
||||
1. `referenceKind`
|
||||
2. `referenceId`
|
||||
3. `label`
|
||||
|
||||
### 4.5 失败请求
|
||||
|
||||
`POST /api/ai/tasks/{taskId}/fail` 请求体冻结为:
|
||||
|
||||
1. `failureMessage`
|
||||
|
||||
### 4.6 成功响应
|
||||
|
||||
本轮统一返回以下 payload:
|
||||
|
||||
1. `AiTaskPayload`
|
||||
2. `AiTaskStagePayload`
|
||||
3. `AiResultReferencePayload`
|
||||
4. `AiTextChunkPayload`
|
||||
5. `AiTaskMutationResponse`
|
||||
6. `AiTaskAcceptedResponse`
|
||||
|
||||
时间字段继续统一为 RFC3339 字符串。
|
||||
|
||||
---
|
||||
|
||||
## 5. `spacetime-client` 冻结口径
|
||||
|
||||
本轮新增以下 facade:
|
||||
|
||||
1. `create_ai_task`
|
||||
2. `start_ai_task`
|
||||
3. `start_ai_task_stage`
|
||||
4. `append_ai_text_chunk`
|
||||
5. `complete_ai_stage`
|
||||
6. `attach_ai_result_reference`
|
||||
7. `complete_ai_task`
|
||||
8. `fail_ai_task`
|
||||
9. `cancel_ai_task`
|
||||
|
||||
### 5.1 输入边界
|
||||
|
||||
1. procedure 输入直接复用 `module-ai` 领域输入结构
|
||||
2. `start_ai_task` 与 `start_ai_task_stage` 直接复用 reducer 输入结构
|
||||
3. 不让 `api-server` 直接依赖 generated binding 类型
|
||||
|
||||
### 5.2 输出边界
|
||||
|
||||
`spacetime-client` 新增下列 record,供 `api-server` 直接消费:
|
||||
|
||||
1. `AiTaskRecord`
|
||||
2. `AiTaskStageRecord`
|
||||
3. `AiTextChunkRecord`
|
||||
4. `AiResultReferenceRecord`
|
||||
5. `AiTaskMutationRecord`
|
||||
|
||||
字符串字段规范:
|
||||
|
||||
1. `taskKind` 使用:
|
||||
- `story_generation`
|
||||
- `character_chat`
|
||||
- `npc_chat`
|
||||
- `custom_world_generation`
|
||||
- `quest_intent`
|
||||
- `runtime_item_intent`
|
||||
2. `stageKind` 使用 `module-ai::AiTaskStageKind::as_str()`
|
||||
3. `status` 使用 snake_case
|
||||
4. `referenceKind` 使用 snake_case
|
||||
|
||||
### 5.3 错误映射
|
||||
|
||||
AI facade 在 `spacetime-client` 内部按以下规则区分:
|
||||
|
||||
1. procedure / reducer 返回的业务拒绝
|
||||
- 映射为 `SpacetimeClientError::Runtime`
|
||||
2. SDK 调用、连接、超时、意外缺字段
|
||||
- 映射为 `Build / Procedure / ConnectDropped / Timeout`
|
||||
|
||||
这样 `api-server` 才能稳定把业务错误映射成 `400`。
|
||||
|
||||
---
|
||||
|
||||
## 6. `api-server` 冻结口径
|
||||
|
||||
### 6.1 鉴权与身份
|
||||
|
||||
所有 `/api/ai/tasks*` 路由继续统一挂 Bearer 鉴权。
|
||||
|
||||
其中:
|
||||
|
||||
1. `ownerUserId` 必须来自 `AuthenticatedAccessToken.claims().user_id()`
|
||||
2. 不接受前端自行写入任务所有者
|
||||
|
||||
### 6.2 时间与 ID
|
||||
|
||||
以下字段不接受外部写入:
|
||||
|
||||
1. `taskId`
|
||||
2. `createdAtMicros`
|
||||
3. `startedAtMicros`
|
||||
4. `completedAtMicros`
|
||||
|
||||
统一由 Axum 在请求进入时生成。
|
||||
|
||||
### 6.3 字段解析
|
||||
|
||||
`api-server` 负责把 HTTP 字符串解析为领域枚举:
|
||||
|
||||
1. `taskKind`
|
||||
2. `stageKind`
|
||||
3. `referenceKind`
|
||||
|
||||
解析失败统一返回 `400`,`details.provider` 分别写:
|
||||
|
||||
1. `ai-task`
|
||||
2. `ai-task-stage`
|
||||
3. `ai-task-reference`
|
||||
|
||||
---
|
||||
|
||||
## 7. 错误映射
|
||||
|
||||
本轮 AI facade 的错误策略冻结如下:
|
||||
|
||||
1. 请求 JSON 非法、路径字段非法、枚举解析失败:`400`
|
||||
2. `SpacetimeClientError::Runtime(_)`:`400`
|
||||
3. 其他 `SpacetimeClientError`:`502`
|
||||
|
||||
`details.provider` 统一写:
|
||||
|
||||
1. 路由入参准备错误:`ai-task`
|
||||
2. SpacetimeDB 上游错误:`spacetimedb`
|
||||
|
||||
---
|
||||
|
||||
## 8. 本轮验收口径
|
||||
|
||||
满足以下条件,视为本轮 facade 基线完成:
|
||||
|
||||
1. `shared-contracts` 已新增 `ai.rs`
|
||||
2. `spacetime-client` 已新增 AI facade 方法与 record 映射
|
||||
3. `api-server` 已新增 `ai_tasks.rs`
|
||||
4. `/api/ai/tasks*` 路由已注册并挂 Bearer 鉴权
|
||||
5. `cargo fmt -p shared-contracts -p spacetime-client -p api-server` 通过
|
||||
6. `cargo check -p shared-contracts -p spacetime-client -p api-server` 通过
|
||||
|
||||
---
|
||||
|
||||
## 9. 下一步建议
|
||||
|
||||
本轮完成后,后续最稳的顺序是:
|
||||
|
||||
1. 为 `start_ai_task / start_ai_task_stage` 增加同步 procedure
|
||||
2. 增加 AI task 查询态或订阅 projection
|
||||
3. 再把 `platform-llm` 流式回调真正接到 `append_ai_text_chunk / complete_ai_stage / fail_ai_task`
|
||||
4. 最后再把 story / npc / custom-world / quest / runtime-item 的 AI 编排主链逐步切到这组新接口
|
||||
Reference in New Issue
Block a user