Files
butubb 876224f876 feat(去重): 跨设备「已做过」账本 —— 同一个号不会做两次 + 「谁做过了」看得见
用户场景(他原话):一台手机登录 5 个抖音号、一共 5 台手机,每个任务只让其中一个
目标号评论;每天跑一次但不知道什么时候跑完,于是"一直重复跑" → 结果
"一个手机还没评论到,一个手机都评论两次了"。

**根因不是"单设备重复",是跨设备没有共享的判断 + 进度不可见。** 所以做两件事:
① 幂等;② 把"谁做过了、还差谁"摆到台面上(不然只能靠重跑确认,而重跑又在制造重复)。

- `core/models.py`:新表 `done_mark`(迁移账本补 v7)。**判据只有 `scope_key` 的
  唯一索引**——多台设备会同时判断"没做过","先查后插"有竞态(两台都插),
  唯一索引 + `INSERT ... ON DUPLICATE KEY`/`INSERT OR IGNORE` 的**受影响行数**才原子。
- `core/dedup.py`(新):`build_key`(`任务|身份|时间桶`)/ `check` / `mark` /
  `list_marks`(带"今天做了几台/几个号"统计)/ `delete_mark` / `clear_job` / `purge_old`。
  自建 app context(照 device_pool 的 `_ctx()`),任务线程/Web/清理都不用关心。
- 任务侧两个部件(**检查在前、记账在后**):
  · `if_el` 新增条件类型 `selector_type="dedup"`:命中=这个身份做过了 → 走 then 分支。
    身份元素在 `ident_type`/`ident_value`(留空 = 用设备 serial,一号一机场景)。
  · 新步骤 `mark_done`「记为已做」(22 种步骤):放动作**成功之后**。
  拆两步的用意:动作失败就不记账,下次重跑还会重试该设备 —— 失败不丢。
- 有效期(`dedup_reset` = day/all/hours)放**任务级**:检查与记账两处各填一份的话,
  填不一致就算出两个 key、去重会**静默失效**,所以强制只配一处(编辑器顶部下拉)。
- 三条防误伤规则(都有测试兜着):
  · 身份读不到 / 身份值过长 → **不去重、当没做过照常执行**。绝不能把"读不到"
    当成空身份——那会让所有设备共用一个 key、第一台记账后其余全被误判成"做过"。
  · `kind='all'`(只做一次)的记录**永不清理**(清了等于语义失效);清理只删 day/hours。
  · 去重的两个易错点在保存时直接告警:身份元素两边不一致、有检查没记账/有记账没检查。
- 「任务 → 去重记录」新子分栏(`static/admin/dedup.js`):统计行 + 明细表 +
  删单条(那个号重跑)/ 清空任务(整批重跑)。接口 3 个(GET/delete/clear,PERM_TASKS)。
- 每日 04:23 清理(挂现有 APScheduler),`TABLE_LABELS` 补中文名(备份覆盖自动派生)。
- AI 建任务草稿校验同步:`dedup` 走自己的规则(要 ident_value、xpath 前缀校验),
  没填身份元素只警告不拦(用设备当身份是合法用法);普通条件空选择器仍然拦。
- 文档:TASK_DEV §4.6(去重专章 + App 内检测的兜底配方与它的三个局限)、
  DATA_MODEL §2.9、API 三个接口、ARCHITECTURE(分层/装配/子分栏/JS 分工/清理)、
  DEPLOY §5.2(15 张表)、步骤数 21→22 全库同步。

自测:单元 + 集成 33 项(**含 8 线程抢同一个身份、恰好一个成功**的原子性断言,
以及"all 记录不被清理""身份读不到不去重""清了能重跑")、
**真机端到端**(cs1 上"检查→动作→记账"跑两遍:第二遍被拦、换 serial 的"另一台设备"
同样被拦、删记录后能重跑)、草稿校验 5 项、GET 冒烟 56 路由 0 个 500。

