# Lime 统一执行追踪(Execution Tracker)技术规划 ## 1. 背景与问题定义 当前 Lime 的执行入口分散在多个模块: - `aster_agent_cmd.rs`(通用 Agent 对话) - `skill_exec_cmd.rs`(Skill 执行) - `heartbeat_service/mod.rs`(定时任务/心跳执行) 虽然各自功能完整,但执行状态、错误语义、追踪字段和观测口径不一致,导致: 1. 故障排查链路长(跨表、跨日志、跨命令) 2. 前端难以给出统一执行状态 3. 后续接入新入口(如 IM、批处理)时容易重复造轮子 4. 技术债风险:若“每个入口各写一套 run 逻辑”,将出现明显胶水代码 本规划目标是在不重写执行引擎的前提下,新增一个高内聚、低侵入的统一追踪层。 --- ## 2. 目标与非目标 ### 2.1 目标(Must Have) 1. 引入统一 `run` 语义模型(跨 chat/skill/heartbeat) 2. 所有入口通过同一服务写入执行生命周期 3. 统一状态枚举与错误归一化,消除字符串散落 4. 支持灰度开关,可快速回滚 5. 首期不影响既有执行能力和用户主流程 ### 2.2 非目标(Not in Scope) 1. 不重构 Aster 执行引擎 2. 不重做前端大页面 3. 不在首期迁移历史数据 4. 不引入复杂事件总线或新中间件框架 --- ## 3. 设计原则(防“垃圾代码”约束) 1. **单点收敛**:仅新增一个核心模块 `execution_tracker` 2. **边界接入**:只在命令/服务边界包裹,不侵入工具执行内部 3. **幂等优先**:重复 finish/fail 不产生状态抖动 4. **最小数据**:`agent_runs` 只存摘要索引,细节继续由现有表承载 5. **先兼容后替换**:短期并存,验证后收敛,避免长期双写 --- ## 4. 总体架构 ### 4.1 新增模块 建议新增:`src-tauri/crates/services/src/execution_tracker.rs` 核心职责: - 创建 run(start) - 更新 run(success/error/timeout/canceled) - 统一错误归一化 - 提供 `with_run` 包装器减少入口代码重复 ### 4.2 统一入口接口(示意) ```rust with_run(ctx, RunSource::Skill, Some("pptx"), Some(session_id), async { // 原有执行逻辑,不改 }) ``` 入口业务只关心“执行什么”,生命周期交给 tracker 处理。 --- ## 5. 数据模型设计 ## 5.1 新表:`agent_runs` 核心字段建议: - `id`(TEXT, PK, run_id) - `source`(TEXT: `chat | skill | heartbeat`) - `source_ref`(TEXT,可选:skill 名称/heartbeat task id) - `session_id`(TEXT,可选) - `status`(TEXT: `queued | running | success | error | canceled | timeout`) - `started_at`(TEXT, ISO8601) - `finished_at`(TEXT,可空) - `duration_ms`(INTEGER,可空) - `error_code`(TEXT,可空) - `error_message`(TEXT,可空) - `metadata`(TEXT,可空,JSON 摘要) ### 5.2 索引建议 - `idx_agent_runs_source_started_at (source, started_at DESC)` - `idx_agent_runs_session_started_at (session_id, started_at DESC)` - `idx_agent_runs_status_started_at (status, started_at DESC)` --- ## 6. 统一状态与错误规范 ## 6.1 状态机(最小版) 允许流转: - `queued -> running` - `running -> success | error | timeout | canceled` 禁止回退与二次终态覆盖(幂等)。 ### 6.2 错误归一化 统一输出: - `error_code`:机器可读(如 `permission_denied` / `provider_unavailable`) - `error_message`:用户可读 - `retryable`:仅内部使用(可放 metadata) --- ## 7. 接入点改造方案 ## 7.1 `skill_exec_cmd.rs`(第一接入点) 原因:边界清晰、风险最低,适合作为 P0 首个落地。 做法: - 在 `execute_skill` 外围包 `with_run` - `source = skill` - `source_ref = skill_name` - `session_id = 当前执行会话 id` ## 7.2 `aster_agent_cmd.rs` 做法: - 在会话执行入口处创建 run - 流式完成/异常时由 tracker 统一收敛状态 - 复用错误归一化,不再各处拼接文本 ## 7.3 `heartbeat_service/mod.rs` 做法: - 每次任务执行都创建 run - `source = heartbeat` - `source_ref = task_id 或 task_description` - 首期可保留旧 `heartbeat_executions`,但需设置退场计划(见第 10 节) --- ## 8. 前端最小改造 1. 新增轻量查询接口(`list_runs/get_run`) 2. 聊天与任务页面先展示统一状态徽标(不新增复杂页面) 3. run 详情仍跳转现有日志与消息明细,避免重复 UI --- ## 9. 配置与灰度策略 新增配置建议: - `agent.execution_tracker.enabled`(默认 `true`) - 环境变量覆盖:`PROXYCAST_EXECUTION_TRACKER_ENABLED` 灰度步骤: 1. 内部开启(开发/测试) 2. 小流量开启(beta 用户) 3. 全量开启后进入旧路径收敛阶段 --- ## 10. 实施计划(P0) ### Phase A:基础能力(2-3 天) 1. 新建 `agent_runs` migration 2. 实现 `execution_tracker` 服务 + `with_run` 3. 新增 DAO 与单元测试 ### Phase B:接入 Skill(1-2 天) 1. 接入 `skill_exec_cmd.rs` 2. 验证状态流转与错误归一化 3. 前端显示最小状态徽标 ### Phase C:接入 Chat 与 Heartbeat(2-4 天) 1. 接入 `aster_agent_cmd.rs` 2. 接入 `heartbeat_service/mod.rs` 3. 增加跨入口一致性测试 ### Phase D:收敛与清理(1-2 天) 1. 标记旧追踪路径为 deprecated 2. 清理重复状态转换代码 3. 完成技术债登记与后续移除计划 --- ## 11. 验收标准(架构 + 产品) ### 11.1 技术验收 1. run 覆盖率:chat/skill/heartbeat >= 90% 2. 终态一致性:无“running 悬挂”记录 3. 幂等性:重复 finish/fail 不改写终态 ### 11.2 产品验收 1. 故障定位可追溯率 >= 95% 2. 平均排障时间下降 >= 50% 3. 前端状态一致性(同一执行在各页面状态一致) --- ## 12. 风险与应对 1. **状态竞争条件**:通过 DB 事务 + 终态幂等保护 2. **双写长期化**:设置明确退场日期,进入 roadmap 追踪 3. **性能回归**:metadata 严格控量,避免大字段写入 4. **认知负担上升**:仅增加“run 摘要层”,不复制业务明细 --- ## 13. 成功标准(Definition of Done) 满足以下条件即判定 P0 完成: 1. 三个执行入口全部接入 `execution_tracker` 2. 至少一个统一查询接口可用于调试与 UI 展示 3. 旧路径进入可下线状态(有迁移清单和时间点) 4. 无新增重复生命周期逻辑(通过 code review checklist 约束)