mirror of
https://github.com/aiclientproxy/proxycast.git
synced 2026-09-24 23:10:56 +08:00
8.4 KiB
8.4 KiB
A2UI 表单数据持久化设计
问题背景
当前 A2UI 表单数据只存在于前端内存中,页面刷新或切换话题后会丢失。用户填写的表单数据需要持久化到数据库,以便重新进入时能够恢复。
数据分析
需要持久化的数据
-
A2UI 响应结构 (
A2UIResponse)id: 响应 IDcomponents: 组件列表(包含表单字段定义)root: 根组件 IDdata: 初始数据模型submitAction: 提交动作配置
-
用户填写的表单数据 (
A2UIFormData)- 键值对形式,key 是组件 ID,value 是用户输入的值
- 例如:
{ "scene": "我和同事说", "feeling": "对 go 很陌生" }
-
表单状态
submitted: 是否已提交submittedAt: 提交时间submittedData: 提交时的数据快照
设计方案
方案 A:扩展 agent_messages 表(推荐)
在现有 agent_messages 表中添加字段存储 A2UI 相关数据:
-- 添加 A2UI 相关字段
ALTER TABLE agent_messages ADD COLUMN a2ui_response_json TEXT;
ALTER TABLE agent_messages ADD COLUMN a2ui_form_data_json TEXT;
ALTER TABLE agent_messages ADD COLUMN a2ui_submitted INTEGER DEFAULT 0;
ALTER TABLE agent_messages ADD COLUMN a2ui_submitted_at TEXT;
优点:
- 数据与消息紧密关联,查询简单
- 不需要额外的表和外键
- 迁移简单
缺点:
- 消息表字段增多
- 如果一条消息有多个 A2UI 表单,需要用 JSON 数组存储
方案 B:独立的 A2UI 表单表
创建独立的表存储 A2UI 表单数据:
CREATE TABLE IF NOT EXISTS a2ui_forms (
id TEXT PRIMARY KEY,
message_id INTEGER NOT NULL,
session_id TEXT NOT NULL,
a2ui_response_json TEXT NOT NULL,
form_data_json TEXT DEFAULT '{}',
submitted INTEGER DEFAULT 0,
submitted_at TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (message_id) REFERENCES agent_messages(id) ON DELETE CASCADE,
FOREIGN KEY (session_id) REFERENCES agent_sessions(id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_a2ui_forms_message ON a2ui_forms(message_id);
CREATE INDEX IF NOT EXISTS idx_a2ui_forms_session ON a2ui_forms(session_id);
优点:
- 数据结构清晰
- 支持一条消息多个表单
- 便于单独查询和管理表单数据
缺点:
- 需要额外的表和外键
- 查询时需要 JOIN
推荐方案:方案 B
考虑到:
- 一条 AI 消息可能包含多个 A2UI 表单
- 表单数据需要独立更新(用户填写时实时保存)
- 未来可能需要表单历史版本、表单模板等功能
数据流设计
┌─────────────────────────────────────────────────────────────────┐
│ 前端 (React) │
├─────────────────────────────────────────────────────────────────┤
│ StreamingRenderer │
│ │ │
│ ▼ │
│ A2UIRenderer ──────► onFormChange() ──────► 防抖保存 │
│ │ │
│ ▼ │
│ onSubmit() ──────────────────────────────► 提交表单 │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Tauri Commands │
├─────────────────────────────────────────────────────────────────┤
│ save_a2ui_form_data(form_id, form_data) │
│ submit_a2ui_form(form_id, form_data) │
│ get_a2ui_forms_by_session(session_id) │
│ get_a2ui_form_by_message(message_id) │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Rust Backend │
├─────────────────────────────────────────────────────────────────┤
│ A2UIFormService │
│ - save_form_data() │
│ - submit_form() │
│ - get_forms_by_session() │
│ - get_form_by_message() │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ SQLite Database │
├─────────────────────────────────────────────────────────────────┤
│ a2ui_forms 表 │
└─────────────────────────────────────────────────────────────────┘
前端改动
-
A2UIRenderer 组件
- 添加
formIdprop(从后端获取或生成) - 添加
initialFormDataprop(从后端加载) - 添加
onFormChange回调(防抖保存)
- 添加
-
useAgentChatUnified / useAsterAgentChat Hook
switchTopic时加载该会话的所有 A2UI 表单数据- 将表单数据与消息关联
-
StreamingRenderer 组件
- 传递表单数据给 A2UIRenderer
后端改动
-
数据库 Schema
- 添加
a2ui_forms表
- 添加
-
Tauri Commands
save_a2ui_form_data: 保存表单数据(防抖调用)submit_a2ui_form: 提交表单get_a2ui_forms_by_session: 获取会话的所有表单create_a2ui_form: 创建新表单记录
-
消息保存逻辑
- 保存 AI 消息时,解析 A2UI 内容并创建表单记录
实现步骤
Phase 1: 数据库层
- 添加
a2ui_forms表到 schema.rs - 创建 A2UIFormDao
Phase 2: 后端服务
- 创建 A2UIFormService
- 添加 Tauri Commands
Phase 3: 前端集成
- 添加 API 调用函数
- 修改 A2UIRenderer 支持数据持久化
- 修改 useAgentChatUnified / useAsterAgentChat 加载表单数据
Phase 4: 测试和优化
- 测试表单数据保存和恢复
- 优化防抖保存策略
- 处理边界情况(网络错误、并发等)
注意事项
- 防抖保存:用户输入时不要每次都保存,使用 500ms 防抖
- 乐观更新:先更新 UI,后台异步保存
- 错误处理:保存失败时提示用户,但不阻塞操作
- 数据清理:删除会话时级联删除表单数据