(注:本分支基于 feat/if-el-multi-value,因为它俩都要改 task.py 的 STEP_TYPES 与
editor.js 的 STEP_LIB 同一区域,分开从 dev 拉必然冲突——这份是超集,合一次两份都进。)
2026-09-24 10:55:29 +08:00

22 KiB
Raw Permalink Blame History

AI 建任务(AI Task Generator)设计文档

状态:P0 已实现(2026-09-13)——§1~§9 是设计稿(与实现基本一致), §10 是实现记录:差异、落地细节、怎么自测,以 §10 为准。 现状请读:AI_CONSOLE.md(AI 控制台已实现的能力)、TASK_DEV.md(任务与步骤)、MCP.md(已实现的 20 个工具)。 关联代码:web/agent_api.py、mcp_server/、mcp_agent/、tasks/generic/、static/admin/editor.js。最后核对:2026-09-10。

1. 背景与目标

平台已有两套能力,但互不相通:

  • AI 控制台:一句话 + 选设备 → 多模态 Agent(DeepSeek)通过 20 个 de_* 工具在手机上「边看边做」(截图看屏、de_ui_tree 拿元素树、de_tap_element/de_tap_text 语义点按),流式回放步骤。
  • 任务系统 + 步骤编辑器:generic_steps 任务 = 一棵可嵌套步骤树(open_app/click/swipe/loop/group/if_el…22 种节点),在编辑器里拖拽编排、单步试跑、定时调度。

目标:让非工程用户用一句自然语言需求(例:「创建一个每日养号刷视频的任务,每天 8:00-9:00 在 100.100.10.13 跑」)得到一条可直接调度、可继续在现有步骤编辑器里手改的任务。AI 先自己在设备上打开 App、看 UI 树、确认可点元素,再直接撰写编辑器的步骤 JSON。

产品体验(一页)

  1. 用户进入「AI 控制台 → 模式=AI 建任务」,选一台空闲设备,填需求 +(可选)任务名/调度/目标。
  2. AI 自探并流式回放(工具卡 + 截图,与现在一致):开 App → dump UI 树 → 确认要点的元素可命中 → 写步骤。
  3. 完成后平台返回任务草稿 draft,服务端 schema 校验后,**前端直接打开现有「新建任务 → 步骤编辑器」**预填。
  4. 用户核对/手改/单步试跑 → 保存 → 进任务列表,走原有调度器执行。

核心信任原则:AI 只“提案”,不直接建库、不直接执行最终任务;最终落库/执行都在用户确认后由现有机制完成。

2. 范围(P0 定稿,2026-09-09 与用户确认)

  • 入口:AI 控制台加「聊天 / AI 建任务」模式开关,复用会话/SSE/设备选择/停止/刷新恢复骨架。不新建顶级 Tab。
  • 产出方式:AI 直接撰写编辑器的步骤 JSON(非操作轨迹翻译)。自探信息只作上下文与审计;探索中的误点/多余截图不会混入任务。
  • 任务类型:只产 generic_steps(通用步骤)——平台当前唯一的任务类型 (原 douyin_nurture 预设参数生成已随该类型删除,不再做)。
  • 定位原则:全部基于 UI 树:
    • 模型定位/点击一律走 de_ui_tree 拿到的元素(text/id/description/text_contains),多实例用 index,或后端 uiauto_helper 生成的 //*[@resource-id=..][k] XPath;
    • de_tap_element 确认能命中后才写进 click 步骤;
    • 默认不产 click_xy 坐标点击;UI 树给不了的元素记入 notes,交给人工/OCR 兜底;
    • 滑动用方向语义 swipe{direction},不用像素坐标。
  • 产出完整任务信封:{name, task_type:"generic_steps", target, schedule, retry, enabled, params:{max_duration, steps}}。

