Files
eryao/docs/plans/2026-04-05-divination-history-profile-backend-source-plan.md
T

9.2 KiB
Raw Blame History

Eryao 解卦历史与个人档案后端单一数据源改造计划

日期:2026-04-05
状态:评审中(未开始编码)

1. 背景与目标

当前移动端存在两类不符合目标架构的问题:

  1. 个人档案(昵称、简介、头像)仍有前端本地状态路径,非后端权威数据源。
  2. 首页历史解卦无法稳定由后端快照直接重建结果页,前端被迫做本地兜底。

本计划目标:

  • 实现“后端为唯一数据源,前端仅缓存”。
  • DIVINATION_DERIVED 的完整结构进入消息 metadata.agent_output 并持久化。
  • 历史接口返回可被前端直接解析的结构化 assistant 输出(不再依赖 ui_schema)。
  • 个人档案全链路后端化,头像使用 avatars bucket。

非目标:

  • 本计划不直接提交代码实现。
  • 本计划不包含 UI 视觉细节改稿。

2. 现状核对(基于仓库代码)

2.1 历史接口与消息转换

  • 历史接口:GET /api/v1/agent/history,定义于 backend/src/v1/agent/router.py
  • 当前转换逻辑在 backend/src/v1/agent/utils.py
    • user 消息主要输出 contentattachments
    • assistant 消息默认走 ui_hints -> ui_schema 编译路径。
  • 历史响应结构 HistoryMessage 当前包含 ui_schema,不直接暴露结构化 agent_output

2.2 DIVINATION_DERIVED 与落库断点

  • 运行时会发出 DIVINATION_DERIVED(见 backend/src/core/agentscope/runtime/runner.py)。
  • 消息落库由 backend/src/core/agentscope/events/store.py 负责。
  • 当前 TEXT_MESSAGE_END 持久化字段包含 sign_level/summary/.../ui_hints,未包含 divination 结构。
  • 结果:历史快照难以完整重建结果页结构。

2.3 Profile 与头像

  • 后端配置已有 storage.avatar.bucket,默认 avatarsbackend/src/core/config/settings.py)。
  • 当前 v1 仅挂载 auth/agent/points 路由(backend/src/v1/router.py),尚无 profile 专用路由。

3. 核心设计决策

决策 A:把 divination_derived 放入 metadata.agent_output

  • AgentOutput 增加字段 divination_derived(强类型,禁止裸 dict)。
  • 事件落库时把 DIVINATION_DERIVED 内容并入 assistant 的 metadata.agent_output.divination_derived
  • sign_level/summary/advice/... 同时持久化,形成一条可回放的 assistant 结构化输出。

理由:

  • 最小改动复用现有消息表,不新增历史结果表即可满足回放需求。
  • 前端可直接从历史响应解析结果页,避免本地拼装。

决策 B:历史接口返回 assistant.agent_output,移除 ui_schema

  • HistoryMessage 改为:
    • user: content + attachments
    • assistant: content + agent_output
  • ui_schema 从接口协议中移除(迁移自通用模块的历史遗留,不在本项目范围)。

理由:

  • 减少中间编译层,契约更稳定、语义更清晰。
  • 前端直接消费业务数据,不依赖通用 UI 编译器。

决策 C:Profile 全后端化 + 头像对象存储

  • 新增 users/profile API,前端只保留缓存层。
  • 头像上传走预签名 URL,bucket 固定 avatars,路径按用户隔离。

4. 协议与接口计划(先文档,后实现)

4.1 新增/修改协议文档

按“协议先行”更新以下文档:

  1. docs/protocols/divination/divination-run-protocol.md
    • 增补:历史回放时 assistant agent_output.divination_derived 的字段契约。
    • 标记:ui_schema 已废弃并移除。
  2. 新增:docs/protocols/profile/profile-protocol.md
    • 定义 profile 读写与头像上传签名协议。
  3. 如涉及错误码新增,更新:
    • docs/protocols/common/http-error-codes.md

4.2 后端 API 契约(目标)

A. 历史快照(改造)

  • GET /api/v1/agent/history
  • 响应中 assistant 消息新增(或替换为)agent_output
    • sign_level
    • summary
    • conclusion
    • focus_points
    • advice
    • keywords
    • answer
    • divination_derived(完整卦象结构)

B. Profile(新增)

  • GET /api/v1/users/me/profile
  • PATCH /api/v1/users/me/profile
  • POST /api/v1/users/me/avatar/upload-url
  • (可选)GET /api/v1/users/me/avatar/signed-url

C. 头像上传约束

  • bucket 固定:config.storage.avatar.bucket
  • 路径前缀建议:avatars/{user_id}/...
  • 文件类型:image/png|image/jpeg|image/webp
  • 体积上限:config.storage.avatar.max_size_mb

5. 数据模型改造计划

