znlgis 博客

GIS开发与技术分享 — GDAL · GeoServer · PostGIS · QGIS · OpenLayers · Cesium · FreeCAD · NPOI

第11章 - 人类行为模拟

机器操作的最大特征是「太完美」——鼠标瞬间直线到达、打字零失误、间隔精确一致。这种机械的完美恰恰是风控系统识别「机器人」的依据。robotgo-flow 的人类行为模拟(Human Behavior Simulation)正是为了让自动化操作「像真人」,从而降低被检测的风险。本章深入其贝塞尔曲线鼠标轨迹、打字错误模拟、可变延时、滚动抖动与空闲行为的实现原理。

11.1 为什么要模拟人类

真实用户的操作充满「不完美」:

  • 鼠标移动是带弧度的曲线,接近目标时会减速,甚至有轻微「过冲」再回调;
  • 打字会偶尔打错,然后退格改正;
  • 每次操作的间隔时间都不一样;
  • 滚动是一段段的,不是匀速的;
  • 等待时手会不自觉地微动。

机器如果把这些「不完美」也模拟出来,就更难与真人区分。这对需要对抗风控的自动化场景(如反爬、防机器人检测)很有价值。

11.2 如何启用

人类行为模拟通过 YAML 的 settings.human 启用:

settings:
  human:
    enabled: true         # 启用
    speed: 0.8            # 速度系数(0.1 ~ 5.0),越小越慢
    mistake_rate: 0.03    # 打字错误率(0.0 ~ 1.0),0.03 = 3%
  • enabled:总开关,false(默认)时所有人类行为均不生效,走普通快速模式;
  • speed:速度系数,范围 0.1~5.0,默认 1.0。数值越小操作越慢(越接近悠闲的真人),越大越快;
  • mistake_rate:打字错误率,范围 0.0~1.0,默认 0.0(不出错)。0.03 表示约 3% 的字符会「打错再改」。

在源码中,引擎的 EnableHuman(enabled, speed, mistakeRate, idle, scroll, overshoot) 会据此创建 humanMode 实例;若未启用则该实例为 nil,引擎所有方法都走普通分支。

11.3 速度系数如何影响延时

humanMode 会根据 speed 推导出一系列延时范围(并做上下限裁剪):

  • 打字延时typeDelayMin = clamp(50/speed, 10, 500) 毫秒,typeDelayMax = clamp(250/speed, 30, 1000) 毫秒;
  • 点击延时clickDelayMin = clamp(80/speed, 20, 600) 毫秒,clickDelayMax = clamp(350/speed, 60, 1500) 毫秒。

可以看出,speed 越小(比如 0.5),分母越小、延时越大,操作越慢;speed 越大,延时越小,操作越快。每次实际延时都在 [min, max] 之间随机取值,保证「不一致」。

11.4 贝塞尔曲线鼠标轨迹

这是人类模拟最直观的部分。普通模式下鼠标用 robotgo.MoveSmooth 走直线;人类模式下走三次贝塞尔曲线

11.4.1 moveMouseTo 的逻辑

引擎移动鼠标时调用 moveMouseTo

  • 短距离(距离 < 20px):直接 robotgo.Move 到位,加 10~40ms 随机延时(近距离没必要画曲线);
  • 长距离:生成一条贝塞尔曲线路径(bezierPath)并沿路径逐点移动(walkPath);
  • 过冲(overshoot):15% 概率先「冲过头」±7px,再走回目标,模拟真人的手部惯性;
  • 收尾:无论如何最后都执行一次精确的 robotgo.Move(x, y),确保最终落点准确。

11.4.2 bezierPath:路径生成

bezierPath 生成曲线的控制点:

  • 两个控制点分别位于起终点距离的 20%~50% 处,并带有 ±45° 的角度偏移——这让每次的曲线形状都不同;
  • 采样点数量为 clamp(int(dist * 0.8), 50, 200)——距离越长采样越多、轨迹越平滑。

11.4.3 walkPath:速度曲线

沿路径移动时,速度不是匀速的,而是模拟真人的「加速—巡航—减速—落点」:

