docs: 更新自动化记忆设计文档与协议路由
- 重构 automation-memory-design.md 为 v2 版本,新增 Execution Profile 抽象层 - 删除 auth-global-rewrite-design.md 和 auth-global-rewrite-plan.md - 更新 agent/api-endpoints.md 协议文档 - 更新 ASR 与 worker token latency 优化 TODO 文档
This commit is contained in:
@@ -1,227 +1,316 @@
|
||||
# 自动化记忆任务设计方案(v1)
|
||||
# 自动化记忆任务设计方案(v2)
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
本方案用于落地后端自动化记忆任务:
|
||||
本方案用于重构自动化记忆能力,目标是:
|
||||
|
||||
- 提供每日/每周自动执行能力;
|
||||
- 自动提取用户近期对话中的可沉淀记忆并写入 `memories`;
|
||||
- 支持“忘记”操作;
|
||||
- 复用现有 Agent 运行链路,避免并行维护两套运行时;
|
||||
- 保证可审计、可追溯,同时对用户界面隐藏自动输入提示词。
|
||||
- 保持单一运行时框架,不并行维护第二套执行系统;
|
||||
- 在运行时中新增一层抽象,使 `router` 与 `executor` 的启用策略可配置;
|
||||
- 将记忆处理职责从通用 worker 中剥离为独立 `memory agent` 角色;
|
||||
- 保留审计能力与用户可见性隔离(自动输入可审计、默认对用户隐藏);
|
||||
- 维持与现有 `/api/v1/agent`、SSE、history 协议的兼容演进路径。
|
||||
|
||||
## 2. 范围与非目标
|
||||
该版本是架构重构设计文档,重点解决职责边界与扩展性问题。
|
||||
|
||||
### 2.1 范围
|
||||
## 2. 设计原则
|
||||
|
||||
- `automation_jobs` 增加 `config`(JSONB,强约束);
|
||||
- 调度器执行 `automation_jobs`,按用户本地时区计算执行时间;
|
||||
- 自动任务复用现有 `router -> agent` 运行策略;
|
||||
- 自动输入落库但对用户不可见;
|
||||
- 工具集按任务配置分发,仅允许白名单工具;
|
||||
- 正式启用 `memory_prompt`,将 `memories` 注入系统提示词。
|
||||
1. **单框架,多角色**
|
||||
- 仍复用同一套 runtime、队列、持久化、SSE 通道;
|
||||
- 不再让 worker 承担所有领域职责。
|
||||
|
||||
### 2.2 非目标
|
||||
2. **配置驱动执行拓扑**
|
||||
- 运行路径由配置决定:是否经过 router、最终由哪个 executor 执行;
|
||||
- 执行拓扑可被审计并可回放。
|
||||
|
||||
- 不开放复杂 DSL 编排;
|
||||
- 不开放细粒度策略配置(如 `execution_profile/history_window_days/memory_policy`);
|
||||
- 不引入第二套独立 Agent 运行框架。
|
||||
3. **记忆职责显式化**
|
||||
- 记忆提取、写入、遗忘由 `memory agent` 执行;
|
||||
- worker 继续承担通用对话/工具编排场景。
|
||||
|
||||
## 3. 关键决策
|
||||
4. **默认安全最小权限**
|
||||
- 工具集必须是 `declared_tools ∩ allowlist`;
|
||||
- memory executor 默认仅开放记忆域工具。
|
||||
|
||||
1. **调度与执行分离**
|
||||
- `run_at/next_run_at/timezone` 负责“何时执行”;
|
||||
- `config` 负责“如何执行”(提示词与工具集)。
|
||||
5. **协议先行**
|
||||
- 任何对运行协议和数据结构的变更,先更新 `docs/protocols/` 再落地代码。
|
||||
|
||||
2. **配置最小化**
|
||||
- `automation_jobs.config` 仅保留:
|
||||
- `prompt: string`
|
||||
- `tools: string[]`
|
||||
## 3. 范围与非目标
|
||||
|
||||
3. **自动输入可审计但用户不可见**
|
||||
- 自动任务提示词作为“用户消息”写入 `messages`;
|
||||
- 在 metadata 加可见性标记并在用户历史接口中默认过滤;
|
||||
- 审计路径可查询完整消息链路。
|
||||
### 3.1 范围
|
||||
|
||||
4. **时区策略**
|
||||
- 调度语义采用“用户本地时区 10:00”;
|
||||
- 存储执行时间统一为 UTC 的 `next_run_at`;
|
||||
- 调度器仅按 UTC 扫描,降低运维复杂度。
|
||||
- `automation_jobs` 引入执行拓扑配置,支持是否启用 router 与 executor 选择;
|
||||
- 运行时新增抽象层:`Execution Profile`;
|
||||
- 支持 executor 类型:`worker`、`memory`;
|
||||
- 自动任务默认使用 memory executor;
|
||||
- 消息可见性和审计策略保持增强。
|
||||
|
||||
## 4. 数据模型设计
|
||||
### 3.2 非目标
|
||||
|
||||
## 4.1 `automation_jobs` 调整
|
||||
- 不引入复杂 DSL 编排语言;
|
||||
- 不开放无限制策略字段;
|
||||
- 不新增独立部署形态的第二运行时;
|
||||
- 不在本阶段改造前端交互形态(仅保证协议兼容)。
|
||||
|
||||
- 保留字段:`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. 核心决策
|
||||
|
||||
### 4.2 `config` 强约束 schema
|
||||
1. **新增抽象层:Execution Profile(关键)**
|
||||
- 在 scheduler/orchestrator 与具体 agent 之间新增执行配置层;
|
||||
- 该层决定是否调用 router、调用哪个 executor、可用工具策略。
|
||||
|
||||
2. **executor 职责分离**
|
||||
- `worker executor`:通用任务、复杂工具编排、常规聊天;
|
||||
- `memory executor`:记忆提取、记忆写入、记忆遗忘、记忆总结。
|
||||
|
||||
3. **router 可配置启用**
|
||||
- 自动记忆任务默认 `enable_router=false`,减少不必要决策成本;
|
||||
- 普通 chat 默认 `enable_router=true`。
|
||||
|
||||
4. **拓扑合法性约束**
|
||||
- 必须保证至少有一个 executor;
|
||||
- 禁止出现 `enable_router=false` 且 `executor` 为空;
|
||||
- executor 仅允许枚举值:`worker | memory`。
|
||||
|
||||
5. **保持事件/审计统一出口**
|
||||
- 无论 executor 类型,SSE 与消息持久化走同一通道;
|
||||
- 仅扩展阶段字段枚举,不拆协议体系。
|
||||
|
||||
## 5. 目标架构
|
||||
|
||||
## 5.1 运行链路(重构后)
|
||||
|
||||
```text
|
||||
scheduler/api trigger
|
||||
-> execution profile resolver
|
||||
-> (optional) router step
|
||||
-> executor step (worker | memory)
|
||||
-> persistence + redis stream
|
||||
-> history/sse consumers
|
||||
```
|
||||
|
||||
与旧链路相比,变化点是新增 `execution profile resolver` 和 `memory executor`。
|
||||
|
||||
## 5.2 Execution Profile 抽象
|
||||
|
||||
建议抽象(运行时内部模型):
|
||||
|
||||
```json
|
||||
{
|
||||
"prompt": "提取最近对话中的稳定记忆并写入",
|
||||
"tools": ["memory_write", "memory_forget"]
|
||||
"name": "automation_memory_default",
|
||||
"enable_router": false,
|
||||
"executor": "memory",
|
||||
"tool_policy": {
|
||||
"mode": "intersection",
|
||||
"allowlist": ["memory_write", "memory_forget"]
|
||||
},
|
||||
"history_policy": {
|
||||
"window": "today_yesterday"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
约束要求:
|
||||
说明:
|
||||
|
||||
- `prompt`:必填、非空、长度受限(建议 <= 2000);
|
||||
- `tools`:必填数组,元素为注册工具名;
|
||||
- `extra="forbid"`,拒绝未知字段;
|
||||
- 执行前二次校验:`tools` 必须与后端 allowlist 取交集,禁止越权。
|
||||
- `enable_router`:是否执行 router 决策步骤;
|
||||
- `executor`:最终执行角色;
|
||||
- `tool_policy`:工具授权策略,默认交集模式;
|
||||
- `history_policy`:上下文窗口策略(保持最小化,不扩展过多字段)。
|
||||
|
||||
### 4.3 `messages.metadata` 扩展
|
||||
## 5.3 默认 Profile 建议
|
||||
|
||||
新增建议字段:
|
||||
- `chat_default`
|
||||
- `enable_router=true`
|
||||
- `executor=worker`
|
||||
|
||||
- `automation_memory_default`
|
||||
- `enable_router=false`
|
||||
- `executor=memory`
|
||||
|
||||
## 6. 数据模型设计
|
||||
|
||||
## 6.1 `automation_jobs` 调整
|
||||
|
||||
保留现有调度相关字段:
|
||||
|
||||
- `id`, `owner_id`, `title`, `schedule_type`, `run_at`, `next_run_at`, `timezone`, `last_run_at`, `status`
|
||||
|
||||
新增/调整字段:
|
||||
|
||||
- `config JSONB NOT NULL`:任务输入与工具声明;
|
||||
- `execution_profile JSONB NOT NULL`:运行拓扑配置(MVP 可先内联,后续可抽到 profile registry)。
|
||||
|
||||
建议结构:
|
||||
|
||||
```json
|
||||
{
|
||||
"config": {
|
||||
"prompt": "提取最近对话中的稳定记忆并写入",
|
||||
"tools": ["memory_write", "memory_forget"]
|
||||
},
|
||||
"execution_profile": {
|
||||
"enable_router": false,
|
||||
"executor": "memory"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 6.2 Schema 约束
|
||||
|
||||
- `config.prompt`:必填非空,建议长度 `<= 2000`;
|
||||
- `config.tools`:必填数组,元素为已注册工具名;
|
||||
- `execution_profile.enable_router`:必填布尔;
|
||||
- `execution_profile.executor`:必填枚举 `worker|memory`;
|
||||
- `extra=forbid`,拒绝未知字段;
|
||||
- 执行前二次校验:`declared_tools ∩ runtime_allowlist`。
|
||||
|
||||
## 6.3 `messages.metadata` 扩展
|
||||
|
||||
建议标准化字段:
|
||||
|
||||
- `hidden_from_user: bool`
|
||||
- `origin: "chat" | "automation"`
|
||||
- `executor: "worker" | "memory"`
|
||||
- `execution_profile_name: string`
|
||||
|
||||
用途:
|
||||
|
||||
- `hidden_from_user=true` 的消息在用户历史默认不可见;
|
||||
- 审计查询可读取全量。
|
||||
- 用户视图过滤自动输入;
|
||||
- 审计可按 executor/profile 追踪行为;
|
||||
- 支持后续灰度比较(同任务不同 profile)。
|
||||
|
||||
## 5. 运行时架构
|
||||
## 7. 运行时行为约束
|
||||
|
||||
## 5.1 复用现有 Agent 链路
|
||||
## 7.1 自动任务
|
||||
|
||||
自动任务不新建运行时,沿用现有 `router -> orchestrator -> worker` 流程:
|
||||
- 会话类型:`session_type=automation`;
|
||||
- 自动输入消息:`role=user` + `hidden_from_user=true`;
|
||||
- 默认 profile:`automation_memory_default`;
|
||||
- executor:`memory`。
|
||||
|
||||
- 输入:来自 `automation_jobs.config.prompt` 的合成“用户输入”;
|
||||
- 上下文:默认加载“今天 + 昨天”历史;
|
||||
- 工具:仅启用 `config.tools` 且通过 allowlist 校验。
|
||||
## 7.2 普通对话
|
||||
|
||||
## 5.2 会话与消息策略
|
||||
- 会话类型:`session_type=chat`;
|
||||
- 默认 profile:`chat_default`;
|
||||
- executor:`worker`。
|
||||
|
||||
- 自动任务写入独立 `session_type=automation` 会话,并关联 `job_id`;
|
||||
- 普通对话维持 `session_type=chat`;
|
||||
- 自动输入消息:`role=user` 但 `hidden_from_user=true`;
|
||||
- Assistant 输出消息:默认可见。
|
||||
## 7.3 工具授权
|
||||
|
||||
## 6. 调度器设计
|
||||
- 输入工具集合来自 `config.tools`;
|
||||
- 实际可调用集合来自 `config.tools ∩ profile_allowlist ∩ system_allowlist`;
|
||||
- 对被拒绝工具记录审计日志与拒绝原因。
|
||||
|
||||
## 6.1 扫描与触发
|
||||
## 8. 调度器设计
|
||||
|
||||
- 周期扫描条件:
|
||||
## 8.1 扫描与触发
|
||||
|
||||
- 扫描条件:
|
||||
- `status='active'`
|
||||
- `next_run_at <= now_utc`
|
||||
- 命中后入队执行命令(复用现有任务队列)。
|
||||
- 命中后投递执行命令,命令中携带 `execution_profile` 快照。
|
||||
|
||||
## 6.2 幂等与并发控制
|
||||
## 8.2 幂等与并发
|
||||
|
||||
- 使用任务槽位去重键:`job_id + scheduled_slot`;
|
||||
- 使用行级抢占或原子更新防止并发重复执行;
|
||||
- 失败可重试并记录错误上下文。
|
||||
- 幂等键:`job_id + scheduled_slot`;
|
||||
- 通过原子更新或行级锁抢占,防止重复执行;
|
||||
- 失败重试并保留错误上下文。
|
||||
|
||||
## 6.3 下次执行时间计算
|
||||
## 8.3 时区策略
|
||||
|
||||
- 每次执行完成后按 `schedule_type + timezone` 计算下一次触发时间;
|
||||
- 保存到 `next_run_at`(UTC);
|
||||
- `last_run_at` 记录实际执行时间。
|
||||
- 调度语义按用户本地时区(例如每日 10:00);
|
||||
- 存储执行点统一使用 UTC `next_run_at`;
|
||||
- 调度器仅按 UTC 扫描,避免多时区计算分散。
|
||||
|
||||
## 7. 默认任务创建
|
||||
## 9. 协议影响与兼容策略
|
||||
|
||||
用户创建后自动创建一条默认 daily 记忆任务:
|
||||
本阶段是设计重构,协议建议如下:
|
||||
|
||||
- `schedule_type = 'daily'`
|
||||
- 本地时区目标时间:10:00
|
||||
- `timezone`:优先用户设置时区,缺省 `Asia/Shanghai`
|
||||
- `config.prompt`:内置记忆提取提示词模板
|
||||
- `config.tools = ["memory_write", "memory_forget"]`
|
||||
1. `docs/protocols/agent/sse-events.md`
|
||||
- `STEP_STARTED/STEP_FINISHED.stepName` 从 `router|worker` 扩展为 `router|worker|memory`;
|
||||
- 当 `enable_router=false` 时,不产出 router step 事件。
|
||||
|
||||
## 8. 记忆工具设计
|
||||
2. `docs/protocols/agent/run-agent-input.md`
|
||||
- 增补运行上下文说明:内部执行可由 profile 决定 executor;
|
||||
- 对外 API 入参保持兼容,不强制前端传 executor。
|
||||
|
||||
## 8.1 `memory_write`
|
||||
3. `docs/protocols/agent/api-endpoints.md`
|
||||
- 增补说明:`/runs` 的运行阶段由后端 profile 决定,历史与 SSE 保持统一消费方式。
|
||||
|
||||
- 用途:写入/更新用户记忆;
|
||||
- 输入:受 schema 限制(标题、内容、来源等);
|
||||
- 安全:owner 作用域强制绑定,不信任模型输入的 owner 信息。
|
||||
兼容要求:
|
||||
|
||||
## 8.2 `memory_forget`
|
||||
- 老客户端仍可消费现有事件;
|
||||
- 仅新增枚举值,不删除既有字段;
|
||||
- 如客户端未识别 `memory`,按未知阶段容错处理。
|
||||
|
||||
- 用途:失效或删除用户记忆;
|
||||
- 输入:`memory_id` 或受限条件;
|
||||
- 安全:仅允许当前 owner 范围内操作。
|
||||
## 10. 默认任务创建
|
||||
|
||||
## 8.3 工具授权
|
||||
用户创建后自动创建 daily 记忆任务:
|
||||
|
||||
- 任务声明工具集来自 `config.tools`;
|
||||
- 运行时执行 `declared_tools ∩ allowlist`;
|
||||
- 非白名单工具直接拒绝并记录审计日志。
|
||||
|
||||
## 9. `memory_prompt` 启用策略
|
||||
|
||||
- 从数据库读取当前用户可用 `memories`;
|
||||
- 组装为系统提示词 memory section;
|
||||
- 设置条数与长度上限,避免 token 膨胀;
|
||||
- 自动任务与普通对话统一使用该注入机制。
|
||||
|
||||
## 10. 前端展示策略
|
||||
|
||||
- 历史消息接口默认过滤 `hidden_from_user=true`;
|
||||
- 用户仅看到 assistant 输出(以及可选系统摘要,不包含原始自动 prompt);
|
||||
- 前端无需大量工具分支 if/else,可统一按 metadata 标记处理。
|
||||
- `schedule_type='daily'`
|
||||
- 本地时区目标时间 `10:00`
|
||||
- `timezone` 优先用户设置,缺省 `Asia/Shanghai`
|
||||
- `config.prompt` 使用内置记忆提取模板
|
||||
- `config.tools=["memory_write","memory_forget"]`
|
||||
- `execution_profile={"enable_router":false,"executor":"memory"}`
|
||||
|
||||
## 11. 安全与审计
|
||||
|
||||
- 关键要求:
|
||||
- 禁止越权工具调用;
|
||||
- 禁止跨用户 memory 读写;
|
||||
- 日志不输出敏感数据和完整私密上下文。
|
||||
- 审计能力:
|
||||
- 自动输入、工具调用、输出全链路可追踪;
|
||||
- 用户界面只显示允许可见内容。
|
||||
安全约束:
|
||||
|
||||
- 禁止越权工具调用;
|
||||
- memory 读写必须强制 owner 作用域;
|
||||
- 日志不输出敏感内容与完整私密上下文。
|
||||
|
||||
审计能力:
|
||||
|
||||
- 自动输入、工具调用、输出全链路可追踪;
|
||||
- 可按 `origin/executor/execution_profile_name` 回放。
|
||||
|
||||
## 12. 迁移与发布步骤
|
||||
|
||||
1. 新增 `config` 字段与 schema 代码;
|
||||
2. 数据迁移:旧 `prompt -> config.prompt`;
|
||||
3. 运行时切换到 `config.prompt/config.tools`;
|
||||
4. 灰度验证后移除旧 `prompt`;
|
||||
5. 上线调度器与默认任务创建;
|
||||
6. 启用 `memory_prompt` 并完成联调。
|
||||
1. 更新 `docs/protocols`(先协议);
|
||||
2. 新增 `execution_profile` schema 与运行时模型;
|
||||
3. 数据迁移:历史任务写入默认 profile;
|
||||
4. 运行时接入 profile resolver;
|
||||
5. 引入 `memory executor` 并接管自动记忆任务;
|
||||
6. 灰度:先新建任务使用 memory executor,再迁移存量任务;
|
||||
7. 稳定后收敛旧逻辑分支。
|
||||
|
||||
## 13. 测试与验收
|
||||
|
||||
### 13.1 单元测试
|
||||
## 13.1 单元测试
|
||||
|
||||
- `config` schema 校验(非法字段、非法工具、空 prompt);
|
||||
- `next_run_at` 计算(跨时区、边界时间);
|
||||
- 隐藏消息过滤逻辑;
|
||||
- `memory_write/memory_forget` owner 边界。
|
||||
- profile schema 校验(非法枚举、空 executor、未知字段);
|
||||
- 工具授权交集逻辑;
|
||||
- `next_run_at` 跨时区计算;
|
||||
- 隐藏消息过滤与审计字段落库。
|
||||
|
||||
### 13.2 集成测试
|
||||
## 13.2 集成测试
|
||||
|
||||
- 用户创建触发默认 daily 任务创建;
|
||||
- 调度触发 -> Agent 执行 -> memory 写入 -> `next_run_at` 更新;
|
||||
- 自动输入在审计可见、在用户历史不可见。
|
||||
- 自动任务触发后进入 memory executor;
|
||||
- `enable_router=false` 时无 router step 事件;
|
||||
- SSE `stepName=memory` 可被稳定消费;
|
||||
- 自动输入审计可见、用户历史不可见。
|
||||
|
||||
### 13.3 验收标准
|
||||
## 13.3 验收标准
|
||||
|
||||
- 每日 10:00(用户本地时区)稳定执行;
|
||||
- 自动化链路有完整审计;
|
||||
- 工具授权边界有效;
|
||||
- 前端历史展示符合“隐藏自动输入、展示输出”的预期。
|
||||
- memory 任务不经过 router 亦可稳定完成;
|
||||
- 工具权限边界有效;
|
||||
- 审计链路完整;
|
||||
- 对现有 chat 流程零回归。
|
||||
|
||||
## 14. 实施顺序建议
|
||||
|
||||
1. 先更新协议文档(`docs/protocols`)明确契约;
|
||||
2. 数据库与 schema 改造(`automation_jobs.config`);
|
||||
3. 工具集与授权边界实现;
|
||||
4. 调度器与默认任务创建;
|
||||
5. `memory_prompt` 注入与端到端验证;
|
||||
6. 前端过滤逻辑收口。
|
||||
1. 协议文档更新(`docs/protocols/agent/*`);
|
||||
2. `automation_jobs` 数据结构改造与迁移;
|
||||
3. profile resolver 与 executor 抽象接入;
|
||||
4. memory executor 接入与工具边界验证;
|
||||
5. 调度器联调与默认任务创建;
|
||||
6. 端到端压测与灰度发布。
|
||||
|
||||
---
|
||||
|
||||
本方案是最小可行版本(MVP)设计,重点保障:
|
||||
本版(v2)将“抽象 Agent 运行”落到可执行的配置模型,核心是:
|
||||
|
||||
- 复用现有架构;
|
||||
- 低配置复杂度;
|
||||
- 可审计与可维护并存;
|
||||
- 为后续扩展保留演进空间。
|
||||
- 新增一层 `Execution Profile`;
|
||||
- 将 `router` 和 `executor` 拓扑配置化;
|
||||
- 使用独立 `memory executor` 承载记忆职责;
|
||||
- 在不引入第二运行时的前提下完成大规模重构演进。
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
# Auth 全局模块重写设计(跨端并存、同端互斥)
|
||||
|
||||
## 1. 目标
|
||||
|
||||
- 彻底消除 Auth 分裂状态:`token` 状态与 `AuthBloc` 状态必须单一真相源。
|
||||
- 会话策略升级为:
|
||||
- 同账号允许跨端并存:`mobile + web + desktop`
|
||||
- 同账号同端互斥:同端新登录会挤下线旧设备
|
||||
- 保证任何 401 链路在刷新失败后都能统一收敛为“未登录 + 清理本地 + 路由回到登录页”。
|
||||
- 消除设备差异导致的不一致行为(部分机型“假登录”或“卡死页”)。
|
||||
|
||||
## 2. 边界与约束
|
||||
|
||||
- 仅重写 `apps/**` 的 Auth 客户端架构与规则,不改后端协议语义。
|
||||
- 保持现有登录/注册 UI 路由入口,避免用户操作路径变化。
|
||||
- 认证属于高风险域,重写必须覆盖:
|
||||
- 启动恢复
|
||||
- 运行时 token 过期
|
||||
- 并发 401
|
||||
- 手动登出与自动过期登出的差异行为
|
||||
|
||||
## 3. 核心架构
|
||||
|
||||
### 3.1 单一真相源(Single Source of Truth)
|
||||
|
||||
- `AuthBloc` 成为唯一认证状态源。
|
||||
- `ApiInterceptor` 只负责协议级拦截与刷新,不直接做路由跳转。
|
||||
- 401 刷新失败时,`ApiInterceptor -> ApiClient callback -> AuthBloc(AuthSessionInvalidated)`。
|
||||
- 路由守卫只看 `AuthBloc` 状态,不再做隐式 token 判定。
|
||||
|
||||
### 3.2 会话状态机
|
||||
|
||||
- `AuthInitial`
|
||||
- `AuthLoading`(启动恢复/会话检查)
|
||||
- `AuthAuthenticated(user)`
|
||||
- `AuthUnauthenticated(reason)`
|
||||
|
||||
`reason` 取值:
|
||||
- `signedOut`
|
||||
- `expired`
|
||||
- `startupRecoveryFailed`
|
||||
|
||||
### 3.3 登出语义分离
|
||||
|
||||
- 手动登出:`deleteSession()`
|
||||
- 尝试调用后端注销
|
||||
- 最终清本地
|
||||
- 自动过期:`clearSessionLocalOnly()`
|
||||
- 仅清本地
|
||||
- 不调用后端注销接口
|
||||
|
||||
### 3.4 并发与抖动控制
|
||||
|
||||
- `ApiInterceptor` 继续使用 refresh singleflight。
|
||||
- 新增 auth failure singleflight:多并发 401 刷新失败,只触发一次全局会话失效事件。
|
||||
|
||||
### 3.5 设备差异治理
|
||||
|
||||
- 启动时 token 读取异常必须兜底:进入 `AuthUnauthenticated(startupRecoveryFailed)`,避免卡死在 Boot。
|
||||
- `FlutterSecureStorage` 显式平台配置:
|
||||
- Android 使用 `encryptedSharedPreferences`
|
||||
- iOS 指定 keychain accessibility(保证行为稳定)
|
||||
|
||||
## 4. 数据流
|
||||
|
||||
### 4.1 冷启动
|
||||
|
||||
1. `main` 触发 `AuthStarted`
|
||||
2. `AuthBloc` 读取 refresh token
|
||||
3. 有 refresh token -> 刷新会话 -> 成功进入 `AuthAuthenticated`
|
||||
4. 无 token 或异常 -> `AuthUnauthenticated(startupRecoveryFailed)`
|
||||
|
||||
### 4.2 运行时 API 请求
|
||||
|
||||
1. 请求携带 access token
|
||||
2. 401 -> 触发 refresh
|
||||
3. refresh 成功 -> 自动重试原请求
|
||||
4. refresh 失败 -> 触发一次全局 auth failure
|
||||
5. `AuthBloc` 收到 `AuthSessionInvalidated(expired)` -> 清本地 -> `AuthUnauthenticated(expired)`
|
||||
6. Router 根据状态回登录页
|
||||
|
||||
## 5. 测试策略
|
||||
|
||||
- `AuthBloc`:
|
||||
- 启动读取 refresh token 异常兜底
|
||||
- session invalidated 事件导致统一未登录
|
||||
- `ApiInterceptor`:
|
||||
- 并发 401 refresh 失败仅触发一次 auth failure
|
||||
- `AuthRepository`:
|
||||
- 手动登出 vs 自动过期清理行为差异
|
||||
|
||||
## 6. 迁移计划
|
||||
|
||||
- 先引入新事件/新回调/新状态原因,不改 UI 交互。
|
||||
- 再改路由守卫识别未登录原因。
|
||||
- 最后补齐 `apps/AGENTS.md` Auth 强约束,防止后续回归为“各处乱写”。
|
||||
|
||||
## 7. 风险与回滚
|
||||
|
||||
- 风险:回调链路接错导致频繁误登出。
|
||||
- 规避:并发 singleflight + 精确触发条件(仅 401 refresh 失败)。
|
||||
- 回滚:保留旧事件兼容层,出现异常可快速退回旧路由判定。
|
||||
@@ -1,158 +0,0 @@
|
||||
# Auth Global Rewrite Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** 将 Flutter 客户端 Auth 重构为全局单一状态源,解决 401 后会话不一致、页面卡死和设备行为分裂问题。
|
||||
|
||||
**Architecture:** 以 `AuthBloc` 为唯一认证真相源,`ApiInterceptor` 仅负责协议层刷新与失败信号上抛。401 刷新失败后通过统一回调触发 `AuthSessionInvalidated`,由 `AuthBloc` 执行本地会话失效与状态切换,Router 仅根据 Auth 状态跳转。
|
||||
|
||||
**Tech Stack:** Flutter, flutter_bloc, dio, flutter_secure_storage, flutter_test, mocktail, bloc_test
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 定义 Auth 失效语义与事件模型
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/lib/features/auth/presentation/bloc/auth_event.dart`
|
||||
- Modify: `apps/lib/features/auth/presentation/bloc/auth_state.dart`
|
||||
- Test: `apps/test/features/auth/presentation/bloc/auth_bloc_test.dart`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
新增失败测试:收到 session invalidated 事件后,状态应进入 `AuthUnauthenticated(expired)`。
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `flutter test test/features/auth/presentation/bloc/auth_bloc_test.dart`
|
||||
Expected: FAIL(事件/状态原因不存在)
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
新增失效来源枚举、失效事件、未登录原因字段。
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `flutter test test/features/auth/presentation/bloc/auth_bloc_test.dart`
|
||||
Expected: PASS
|
||||
|
||||
### Task 2: 重写 AuthBloc 启动恢复与失效收敛逻辑
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/lib/features/auth/presentation/bloc/auth_bloc.dart`
|
||||
- Modify: `apps/lib/features/auth/data/auth_repository.dart`
|
||||
- Modify: `apps/lib/features/auth/data/auth_repository_impl.dart`
|
||||
- Test: `apps/test/features/auth/presentation/bloc/auth_bloc_test.dart`
|
||||
- Test: `apps/test/features/auth/data/auth_repository_test.dart`
|
||||
|
||||
**Step 1: Write failing tests**
|
||||
|
||||
- 启动读取 refresh token 抛异常 -> `AuthUnauthenticated(startupRecoveryFailed)`
|
||||
- 自动过期登出只清本地不调后端
|
||||
|
||||
**Step 2: Run tests to verify failure**
|
||||
|
||||
Run: `flutter test test/features/auth/presentation/bloc/auth_bloc_test.dart test/features/auth/data/auth_repository_test.dart`
|
||||
Expected: FAIL
|
||||
|
||||
**Step 3: Implement minimal code**
|
||||
|
||||
- `AuthBloc._onStarted` 增加异常兜底
|
||||
- `AuthRepository` 新增 `clearSessionLocalOnly()`
|
||||
- `AuthBloc` 处理 `AuthSessionInvalidated`
|
||||
|
||||
**Step 4: Run tests to verify pass**
|
||||
|
||||
Run: `flutter test test/features/auth/presentation/bloc/auth_bloc_test.dart test/features/auth/data/auth_repository_test.dart`
|
||||
Expected: PASS
|
||||
|
||||
### Task 3: 改造 ApiInterceptor / ApiClient 全局失效回调链
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/lib/core/api/api_interceptor.dart`
|
||||
- Modify: `apps/lib/core/api/api_client.dart`
|
||||
- Modify: `apps/lib/core/di/injection.dart`
|
||||
- Test: `apps/test/core/api/api_interceptor_test.dart`
|
||||
|
||||
**Step 1: Write failing test**
|
||||
|
||||
并发 401 + refresh 失败时,`onAuthFailure` 仅触发一次。
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `flutter test test/core/api/api_interceptor_test.dart`
|
||||
Expected: FAIL
|
||||
|
||||
**Step 3: Implement minimal code**
|
||||
|
||||
- interceptor 新增 auth failure singleflight
|
||||
- api client 新增 `setAuthFailureCallback`
|
||||
- DI 中将回调绑定到 `AuthBloc(AuthSessionInvalidated)`
|
||||
|
||||
**Step 4: Run test to verify pass**
|
||||
|
||||
Run: `flutter test test/core/api/api_interceptor_test.dart`
|
||||
Expected: PASS
|
||||
|
||||
### Task 4: 平台安全存储配置与稳定性增强
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/lib/core/di/injection.dart`
|
||||
|
||||
**Step 1: Add platform options**
|
||||
|
||||
为 `FlutterSecureStorage` 显式设置 Android/iOS 选项,减少机型差异。
|
||||
|
||||
**Step 2: Run targeted tests/analyze**
|
||||
|
||||
Run: `flutter analyze lib/core/di/injection.dart`
|
||||
Expected: PASS
|
||||
|
||||
### Task 5: 路由与使用点适配
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/lib/core/router/app_router.dart`
|
||||
- Modify: `apps/lib/features/settings/ui/screens/account_screen.dart`
|
||||
- Modify: `apps/lib/features/settings/ui/screens/change_password_screen.dart`
|
||||
|
||||
**Step 1: Update route/auth checks**
|
||||
|
||||
兼容 `AuthUnauthenticated(reason)` 新结构,保持原有登录流 UX。
|
||||
|
||||
**Step 2: Run focused tests**
|
||||
|
||||
Run: `flutter test test/features/auth`
|
||||
Expected: PASS
|
||||
|
||||
### Task 6: 增加 Auth 全局强约束
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/AGENTS.md`
|
||||
|
||||
**Step 1: Add mandatory auth rules**
|
||||
|
||||
新增“Auth 全局模块(MUST)”章节:
|
||||
- 401 只允许走统一失效回调链
|
||||
- 禁止 feature 私自清 token/私自跳登录
|
||||
- Auth 状态只能由全局模块写入
|
||||
|
||||
**Step 2: Verify docs consistency**
|
||||
|
||||
Run: `git diff -- apps/AGENTS.md`
|
||||
Expected: 仅新增约束,不改现有视觉/UI强规则
|
||||
|
||||
### Task 7: 全量验证
|
||||
|
||||
**Files:**
|
||||
- Modify if needed after fixes
|
||||
|
||||
**Step 1: Run test suites**
|
||||
|
||||
Run: `flutter test test/core/api/api_interceptor_test.dart test/features/auth`
|
||||
|
||||
**Step 2: Run analyze on touched auth scope**
|
||||
|
||||
Run: `flutter analyze lib/core/api lib/features/auth lib/core/router/app_router.dart lib/core/di/injection.dart`
|
||||
|
||||
**Step 3: Report residual risks**
|
||||
|
||||
输出剩余风险、可观测性建议、生产灰度建议。
|
||||
@@ -45,7 +45,6 @@ Base URL: `/api/v1/agent`
|
||||
|
||||
- `401` 未认证
|
||||
- `422` 请求结构校验失败
|
||||
- `429` 超过 run 请求速率限制
|
||||
|
||||
---
|
||||
|
||||
@@ -217,7 +216,6 @@ WAV 音频转写。
|
||||
|
||||
- 内容类型:`audio/wav` / `audio/x-wav` / `audio/wave`
|
||||
- 文件大小:最大 10MB
|
||||
- 速率限制:20 次/分钟/用户
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
- 语音识别计费
|
||||
当前项目有语音识别功能,但是语音识别的cost成本计算没有实现。目前我们用的模型是fun-asr-realtime-2026-02-28,价格是0.00033元/每秒。我希望把它做到backend/src/core/config/static/database/llm_catalog.yaml,加一个asr字段,引入model_code代替原agent router里的硬编码,通过加载配置获取模型信息和报价,然后根据后端路由接收到的音频长度然后来估算价格,或者看看dashscope的sdk是否会返回消耗token金额,将这个token金额看看如何审计
|
||||
|
||||
@@ -1,41 +1,78 @@
|
||||
# Worker Token/Latency Optimization TODO
|
||||
# Worker Token/Latency 优化 TODO
|
||||
|
||||
Date: 2026-03-17
|
||||
日期: 2026-03-17
|
||||
Owner: backend runtime
|
||||
Status: pending
|
||||
状态: pending
|
||||
|
||||
## Background
|
||||
## 背景
|
||||
|
||||
- Router cost/latency is acceptable.
|
||||
- Worker stage (deepseek-chat) has significantly higher input tokens and latency.
|
||||
- Current optimization work is deferred due to prioritization.
|
||||
- Router 阶段成本与延迟基本可接受。
|
||||
- Worker 阶段(deepseek-chat)`input_tokens` 与 `latency` 显著偏高,是总成本的主要来源。
|
||||
- 优化目标是在不降低结果质量与稳定性的前提下,优先压缩 Worker 输入 token。
|
||||
|
||||
## Observations (from `public.messages`)
|
||||
## 现状观察
|
||||
|
||||
- Worker avg input tokens are much higher than router (about 12k+ vs 3k).
|
||||
- Worker avg latency is much higher than router (about 41s vs 4s).
|
||||
- Worker cost dominates total cost.
|
||||
- Worker 平均 `input_tokens` 明显高于 Router。
|
||||
- Worker 平均延迟明显高于 Router。
|
||||
- 成本主要由 Worker 阶段贡献。
|
||||
|
||||
## Root Cause Hypothesis
|
||||
## 核心优化方向(按优先级)
|
||||
|
||||
- Worker ReAct path repeatedly includes full tool schemas per model call.
|
||||
- `calendar_write` tool schema is large and contributes major prompt overhead.
|
||||
- Finalize JSON step performs an additional model call after ReAct.
|
||||
### P0(优先执行,低风险高收益)
|
||||
|
||||
## Deferred Optimization Items
|
||||
1. 路由提示词瘦身:从“全量路由清单”改为“route_id 约束 + 服务端映射”。
|
||||
- 模型仅输出 `route_id` 与必要参数。
|
||||
- 后端基于静态 route catalog 映射到最终 `path`。
|
||||
- 目标:减少每次 system prompt 的固定 token 开销。
|
||||
|
||||
1. Tool schema slimming for calendar write path.
|
||||
- Split `calendar_write` into focused tools (`calendar_create`, `calendar_update`, `calendar_delete`).
|
||||
- Reduce redundant/verbose field descriptions where possible.
|
||||
2. Dynamic tool set exposure by routed intent.
|
||||
- Only expose tools needed for current task.
|
||||
3. Evaluate finalize overhead.
|
||||
- Verify whether finalize call can be reduced or replaced in specific flows.
|
||||
4. Add before/after benchmark script.
|
||||
- Compare worker `input_tokens`, `latency_ms`, and `cost` for the same scripted multi-turn scenario.
|
||||
2. Finalize 最小上下文化:避免 finalize 回放完整 memory。
|
||||
- finalize 阶段仅输入:最后一轮候选答案 + 必要工具结果摘要 + schema 指令。
|
||||
- 不再注入完整历史会话。
|
||||
- 目标:降低两段式结构化输出的额外输入成本。
|
||||
|
||||
## Acceptance Metrics (target)
|
||||
3. 工具按需暴露(dynamic tool allowlist)。
|
||||
- 按 router 的 task/result typing 只下发当前任务必需工具。
|
||||
- 避免每轮 ReAct 携带全量工具 schema。
|
||||
- 目标:降低每次 reasoning 的工具描述负担。
|
||||
|
||||
- Reduce worker input tokens by >= 30% in multi-turn calendar CRUD scenario.
|
||||
- Reduce worker p95 latency by >= 25%.
|
||||
- Keep functional behavior unchanged for agent runs.
|
||||
### P1(次优先,稳定收益)
|
||||
|
||||
4. system prompt 分层裁剪。
|
||||
- 按 `agent_type` 与 `ui_mode` 组装最小提示词集合。
|
||||
- Router 不携带 Worker 专属规则;`ui_mode=none` 不携带 rich UI 细则。
|
||||
|
||||
5. 输出体积约束。
|
||||
- 限制 `key_points`、`suggested_actions`、`ui_hints.actions` 数量与文本长度。
|
||||
- 降低 `output_tokens`,同时减少前端渲染负担。
|
||||
|
||||
6. 上下文策略优化(摘要 + 最近少量原文)。
|
||||
- 从“固定最近 N 轮原文”改为“结构化摘要 + 最近 1~2 轮原文”。
|
||||
- 控制长会话 token 膨胀。
|
||||
|
||||
### P2(可选增强)
|
||||
|
||||
7. Prompt 缓存命中优化。
|
||||
- 固定可缓存前缀,动态段后置。
|
||||
- 利用 provider prompt cache 降低计费 token(若模型侧支持)。
|
||||
|
||||
## 不建议作为当前主线
|
||||
|
||||
- 直接切换为 ReAct 原生 `structured_model` 作为主方案(当前实测稳定性与成本不占优)。
|
||||
- 在未完成 P0 优化前,优先投入复杂的 ReAct 内核重写。
|
||||
|
||||
## 验收指标(更新)
|
||||
|
||||
- 在典型多轮场景中,Worker `input_tokens` 降低 >= 30%。
|
||||
- Worker p95 `latency_ms` 降低 >= 20%。
|
||||
- 结构化输出校验成功率不低于当前基线。
|
||||
- 关键路径功能行为保持不变(agent run 结果与前端交互不回退)。
|
||||
|
||||
## 验证方式
|
||||
|
||||
1. 固定场景脚本对比(优化前/后同输入):
|
||||
- 指标:`input_tokens`、`output_tokens`、`latency_ms`、`cost`、结构化成功率。
|
||||
2. 线上观测(`public.messages`):
|
||||
- 按 stage(router/worker)聚合对比日均与 p95。
|
||||
3. 回归校验:
|
||||
- 工具调用结果一致性;
|
||||
- `ui_hints`/`ui_schema` 可渲染性与导航动作正确性。
|
||||
|
||||
Reference in New Issue
Block a user