2025年7月11日,距离今天2026年4月2日,只有短短9个月而已,但是对于突飞猛进的AI而言,可能已经是相当长的时间了。在这里记录一下那一天9个月前,我用豆包AI帮助(主导)做的手机上的两个点赞工具,以及近期的一个小工具。
因为工具较小,我几乎没有编程。我在手机上与豆包沟通,在手机上复制粘贴代码到工具,全程没有用PC,没用键盘没改代码。
积累到的经验包括,1.可以与AI讨论技术方案,其实有的时候它的代码很差,技术就可以相当不错,这两件事情是对立的;2.人类描述故障现象,以及猜测使用什么样的技术方案更合理,尤其是当AI看不到现象时,这两点都很有帮助;3.人类需要部署代码、运行,以及观察和描述现象。
1. keep点赞工具
Keep是一个运动软件,里面的社交功能,可以看到其他人锻炼消息。我习惯于为每一条消息点赞,用鼓励为同道中人提供些情绪价值。不过有的时候,有的人真的是太能练了,运动的记录哗哗刷屏。我点起赞来也有些疲惫,所以想到做自动化工具吧。
(1)先讨论技术方案。

豆包的答复表明,对技术方案讨论是必要的,我的思路太狭隘了。我以为她会selenium之类的,她给出了3种PC端控制方案,3种安卓的方案,还有混合方案。其中,她推荐的方案是纯安卓的方案中 autox.js,我听HYR同学提到过,就选了这个方案。
(2)请教工具使用


(3)降低颗粒度,提高确定性
A. 一蹴而就 是幻想

当时的实践表明,这么复杂的功能,一次性沟通是个失策。换句话说,如果你作为人类读了上面这一段一段文字,感觉累的话,那么AI也不行,至少当时的情况是这样的。当她信誓旦旦反复说这一次肯定就可以逆行了的时候,你还会有情绪反应。
当你对于使用的工具不熟悉时,也会有挫败感。AI通常会假设你对工具充分熟悉,不过好在,当你发问的时候,她并不会嘲笑你。


这样折腾了不少的轮次,我就不一一列举了,请想象一下我挫败的感觉。
后来终于找到了绿色喇叭。
![]()
B. 在结对编程中的任务分工,某部分 谁能力强就谁做
到这时,我已经了解到AI的设计和模式识别非常一般,这种事儿,人手操作容易得多。



C.降低颗粒度
AI倾向于他认为规模不大的时候,一次性生成所有代码。但是实践表明,她对于运行环境的错误猜测可能给调试带来很多麻烦,所以每次测试的单一的一件事情,更容易一些。


就这样,我把功能需求当中的每一个技术要点分别要求AI开发出来。我测试有效算完成,完成一个,再要求写下一个。


D. 现实一些,削减需求
有的功能并不太重要,但是实现很麻烦,很可能不是因为AI的能力不行,而是因为手机环境导致获取信息的方法受限。



所以后来有的功能就放弃了,比如跳过我自己,干脆连自己一起赞了。这是削减需求。
再例如,超过24小时以上不点赞这个功能后来也取消了,由我手动停止脚本。自己使用的工具,不提供给其他用户使用,才具有这样的便利。事实上,多数产品当中的多数代码都是设置环境、限制用户操作,以及防呆。并没有多少积分难度,但是工作量不小。
由autox.JS自动运行keep再切换到特定的tab,这个功能也放弃了,由我手动操作到达这个位置再开始运行脚本好了。
E. 情绪感受、编程环境,以及“看到了才知道这不是我要的”
有的调试体验还是不错的。
例如,我搞混了 向上 和 向下 滑动,纠正的时候,AI并没有嘲笑我,说“连这个都分不清吗?”

为了调试方便,我禁止AI要求我增量修改代码,她遵从了,虽然需要反复重申很多次。事实上,即使我的环境是否粘贴代码,我也觉得AI对于描述如何修改的表述能力欠佳,精确程度不足,指代不明经常出现,可能她学习的语料库就是这样的。


有的地方,人类也没有想到界面(需求)会是这样,现象出现了才意识到还有这些情况。

F.集成
当所有技术类型都完成,并删除实现困难的需求以后,把所有代码集成在一起。

视频
Keep点赞工具 - 杨贵福的视频 - 知乎
https://www.zhihu.com/zvideo/1927419261376267022
代码在本文最后。
2. 微信读书点赞
因为有了上一个点赞工具的合作开发经验,所以这次我跳过了对绿喇叭、红心之类可见目标的描述,一开始就用布局分析工具找到了空间的ID。

接下来几轮,修改AI的代码风格,这样我就不必手动修改了。


![]()
接着,再次重申要求。

有对技术细节的讨论,包括我逐步才意识到的代码需要识别和应对的环境特征。
屏幕我每次上滑半页,所以显示的范围有所重复。如果完全避免重复,我担心需要精确定位,或者有遗漏。为了避免重复中文网友被我连续点赞两次导致点赞被清除,下面这个功能是识别是否点过赞,点过的话,不要再点一次。

事实上,这么简单的“复杂”情情情况只出现了一次。接着我的要求增加了新的功能,也就是最后一个功能。

运行时,我用肉眼跟踪,如果点赞到了本周完全没有阅读的网友,我就手动停止脚本。微信读书的启动和调整到特定页面也是由我手动操作的。这些需求的削减依据在第一个项目中积累的经验。相比之下,上一个小项目,主要时间花费在(1)实现困难而意义不大的需求,最后可能还是放弃了,(2)要求AI做她不擅长的事情,后来难以触达的真实环境,例如识别某个控件的ID,(3)颗粒度过大。
所以,没有几轮讨论,开发就完成了。