5.1 Runtime 模型

  • 文件:backend/src/schemas/agent/runtime_models.py
  • 变更:AgentOutput 增加 divination_derived 字段(类型复用 schemas/domain/divination.py)。
  • 规则:保持 extra="forbid",禁止无类型漂移。

5.2 事件到落库链路

  • 文件:backend/src/core/agentscope/runtime/stage_emitter.py
    • TEXT_MESSAGE_END payload 带上 divination_derived
  • 文件:backend/src/core/agentscope/events/store.py
    • worker_output_fields 纳入 divination_derived 并写入 metadata.agent_output

5.3 历史响应转换

  • 文件:backend/src/v1/agent/utils.py
    • 删除 ui_hints -> ui_schema 编译路径。
    • assistant 消息改为抽取并返回受控 agent_output
  • 文件:backend/src/v1/agent/schemas.py
    • HistoryMessage 改字段定义(去 ui_schema,加 agent_output)。

6. 前端消费与缓存策略

6.1 历史与结果页

  • 历史列表数据源改为后端 agent/history
  • 点开历史项时:
    • 直接解析 assistant.agent_output.divination_derived + 解释文本字段。
    • 本地仅做缓存,不做真源 fallback。

6.2 Profile

  • 设置页资料读取改为 GET /users/me/profile
  • 编辑资料写入 PATCH /users/me/profile
  • 头像更新走 upload-url + 上传 + profile 更新引用路径。

6.3 点数

  • 保持后端余额接口作为权威数据源(现有已接)。
  • 前端只做短期缓存,解卦完成后强制 refresh。

7. 代码清理边界(你关心的“删除通用遗留”)

原则:先去引用,再删定义,最后删文件,避免误删。

分三步:

  1. 第一阶段(本次改造内)
    • 删除 agent/historyui_schema 的输出与依赖。
    • 删除前端对 ui_schema 的消费路径(若存在)。
  2. 第二阶段(安全清理)
    • 搜索 schemas/domainschemas/agent/ui_hints 的实际引用。
    • 对“零引用 + 非协议字段”进行清理。
  3. 第三阶段(文档与测试补齐)
    • 更新协议文档、错误码、回归测试。

备注:

  • 不建议在同一 PR 里“功能改造 + 大规模 schema 删除”,建议拆成两个 PR,降低回归风险。

8. 测试计划(必须项)

8.1 后端单元/集成

  1. TEXT_MESSAGE_END 持久化:metadata.agent_output.divination_derived 落库断言。
  2. GET /api/v1/agent/historyassistant 返回 agent_output,且不再返回 ui_schema
  3. 历史分页与 owner 校验不回退。
  4. profile API:读写、权限、字段约束、头像路径安全性。
  5. 头像签名 URLbucket/path/mime/size 约束。

8.2 前端

  1. 历史列表从后端数据渲染。
  2. 点击历史项成功进入结果页,字段一致性校验。
  3. profile 页面读写闭环(昵称/简介/头像)。
  4. 点数刷新与缓存失效策略验证。

9. 风险与回滚

主要风险:

  • 历史消息中旧数据可能没有 divination_derived,前端需兼容空值。
  • ui_schema 下线后,若有隐藏调用方会断。

回滚策略:

  • 协议层采用短期双读兼容窗口(仅过渡期):
    • 新字段优先;旧字段仅用于读,不再写。
  • 若线上异常,先回滚 history 响应变更,再保持落库新增字段不删。

10. 实施顺序(最小风险)

  1. 协议文档更新并评审通过。
  2. 后端:AgentOutput + 事件落库 + history 响应新增 agent_output(先加后切)。
  3. 前端:改消费到 agent_output,移除本地真源。
  4. 后端:移除 ui_schema 输出。
  5. profile API + 前端接入头像上传。
  6. 清理无用 schema(独立 PR)。

11. 验收标准(DoD

全部满足才算完成:

  1. 解卦后写入的 assistant 消息在 DB 中可见 metadata.agent_output.divination_derived
  2. 首页历史完全来自后端,清空本地缓存后仍可正确展示。
  3. 历史详情可完整还原结果页,不依赖 ui_schema
  4. profile 读写走后端,头像实际落 avatars bucket。
  5. 前端不再把 profile/history 作为本地权威数据源。
  6. 协议文档与实现一致,相关测试通过。

12. GSTACK REVIEW REPORT

Review Trigger Why Runs Status Findings
Eng Review /plan-eng-review 锁定架构、契约、测试闭环 1 Done 确认后端单一数据源方向;建议分阶段移除 ui_schema 并将 schema 清理拆分独立 PR
CEO Review /plan-ceo-review 范围与优先级 0
Design Review /plan-design-review UI/UX 风险 0
DX Review /plan-devex-review 开发体验风险 0

VERDICT:可以进入实现阶段,但必须先完成协议文档更新并冻结字段契约。