feat: 优化 Agent 运行时与聊天设置体验

This commit is contained in:
qzl
2026-03-16 18:32:09 +08:00
parent 3f79cf0df7
commit 5a34616287
41 changed files with 2603 additions and 1263 deletions
@@ -0,0 +1,86 @@
# 账户页与主页工具渲染统一表面语言 Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 统一 `我的账户` 页面与主页 `ui_schema` 渲染的视觉语言,修正 STEP 协议映射并将等待文案从“正在思考”切换为协议阶段语义。
**Architecture:** 设置域继续复用 `AccountSurfaceScaffold + AccountSectionCard`;聊天域在 `UiSchemaRenderer` 中重做 surface 层级与按钮/徽章/KV 呈现,保持协议字段不变。将 stage 映射改为 `router/worker` 协议值,在 UI 显示为“意图识别中/任务执行中/任务处理中”。
**Tech Stack:** Flutter, flutter_bloc, design tokens (`AppColors/AppSpacing/AppRadius`), AG-UI SSE protocol mapping
---
### Task 1: 先补失败测试(Stage 文案与 UI Schema 呈现)
**Files:**
- Create: `apps/test/features/chat/presentation/agent_stage_mapping_test.dart`
- Modify: `apps/test/features/chat/ui_schema_renderer_test.dart`
**Step 1: 写失败测试**
- 新增 stage 映射测试,断言 `router -> intentLabel``worker -> executionLabel`、未知 -> processingLabel
- 扩展 `ui_schema_renderer_test.dart`,断言 card surface、按钮文案、badge 仍可渲染
**Step 2: 跑测试确认失败**
Run: `flutter test test/features/chat/presentation/agent_stage_mapping_test.dart test/features/chat/ui_schema_renderer_test.dart`
Expected: FAIL(映射函数/新视觉结构尚未实现)
**Step 3: 最小实现使测试通过**
- 新增 stage 映射文件并接入 chat/home
- 重构 `UiSchemaRenderer` 的 surface 风格并保持现有 schema 兼容
**Step 4: 再跑测试确认通过**
Run: `flutter test test/features/chat/presentation/agent_stage_mapping_test.dart test/features/chat/ui_schema_renderer_test.dart`
Expected: PASS
### Task 2: 重构我的账户页面为统一表面语言
**Files:**
- Modify: `apps/lib/features/settings/ui/screens/account_screen.dart`
**Step 1: 写失败测试(轻量)**
- 可选新增页面结构测试,断言标题/分组/退出按钮存在
**Step 2: 运行测试确认失败(若新增测试)**
Run: `flutter test test/features/settings/ui/screens/...`
Expected: FAIL
**Step 3: 最小实现**
-`AccountSurfaceScaffold` 承载页面
-`AccountSectionCard` 承载账户菜单与安全操作分组
- 退出登录按钮改为统一 token 风格
**Step 4: 测试通过**
Run: `flutter test test/features/settings/ui/screens/...`
Expected: PASS
### Task 3: 协议对齐与主页等待文案修正
**Files:**
- Modify: `apps/lib/features/chat/presentation/bloc/chat_bloc.dart`
- Modify: `apps/lib/features/home/ui/screens/home_screen.dart`
- Create: `apps/lib/features/chat/presentation/bloc/agent_stage.dart`
**Step 1: 保持协议语义**
- 将 step 映射改为文档约定:`router``worker`
- 显示文案:`意图识别中``任务执行中``任务处理中`
**Step 2: 运行相关测试**
Run: `flutter test test/features/chat/presentation/agent_stage_mapping_test.dart test/features/home/ui/widgets/home_screen_layout_test.dart`
Expected: PASS
### Task 4: 全量验证
**Step 1: 静态检查**
Run: `flutter analyze`
Expected: 无新增错误
**Step 2: 回归测试**
Run: `flutter test`
Expected: PASS
@@ -0,0 +1,99 @@
# 设置-账户子页面视觉重构设计(编辑资料 / 修改密码)
## 背景与目标
当前 `编辑资料``修改密码` 子页面存在明显的“文档页/默认表单堆叠”观感,未满足 `apps/rules/visual_design_language.md` 要求的“高级、柔和、分层、助手产品感”。
本次设计目标:
- 延续项目现有蓝灰体系,不改变品牌语气
- 建立清晰的表面层级:背景层 -> 主内容层 -> 次级分组层 -> 交互强调层
-`修改密码` 对齐 `忘记密码` 的设计语言(分段、状态提示、密码输入体验),但保持“设置域”语义
- 严格使用 `AppColors` / `AppSpacing` / `AppRadius`,移除新增硬编码视觉值
## 约束来源
- `apps/AGENTS.md`
- `apps/rules/visual_design_language.md`
- 现有共享组件体系(`AppButton``AppBanner``FixedLengthCodeInput`、Toast 系统)
## 方案选择
已确认采用方案 B:设置域统一壳层。
方案要点:
- 在 settings feature 内建立可复用的页面壳与分组卡片语义
- 两个子页面共享同一结构语言与间距节奏
- `修改密码` 采用步骤型结构(Step 1 验证邮箱 / Step 2 设置新密码)
## 信息架构
### 编辑资料
- 页面头部:返回 + 标题 + 简短辅助说明
- 主内容卡:
- 资料概览组(头像占位、当前账号信息)
- 基础信息组(用户名输入)
- 个人简介组(多行输入 + 字数)
- 底部强调操作区:`保存修改` 按钮(仅在有变更且可提交时可用)
### 修改密码(步骤型)
- 页面头部:返回 + 标题 + 简短辅助说明
- Step 1:邮箱验证与验证码发送
- 展示当前邮箱
- 发送/重发验证码 + 倒计时
- Step 2:输入验证码并设置新密码
- 验证码输入
- 新密码、确认密码
- 状态提示(使用 `AppBanner`
- 底部强调操作区:`确认修改`
## 视觉与交互规则
- 背景:`AppColors.surfaceSecondary`
- 主卡:`AppColors.white` + `AppColors.borderSecondary` + 圆角 `AppRadius.lg/xl`
- 次级分组:`AppColors.surfaceTertiary/surfaceInfoLight`(按语义使用)+ `AppColors.borderTertiary`
- 输入聚焦态:`AppColors.blue500`
- 间距节奏:
- 页面外边距:`AppSpacing.xl`
- 组内:`AppSpacing.sm/lg`
- 组间:`AppSpacing.xxl`
- 动效策略:仅保留柔和状态反馈(按钮 loading/disabled、步骤区显隐、输入 focus),不添加噱头动画
## 组件与复用策略
- 优先复用:
- `AppButton`
- `FixedLengthCodeInput`
- `AppBanner`
- Toast 系统
- `PasswordField`(与忘记密码页统一密码输入体验)
- 在 settings 域新增可复用 UI
- `account_surface_scaffold.dart`
- `account_section_card.dart`
## 影响文件
- 修改:`apps/lib/features/settings/ui/screens/edit_profile_screen.dart`
- 修改:`apps/lib/features/settings/ui/screens/change_password_screen.dart`
- 新增:`apps/lib/features/settings/ui/widgets/account_surface_scaffold.dart`
- 新增:`apps/lib/features/settings/ui/widgets/account_section_card.dart`
- 可能补充:`apps/lib/core/theme/design_tokens.dart`(仅缺失 token 时)
## 验收标准
- 两个页面具备一致的“柔和卡片分层”结构
- 修改密码页面与忘记密码页面在设计语言上可感知一致
- 不出现“纯白文档页/后台表单页”观感
- 不新增视觉硬编码,遵守 token 体系
- 核心交互逻辑不回归(验证码、倒计时、提交、错误提示)
## 验证计划
- `flutter analyze`
- `flutter test`(至少覆盖 auth/reset-password 相关与 settings 相关用例)
- 手工验证:
- 编辑资料:无改动禁用、改动后可保存、成功后返回
- 修改密码:发送验证码、倒计时、错误提示、成功返回
@@ -0,0 +1,151 @@
# 设置账户子页面视觉重构 Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 重构设置页中的编辑资料与修改密码子页面,使其符合视觉设计语言并统一为柔和卡片分层风格。
**Architecture:** 在 settings feature 内新增复用型页面壳与分组卡组件,统一两页的表面层级、间距节奏与交互强调区;修改密码页面借鉴忘记密码页面的步骤分段与状态提示语义,但保留设置域信息架构与文案。保持现有业务逻辑不变,优先做样式与结构重构。
**Tech Stack:** Flutter, go_router, flutter_bloc, formz, design tokens (`AppColors/AppSpacing/AppRadius`), shared widgets (`AppButton`, `AppBanner`, `FixedLengthCodeInput`, Toast)
---
### Task 1: 创建设置域复用 UI 壳层组件
**Files:**
- Create: `apps/lib/features/settings/ui/widgets/account_surface_scaffold.dart`
- Create: `apps/lib/features/settings/ui/widgets/account_section_card.dart`
**Step 1: 写一个失败的基础渲染测试(可选,若项目已有 widget test 基础)**
```dart
testWidgets('AccountSectionCard renders title and child', (tester) async {
await tester.pumpWidget(...);
expect(find.text('标题'), findsOneWidget);
});
```
**Step 2: 运行测试确认失败(若执行 Step 1)**
Run: `flutter test apps/test/... -r expanded`
Expected: FAIL(新组件不存在)
**Step 3: 实现最小组件**
- `AccountSurfaceScaffold`:统一背景、头部、滚动主内容、可选底部强调区
- `AccountSectionCard`:统一分组卡片容器(标题、描述、child)
**Step 4: 运行测试确认通过(若执行 Step 1)**
Run: `flutter test apps/test/... -r expanded`
Expected: PASS
**Step 5: Commit**
```bash
git add apps/lib/features/settings/ui/widgets/account_surface_scaffold.dart apps/lib/features/settings/ui/widgets/account_section_card.dart
git commit -m "feat: add reusable account surface widgets"
```
### Task 2: 重构编辑资料页面为柔和卡片分层
**Files:**
- Modify: `apps/lib/features/settings/ui/screens/edit_profile_screen.dart`
**Step 1: 写失败测试(仅在当前测试基建允许时)**
```dart
testWidgets('Edit profile shows grouped sections and disabled save initially', (tester) async {
// verify section titles and save button disabled by default
});
```
**Step 2: 运行测试确认失败(若执行 Step 1)**
Run: `flutter test apps/test/... -r expanded`
Expected: FAIL(旧结构不满足)
**Step 3: 实现最小重构**
- 接入 `AccountSurfaceScaffold`
- 将头像区、用户名区、简介区改为分组卡语义
- 统一输入风格(token 化)
- 底部强调操作区承载 `保存修改`
**Step 4: 运行测试确认通过(若执行 Step 1)**
Run: `flutter test apps/test/... -r expanded`
Expected: PASS
**Step 5: Commit**
```bash
git add apps/lib/features/settings/ui/screens/edit_profile_screen.dart
git commit -m "feat: redesign edit profile screen with layered surfaces"
```
### Task 3: 重构修改密码页面为步骤型结构
**Files:**
- Modify: `apps/lib/features/settings/ui/screens/change_password_screen.dart`
- Reference: `apps/lib/features/auth/ui/screens/reset_password_screen.dart`
**Step 1: 写失败测试(仅在当前测试基建允许时)**
```dart
testWidgets('Change password renders step sections and submit state', (tester) async {
// verify step titles and CTA disabled before code sent
});
```
**Step 2: 运行测试确认失败(若执行 Step 1)**
Run: `flutter test apps/test/... -r expanded`
Expected: FAIL(步骤结构不存在)
**Step 3: 实现最小重构**
- 接入 `AccountSurfaceScaffold` + `AccountSectionCard`
- 按 Step 1/Step 2 分段展示(邮箱验证 -> 验证码与新密码)
- 引入 `AppBanner` 做状态提示
- 保持验证码、倒计时、提交逻辑不变
**Step 4: 运行测试确认通过(若执行 Step 1)**
Run: `flutter test apps/test/... -r expanded`
Expected: PASS
**Step 5: Commit**
```bash
git add apps/lib/features/settings/ui/screens/change_password_screen.dart
git commit -m "feat: redesign change password flow with step-based sections"
```
### Task 4: 验证与文档同步
**Files:**
- Modify (if needed): `docs/plans/2026-03-16-settings-account-subpages-design.md`
**Step 1: 运行静态检查**
Run: `flutter analyze`
Expected: PASS
**Step 2: 运行回归测试**
Run: `flutter test`
Expected: PASS
**Step 3: 手工验收**
- 编辑资料:无变更禁用、有变更可保存
- 修改密码:发码、倒计时、错误提示、成功返回
- 视觉层级:背景/主卡/分组/强调区清晰
**Step 4: Commit**
```bash
git add docs/plans/2026-03-16-settings-account-subpages-design.md
git commit -m "docs: finalize account subpages redesign validation notes"
```
+4 -1
View File
@@ -112,7 +112,10 @@ Base URL: `/api/v1/agent`
seq: number;
role: "user" | "assistant" | "tool";
content: string;
url?: string | null; // user 附件签名链接
attachments?: Array<{ // user 附件签名链接列表
mimeType: string;
url: string;
}>;
ui_schema?: object | null; // assistant/tool 的编译后 UI
timestamp: string; // ISO-8601
}>;
+20 -14
View File
@@ -163,19 +163,14 @@ interface Tool {
}
```
### Backend Processing
### Frontend Tool Channel Semantics
Backend 使用 `build_tools_prompt` 函数将 tools 转换为 prompt 格式:
- `RunAgentInput.tools` 用于前端工具通道(供前端/HITL 审批链路透传与回放)。
- 当前后端 AgentScope 执行阶段**不**使用该字段来注册或筛选后端工具。
- 后端可调用工具以 `toolkit.register_tool_function(...)` 注册结果为准(例如 `calendar_write`)。
- 因此 `tools` 可为空数组,不影响后端已注册工具的调用能力。
```
<!-- TOOLS_START -->
- get_weather: Get current weather for a location
- args_schema: {"type":"object","properties":{"location":{"type":"string","description":"City name"},"unit":{"type":"string","enum":["celsius","fahrenheit"]}},"required":["location"]}
- searchDocuments: Search for documents
- args_schema: {"type":"object","properties":{"query":{"type":"string"}},"required":["query"]}
Note: tool arguments must strictly match args_schema.
<!-- TOOLS_END -->
```
> 注意:请勿把后端已注册工具重复写入 `RunAgentInput.tools`,避免命名/参数描述冲突。
---
@@ -206,6 +201,7 @@ Backend 实现了以下验证规则:
| binary 必须是 image/* | `binary content requires image mimeType` |
| binary 必须有 url | `binary content requires url` |
| binary 不允许使用 data | `binary content data is not allowed` |
| 单条消息最多 3 张附件 | `Too many attachments` |
---
@@ -349,10 +345,15 @@ interface HistoryMessageUser {
seq: number;
role: "user";
content: string;
url: string | null; // 附件临时访问 URL
attachments: HistoryAttachment[];
timestamp: string; // ISO-8601 timestamp
}
interface HistoryAttachment {
mimeType: string;
url: string;
}
// role = "tool"
interface HistoryMessageTool {
id: string;
@@ -410,7 +411,12 @@ interface UiSchemaRenderer {
"seq": 1,
"role": "user",
"content": "帮我创建一个日程",
"url": null,
"attachments": [
{
"mimeType": "image/png",
"url": "https://project.supabase.co/storage/v1/object/sign/agent-inputs/..."
}
],
"timestamp": "2026-03-15T10:00:00Z"
},
{
@@ -446,5 +452,5 @@ interface UiSchemaRenderer {
- `UserMessage.content` 支持 string 或 array 格式,前端优先使用 array 格式以支持多模态
- binary content 的 url 必须是有效的 signed URL,由 `/api/v1/agent/attachments` 端点生成
- backend 验证通过后,会将 binary url 转换为内部存储路径
- tools 为空数组时,prompt 中不会包含工具说明
- `tools` 是前端工具通道字段;当前后端运行时不基于该字段构造后端工具 prompt
- `RunAgentInput` 同时接受 camelCase 与 snake_case 别名输入(推荐统一使用 camelCase)