3. 现状与可复用点(实现依据)

  • 多模态 Agent 链路:web/agent_api.py(POST /api/agent/run 起后台线程、SSE delta/step/done/error、/stop、刷新恢复 GET /api/agent/run)→ mcp_agent/agent.py(OpenAI 兼容流式,工具经 fastmcp Client 拉 8033 的 20 个 de_*)→ mcp_server/mcp_server.py。
  • 前端 AI 控制台骨架:templates/admin/monitor.html #tab-agent + static/admin/agent.js(设备选择、sendAgentMsg、listenStream 渲染 agent-toolcard、实时画面跟随)。
  • 步骤权威 schema(前后端同构):tasks/generic/task.py STEP_TYPES(L44-83)/ DEFAULT_PARAMS(L86-103) 与 static/admin/editor.js STEP_LIB(L3-23)。
  • 元素抓取 → XPath:web/tasks_api.py /api/uiauto/elements(L224)→ core/uiauto_helper.get_elements(suggested.value 即编辑器可用的 XPath)。
  • 单步试跑:/api/steps/test(tasks_api.py L249)+ editor.js _testStep;tasks/generic/task.py test_step(L633)。
  • 任务/调度/目标:core/task_manager.py TaskJob(L121-199);POST /api/jobs(tasks_api.py L68)。
  • 设备占用语义:MCP 写工具 _ensure_device_free(mcp_server.py L60-78,running/connecting → device_busy);agent_api run 入口同样对 worker running/connecting 拒绝(409)。
  • 经验库自进化:agent_experience + bigram 检索注入 + 每日巡检(web/agent_api.py)——P1 沉淀模板的现成载体。

4. 架构与数据流

需求+设备(空闲) ──▶  Designer Agent(自探, 模式=designer)
                        │  de_open_app / de_ui_tree / de_screenshot / de_tap_element…
                        ▼
                平台工具 submit_task(draft)  ← 结束性调用
                        │  服务端 draft schema 校验(白名单+必填+深度≤5)
                        ▼
        前端: 打开 openTaskModal 步骤编辑器, 预填 draft.task
                        │  用户核对/手改/单步试跑(/api/steps/test)
                        ▼
                POST /api/jobs  → 任务列表(原调度器执行)
   (P1) draft+需求 沉淀 agent_experience, 相似需求注入参考

4.1 Designer Agent(新增模式,复用现有 Agent)

  • mcp_agent 增加 designer 系统提示词:角色=自动化任务设计师;行为约束见 §5.1。
  • 增加平台级工具(不属设备 de_*):
    • submit_task(draft):结束性工具,模型完成自探后提交草稿即停止,服务端立即校验。
  • 会话内同时记录结构化 trace(每轮 on_tool 的 {tool, args 精简, 屏号/证据}),用于:校验证据(每个 selector 来自哪次树)、审计、P2 回放。
  • POST /api/agent/run 增加 mode:"designer";done 事件负载携带 draft(校验通过)或 draft_error(校验失败+原因,让模型补一轮)。

4.2 draft 契约

{
  "summary": "每日8-9点刷抖音养号:开抖音→循环(看5~35s+上滑)+随机间隔",
  "task": {
    "name": "抖音每日养号",
    "task_type": "generic_steps",
    "target": {"mode":"serial","serial":"100.100.10.13:5555"}
            | {"mode":"group","group_name":"测试"} | {"mode":"all"},
    "schedule": {"mode":"once"} | {"mode":"cron","cron":"0 8 * * *"}
               | {"mode":"cron_stop","cron":"0 8 * * *","stop_cron":"0 9 * * *"},
    "retry": {"max_attempts":5,"delay":30},
    "enabled": true,
    "params": {
      "max_duration": 0,
      "steps": [
        {"type":"open_app","label":"打开抖音","params":{"package":"com.ss.android.ugc.aweme","wait_home":true}},
        {"type":"loop","params":{"loop_mode":"rounds","max_iterations":30,"children":[
           {"type":"wait","params":{"min":5,"max":35}},
           {"type":"swipe","params":{"direction":"up","duration_min":0.25,"duration_max":0.5}}
        ]}}
      ]
    }
  },
  "notes": ["评论按钮需先进入视频页才可见"],
  "evidence": [{"screen":"抖音首页","element":{"text":"关注","id":"..."},"xpath":"//*[@resource-id=\".../gvo\"]"}]
}
  • 步骤节点结构:{id?, type, label?, params};id 执行端忽略(编辑器重新生成),params 必填。
  • 必填字段语义(空值会被执行端静默跳过,校验器必须拦):
    • click / long_click / swipe_until / wait_el / if_el → params.selector_value
    • open_app / stop_app → params.package
    • 容器:loop / group → params.children(非空);if_el → params.then(else 可选)
    • swipe_until → direction + max_swipes;click_xy P0 不产(如允许则 x/y 0-100)
    • 节点公共可选 params.probability(0-100,缺省 100)
  • selector_type 允许值:xpath / description / text / resourceId / descriptionContains / className(if_el 可 ocr)。