进度区间 每点耗时 阶段
0~30% 1~3 ms 加速
30~70% 2~5 ms 巡航
70~90% 5~12 ms 减速
90~100% 10~25 ms 落点

越接近目标越慢,正是人手操作鼠标的典型节奏。

11.5 打字错误模拟

人类模式下,TypeText 逐字输入,并按 mistake_rate 概率制造「打错再改」的效果。当 rand.Float64() < mistakeRate 时,随机采用三种错误类型之一:

类型 行为
类型 0:相邻键错误 先按下相邻的错误键 → 退格 → 再按正确键
类型 1:漏字 先跳过当前字打下一个字 → 退格 → 补回当前字 → 再打下一个字(顺序修正)
类型 2:顺序颠倒 先把两个字颠倒打出 → 连退两格 → 按正确顺序重打

其中「相邻键错误」依赖 keyboard_layout.go 中的 QWERTY 键盘邻接映射adjacentKeys)——它记录了每个字母键在键盘上的相邻键,从而模拟「手指按偏了一个键」的真实错误。

每个字符之间还有 [typeDelayMin, typeDelayMax] 的随机间隔,让打字节奏自然。

11.6 滚动抖动

普通模式下 scroll 一次滚到位;人类模式下(启用滚动抖动 scrollJitter):

  • 把滚动量拆成 1~3 单位的小步,逐步执行;
  • 每步之间有 30~120ms 的随机间隔;
  • 结束后有 20% 概率轻微反向回滚 1~2 单位——模拟真人「滚过头又拉回来一点」。

若滚动抖动未启用或滚动量很小(≤ 1),则直接调用 robotgo.ScrollDir 一次滚动。

11.7 空闲行为与鼠标微抖

真人在等待时,手不会完全静止。人类模式下的等待(idleWait)会:

  • 把总等待时间拆成 200~500ms 的小块;
  • 每一块有 15% 概率触发 idleWiggle——把鼠标微移 ±5px 再复位,模拟手的轻微抖动。

此外,步骤之间也有一定概率把鼠标移动到随机位置,进一步增加「人味」。

11.8 速度、错误率如何取值

参数的选择取决于你的目标:

  • 追求隐蔽性speed 取 0.5~0.9(偏慢更像真人),mistake_rate 取 0.02~0.05(少量错误);
  • 追求效率speed 取 1.5~3.0(更快),mistake_rate 设 0(不出错);
  • 演示/调试speed 取较小值让操作肉眼可见,便于观察每一步。

注意:错误率过高(如 > 0.1)会显著拖慢打字并可能引入意外,一般不建议;框架也会把 mistake_rate 裁剪到 [0.0, 1.0]、speed 裁剪到 [0.1, 5.0] 的合法范围。

11.9 完整示例

一个启用人类行为模拟、偏隐蔽的登录工作流片段:

name: "拟人登录"

settings:
  element_timeout: 15
  human:
    enabled: true
    speed: 0.7          # 偏慢,更像真人
    mistake_rate: 0.03  # 3% 打字错误

inputs:
  - name: user
    label: "账号"
    required: true
  - name: pass
    label: "密码"
    required: true
    mask: true

steps:
  - name: "登录"
    actions:
      - open_url: "https://example.com/login"
      - wait: "templates/form.png"
      - type: {into: "templates/input_user.png", text: "$input.user"}
      - type: {into: "templates/input_pass.png", text: "$input.pass"}
      - click: "templates/btn_login.png"
      - wait: "templates/home.png"

启用后,鼠标会以曲线移动、打字偶有修正、操作间隔不一,整体呈现「真人操作」的观感。

11.10 小结

本章我们深入了人类行为模拟这一核心机制:通过 settings.human 启用,speed 控制整体快慢并推导各类延时,mistake_rate 控制打字错误率。我们剖析了贝塞尔曲线鼠标轨迹(含过冲与加速—减速的速度曲线)、三种打字错误类型(相邻键/漏字/颠倒,依赖 QWERTY 邻接表)、滚动分块抖动与回滚、空闲时的鼠标微抖等实现细节,并给出了参数取值建议。这套机制让 robotgo-flow 的操作更贴近真人,降低被检测风险。下一章,我们回到工具层面,详解 CLI 的四个命令。