refactor: 移除 LiteLLM proxy 架构,后端直连 Provider API

- 移除 backend/scripts/build_litellm_proxy_config.py
- 简化 LiteLLMService,移除 run_completion_with_cost 方法
- AgentScopeRunner 改为从 LlmFactory 获取 api_base 和 api_key
- 部署配置移除 litellm/litellm-config-job 服务
- Flutter 新增 AuthBootScreen 引导页
- Android 添加通知权限 (POST_NOTIFICATIONS, RECEIVE_BOOT_COMPLETED, SCHEDULE_EXACT_ALARM)
- 优化 LocalNotificationService 调度失败 fallback
- 更新 manifest.json (version 3)
This commit is contained in:
qzl
2026-03-17 18:05:49 +08:00
parent cf56b358ad
commit 19981964fb
26 changed files with 417 additions and 1018 deletions
@@ -1,86 +0,0 @@
# 账户页与主页工具渲染统一表面语言 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
@@ -1,285 +0,0 @@
# Calendar Timezone Unification Implementation Plan
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Eliminate calendar time mismatches by enforcing one end-to-end timezone policy across App input, Agent runtime context, tool execution, and UTC database storage.
**Architecture:** Keep database schema unchanged (`start_at/end_at TIMESTAMPTZ + timezone`) and enforce strict runtime normalization. Device timezone is injected from `RunAgentInput.forwardedProps`, resolved into a single `effective_timezone`, then written explicitly into tool arguments and persisted as event timezone while timestamps are stored in UTC. Calendar read responses include deterministic event-timezone-rendered values so frontend rendering is stable and no implicit `toLocal()` conversion remains.
**Tech Stack:** FastAPI, Pydantic v2, AgentScope runtime/tooling, Flutter (Dart), PostgreSQL TIMESTAMPTZ, pytest, Flutter test.
---
## Chunk 1: Protocol and Backend Runtime Normalization
### Task 1: Freeze protocol and timezone precedence contract
**Files:**
- Modify: `docs/protocols/agent/run-agent-input.md`
- Create: `docs/protocols/calendar/timezone-policy.md`
- [ ] **Step 1: Write protocol delta checklist in docs first**
Document the exact policy:
- `event_timezone > device_timezone > profile.timezone > UTC`
- `event_timezone` must be present in final tool call
- `start_at/end_at` must be timezone-aware
- DB stores UTC timestamps and IANA timezone string
- [ ] **Step 2: Update RunAgentInput protocol with forwardedProps contract**
Add canonical payload example:
```json
{
"forwardedProps": {
"client_time": {
"device_timezone": "America/Los_Angeles",
"client_now_iso": "2026-03-16T09:12:33-07:00",
"client_epoch_ms": 1773658353000
}
}
}
```
- [ ] **Step 3: Add calendar timezone policy protocol doc**
Include:
- accepted datetime formats
- explicit error codes
- write/read response semantics
- DST handling rule
- [ ] **Step 4: Verify docs consistency**
Run: `cd backend && uv run python -m pytest tests/unit/core/agentscope/test_system_prompt.py -q`
Expected: PASS (no protocol-breaking prompt assumptions)
### Task 2: Parse forwarded device time and compute effective timezone
**Files:**
- Modify: `backend/src/core/agentscope/schemas/agui_input.py`
- Modify: `backend/src/core/agentscope/runtime/runner.py`
- Modify: `backend/src/core/agentscope/prompts/system_prompt.py`
- Test: `backend/tests/unit/core/agentscope/test_system_prompt.py`
- [ ] **Step 1: Write failing tests for effective timezone resolution**
Add tests covering:
- forwarded `device_timezone` present -> selected
- missing forwarded timezone -> fallback profile timezone
- invalid forwarded timezone -> fallback profile timezone
- [ ] **Step 2: Run tests to confirm RED**
Run: `cd backend && uv run pytest tests/unit/core/agentscope/test_system_prompt.py -k timezone -v`
Expected: FAIL on new assertions
- [ ] **Step 3: Implement minimal runtime context extraction**
Implement a typed helper in runner path to read:
- `run_input.forwarded_props.client_time.device_timezone`
- `client_now_iso`
- `client_epoch_ms`
Compute `effective_timezone` using fixed precedence and pass it into `build_system_prompt(...)`.
- [ ] **Step 4: Inject effective_timezone into ENV section**
Update `build_system_prompt` env payload to include:
- `timezone_profile`
- `timezone_device`
- `timezone_effective`
Update guidance sentence to resolve ambiguous time with `timezone_effective`.
- [ ] **Step 5: Run tests to confirm GREEN**
Run: `cd backend && uv run pytest tests/unit/core/agentscope/test_system_prompt.py -v`
Expected: PASS
### Task 3: Remove timezone ambiguity and hidden fallbacks from calendar write
**Files:**
- Modify: `backend/src/core/agentscope/tools/utils/calendar_domain.py`
- Modify: `backend/src/core/agentscope/tools/custom/calendar.py`
- Modify: `backend/src/v1/schedule_items/schemas.py`
- Modify: `backend/src/v1/schedule_items/service.py`
- Test: `backend/tests/unit/core/agentscope/test_calendar_tools.py`
- Test: `backend/tests/unit/v1/schedule_items/test_schemas.py`
- Test: `backend/tests/unit/v1/schedule_items/test_service.py`
- [ ] **Step 1: Write failing tests for forbidden naive datetime and required timezone**
Add tests for:
- naive `start_at` rejected
- missing `event_timezone` rejected in tool path
- parse failure does not fallback to `now + 1h`
- [ ] **Step 2: Run tests to confirm RED**
Run: `cd backend && uv run pytest tests/unit/core/agentscope/test_calendar_tools.py tests/unit/v1/schedule_items/test_schemas.py -v`
Expected: FAIL on new constraints
- [ ] **Step 3: Implement strict parsing and normalization**
Implementation requirements:
- `parse_iso_datetime` rejects naive input
- remove default `Asia/Shanghai` in tool
- remove fallback auto-generated start time
- validate IANA timezone and normalize `start_at/end_at` to UTC before persistence
- [ ] **Step 4: Enforce service-level invariants**
Service invariant set:
- timezone non-empty and valid IANA
- `end_at is None or end_at >= start_at`
- [ ] **Step 5: Run backend tests**
Run: `cd backend && uv run pytest tests/unit/core/agentscope/test_calendar_tools.py tests/unit/v1/schedule_items/test_schemas.py tests/unit/v1/schedule_items/test_service.py tests/integration/test_schedule_items_routes.py -v`
Expected: PASS
### Task 4: Keep DB schema, add non-breaking constraint migration only
**Files:**
- Create: `backend/alembic/versions/20260316_000x_schedule_items_time_constraints.py`
- Test: `backend/tests/integration/test_schedule_items_routes.py`
- [ ] **Step 1: Write migration test expectation first**
Add/extend integration assertion for invalid `end_at < start_at` returning 422.
- [ ] **Step 2: Run integration test to confirm RED**
Run: `cd backend && uv run pytest tests/integration/test_schedule_items_routes.py -k end_at -v`
Expected: FAIL
- [ ] **Step 3: Implement migration with CHECK only (no new columns)**
Migration includes:
- `CHECK (end_at IS NULL OR end_at >= start_at)`
- [ ] **Step 4: Run migration + integration test**
Run: `cd backend && uv run alembic upgrade head && uv run pytest tests/integration/test_schedule_items_routes.py -v`
Expected: PASS
---
## Chunk 2: Frontend Deterministic Display and Agent Input Wiring
### Task 5: Wire device timezone into RunAgentInput forwardedProps
**Files:**
- Modify: `apps/lib/features/chat/data/services/ag_ui_service.dart`
- Modify: `apps/lib/features/chat/data/models/ag_ui_event.dart` (only if serialization helper is needed)
- Test: `apps/test/features/chat/ag_ui_event_test.dart`
- [ ] **Step 1: Write failing test for forwarded client_time payload**
Assert outgoing run request contains:
- `forwardedProps.client_time.device_timezone`
- `client_now_iso`
- `client_epoch_ms`
- [ ] **Step 2: Run test to confirm RED**
Run: `cd apps && flutter test test/features/chat/ag_ui_event_test.dart`
Expected: FAIL
- [ ] **Step 3: Implement payload injection in one place**
Add a single helper to build client time context and attach it to run input requests.
- [ ] **Step 4: Run test to confirm GREEN**
Run: `cd apps && flutter test test/features/chat/ag_ui_event_test.dart`
Expected: PASS
### Task 6: Remove implicit local-time rendering and render by event timezone
**Files:**
- Modify: `apps/lib/features/calendar/data/models/schedule_item_model.dart`
- Modify: `apps/lib/features/calendar/ui/screens/calendar_event_detail_screen.dart`
- Modify: `apps/lib/features/calendar/ui/screens/calendar_dayweek_screen.dart`
- Modify: `apps/lib/features/calendar/ui/screens/calendar_month_screen.dart`
- Modify: `apps/lib/features/messages/ui/widgets/calendar_message_card.dart`
- Modify: `apps/lib/features/calendar/ui/widgets/create_event_sheet.dart`
- Test: `apps/test/features/calendar/ui/calendar_time_utils_test.dart`
- Test: `apps/test/features/calendar/ui/create_event_sheet_time_align_test.dart`
- [ ] **Step 1: Write failing tests for timezone-specific rendering**
Cover cases:
- same UTC event shows different local clock time under different `event.timezone`
- list/day/week/month are consistent for one event
- create sheet sends explicit timezone in payload
- [ ] **Step 2: Run tests to confirm RED**
Run: `cd apps && flutter test test/features/calendar/ui/calendar_time_utils_test.dart test/features/calendar/ui/create_event_sheet_time_align_test.dart`
Expected: FAIL
- [ ] **Step 3: Implement deterministic time conversion utility**
Implement one utility used by all calendar UI surfaces:
- input: UTC datetime + IANA timezone
- output: event-local datetime
Replace direct `.toLocal()` usage in calendar model/view with this utility.
- [ ] **Step 4: Enforce explicit timezone on create/update payload**
Create/update must always include `timezone` field from selected event timezone.
- [ ] **Step 5: Run Flutter tests**
Run: `cd apps && flutter test test/features/calendar/ui/calendar_time_utils_test.dart test/features/calendar/ui/create_event_sheet_time_align_test.dart`
Expected: PASS
### Task 7: End-to-end verification matrix and release checklist
**Files:**
- Modify: `docs/plans/timezone-e2e-checklist.md`
- Test: `backend/tests/integration/test_schedule_items_routes.py`
- Test: `apps/test/features/calendar/ui/create_event_sheet_time_align_test.dart`
- [ ] **Step 1: Add reproducible matrix**
Matrix axes:
- device timezone: `America/Los_Angeles`, `Asia/Shanghai`
- profile timezone: `Asia/Shanghai`, `Europe/Paris`
- explicit event timezone: `Asia/Tokyo`
- [ ] **Step 2: Run backend + frontend verification commands**
Run:
- `cd backend && uv run pytest tests/unit/core/agentscope/test_system_prompt.py tests/unit/core/agentscope/test_calendar_tools.py tests/integration/test_schedule_items_routes.py -v`
- `cd apps && flutter test test/features/chat/ag_ui_event_test.dart test/features/calendar/ui/calendar_time_utils_test.dart test/features/calendar/ui/create_event_sheet_time_align_test.dart`
Expected: all PASS
- [ ] **Step 3: Manual scenario check**
Manual script:
1. device timezone set to Los Angeles
2. profile timezone set to Shanghai
3. ask agent create "明天上午9点开会"
4. verify assistant text, tool card, DB UTC value, and calendar detail all align to chosen event timezone semantics
- [ ] **Step 4: Capture release notes**
Record:
- removed hidden timezone defaults
- deterministic precedence
- no schema expansion
---
Plan complete and saved to `docs/superpowers/plans/2026-03-16-calendar-timezone-unification.md`. Ready to execute?
@@ -1,99 +0,0 @@
# 设置-账户子页面视觉重构设计(编辑资料 / 修改密码)
## 背景与目标
当前 `编辑资料``修改密码` 子页面存在明显的“文档页/默认表单堆叠”观感,未满足 `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 相关用例)
- 手工验证:
- 编辑资料:无改动禁用、改动后可保存、成功后返回
- 修改密码:发送验证码、倒计时、错误提示、成功返回
@@ -1,151 +0,0 @@
# 设置账户子页面视觉重构 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"
```
@@ -0,0 +1,227 @@
# 自动化记忆任务设计方案(v1
## 1. 背景与目标
本方案用于落地后端自动化记忆任务:
- 提供每日/每周自动执行能力;
- 自动提取用户近期对话中的可沉淀记忆并写入 `memories`
- 支持“忘记”操作;
- 复用现有 Agent 运行链路,避免并行维护两套运行时;
- 保证可审计、可追溯,同时对用户界面隐藏自动输入提示词。
## 2. 范围与非目标
### 2.1 范围
- `automation_jobs` 增加 `config`JSONB,强约束);
- 调度器执行 `automation_jobs`,按用户本地时区计算执行时间;
- 自动任务复用现有 `router -> agent` 运行策略;
- 自动输入落库但对用户不可见;
- 工具集按任务配置分发,仅允许白名单工具;
- 正式启用 `memory_prompt`,将 `memories` 注入系统提示词。
### 2.2 非目标
- 不开放复杂 DSL 编排;
- 不开放细粒度策略配置(如 `execution_profile/history_window_days/memory_policy`);
- 不引入第二套独立 Agent 运行框架。
## 3. 关键决策
1. **调度与执行分离**
- `run_at/next_run_at/timezone` 负责“何时执行”;
- `config` 负责“如何执行”(提示词与工具集)。
2. **配置最小化**
- `automation_jobs.config` 仅保留:
- `prompt: string`
- `tools: string[]`
3. **自动输入可审计但用户不可见**
- 自动任务提示词作为“用户消息”写入 `messages`
- 在 metadata 加可见性标记并在用户历史接口中默认过滤;
- 审计路径可查询完整消息链路。
4. **时区策略**
- 调度语义采用“用户本地时区 10:00”;
- 存储执行时间统一为 UTC 的 `next_run_at`
- 调度器仅按 UTC 扫描,降低运维复杂度。
## 4. 数据模型设计
## 4.1 `automation_jobs` 调整
- 保留字段:`id`, `owner_id`, `title`, `schedule_type`, `run_at`, `next_run_at`, `timezone`, `last_run_at`, `status` 等;
- 新增字段:`config JSONB NOT NULL`
- 迁移策略:
- 将历史 `prompt` 迁移到 `config.prompt`
- 切换业务代码后删除旧 `prompt` 字段(可分两步灰度)。
### 4.2 `config` 强约束 schema
```json
{
"prompt": "提取最近对话中的稳定记忆并写入",
"tools": ["memory_write", "memory_forget"]
}
```
约束要求:
- `prompt`:必填、非空、长度受限(建议 <= 2000);
- `tools`:必填数组,元素为注册工具名;
- `extra="forbid"`,拒绝未知字段;
- 执行前二次校验:`tools` 必须与后端 allowlist 取交集,禁止越权。
### 4.3 `messages.metadata` 扩展
新增建议字段:
- `hidden_from_user: bool`
- `origin: "chat" | "automation"`
用途:
- `hidden_from_user=true` 的消息在用户历史默认不可见;
- 审计查询可读取全量。
## 5. 运行时架构
## 5.1 复用现有 Agent 链路
自动任务不新建运行时,沿用现有 `router -> orchestrator -> worker` 流程:
- 输入:来自 `automation_jobs.config.prompt` 的合成“用户输入”;
- 上下文:默认加载“今天 + 昨天”历史;
- 工具:仅启用 `config.tools` 且通过 allowlist 校验。
## 5.2 会话与消息策略
- 自动任务写入独立 `session_type=automation` 会话,并关联 `job_id`
- 普通对话维持 `session_type=chat`
- 自动输入消息:`role=user``hidden_from_user=true`
- Assistant 输出消息:默认可见。
## 6. 调度器设计
## 6.1 扫描与触发
- 周期扫描条件:
- `status='active'`
- `next_run_at <= now_utc`
- 命中后入队执行命令(复用现有任务队列)。
## 6.2 幂等与并发控制
- 使用任务槽位去重键:`job_id + scheduled_slot`
- 使用行级抢占或原子更新防止并发重复执行;
- 失败可重试并记录错误上下文。
## 6.3 下次执行时间计算
- 每次执行完成后按 `schedule_type + timezone` 计算下一次触发时间;
- 保存到 `next_run_at`UTC);
- `last_run_at` 记录实际执行时间。
## 7. 默认任务创建
用户创建后自动创建一条默认 daily 记忆任务:
- `schedule_type = 'daily'`
- 本地时区目标时间:10:00
- `timezone`:优先用户设置时区,缺省 `Asia/Shanghai`
- `config.prompt`:内置记忆提取提示词模板
- `config.tools = ["memory_write", "memory_forget"]`
## 8. 记忆工具设计
## 8.1 `memory_write`
- 用途:写入/更新用户记忆;
- 输入:受 schema 限制(标题、内容、来源等);
- 安全:owner 作用域强制绑定,不信任模型输入的 owner 信息。
## 8.2 `memory_forget`
- 用途:失效或删除用户记忆;
- 输入:`memory_id` 或受限条件;
- 安全:仅允许当前 owner 范围内操作。
## 8.3 工具授权
- 任务声明工具集来自 `config.tools`
- 运行时执行 `declared_tools ∩ allowlist`
- 非白名单工具直接拒绝并记录审计日志。
## 9. `memory_prompt` 启用策略
- 从数据库读取当前用户可用 `memories`
- 组装为系统提示词 memory section
- 设置条数与长度上限,避免 token 膨胀;
- 自动任务与普通对话统一使用该注入机制。
## 10. 前端展示策略
- 历史消息接口默认过滤 `hidden_from_user=true`
- 用户仅看到 assistant 输出(以及可选系统摘要,不包含原始自动 prompt);
- 前端无需大量工具分支 if/else,可统一按 metadata 标记处理。
## 11. 安全与审计
- 关键要求:
- 禁止越权工具调用;
- 禁止跨用户 memory 读写;
- 日志不输出敏感数据和完整私密上下文。
- 审计能力:
- 自动输入、工具调用、输出全链路可追踪;
- 用户界面只显示允许可见内容。
## 12. 迁移与发布步骤
1. 新增 `config` 字段与 schema 代码;
2. 数据迁移:旧 `prompt -> config.prompt`
3. 运行时切换到 `config.prompt/config.tools`
4. 灰度验证后移除旧 `prompt`
5. 上线调度器与默认任务创建;
6. 启用 `memory_prompt` 并完成联调。
## 13. 测试与验收
### 13.1 单元测试
- `config` schema 校验(非法字段、非法工具、空 prompt);
- `next_run_at` 计算(跨时区、边界时间);
- 隐藏消息过滤逻辑;
- `memory_write/memory_forget` owner 边界。
### 13.2 集成测试
- 用户创建触发默认 daily 任务创建;
- 调度触发 -> Agent 执行 -> memory 写入 -> `next_run_at` 更新;
- 自动输入在审计可见、在用户历史不可见。
### 13.3 验收标准
- 每日 10:00(用户本地时区)稳定执行;
- 自动化链路有完整审计;
- 工具授权边界有效;
- 前端历史展示符合“隐藏自动输入、展示输出”的预期。
## 14. 实施顺序建议
1. 先更新协议文档(`docs/protocols`)明确契约;
2. 数据库与 schema 改造(`automation_jobs.config`);
3. 工具集与授权边界实现;
4. 调度器与默认任务创建;
5. `memory_prompt` 注入与端到端验证;
6. 前端过滤逻辑收口。
---
本方案是最小可行版本(MVP)设计,重点保障:
- 复用现有架构;
- 低配置复杂度;
- 可审计与可维护并存;
- 为后续扩展保留演进空间。