[AI_TOOLS_LOG]// 2026.04.15 // 12 分钟阅读

用 AutoGLM 帮我自动打卡,跑了三个月

#AI#AUTOGLM#AUTOMATION#ADB

这套脚本的出发点其实没那么宏大,就是为了应付打卡。

说直白一点,我本来就是个经常迟到的人。考勤弹性还算大,不太会因为晚几分钟就出什么事,但飞书里的记录不会陪你演戏:迟到就是迟到,缺卡就是缺卡,一直挂在那里,看久了还是挺难看的。

而且我不只是会迟到,还有概率忘记打卡。有时候人已经到公司了,一坐下就开始回消息、开会、处理事情,等想起来的时候,考勤记录已经不好看了。

所以我想得很直接:既然这件事本身没有价值,又每天都要重复,那就把它自动化掉。

问题在于,飞书考勤不是网页,也没有现成 API 可以调。你没法像调一个 HTTP 接口那样直接“提交打卡”,必须真的有个东西去打开 App、进入考勤页面、看当前状态、再决定要不要点按钮。

我手边刚好有一台长期插在 Mac 上的备用 Android 测试机,AutoGLM 也刚开源不久。它比较适合这种场景:给它一句自然语言指令,它会自己截图、理解界面、点按钮。我需要补上的,不是“怎么识别页面”,而是外围那一圈更脏的工程活:

  • 什么时候跑
  • 今天到底是不是工作日
  • 不同时段怎么随机出一个像人类的时间
  • 手机怎么唤醒、解锁、启动飞书
  • 如果今天已经打过卡了,怎么确保别再点第二次
  • 跑完之后怎么留痕,方便我事后核对

换句话说,AutoGLM 只负责最后那一下“在飞书里完成打卡”,剩下的调度、状态和设备控制,都是我自己补的。

整体架构

系统架构图

整条链路其实很朴素:

Mac 上的 launchd 拉起 Python 调度器,调度器每天重新生成当天的随机打卡时间;到点之后,通过 ADB 把 USB 连着的 Android 手机唤醒、解锁、拉起飞书,再把操作指令交给 AutoGLM;执行完以后保存截图、更新状态文件,最后把手机和电脑屏幕都收回去。

所以这不是一个“AI 自己在后台神奇地完成一切”的系统,本质上还是传统自动化,只不过把最难写死规则的那段 UI 操作,交给了 Agent。

这套脚本每天是怎么跑的

我现在的配置是上午和晚上各打一轮,时间都不是固定点,而是在窗口里随机:

  • 上午:10:19 到 10:27
  • 晚上:20:00 到 20:07

调度器会在每天 00:01 先把当天任务重新排一遍。这样第二天的执行时间会重新随机,不会每天都卡在同一分钟。

真正触发时,流程大概是这样:

  1. 先判断今天是不是工作日
    这一步不是简单看周几。我接了 timor.tech 的节假日 API,优先按官方调休数据判断;如果接口挂了,再回退到本地的周一到周五逻辑。

  2. 再看这个时段今天是不是已经打过了
    状态会写到 logs/checkin_status.json。如果当天这个时段已经成功过,后面就什么都不做,直接退出,避免重复打卡。

  3. 准备手机环境
    通过 ADB 唤醒屏幕、输入锁屏密码、确认设备连通;然后把飞书进程先干掉,再从首页重新拉起,尽量避免 App 留在一个奇怪页面。

  4. 把最后的页面操作交给 AutoGLM
    指令里会明确告诉它:去哪里找打卡入口,看到“上班打卡”或“下班打卡”时才点击;如果页面已经显示“已打卡 XX:XX”,就直接退出;如果看到“缺卡”或者其他异常状态,也不要乱点,更不要申请补卡。

  5. 收尾
    无论成功与否,都会尽量截图留档;然后写日志、更新状态文件,最后把手机熄屏,Mac 这边也让显示器休眠。

整个流程里,最像“AI”的只有中间那一步;前后基本都是普通脚本在兜底。

几个地方如果不处理好,会很容易翻车

