Files
Genarrative/docs/technical/M4_MODULE_AI_AXUM_FACADE_DESIGN_2026-04-22.md
kdletters cbc27bad4a
Some checks failed
CI / verify (push) Has been cancelled
init with react+axum+spacetimedb
2026-04-26 18:06:23 +08:00

7.8 KiB
Raw Blame History

M4 module-ai Axum facade 设计2026-04-22

更新时间:2026-04-22

0. 文档目标

本文件只冻结一件事:

把已经在 spacetime-module 落地的 module-ai 任务真相表与最小 procedure / reducer继续向上接到 shared-contractsspacetime-clientapi-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_taskstart_ai_task_stage 直接复用 reducer 输入结构
  3. 不让 api-server 直接依赖 generated binding 类型

5.2 输出边界

spacetime-client 新增下列 recordapi-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

解析失败统一返回 400details.provider 分别写:

  1. ai-task
  2. ai-task-stage
  3. ai-task-reference

7. 错误映射

本轮 AI facade 的错误策略冻结如下:

  1. 请求 JSON 非法、路径字段非法、枚举解析失败:400
  2. SpacetimeClientError::Runtime(_)400
  3. 其他 SpacetimeClientError502

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 编排主链逐步切到这组新接口