Files
nodeagent/docs/RETROSPECTIVE.md
T
jkooe 3f91e1ddc0 docs: 新增《实战复盘:短板与改进优先级》
基于真机部署 + v2rayN 故障排查 + 重装前采集的真实使用,梳理短板:

P0(决定能不能用)
1. GUI 语义能力缺失 —— 有截屏有鼠标,但没有「界面元素 -> 坐标」的映射,
   切个节点反复点错三次,最后靠键盘导航才成功。建议引入 UIA。
2. 编码不统一 —— 三个真实事故均源于此:Set-Content 写 BOM 致 agent 起不来、
   PowerShell 管道损坏中文致清单报废、控制台 GBK 误判。
3. 自断生路 —— 重启 agent 时分离进程被自身进程树清理一起杀掉,导致失联。
   建议提供受控重启(system.agent.restart)+ 看门狗自愈 + 配置回滚。
4. exec 输出未结构化 —— 尾部附加 [退出码/耗时] 文本,污染 JSON 解析。

P1:长任务异步化、定时采样、剪贴板、窗口管理、CLI 常驻 daemon
P2:事件订阅、文件监控、录屏、拖拽/宏、多设备并行、DPAPI

一句话:能看能算能跑命令已扎实,短板在「语义」(读不懂界面)、
「状态」(只看当下)与「自持」(改不动自己)。
2026-09-28 22:01:15 +08:00

10 KiB
Raw Blame History

nodeagent 实战复盘:短板、瓶颈与改进优先级

基于 2026-09-27 ~ 09-28 的真实使用(Windows 真机部署 + v2rayN 故障排查 + 重装前信息采集) 所有问题均来自实际踩坑,非理论推演。


一、最痛的问题:GUI 语义能力缺失(P0)

场景还原

排查 v2rayN 节点问题时,臣需要切换活动节点:

尝试 结果
算坐标点击第 2 行 ❌ 点到第 1 行
重新算坐标再点 ❌ 又点到第 3 行
改用键盘 ↑/↓/Enter ✅ 成功

问题本质:臣有 screen.capture(能看)和 input.mouse.*(能点),但没有把「看到的界面元素」映射为「可点击坐标」的能力。

每次都要:截图 → 人眼估算像素 → 换算缩放的 region 偏移 → 点击 → 再截图验证。误差极大,反复试错。

改进方向(按性价比排序)

  1. UI 元素定位能力(最关键)

    • screen.find(text) / screen.find(image):返回元素坐标
    • Windows 侧可用 UI Automation (UIA) —— 原生支持,能直接枚举窗口控件树
    • 实现成本中等,收益极高
  2. 截图附带坐标网格

    • screen.capture 可选叠加坐标标尺(每 100px 标线)
    • 臣看图即可精确推算坐标,无需盲试
  3. 鼠标相对定位

    • input.mouse.move 支持 --relative dx dy(当前只有绝对坐标)
  4. 窗口管理

    • window.list / window.focus(title) / window.rect(title)
    • 先聚焦目标窗口并拿到精确矩形,再在窗口内相对定位 —— 比全屏算坐标可靠得多

二、编码与跨平台:反复踩的坑(P0)

三个真实事故

# 事故 根因 后果
1 agent 装了却起不来 PowerShell Set-Content -Encoding UTF8 写入了 BOM JSON.parse 崩,排查半天
2 软件清单中文全变 ����� PowerShell → shell 管道损坏 UTF-8 清单报废,重采一次
3 终端输出中文乱码 Windows 控制台默认 GBK 多次误判