4.3 服务端新增

  • core/task_draft.py:
    • STEP_TYPES 白名单 + 每类必填/深度校验 validate_steps(steps, depth)(嵌套≤5);
    • validate_draft(draft):任务信封(name 非空、task_type==generic_steps、target mode ∈ {all,group,serial}(group 名存在)、schedule cron 合法、steps 校验);
    • 归一化:把 schedule 里「每天 8-9 点」这类由前端/向导填的值转成 cron/cron_stop。
  • web/taskgen_api.py(或并入 agent_api,推荐并入以最大化复用):
    • 入口检查:serial 必须、设备在池/在线、worker 非 running/connecting(409,与现有语义一致);
    • 起 designer 后台线程;SSE 事件在现有 delta/step/done/error 基础上,done 可带 draft。
  • 注意:现有 POST /api/jobs 对 params 盲存(只校验 name+task_type)。AI 通道在保存前必须过 validate_steps,避免「任务 done 但什么都没做」(执行器对未知 type/空 selector 静默跳过)。

4.4 前端

  • monitor.html AI 控制台加模式切换;建任务模式下输入栏旁有折叠「任务设置」(名称/调度时间/目标 serial·分组·全部/备注)。
  • agent.js:done 携带 draft 后:
    • generic_steps → 调 openTaskModal()(tasks.js)并把 draft.task 灌入步骤编辑器(step 卡片可视化、可拖改、单步试跑、保存);
    • 弹窗内对 notes(含“需人工复核/OCR 兜底”项)给出醒目提示。
  • 过程回放沿用现有 agent-toolcard 渲染;可标记当前为 designer 轮以便后续区分。

5. 约束与安全(红线)

5.1 Designer 自探规则(写入提示词)

  1. 先 de_open_app(package),再 de_ui_tree + de_screenshot 看每屏;点到关键状态后再 dump 下一屏。
  2. 每个将写入步骤的目标元素,先用 de_tap_element(by=text/id/desc…)+ 截图确认可命中,并记下证据。
  3. 不做破坏性动作:需“评论/发送”时只确认输入框/发送键存在,不真发;产物里这类步骤 probability 调低并在 notes 标注“请人工复核”。
  4. 探索步数上限(P0 建议 30 步),可被 /stop 打断;结束后尽力还原前台 App。
  5. 未命中的元素一律不进任务;拿不准的进 notes 而非硬编。

5.2 平台级约束

  • 自探/试跑只在用户选的空闲设备(busy → 409),杜绝与运行中任务在设备上物理打架。
  • AI 不直接建库;生成任务仍需用户点保存(POST /api/jobs 现有权限)。
  • 尽量不写死坐标;P0 默认禁 click_xy,产物以树元素定位为主。

