diff --git a/docs/plans/2026-03-17-automation-memory-design.md b/docs/plans/2026-03-17-automation-memory-design.md index b343842..5118243 100644 --- a/docs/plans/2026-03-17-automation-memory-design.md +++ b/docs/plans/2026-03-17-automation-memory-design.md @@ -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` 承载记忆职责; +- 在不引入第二运行时的前提下完成大规模重构演进。 diff --git a/docs/plans/2026-03-18-auth-global-rewrite-design.md b/docs/plans/2026-03-18-auth-global-rewrite-design.md deleted file mode 100644 index 3df4a25..0000000 --- a/docs/plans/2026-03-18-auth-global-rewrite-design.md +++ /dev/null @@ -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 失败)。 -- 回滚:保留旧事件兼容层,出现异常可快速退回旧路由判定。 diff --git a/docs/plans/2026-03-18-auth-global-rewrite-plan.md b/docs/plans/2026-03-18-auth-global-rewrite-plan.md deleted file mode 100644 index a0c43d0..0000000 --- a/docs/plans/2026-03-18-auth-global-rewrite-plan.md +++ /dev/null @@ -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** - -输出剩余风险、可观测性建议、生产灰度建议。 diff --git a/docs/protocols/agent/api-endpoints.md b/docs/protocols/agent/api-endpoints.md index 1aacb08..5618210 100644 --- a/docs/protocols/agent/api-endpoints.md +++ b/docs/protocols/agent/api-endpoints.md @@ -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 次/分钟/用户 --- diff --git a/docs/todo/2026-03-17-asr.md b/docs/todo/2026-03-17-asr.md index 7730c05..1f8a32f 100644 --- a/docs/todo/2026-03-17-asr.md +++ b/docs/todo/2026-03-17-asr.md @@ -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金额看看如何审计 diff --git a/docs/todo/2026-03-17-worker-token-latency-optimization.md b/docs/todo/2026-03-17-worker-token-latency-optimization.md index 1b7772d..1a265fc 100644 --- a/docs/todo/2026-03-17-worker-token-latency-optimization.md +++ b/docs/todo/2026-03-17-worker-token-latency-optimization.md @@ -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` 可渲染性与导航动作正确性。