Agent 自动化跑着跑着就停,问题多半不在模型和脚本,而在浏览器环境这一层。这里按四类高频失败,分别讲清可观测现象和对应的工程做法。
用 LangChain、AutoGen 或 CrewAI 搭一个 Agent,让它调 Playwright、Puppeteer 去点网页,搭起来不难。难的是让它连着跑下去。
刚上线的时候通常看不出问题。任务量一上来,异常就密集了:任务被站点拦下、账号登录态忽然失效、几个 Agent 同时跑时互相踩到。多数人的第一反应是回去翻代码,翻到最后发现代码没问题。
问题往往落在浏览器环境这一层。跑得多的项目里,失败的形态其实就那几类,认出来之后处理并不算复杂。

环境还没就绪就开跑
新建的浏览器环境,第一次就拿去执行任务,常见的结果是登录不上、页面元素加载不全、第一步就弹验证。原因不复杂:这个环境里没有历史访问记录,没有 Cookie,也没有任何浏览轨迹,平台看到的是一台完全陌生的设备,信任度自然低。
可观测的现象是:失败集中在环境刚创建后的头几次任务;把同一个任务挪到一个用了一段时间的环境里,就能正常跑完。
对应的做法是把环境就绪做成一个显式状态,而不是默认假设它可用。环境创建之后先让它跑一段低强度的浏览,等状态稳定了再交给正式任务;调度器在派发任务前先确认这一步,而不是拿到环境就直接用。
几个任务抢同一个环境
并发一上来,最直观的表现是进程越堆越多、内存被吃满、系统变卡。更麻烦的是隐蔽的那一类:两个任务先后用了同一套 Cookie 和本地存储,A 的登录态把 B 顶掉,日志里看起来就是某个不固定的任务偶尔失败,很难定位。
这时候需要的是把浏览器环境当成可申请、可回收的资源:任务开始时申请一个环境,结束后释放,任务和环境一对一。环境之间存储互不可见,一个任务的登录态就不会渗到另一个任务里。扩展到几十个 Agent 并行时,这种方式和“在脚本里自己起一堆浏览器进程”的差别会非常明显。
如果场景本身涉及多账号,隔离还要更彻底一些:一个账号固定一套环境,指纹参数和存储都不与别人重叠。PurpleMark 在这里提供的就是环境隔离和集中调度这一层能力,让账号和环境保持稳定的一一对应。
会话过期了没人发现
这一类最容易被忽略,因为它不报错。任务还在往下跑,日志也在输出,但返回的其实是登录页或者空数据,直到结果进了数据管道才被发现,排查要从下游倒推回去,成本很高。
做法是把登录态当成前置条件显式检查:任务开始前先确认当前会话还有效,失效就走一次完整的登录流程,而不是让任务带着失效状态继续跑。状态本身要落在环境层,Cookie、本地存储、浏览历史都存在环境里,环境再次启动时能完整恢复,账号任务就不必每次重新初始化。
顺带一个经验:长期运行的账号,登录态频繁变动本身就容易被平台当成异常信号,触发额外验证。能不做无谓的重新登录,就尽量别做。
被拦下之后整批停摆
还有一种失败是突然成批出现,一大批任务同时交不上结果。它的特点是站点不一定给出明确的拒绝,更常见的是返回降级内容或空页,Agent 拿到没有意义的数据继续往下跑,最后在数据环节才暴露。
遇到这种情况,第一件事是把拦截和普通失败区分开。如果现象是同一批环境在相近时间点集体异常,基本可以判断问题出在环境这一层,这时候继续重试只会扩大范围,应该先把这批环境停下来隔离,再回头查触发原因。
常见的触发点有三个方向:多个环境用了高度重合的指纹配置,比如 WebGL、Canvas、字体列表、内核版本几乎一样;出口 IP、时区、语言三者对不上,比如美国 IP 配了亚洲时区;以及操作间隔过于规律,节奏本身就成了特征。把参数配置核对一致、把节奏控制住、把环境状态和任务结果都留成日志,才能在成批失败之前先看到苗头。
把这一层单独拿出来
成熟的项目通常会把浏览器环境从 Agent 里拆出来,单独当成一层:Agent 负责规划和决策,环境层负责身份和状态,执行层还是原来的 Playwright 或 Puppeteer。拆开之后,身份是否真实、状态是否可恢复、任务之间是否隔离,这三件事就都有地方管了。
回头看,上面四类失败有个共同点:它们都不在模型里,也不在脚本逻辑里。模型和代码当然还要继续优化,但决定自动化能不能长期跑下去的,往往是更下面这一层。
以上内容用于技术研究与开发实践分享,自动化手段应在合法合规的前提下使用,并遵守目标平台的服务条款与当地法律法规。