改进方向

  1. 能力层统一 UTF-8(在 Windows 侧强制)

    • system.shell.exec 应自动注入:
      [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
      $OutputEncoding = [Console]::OutputEncoding
      
    • 从根上解决 2、3
  2. 新增 fs.write_text / fs.read_text(明确 UTF-8 无 BOM 语义)

    • 或者给 fs.write 增加 encoding: 'utf8' | 'utf8-bom' | 'gbk' 参数
    • 让「写配置文件」这类高频操作默认安全
  3. 结构化返回(不靠文本传递)

    • 现在 exec 的输出是纯文本 + 尾部附加 [退出码 0 · 410ms],导致臣解析 JSON 时被污染
    • 应改为结构化字段:{ stdout, stderr, exit_code, duration_ms } —— 后端本来就有,只是 CLI 拼接成文本了

三、进程与生命周期:自断生路(P0)

场景还原

臣用 system.shell.exec 执行「杀旧 agent + 启新 agent」来让配置生效:

Start-Process powershell -ArgumentList '... Start-Sleep 3; Stop-Process ...; Start-Process node.exe ...'

结果:分离的 PowerShell 被 agent 的进程树清理逻辑一起杀掉了 → 新 agent 从未启动 → 端口僵死 → 臣彻底失联,只能请皇上手动恢复。

改进方向

  1. 受控重启能力(最高优先)

    system.agent.restart { delay_ms: 2000 }
    
    • 由 agent 自己实现:fork 一个「延时重启器」(脱离进程组),或注册一次性计划任务
    • 重启后主动回连/上报,让控制端知道「我回来了」
    • 这样 90% 的配置变更新场景都不再需要皇上介入
  2. 健康检查与自愈

    • agent 内置看门狗:若 N 秒内未响应,自我重启
    • 启动失败时自动回滚到上一份可用配置(配合配置快照)
  3. 分离进程的明确语义

    • 提供 spawn 能力(明确「不随命令结束而终止」),而不是让用户用 Start-Process 碰运气
    • 或者:exec 增加 detach: true 参数
  4. 改端口/改配置前先"预演"

    • 支持用一个临时配置在旁路端口试启动,成功再切换 —— 这正是这次出事后臣总结的正确做法

四、可观测性与长时任务(P1)

场景还原

  • 代理不通问题反复发作,但臣只能看当下快照(netstat、Get-Process),无法看到 5 分钟前的状态
  • 想跑「持续 5 分钟、每 2 秒记录一次连接状态」的采样 → exec 默认 30 秒超时,长命令直接被砍
  • 排查 360 拦截时,需要「退出 360 前后对比」→ 臣无法安排这个时序

改进方向

  1. 长任务异步化

    system.shell.exec { command, timeout_ms, async: true } → 返回 task_id
    system.task.get { task_id } / system.task.log { task_id } / system.task.kill
    
    • 现在超时只能靠调用方抬 timeout_ms,且命令一旦超时整条被杀,无法续接
  2. 定时采样能力

    monitor.start { interval_ms, command | metric, duration_ms } → 落盘
    monitor.report { id }
    
    • 专治「间歇性问题」—— 这次代理不通就是典型的间歇性/条件性问题
  3. 事件订阅(push 模型)

    • 现在全是拉取式(臣问一次,答一次)
    • 应该支持:subscribe { events: ['process.crash','net.change','file.change'] }
    • 让被控端主动推异常,而不是等臣去查
  4. 日志聚合查询

    • 这次查 v2rayN 日志,臣只能 Get-Content -Tail N 手工翻
    • 应提供 log.query { path, since, level, pattern, limit },由被控端侧做高效过滤

五、能力缺口清单(P1~P2)

缺口 场景影响 优先级
剪贴板读写(clip.get/clip.set) 传文本要绕大圈(写文件→读文件) P1
窗口管理(列举/聚焦/移动) GUI 操作的基础设施 P1
文件监控(fs.watch) 等某个文件生成、监控配置变更 P2
截屏区域智能裁剪 现在要手工算 region P2
录屏 / 连续帧 动态过程(如安装向导)无法观察 P2
键盘组合键序列(宏) 现在 key press 一次只能一种组合 P2
鼠标拖拽(drag & drop) 只支持 move/click/scroll P2
app.list 的 winget 补充 与 PRD 描述不符(只有注册表+商店) P2
app.install 的静默安装降级 winget 之外无兜底(PRD T2 未闭环) P2
并发 RPC WS 单连接串行,批量操作慢 P2
多设备并行执行 现在只能 --node 串行切换 P2

六、性能观察(实测数据)

操作 实测 评价
31MB 文件传输 1.66 秒(~19MB/s) ✅ 够用
截屏 1720×720 ~1-2 秒 ⚠️ base64 传输有开销
列出 178 款软件 ~9 秒(含 winget 调用) ⚠️ 偏慢
单次 exec 往返 300~600ms ⚠️ 每次重建连接的代价
连接建立(wss 握手) ~1-2 秒 ⚠️ CLI 每次调用都重连

瓶颈判断:

  • 最慢的是"每次调用重建连接" —— CLI 模式无连接复用
  • 建议:CLI 增加常驻 daemon 模式(nodeagent serve 本地跑一个,命令走本地 socket 转发)

七、体验问题

问题 具体表现
CLI 参数记忆成本高 --args 的 JSON 转义极易出错(这次反复失败)
错误信息不够可操作 E_CAPABILITY_DISABLED: 输入控制已禁用 —— 没说「怎么开」
无交互式会话 每次命令独立,无法「进入某个上下文后连续操作」
日志与结果混在一起 agent 的日志和命令输出同流,污染数据
key press 多键限制 key press up up up 报 E_PARAM_INVALID,只能循环调用
审计日志无查询便利 能做 audit 但无过滤/聚合

八、安全方面(已有基础,待加固)

项 现状 建议
密钥存储 明文落盘(600 权限) DPAPI(Windows)/ Keychain(macOS)
审计完整性 JSONL 追加 增加链式哈希防篡改
调用取消 ❌ 无 长任务应支持 cancel
能力授权 ✅ 能力级 ACL(做得不错) 可细化到「路径级」「命令白名单级」
速率限制 ✅ 有 可增加「异常行为告警」

九、优先级建议(如果要排期)

P0 —— 直接决定"能不能用起来"

# 改进 理由
1 UI 元素定位(UIA) GUI 操作从"反复试错"变"一次命中",这次最痛
2 受控重启 + 自愈 从根上消灭「自断生路」,配置变更不再需要人工
3 编码统一(UTF-8 全链路) 三个事故都由此而来,且每次排查都耗时
4 exec 结构化返回 消除输出污染,是所有自动化的基础

P1 —— 决定"用得顺不顺"

# 改进
5 长任务异步化(async: true + task_id)
6 定时采样 / 监控(治间歇性问题)
7 剪贴板读写
8 窗口管理
9 CLI 常驻 daemon(连接复用,提速)

P2 —— 决定"用得爽不爽"

  1. 事件订阅(push 模型)
  2. 文件监控 / 录屏
  3. 日志聚合查询
  4. 鼠标拖拽 / 键盘宏
  5. 多设备并行
  6. DPAPI 密钥保护

十、一句话总结

nodeagent 在「能看、能算、能跑命令」这条线上已经相当扎实 —— 19 项能力、真实文件传输、审计闭环都经得起实战。

它现在的真正短板集中在三个字:语义 与 状态。

  • 语义:看得到画面,但读不懂界面(缺 UI 元素理解)
  • 状态:只看得到当下,看不到变化(缺长时观测)
  • 自持:改得动别人,改不动自己(缺自愈与受控重启)

把这三块补上,它就从「远程命令行工具」进化为「真正的远程操作代理」。