6. 里程碑

  • P0(本设计主体):designer 模式 → 自探(UI 树定位)→ 直接撰写 generic_steps draft → 服务端 schema 校验 → 前端步骤编辑器预填确认保存。验收:一句话在真实设备上生成一条可调度的 generic_steps,步骤全部来自 UI 树且编辑器可打开。(平台只剩 generic_steps 一种任务类型,与原规划一致)
  • P1:模板沉淀:把 draft+需求写入 agent_experience(新列存结构化 steps 或 JSON),相似需求注入参考;整链「演示试跑」(把 steps 在设备上以受控方式跑一遍并截图回报,需新增端点,复刻 device_busy 拒绝语义)。
    • 进展(2026-09-10):动作级沉淀已落地——agent_action 表 + 从成功步骤蒸馏"命名动作"(steps 用编辑器 schema、带元素定位、禁坐标)+ 执行前按名/别名召回注入(web/agent_api.py)。AI 建任务可直接把这些动作当作 generic_steps 的预制件复用。
  • P2:自定义动作支持(内联展开成 group,或新增 action_ref 节点 + 执行器/编辑器同步);多设备并行;成本与 token 控制。

7. 实现时需新增/改动文件(规划)

  • 改:mcp_agent/(designer 提示词与 submit_task 工具、结构化 trace)、web/agent_api.py(mode=designer、done 带 draft)、static/admin/agent.js + templates/admin/monitor.html(模式切换/任务设置/draft 预填)、doc/(本文档关联)。
  • 新:core/task_draft.py(schema+校验+归一化)、(可选)web/taskgen_api.py。
  • 不动:任务执行器、调度器、POST /api/jobs 主体(保持现有盲存,只在 AI 通道校验)。

8. 验收(P0 实现后自测)

  1. 目标设备空闲时:需求「每日 8-9 点刷抖音养号」→ 生成 generic_steps 任务,步骤为 open_app → loop(wait+swipe) 结构,调度 cron_stop 8-9 点。
  2. 打开编辑器中该任务:步骤卡片完整、可拖改、单步试跑命中;保存后任务列表出现且下次运行时间正确。
  3. 反例:模型产出含 click_xy 或未知 type / 空 selector → 服务端校验拦截并让模型补正;busy 设备入口 409。
  4. 探索全程可在前端回放(工具卡+截图),未发送真实评论/未污染设备状态。

9. 需要转成 MCP / 平台工具的能力(分层,2026-09-09 与用户确认)

背景问答结论:目前 MCP 只有设备层 20 个 de_*(控制 + 只读 de_list_tasks),平台 CRUD(任务增改/启停/立即运行、分组、设备池、自定义动作、APK、备份、用户)都还没 MCP 化。方向认同「先把工具链补完善」,但不做"把所有平台 CRUD 一次性搬成 MCP"的大而全——按消费方(AI 建任务 / 外部自动化)分层、按需补。新增 MCP 工具一律:进 doc/MCP.md 手册 + doc/MCP_DESIGN.md 规格 + 与 web 同源的权限/busy/审计 + 校验逻辑下沉到 core/ 共用(防双份漂移)。

9.1 现状盘点

  • MCP(mcp_server/mcp_server.py,20 个 de_*)= 设备控制 + 设备只读 + de_list_tasks(平台任务只读)。
  • 任务创建/修改/删除、toggle、立即运行、分组、设备池管理、自定义动作、APK、系统备份等 REST 路由只给前端用,未暴露 MCP。

9.2 P0 —— AI 建任务链路真正需要的「平台工具」(最小集)

只补两小类,其余设备操作全部复用现有 de_*:

  1. 只读清单(供自探确定 target/能不能做):
    • list_task_types / list_groups(target 选 group 用)/ list_pool(可调度设备,含 busy 状态)
    • (可与现有 de_list_devices 合并语义,避免重复)
  2. 校验 + 提交(结束性):
    • submit_task(draft) → 服务端用 共用 core/task_draft.validate_steps()/validate_draft() 校验,不直接落库,返回 draft 供前端打开步骤编辑器预填、人工确认后走 POST /api/jobs。