微信读书点赞 - 杨贵福的视频 - 知乎
https://www.zhihu.com/zvideo/1927736304004735247
代码在本文最后。
3. 微信读书左划
需要这个脚本的原因是,微信读书每隔一段时间内发放一批免费的书,可以领2本,要求每一本阅读5分钟。看到消息的时候,我可能刚好没有时间读10分钟,又担心过来事儿,把这事忘了,错过了占便宜。
所以我让豆包写一段脚本。

之所以滑动的间隔和持续的时间定这么短,是为了能得到尽快响应,调试方便。代码一次性跑的很好,因为避免了前述提到的问题 困难而意义不大的需求、在无法真正触达的情况下与真实环境的交互、过大的颗粒度。
这增加的一个需求是我事先没有想到的。
![]()
脚本刚一完成,我就意识到它并无必要,因为微信读书支持朗读的功能,你让它读一分钟就算数。不过,我也没有什么可遗憾的,因为写这个脚本也就是两三分钟的时间,在豆包的帮助下。
代码在本文最后。
这一段我也不附视频了,文字描述一下,就是每半分钟左滑一次,持续6分钟。我还考虑到,如果遇到识别机器人的机制,可以在每次左滑中加一个随机的时间间隔。没有用到。
4. 复杂任务,不行
我最近尝试过用豆包开发一脚的autox.js朗读电子书,她信誓旦旦地说,容易实现。并不。稍微复杂一些的功能,她既没有能力实现(当然,也可能由于合作者我的无能),也没有自知之明。也许性格设定上,她就是那种大喊“没问题!”的昭和男儿吧。
简单的经验是,如果你感觉对话的轮数较多,而仍未实现,那么无论希望多么迫近和逼真,非常可能,那就是不能实现。别相信她。
另,语音朗读电子书,@voice aloud reader 非常好。
代码 微信读书点赞
"nodejs";
// 自动点赞并上滑脚本
function main() {
// 请求无障碍服务权限
auto.waitFor();
// 定义循环次数
const TOTAL_CYCLES = 10;
for (let cycle = 1; cycle <= TOTAL_CYCLES; cycle++) {
log(`=== 开始第 ${cycle}/${TOTAL_CYCLES} 轮点赞 ===`);
// 设置查找元素超时时间
sleep(1000);
// 查找所有点赞容器
let containers = id("friends_rank_praise_container").find();
if (containers.empty()) {
log("未找到点赞容器,可能已到达页面底部");
break; // 跳出循环
}
log(`本轮找到 ${containers.size()} 个点赞容器`);
// 遍历容器并处理
for (let i = 0; i < containers.size(); i++) {
let container = containers.get(i);
let icon = container.findOne(id("friends_rank_praise_icon"));
if (icon) {
let isSelected = icon.selected();
log(`容器 ${i+1} 内的图标选中状态: ${isSelected}`);
// 如果未选中,则点击容器
if (!isSelected) {
container.click();
log(`已点击容器 ${i+1}`);
// 点击后适当延迟,避免操作过快
sleep(500);
}
} else {
log(`容器 ${i+1} 内未找到点赞图标,跳过`);
}
}
log(`=== 第 ${cycle}/${TOTAL_CYCLES} 轮点赞完成,准备上滑 ===`);
// 执行上滑操作
swipe(device.width / 2, device.height * 0.8, device.width / 2, device.height * 0.2, 300);
log("已执行上滑操作");
// 上滑后等待页面加载
sleep(1500);
}
log("==== 全部点赞任务完成 ====");
}
// 执行主函数
main();
代码 keep点赞
"nodejs";
auto.waitFor();
// 定义判断是否已点击的函数
function hasClicked(horn) {
return horn.selected();
}
// 定义点赞第一个绿色小喇叭的函数
function clickFirstHorn() {
let horn = id("layoutContent").findOne();
if (horn && !hasClicked(horn)) {
horn.click();
sleep(500); // 点击间隔,避免过快
log(`已点赞第一个绿色小喇叭`);
}
}
// 定义向上滑动半屏的函数
function swipeUp() {
const width = device.width;
const height = device.height;
const startX = width / 2;
const startY = height * 0.8; // 起点:屏幕下部80%位置
const endY = height * 0.3; // 终点:屏幕上部30%位置(上滑半屏)
const swipeDuration = 500; // 滑动持续时间(毫秒)
const intervalBetweenSwipes = 800; // 每次滑动间隔时间(毫秒)
swipe(startX, startY, startX, endY, swipeDuration);
sleep(intervalBetweenSwipes);
}
// 主循环,假设执行10次滑动和点赞操作
const loopCount = 100;
for (let i = 0; i < loopCount; i++) {
clickFirstHorn();
swipeUp();
log(`完成第 ${i + 1} 次滑动和点赞操作`);
}
toast("点赞和滑动操作完成");
代码 微信读书左划
// 设置时间参数(单位:毫秒)
var interval = 1000*60; // 每次滑动间隔(1分钟)
var duration = 1000*60*7; // 总持续时间(7分钟)
// 记录开始时间
var startTime = new Date().getTime();
// 定义滑动函数
function swipeLeft() {
var width = device.width;
var height = device.height;
swipe(width * 0.8, height / 2, width * 0.2, height / 2, 300);
console.log("已向左滑动一次");
}
// 循环执行滑动
while (true) {
swipeLeft();
sleep(interval);
// 判断是否达到总持续时间
if (new Date().getTime() - startTime >= duration) {
console.log("已达到指定持续时间,脚本结束");
// 弹出任务完成提示
alert("任务完成", "已按设定完成所有滑动操作");
break;
}
}