用户报的"探索完无法点击创建任务"真因:一个 run 的事件原先只有**一条** queue.Queue, 聊天页与建任务页同时开着时两个 EventSource 会**瓜分**它——建任务页的回放卡在中间、 `done` 被聊天页取走 → 永远等不到草稿,页面上自然没有可点的"创建"。 一、修(根因 + 表现) - `web/agent_api.py` 新增 `_Fanout`:**每个订阅者一个专属队列**,多开页面各看各的, 还带单轮事件缓冲(晚订阅/刷新重连也能补齐回放,终止事件一定送达)。 实测两路订阅者收到完全一致的 1039 条事件(含 done)。 - `static/admin/agent.js`:断线重连的兜底订阅也按 `mode` 让开(此前漏了这一处)。 二、补齐上一批的三项 - **「直接创建」**:`POST /api/agent/task_draft/create`(草稿体只在服务端、创建前再校验一次、 成功后清草稿避免重复建)+ 草稿预览里的「✓ 直接创建任务」按钮 + 「探索完直接创建任务」勾选框。 - **草稿沉淀经验/动作**:designer 轮次也走 `_distill_experience/_distill_actions`, 但**只在草稿通过校验时**(没走通的试错不入库,免得把误点当经验)。 - **MCP `de_snapshot`**(第 20 个工具):截图+元素树一次取齐(省一次来回、不会因界面在动而错位), 附带 `screen_state`/`unstable`;两套提示词都改为优先用它。 平台侧 `/api/uiauto/snapshot` 随之多返回 `screen_state`。 三、文档 - AI_TASK_GEN §10:§10.3 记两个 bug 的真因与修法、§10.4 三项标完成、§10.5 剩余项。 - AI_CONSOLE(扇出语义、多页面同时看一轮)、API(task_draft/create、snapshot 字段)、 MCP/MCP_DESIGN/staffdeck/README/ARCHITECTURE:工具数 19→20 + de_snapshot 条目。 - backlog:记一条新发现的缺陷——`mcp_server/platform_client._login()` 会把"登录页 200" 当成登录成功(现场进程缺 `MCP_PLATFORM_PASS` 时表现为含糊的 platform_unavailable)。 自测:真机浏览器端到端(勾上"探索完直接创建")→ 探索 12 步 → 草稿 → 自动建任务成功; `de_snapshot` 直连真机校验;校验器 21 条用例、扇出单元用例、本地工具契约用例全绿。 自测产生的任务/草稿已全部清理(未碰用户既有数据)。
133 lines
10 KiB
Markdown
133 lines
10 KiB
Markdown
# 待完成项 / 已知问题(Backlog)
|
||
|
||
> 用途:记录**已确认但暂缓**的功能、优化与**已知缺陷**,避免丢线索。完成时移出本文件,并按「doc 同步红线」更新对应文档。
|
||
> 维护:新条目写清 **背景 / 期望 / 涉及文件 / 验收**;缺陷写清 **现象 / 复现 / 影响**。
|
||
> 相关:[doc/README.md](../README.md)(文档索引与维护约定)。
|
||
|
||
---
|
||
|
||
## A. 已知缺陷(尚未修)
|
||
|
||
### A1. `GET /locate` 返回 500(设备定位大字页打不开)
|
||
|
||
- **现象**:访问 `/locate?serial=…` 直接 500。
|
||
- **原因**:`web/monitor.py` 的 `locate_page()` 用了 `render_template_string` 与 `_esc`,但该文件只导入了 `render_template`,也没有 `_esc` 定义。
|
||
- **复现**:`curl -s -o /dev/null -w "%{http_code}" "http://127.0.0.1:18050/locate?serial=1.2.3.4:5555"` → 500(路由扫描脚本已确认)。
|
||
- **影响**:「工具 → 设备池管理 → 定位」在设备上打开的大字页不可用(亮屏那半仍生效)。
|
||
- **涉及**:`web/monitor.py`(补 import / 补转义实现,或改用 `render_template` + 模板文件)。
|
||
- **验收**:`/locate` 返回 200 且页面正确显示 serial/IP;纳入回归脚本。
|
||
|
||
### A2. `POST /api/device/locate`(`show=true`)失败
|
||
|
||
- **现象**:带 `show=true` 调用时抛异常 → 502。
|
||
- **原因**:`web/monitor.py` 用了 `urllib.parse.quote` 但未 `import urllib`。
|
||
- **影响**:无法远程把设备浏览器打开到定位页。
|
||
- **涉及**:`web/monitor.py`。**验收**:`show=true` 时设备浏览器打开定位页且返回 200。
|
||
|
||
### A3. CSRF 实际未启用
|
||
|
||
- **现象**:`/api/csrf` 会签发 token、前端所有非 GET 请求都会带 `X-CSRF-Token`,但服务端**从不校验**。
|
||
- **原因**:`web_server.py` 只 `from web.auth import _csrf_protect`,**没有注册** `app.before_request(_csrf_protect)`;而 `web/auth.py` 的 docstring 声称"由 web_server 注册"。
|
||
- **影响**:CSRF 防护形同虚设(同网段内可伪造写请求)。
|
||
- **涉及**:`web_server.py`(注册钩子)+ 需要回归所有写接口(前端、脚本、MCP 的 `platform_client` 都已带 token,风险点是第三方直接调 API)。
|
||
- **验收**:注册后所有写接口在缺 token 时返回 403,且前端/MCP 全链路正常。
|
||
|
||
### A5. 前端 `--card-line` 变量未定义
|
||
|
||
- **现象**:AI 控制台的会话/经验/动作模态框边框失效(`border:1px solid var(--card-line)` 解析为无效值)。
|
||
- **原因**:该变量只在 `templates/admin/wall.html` 的 `:root` 定义,`monitor.html` 的 `:root` 里没有,但被引用了 5 处。
|
||
- **涉及**:`templates/admin/monitor.html`(`:root` 补 `--card-line:#232936`)。
|
||
|
||
### A6. `monitor.js` 引用了未定义变量 `countParts`
|
||
|
||
- **现象**:进度为 0 的边界分支会抛 `ReferenceError`(被外层 `try/catch` 吞掉,用户无感)。
|
||
- **涉及**:`static/admin/monitor.js`(该分支的进度渲染)。
|
||
|
||
### A7. 其它小问题(低优先)
|
||
|
||
- `static/admin/custom.css` 是**死文件**(无任何引用)。
|
||
- `templates/admin/monitor.html` 里 `.agent-shell` 有**完全重复的 CSS 声明**(后者覆盖前者的 `min-height`)。
|
||
- 设备表空态 `colspan="11"` 与实际列数(10)不符。
|
||
- `core/device_worker.py` 的 `_u2_connect_remote` 有两个结构相同的 `except Exception`,**后者是死代码**。
|
||
- `core/ssh_client.py` **全项目无人 import**(`STF_SSH_*` 配置也只被它读取);要么接上用途,要么删除。
|
||
- 若干按功能域拆包时留下的**重复函数定义**:`_merged_device_list`(`web/common.py` / `apks_api.py` / `tools_api.py` 各一份)、`tools_api.py` 的三个辅助函数、`monitor.py` 的屏相关函数、`tasks_api.py` 的 `_job_next_run`(各定义两次)。
|
||
|
||
---
|
||
|
||
## B. 设备与连接
|
||
|
||
- [ ] **adb 远程终端加「目标切换」(本机 / 220)**
|
||
- 背景:终端默认走 `.env` 的 `ANDROID_ADB_SERVER_ADDRESS=192.168.20.220`(adb 客户端生效,非本项目代码读取),因此终端里看不到**本机 USB 设备**。
|
||
- 期望:`/api/adb/devices`、`/api/adb/cmd` 支持 `target=local|remote`;前端 `tools.js` 加下拉。
|
||
- 涉及:`web/tools_api.py`、`static/admin/tools.js`、`doc/API.md`、`doc/DEVELOPMENT.md`(配置速查)。
|
||
- 验收:切「本机」能列本机 USB + 已连 `IP:5555`;切「220」列 220 侧设备;`adb cmd` 同样生效。
|
||
|
||
- [ ] **`.env` 的 adb 路由键要在文档里说明(或去掉)**
|
||
- `ANDROID_ADB_SERVER_ADDRESS/HOST/PORT` 会被 `.env` 注入 `os.environ`,**adb 客户端**据此把所有调用指向 220;`ANDROID_ADB_SERVER_HOST` 是非标准键(adb 不认)。
|
||
- 期望:在配置速查里写明该行为与取舍,或本地去掉这三行改走本机 adb。
|
||
|
||
- [ ] **`.gitignore` 漏项**:`data/mcp_audit.log` 已补(`data/*.log`,2026-09-13);**`data/uiauto.pid` 仍未忽略**(`git status` 会显示为未跟踪);`data/apks/` 目录无占位文件。
|
||
|
||
- [ ] 220 的 Tailscale / adb server(5037) 可达性巡检(曾出现连不通超时)。
|
||
|
||
---
|
||
|
||
## C. 编辑器 / 元素抓取
|
||
|
||
- [ ] **未命中要「明确提示」,别混成「执行了这步」**
|
||
- 背景(2026-09-10):click/long_click/wait_el/swipe_until/if_el 未命中时只写 `[WARNING] ... 未找到元素`,前端进度仍按"执行了一步"计数 → 用户看到像"成功",实际没点(db03f16e 案例就是这么误判的)。
|
||
- 期望:执行器把**未命中**作为该步结果(✗)返回并计入统计;前端标红 ✗ 并给出所用选择器;任务结束汇总未命中步骤数。
|
||
- 涉及:`tasks/generic/task.py`、`static/admin/{tasks,monitor}.js`、`doc/TASK_DEV.md`。
|
||
|
||
- [ ] **元素抓取超时——部分设备 dump 慢于平台超时**
|
||
- 背景(2026-09-10 实测):`core/uiauto_helper.py` 的 `_TIMEOUT=(1,8)`;某设备 dump **~18s** → 平台报「uiauto2 请求超时」,编辑器抓不到元素(设备本身正常)。
|
||
- 期望:读超时提到 `(2, 30)` + 前端加载中有明确提示;可选"同界面短时缓存"。
|
||
- 涉及:`core/uiauto_helper.py`、`web/tasks_api.py`、`static/admin/editor.js`、`doc/ARCHITECTURE.md`。
|
||
|
||
- [ ] **序号型 XPath / 界面就绪 的防呆**(2026-09-10 db03f16e 案例)
|
||
- 背景:`(//*[@resource-id="…"])[6]` 依赖"抓取那一刻该属性有 ≥6 个实例";运行时界面不同(App 仍在闪屏页)→ 序号必然失配。
|
||
- 期望:① `open_app` 提示勾选「等待首页」;② ~~抓取器对**带序号**的选择器加醒目提示~~(✅ 2026-09-13 已完成,且更进一步:抓取器**优先产出语义选择器**再去掉序号,见 [research/U2_ELEMENT_SELECTORS.md](../research/U2_ELEMENT_SELECTORS.md) §五);③ 抓取弹窗提醒"请先把设备停在任务运行到该步时的同一界面再抓"。
|
||
- 涉及:`core/uiauto_helper.py`、`static/admin/editor.js`、`tasks/generic/task.py`、`doc/TASK_DEV.md`。
|
||
|
||
- [ ] 自定义动作支持 `action_ref` 引用型节点(现状:拖入画布会**展开成 group**,改动需同步执行器 + 编辑器)。
|
||
|
||
---
|
||
|
||
## D. AI 控制台 / 经验与动作
|
||
|
||
- [ ] 「🧠 经验库 / 🎬 动作库」合为一个面板(标签页切换)。
|
||
- [x] ~~AI 建任务 P0~~(✅ 2026-09-13:独立子页「AI 建任务」+ `submit_task` 校验 + 草稿预览/预填;✅ 2026-09-14 补齐「直接创建」`POST /api/agent/task_draft/create`、草稿→经验/动作沉淀、MCP `de_snapshot`。见 [AI_TASK_GEN.md](../AI_TASK_GEN.md) §10)。
|
||
- 仍未做:平台级 MCP 只读清单(`task_types/groups/pool`)、把**动作库**当 generic_steps 预制件复用、探索成本控制。
|
||
- [ ] `mcp_server/platform_client._login()` 对"登录失败"不设防:平台把登录页当 200 返回时会被当成
|
||
登录成功,后续所有调用都拿不到 JSON,最终只报含糊的 `platform_unavailable`(2026-09-14 排查
|
||
`de_snapshot` 时踩到:现场进程没带 `MCP_PLATFORM_PASS`)。应校验响应内容/最终 URL,并明确报
|
||
"平台账号或密码不对"。
|
||
- [ ] 新增 MCP `de_screen_text`(文本化看屏:前台包名 + screen_state + 可点元素文本 + OCR),并把操作纪律写进工具 description。
|
||
- [ ] 经验召回改进:现为 bigram + `ORDER BY hits`(候选只看 top50、hits 对所有命中行回写 → 马太效应);改为对称相似度 / 更合理候选集,`hits` 仅在真正注入时 +1。
|
||
- [ ] 经验/动作入库依赖模型蒸馏成功:加失败重试与可视化观测(现在只在日志里)。
|
||
|
||
---
|
||
|
||
## E. 工程化 / 文档
|
||
|
||
- [ ] **MCP 白名单语义缺口**:`MCP_ALLOWED_SERIALS` 为空时并未按设计限制为"平台设备池内"(当前只校验非空),见 [MCP.md](../MCP.md);如需收紧需补实现。
|
||
- [ ] **`MCP_DESIGN.md` 里的"演进备选"(独立 mcp-server 容器)未落地**:已在文中标注,若确定不做可删除该节,避免后续误读。
|
||
- [ ] **STF 容器状态确认**:`doc/DEVELOPMENT.md`(已停用)与 `doc/STF_REMOVAL.md`(待人工确认)说法矛盾,需以 220 实际为准后统一。
|
||
- [ ] **`DISCOVERY_SUBNETS` 无 env 支持**:`config.py` 里是硬编码列表,其余发现配置走 `app_meta`;若要支持 `.env` 覆盖需补实现。
|
||
|
||
---
|
||
|
||
## F. 已完成(留档,便于追溯)
|
||
|
||
- [x] `platform-tools` → `auto_control` 命名统一(2026-09-10:文档/README/`scripts/pack.py` 产物名)。
|
||
- [x] 删除抖音养号任务类型(`douyin_nurture`),平台只保留 `generic_steps`;残留旧类型任务改为**启动告警 + 执行时明确报错**。
|
||
- [x] `generic_steps` 去掉默认步骤;空步骤任务执行时明确报错(不再静默空跑)。
|
||
- [x] 重试耗尽时的 `last_error` 带上真实失败原因(不再只有"重试 N 次失败")。
|
||
- [x] 监控页任务卡去掉「编辑/删除」,只留执行/停用 + 显示**覆盖设备**。
|
||
- [x] 备份覆盖清单补 `agent_action` / `app_meta` + 导出/导入双向自检。
|
||
- [x] AI 控制台:Markdown 渲染、推理链可折叠、token 用量显示。
|
||
- [x] 路由回归脚本 Windows 可用(2026-09-13:`signal.alarm` 加 `hasattr` 守卫)+ 新增安全闸:
|
||
配置或目标库被标为 `prod` 时**拒绝运行**(脚本会发真实写请求,不能拿生产数据做实验)。
|
||
- [x] 数据库从 SQLite 迁到 MySQL:连接层多环境配置 + 防混库校验、schema 收敛到 ORM metadata、
|
||
备份/恢复改为「SQLite 归档 + 单事务整库替换」、迁移脚本(2026-09-13)。
|