1. 工作日判断不能只看周一到周五

这是最开始就踩到的坑。

如果只是简单判断星期几,那碰上调休一定会出问题。周六可能要补班,某个工作日也可能因为节假日安排变成休息日。对“自动打卡”这种东西来说,时间判断错一次,就不是小问题。

所以我把国务院节假日数据接进来了,按年缓存到本地,缓存有效期是 30 天。这样平时不会天天请求接口,接口偶尔挂了也还能用旧缓存兜住。

2. 时间必须随机,不然太假

固定在同一分同一秒执行,日志看起来就很不像人。

所以我给上午和晚上各配了一个很窄的时间窗口,每天凌晨重新抽一次。它不会偏离太远,但也不会每天都一模一样。这个改动很小,效果却很直接,日志读起来顺眼得多。

3. 幂等性一定要做

Agent 不是每次都稳定。

有时候是 ADB 没连上,有时候是飞书页面加载慢了,有时候是 AutoGLM 没找到入口就超时。如果失败后没有状态控制,脚本一重跑,就可能在已经打过卡的情况下再点一次。

所以我这里的原则很死板:只要当天这个时段已经记录为成功,就绝不再执行。

状态文件大概长这样:

{
  "2026-04-15": {
    "morning": {
      "success": true,
      "message": "morning 打卡流程结束",
      "timestamp": "2026-04-15 10:24:37"
    }
  }
}

这种本地 JSON 很土,但对这种单机脚本来说完全够用,而且排查起来非常直接。

4. 给 AutoGLM 的指令不能只写“帮我打卡”

这一点也很关键。

如果你只丢一句“帮我打卡”,Agent 会自己理解,但它的理解不一定和你要的一样。比如页面已经显示“已打卡”,它可能还会继续尝试操作;再比如看到“缺卡”,它可能会顺手去点补卡相关入口。

所以我的做法不是写一句目标,而是把边界写清楚:

只有看到蓝色打卡按钮时才点击;如果已经显示已打卡,就退出;如果是缺卡或其他异常状态,不要处理,直接结束任务。

这种约束写清楚以后,稳定性提升很明显。对 Agent 来说,“不要做什么”往往和“要做什么”一样重要。

5. 截图和日志不能省

自动化一旦出问题,最怕的是你根本不知道它停在哪一步。

所以每次执行后我都会尽量保留截图,日志里也会把调度、ADB 状态、当前包名、Agent 执行结果这些关键信息打出来。这样哪天记录不对,我至少能很快判断:

  • 是不是 USB 线松了
  • 是不是手机没解锁成功
  • 是不是飞书没拉起来
  • 还是 Agent 在页面里迷路了

真正排查时,截图比一句“失败了”有用得多。

这三个月跑下来怎么样

从日志看,主要的失败原因其实很集中:

  • ADB 断连,比如 USB 接触不稳
  • 飞书页面没到预期状态,导致 Agent 超时

这两类问题都不算“逻辑写错”,更像是外部环境不稳定。脚本本身做的事情,主要就是在这些不稳定外面再包一层保护:工作日判断、随机时间、幂等、截图、日志、失败后下次继续跑。

跑了三个月,整体成功率大概在 95% 左右。对一个靠 ADB + 手机 + Agent 串起来的小工具来说,我已经挺满意了。

更重要的是,这件事从“我每天都得记着”变成了“它自己跑,偶尔我看一眼日志”。考勤记录没那么难看了,我也不用每天早上给自己加一道没意义的提醒。


如果你也碰到类似场景:某个手机 App 里有一段固定流程,没有公开 API,但你又不想为这件事手搓一整套 UIAutomator 规则,那 AutoGLM 这种 Phone Agent 方案确实值得试一试。

它不神奇,也不是什么银弹。真正让这件事能长期跑起来的,还是外围那些看起来不酷、但必须做扎实的工程细节。

SX
Written by sunxu

Android 工程师,写代码也写字。

© 2026 sunxu · CC BY-NC 4.0转载请注明原文链接,不可商用