关键:P0 的 Agent 不暴露任务 CRUD 写权限(create/update/toggle/run),否则模型可绕过"AI 提案 → 人工确认"直接入库,破坏信任模型。

9.3 P1 —— 外部自动化 / 后续 Agent 的「写 MCP」(受权限约束,逐块加)

若目标延伸为"外部程序能像调 REST 一样操控平台",则按此清单逐个补(每加一个都做权限+busy+审计+校验下沉):

  • 任务:create_job / update_job / delete_job / toggle_job / run_job_now / query_jobs
  • 分组:list_groups / create_group / update_group / delete_group
  • 设备池:pool_list / pool_add / pool_remove / pool_toggle
  • 自定义动作 / APK 清单 等视使用再加
  • 只读清单优先搬;写类确认有真实消费方再做,避免空转。

9.4 分层与登记(红线)

  • 每新增/修改/删除一个 MCP 工具或平台配置:同步更新 doc/MCP.md(全清单)、doc/MCP_DESIGN.md(规格/层级),并在提交里体现——见全局「doc 同步红线」。
  • core/task_draft.py 是 web 校验与 MCP 校验的唯一来源,杜绝两套规则漂移。

10. 实现记录(2026-09-13)

10.1 与设计稿的差异(以本节为准)

设计稿 实际实现 为什么
聊天页加「模式」开关 AI 控制台下的独立子分栏「🧭 AI 建任务」 用户要求独立页面;探索回放 + 草稿预览需要自己的版面
submit_task 作为平台工具(§9.2,未指定放哪层) Agent 本地工具(mcp_agent/agent.py 的 LOCAL_TOOL_SPECS),不进 MCP Server 放进 MCP 等于给外部客户端开"写任务"的口子,还要连带改 MCP.md/MCP_DESIGN.md 与审计;收益为零
draft 经前端灌进步骤编辑器 同上,但草稿先落 app_meta(agent_task_draft,只留最近一份) 刷新/重进页面能拿回来;不新建表,绕开备份覆盖红线(SUMMARY_TABLES / DEPLOY §3.5)
(未提) designer 跑不绑会话、不吃聊天历史,且不沉淀经验/动作 探索轨迹里有试错与误点,沉淀会污染记忆库;"draft→经验"归到 P1
—— 校验失败把 errors 原样回灌模型,最多重提 3 次(提示词约束) 这是"AI 写坏任务"的唯一闸门:POST /api/jobs 对 params 盲存、执行器又静默跳过错误步骤

10.2 关键实现点

  • 校验器:core/task_draft.py。validate_draft() 逐条复刻执行器的"静默跳过点" (未知 type / 空 selector / 空 children / 嵌套 >5 / 深度节点数 >60 / cron 非法 / 必填 params 缺失 / click_xy 直接拒)→ 返回可被模型读懂的 errors + 不拦的 warnings。 normalize_draft 负责兜底(任务名、target、schedule、retry、时长);页面上的任务设置 以 overrides 形式优先于模型给的。
  • 提示词:mcp_agent/agent.py 的 DESIGNER_SYSTEM_PROMPT(16 条规则:先看再动、 定位优先级、禁坐标、禁序号型 XPath、副作用动作只核对不真点、时长语义映射、 随机化三件套、规模上限、收尾方式)。
  • 护栏:单工具调用上限 40 次(Agent.tool_call_limit;总步数由 max_steps 兜底, 两个值都要大于正常重试次数,否则会把一轮探索截断在"改字段"上——实测踩过);输出上限 8192(designer); json.loads 容错(草稿被截断时返回可读错误而不是整轮崩)。
  • 前端:static/admin/taskgen.js + #agent-sub-taskgen(子 Tab 机制见 static/admin/base.js 的 showSubTab)。草稿预览能直接点「在步骤编辑器中打开」→ openTaskModal(null, prefill) 预填(用唯一 draftKey,避开 localStorage 旧草稿覆盖)。
  • 两个页面共用一个运行槽:GET /api/agent/run 回 mode,两个前端各按 mode 决定 是否订阅 SSE;服务端用 _Fanout 给每个订阅者一条自己的队列(谁都不丢事件)。
  • 切 Tab 不丢回放:initTaskGen() 每次进入子页都会 tgRestore()——已经在盯同一轮就 不重复订阅(否则服务端会把这一轮从头补发一遍,卡片成两份);换了一轮/刚刷新过页面才清空 回放区重新订阅(_Fanout 的历史缓冲会把已发生的事件补回来)。跟随画面的 MJPEG 长连接 在切回来时重新拉一次(tgRearmLive),避免定格。
  • 落库出口:页面上的「✓ 直接创建任务」(POST /api/agent/task_draft/create)与 「在步骤编辑器中核对」(预填编辑器 → 用户点保存)——两条路最终都汇到 add_job。

