大家好,我是Zack。
今天早上发生一件小事,把我用了快一个月的做法直接打翻了。
我有个脚本,每天早上替我把排好的内容一条条发出去。发完它会自己判断「到底发出去没有」,往日志里写一行结果。
今天日志长这样:
=== posting 1 at 2026-10-05 10:53:40 ===
URL https://x.com/home
POSTED_OK: false
=== posting 2 at 2026-10-05 10:56:21 ===
URL https://x.com/home
POSTED_OK: false
=== posting 3 at 2026-10-05 10:57:12 ===
URL https://x.com/home
POSTED_OK: false
连着三行 false。
麻烦的地方在于:false 有两种可能。
一种是真的没发出去,一种是发出去了但它没认出来。
这两种情况我要干的事完全相反,但日志告诉我的信息是一样的。
我原来的办法:让脚本自己点一遍去确认
我当时的想法很简单,既然不确定,那就再点一次发帖按钮,看它报不报错。
结果那条「确认用」的半截正文,真的发出去了。
多出来一条 0 张图、正文说了一半的推文,id 是 2106920089501282552。
我用 scripts/delete_many.js 把它删了,再数一遍,时间线上只剩本来就该有的那 1 条。
从误发到删干净,前后 6 分钟。
6 分钟不算多,但这件事真正吓人的是:我为了「确认」而做的一个动作,本身是会改数据的。
验证动作不只读,那它就不叫验证,它叫又执行了一遍。
为什么它连「发没发出去」都判断不准
我去翻了它的判定逻辑,问题出在读哪儿。
它读的是我的主页时间线,抓最近几条,比对正文前 18 个字。
听着挺合理,实测两次全废:
- 第一次:读到了东西,但全是 2022 年的旧推文,当天的没有
- 第二次:直接返回一个空数组
[],什么都没读到
同一个脚本,两次结果不一样,第三次我也不敢信了。
页面结构变了、元素没渲染完、登录态掉了,任何一个原因都能让主页抓取静默失败——它不报错,它只是给你一个空结果。
这比报错难查多了。
换成只读的查法
我把判定改成查搜索页,不再读主页:
https://x.com/search?q=<正文前几个字> from:kaiz_amm&f=live
三个参数说一下:
q放正文开头那几个字,别放全句,标点一多就搜不到from:kaiz_amm限定账号,不然会搜出别人的同名内容f=live是按时间排,默认那个排序会把你要找的推到后面去
搜索页比主页稳的原因很土:它返回的是数据,不是页面上那堆会变的 DOM。
改完之后,一次就查到了当天那条,没再出现过空结果。
四条规矩,可以直接抄
这轮之后我给所有「让 agent 自己验收」的活定了四条,都是踩出来的。
一、验收脚本里不许出现提交类动作。
点「发帖」「发送」「提交」「确认」,敲回车提交表单,全部禁掉。
只允许 goto 打开页面、evaluate 读 DOM、读接口返回。
我在提示词里是这么写的:
验证阶段只读,不许做任何会改变状态的操作:
- 不许 click 任何按钮
- 不许 submit 表单
- 不许敲 Enter
如果发现状态不对,停下来告诉我,不要自己重试。
二、判定要看终态数据源,不看执行者自己的报告。
脚本说「完成了」,那是它的自述,不算证据。
证据是另一个地方能看到的东西:搜索页、线上列表、数据库里那行记录、文件真的多了几个字节。
这两条得分开,放同一条链路上等于没验。
三、每个动作留一行日志,三个字段就够。
=== posting 1 at 2026-10-05 10:53:40 ===
URL https://x.com/home
POSTED_OK: false
时间、URL、结果。
日志的作用是让你事后能复盘,不是让你当时盯着看。
今天我能把误发那条揪出来,靠的就是日志里有时间戳和 id,翻一下就知道是哪一条。
四、跑之前先确认没有残留进程。
pgrep -fa "ego-browser|run_queue" | wc -l
不是 0 就先清干净再跑。
今天遇到的两个报错,根因都是上一轮的进程没退干净:
Error: page label not found: p1
Error: task.listTabs: Task space not found: 134
看着像页面没了,其实是两个进程在抢同一个浏览器会话。page label not found 这条我一开始还以为是页面崩了,查了半天方向都错了。
前后对比
| 环节 | 改之前 | 改之后 |
|---|---|---|
| 判断发出去没 | 脚本再点一遍按钮 | 搜索页读一次 |
| 误发条数 | 1 条 | 0 条 |
| 读主页抓当天内容 | 2 次,0 次成功 | 不用了 |
| 出错后排查 | 翻聊天记录猜 | grep 日志定位 |
| 跑之前 | 直接起 | 先 pgrep 确认为 0 |
两条能带走的原则
第一,验证动作必须只读。
这条放到哪儿都成立。CI 里让 agent 检查部署结果,就别让它顺便点「发布」;让它核对数据库,用 SELECT 别用 UPDATE。
判断「我做没做成」的这个动作,如果自己也会改变状态,那它每跑一次就多制造一个问题。
第二,判定要看终态,不看自述。
agent 说做完了、脚本返回 true、日志写着 OK,这些都是执行者自己的说法。
真正算数的是另一个地方能独立看到的那个结果。
这两条其实是一句话:让检查的人和被检查的人,别是同一个人。
顺带说一句,这套方法不是万能的。
搜索页也有抓不到的时候,比如刚发完那几十秒索引还没建好,这时候查出来会是空的。我现在遇到空结果会等 30 秒再查一次,连续两次空才判定没发。
所以这篇讲的是「怎么少踩坑」,不是「怎么做到零错误」。零错误我做不到,谁说能做到我都不太信。
你让 agent 自动干活的时候,踩过哪种「验证反而搞出事」的坑?或者你有什么更稳的判定办法,欢迎留言聊聊,我挑几个试试。
我是 Zack,关注公众号「Zack说AI」,一起把工具用明白。