10.3 附带修掉的两个 bug

(1) 事件被另一个页面抢走(用户报的"探索完没法创建任务"的真因):一轮的事件原先只有一个 queue.Queue,聊天页与建任务页同时开着时,两个 EventSource 瓜分同一条队列——建任务页的回放 会卡在中间,done 事件被聊天页取走 → 永远等不到草稿 → 页面上没有可点的"创建"。 现改为扇出(web/agent_api.py 的 _Fanout):每个订阅者一个专属队列,多开页面各看各的; 前端两侧也按 mode 门控,不互相订阅。

(2) 工具消息配对

Chat 模式同样受益:一轮里若模型同时调了 de_screenshot 与别的工具,旧实现会把截图图像 作为一条 user 消息插在两条 tool 消息之间,模型侧判定"工具回应不足"直接 400 (An assistant message with 'tool_calls' must be followed by tool messages…)。 现改为:本轮 tool 消息发完后再附一条 user 图像消息;_repair_tool_messages 也改成 只数连续的 tool 消息。

10.4 后续批次(2026-09-14 已完成的三项)

  • ✅ 「直接创建」:POST /api/agent/task_draft/create + 页面上的按钮与「探索完直接创建任务」 勾选框。两条设计约束:草稿体只存在于服务端(客户端只能传 overrides,少一个可篡改入口); 创建前再校验一次,通过后走与「新建任务」完全相同的 context.mgr.add_job, 创建成功后清草稿(同一份草稿不会被重复建成多个任务)。
  • ✅ 草稿沉淀经验/动作:designer 轮次也走 _distill_experience / _distill_actions 沉淀,但只在草稿通过校验时(说明这轮探索确实走通了一条路;没草稿的试错不入库)。
  • ✅ MCP de_snapshot:截图+元素树一次取齐(20 个 de_* 工具了),并把 screen_state 一并返回;designer/聊天两套提示词都改为优先用 de_snapshot。 见 MCP.md §4.2。

10.5 仍未做

  • 多设备并行探索、探索成本/token 控制(一轮真机探索 3~30 万 token)。
  • 把动作库当 generic_steps 的预制件复用(设计稿 §9.2 的只读清单类 MCP 工具)。
  • 平台的 /api/uiauto/snapshot 目前只被 MCP 与抓取弹窗用,还可在 designer 里做"同屏缓存"。

10.6 自测怎么跑

  1. 纯逻辑:core/task_draft 的正反例(未知 type / 空 selector / click_xy / cron 少段 / 深度 6 / probability 越界 …),断言 errors 文案模型能读懂。
  2. 真机:AI 控制台 →「AI 建任务」→ 选空闲设备 → 描述需求 → 看回放 → 核对草稿 → 「在步骤编辑器中打开」→ 单步试跑 → 保存。
  3. 反例:需求里出现"自动评论并发送" → 草稿的这类步骤应是"只核对存在性"(notes 里提示 人工复核),探索期审计日志里不应有对发送键的 de_tap_*。
  4. 自测产生的东西(草稿/自建任务)用后即删,别碰用户真实数据。