自主行动AI Agent实战:从零搭建自动操作网页的智能体

发布时间:2026/8/29 14:10:24
自主行动AI Agent实战:从零搭建自动操作网页的智能体 这次我们来看一个 Agent 自主行动的真实案例为了让用户抢到热门健身课的一个名额一个 AI Agent 自己完成了一整套网页操作最终拿到了结果。新闻标题用了 “Rogue AI agent hacks gym” 来形容它其实多数时候这不是在攻击系统漏洞而是 Agent 用更快的速度、更连贯的决策抓住了真实业务系统里“人很难抓住的那个操作窗口”。这个案例放在 AI Agent 开发里很有参考价值。它不是那种“聊两句再生成一段文字”的对话机器人而是把规划、决策、浏览器操作、循环重试和上下文记忆串在一个闭环里去解决真实世界里的一个具体任务。这类 Agent 通常是“自主行动型智能体”也是目前 AI 应用开发里最热门的方向之一。这篇文章会从架构、环境准备、最小代码实现、动作路径拆解、资源占用、安全边界几个角度把这个演示背后通用的 Agent 搭建方法拆成可复用的步骤。如果你打算开发一个能自己操作网页、调用接口、处理批量任务的 Agent这篇文章可以当作第一份落地清单。1. 核心能力速览能力项说明项目类型自主行动型 AI Agent 演示核心任务在真实预约系统中完成“登录-浏览-预约-确认”的完整闭环关键技术大模型工具调用、浏览器自动化、循环执行、上下文记忆典型入口带登录态的网页预约系统是否需要 GPU取决于大模型部署方式云端 API 可以不需要 GPU是否支持 API通常需要接入 LLM API 和业务系统 API是否支持批量任务可以通过队列设计支持多任务并发适合场景自动化抢约、表单填报、信息收集、系统操作测试这套能力组合的核心不是“单个动作”有多强而是 Agent 能根据页面反馈不断调整下一步动作。也就是说它不是写死脚本而是“动态决定”下一步做什么。正因如此它才能在遇到弹窗、页面变化、按钮不可点等异常情况时通过重新读取页面状态来修正行为。2. Agent 架构拆解为什么它能“自己把事情做完”要理解这个案例先看一个自主行动 Agent 的基本架构。它通常由四个部分组成大脑、手、循环和记忆。大脑LLM负责规划。大模型接收任务描述和当前页面状态输出下一步要执行的动作比如“点击某个按钮”“填写某个输入框”“刷新页面”。这里的动作不局限于聊天回复而是结构化指令通常表现为工具调用参数。手工具层负责执行。浏览器自动化工具如 Playwright、Selenium 是常见“手”的实现。Agent 把大模型输出的动作转成真实的鼠标点击、键盘输入和页面跳转。也有一部分 Agent 直接调用后端 API让执行速度更快。循环负责“观察-决策-行动”。这是 Agent 和普通脚本最大的区别。普通脚本按固定顺序执行Agent 则每完成一个动作后重新观察页面把新状态喂回大模型由大模型判断结果是否符合预期再决定继续还是换个办法。记忆负责保持任务连贯。一次预约往往有多个步骤用户是谁、目标课程是什么、当前预约状态如何、已经尝试过多少次这些信息要保存在上下文里。更完整的 Agent 还会把历史结果写入外部存储方便多轮任务复用。这个演示里的 Agent 之所以“看起来像黑客”本质上就是它利用了大模型对自然语言和网页结构的理解把原本需要人盯着屏幕反复操作的过程自动化了。说白了它不比你聪明但它比你快而且不会因为页面变一下就慌张报错。3. 环境准备搭建一个能操作网页的 Agent要在本地复现这种能力环境并不复杂。优先推荐 Python 3.10 以上配合 Playwright 和 OpenAI SDK也可以替换成任何兼容的大模型 API。# 安装依赖 pip install playwright openai # 安装浏览器驱动 playwright install chromium如果你打算用本地大模型可以再安装 vLLM 或 Ollama 这类推理服务如果直接用云端 API则不需要处理 GPU 环境。这里以最省事的云端 API 为例。项目目录建议这样组织agent-gym-demo/ ├── agent.py # 核心 Agent 循环 ├── browser.py # 浏览器操作封装 ├── tools.py # 工具函数定义 ├── config.yaml # 模型和任务配置 ├── log/ # 运行日志 ├── data/ # 页面快照、中间结果 └── output/ # 最终结果这种拆分是为了后续调试方便。Agent 运行过程中会产生大量中间状态把浏览器操作和核心决策逻辑分开能让你在 Agent“误判”时快速定位问题到底出现在模型决策层还是动作执行层。4. 从零写一个最小 Agent 循环下面给出一段最小可运行的 Agent 循环代码。它只保留最核心的“决策-执行-观察”框架不依赖任何重型框架。import json import time from openai import OpenAI client OpenAI() TOOLS [ { type: function, function: { name: browser_action, description: 在当前浏览器页面执行操作, parameters: { type: object, properties: { action: { type: string, enum: [goto, click, fill, read, refresh, wait] }, target: {type: string}, value: {type: string} }, required: [action, target] } } } ] def run_agent(task: str, max_steps: int 20): messages [ { role: system, content: 你是一个网页操作助手。请根据用户任务和当前页面状态调用工具完成操作。每次只执行一个动作执行完等待页面反馈。 }, {role: user, content: task} ] for step in range(max_steps): response client.chat.completions.create( modelgpt-4o, messagesmessages, toolsTOOLS, tool_choiceauto ) msg response.choices[0].message if not msg.tool_calls: # 大模型认为任务已完成 print(Agent 认为任务完成, msg.content) return msg.content messages.append(msg) for call in msg.tool_calls: args json.loads(call.function.arguments) print(f[step {step}] 执行 {args[action]} - {args.get(target)}) result execute_browser_action(args) messages.append({ role: tool, tool_call_id: call.id, content: json.dumps(result, ensure_asciiFalse) }) time.sleep(1) raise TimeoutError(Agent 执行步骤超出限制)对应的浏览器操作函数可以这样封装from playwright.sync_api import sync_playwright browser None page None def execute_browser_action(args): global browser, page action args[action] if action goto: if page is None: pw sync_playwright().start() browser pw.chromium.launch(headlessFalse) page browser.new_page() page.goto(args[target]) return {ok: True, url: page.url, title: page.title()} if action click: page.click(args[target]) page.wait_for_load_state(networkidle) return {ok: True, url: page.url} if action fill: page.fill(args[target], args.get(value, )) return {ok: True} if action read: text page.inner_text(args[target]) return {ok: True, content: text[:2000]} if action refresh: page.reload() page.wait_for_load_state(networkidle) return {ok: True, url: page.url} if action wait: page.wait_for_timeout(int(args.get(value, 1000))) return {ok: True} return {ok: False, error: f未知动作{action}}这段代码足够让你跑通一个“打开页面-读取内容-点击元素”的最小闭环。真实系统里还需要处理登录态、弹窗、选择器和页面加载失败等情况但整体框架不变。5. “抢课”动作拆解Agent 的自主执行路径回到健身课预约这个案例。按这类系统的常见流程Agent 在执行时大致会走这样一条路径5.1 打开预约页面Agent 先进入健身房课程列表页。这里的关键是要能识别出目标课程。大模型会读取页面文本在课程列表中找到包含“热点课”“热门”“名额紧张”等关键词的行或者直接匹配用户指定的课程名。5.2 登录与状态保持预约系统通常要求登录。Agent 需要提前保存好 Cookie 或登录态避免每次运行时都被登录页拦住。更规范的做法是在浏览器自动化工具里复用同一个用户数据目录把登录态持久化。# 复用浏览器用户数据目录保存登录态 browser pw.chromium.launch_persistent_context( user_data_dir./user_data, headlessFalse )5.3 检查余量并轮询这是整个流程里最“机械”的部分也是最耗时间的部分。Agent 需要周期性刷新页面读取课程剩余名额。如果名额为 0就等待 10 秒或 20 秒后再次刷新。这里有三个优化点轮询间隔不能太短否则容易被系统限流或封禁。轮询时要记录重试次数防止无限循环。页面文案经常变化比如“已满员”变成“名额不足”Agent 要能根据语义而不是硬匹配来判断状态。5.4 点击预约并处理弹窗当页面状态变成“可预约”时Agent 立即执行点击操作。但真实系统往往不只一个按钮有二次确认弹窗、有服务条款勾选、有验证码甚至有排队等待页。Agent 每点完一步都需要重新读取页面来判断是否进到了下一步。如果遇到验证码我的建议是直接停止并把控制权交给人。很多自动化项目在验证码上投入大量精力本质上是在对抗系统的反自动化策略这类做法既不稳定也不合规。5.5 确认结果并记录预约成功后Agent 要读取确认页中的时间、名额、订单号等关键信息然后把结果写入日志或发通知。# 记录预约结果 result { course: 热门单车课, time: 2025-06-01 19:00, status: success, order_id: ORD20250601342 } with open(output/result.json, w, encodingutf-8) as f: json.dump(result, f, ensure_asciiFalse, indent2)这个动作拆解过程不仅是给“抢课”用的。任何浏览器自动化任务包括数据填报、定时打卡、信息订阅、后台配置同步都可以按同样的思路拆解成“打开-登录-检查-操作-确认”五个阶段。6. 接口 API、批量任务与自动化运维如果只是在浏览器里跑一次任务Agent 的价值有限。真正的工程化使用是把 Agent 封装成服务支持接口调用和批量任务。6.1 把 Agent 封装成 API 服务用 FastAPI 包一层接口是最常见的做法。from fastapi import FastAPI, BackgroundTasks from pydantic import BaseModel app FastAPI() class TaskRequest(BaseModel): task: str max_steps: int 20 callback_url: str app.post(/agent/run) async def run_agent_task(req: TaskRequest, background_tasks: BackgroundTasks): task_id generate_task_id() background_tasks.add_task(execute_agent, task_id, req) return {code: 0, task_id: task_id}接口返回task_id业务方拿着这个 ID 去查结果。这种方式适合耗时较长的任务不用让请求一直挂着等待 Agent 执行完。6.2 批量任务队列批量任务的难点不是“能不能并发”而是“如何控制节奏”。如果一个 Agent 同时开几十个浏览器窗口机器内存很容易被打满。更稳妥的设计是维护一个任务队列{ queue: [ { task_id: task_001, type: reserve_course, target: 2025-06-01 19:00, retry_count: 3, interval_seconds: 15 }, { task_id: task_002, type: reserve_course, target: 2025-06-02 19:00, retry_count: 3, interval_seconds: 15 } ] }执行引擎从队列里取出任务按配置的interval_seconds逐个执行。每个任务独立记录日志失败时重试重试次数用完后标记为失败并通知管理员。6.3 调用方式的兼容性如果你希望 Agent 也能被 Click 或表单工具调用还需要把浏览器动作层拆成无状态的函数。比如goto(url)、click(selector)、fill(selector, value)这种只接收参数、返回统一结果结构的形式。这样上层无论是 Web API、命令行还是消息机器人都能共用同一套执行逻辑。6.4 任务日志与可观测性自主行动的 Agent 最怕“跑着跑着不知道它在干嘛”。工程环境里一定要加以下日志每步动作的时间戳大模型返回的原始决策页面截图或 HTML 快照token 消耗数重试原因和次数# 保存每一步的页面快照便于事后排查 def save_snapshot(page, step): page.screenshot(pathflog/step_{step:03d}.png) with open(flog/step_{step:03d}.html, w, encodingutf-8) as f: f.write(page.content())这一步看起来简单但实际排查 Agent 问题时页面快照往往比日志文本有用得多因为它能还原当时的真实页面状态。7. 资源占用与性能观察这一类自主行动 Agent 的资源占用主要来自两个地方浏览器进程和大模型 API 调用。浏览器进程本身非常吃内存。一个带界面的 Chromium 实例空闲时占 300MB 到 500MB 内存很正常如果打开了多个标签页内存占用还会上升。所以批量任务并发度不建议一开始就调很大先用 1 到 2 个实例跑通流程再逐步增加。大模型 API 调用是另一块成本。每次把页面文本和工具结果喂给大模型都要消耗 token。一个简单的“读取页面-判断状态-点击按钮”动作来回可能需要数百甚至上千 token。轮询次数越长token 成本越高。降低成本的思路有两个减少每个步骤传入的文本长度。只截取当前页面中与目标按钮相关的区域而不是把整页全部发给大模型。合并状态判断。连续多次“读取-判断”可以交给代码内置规则只在出现特殊状态或决策分歧时才调用大模型。性能观察方面建议从四个指标入手单任务完成时间。从点击开始到确认结束目标通常控制在几十秒内。大模型调用次数。如果一个任务调用了超过 20 次说明决策链路里存在大量无意义往返。轮询失败率。频繁出现页面加载超时、元素找不到往往说明选择器不可靠。并发执行时的内存增长曲线。如果每新增一个任务就增加 500MB 内存说明浏览器实例复用做得不好。8. 常见问题与排查方法问题现象可能原因排查方式解决方案Agent 停在某个页面不动大模型输出动作无法匹配到页面元素查看工具返回的页面截图和错误信息给选择器增加更稳定的文本锚点或改用语义化定位一直刷新但没有点击页面状态判断不准确检查大模型接收到的页面文本是否包含“可预约”关键字对状态字段做预处理只把时间、名额数等关键内容传给大模型验证码拦截系统启用了反自动化策略观察页面是否出现验证码组件自动化流程中遇到验证码直接暂停转人工处理代理运行一段时间后内存上涨浏览器实例没有关闭检查进程树查看残留的 chromium 进程每次任务结束调用browser.close()并定期重启浏览器进程接口频繁超时Agent 执行时间超过 HTTP 请求超时时间查看后端超时配置改为后台异步任务 轮询结果接口登录态失效Cookie 过期或用户数据目录损坏重新打开浏览器确认登录状态使用独立的 user_data_dir并定期验证登录态批量任务积压队列消费速度慢于任务生成速度查看队列长度和每个任务的执行时长增加并发数或优化单任务耗时输出结果不稳定大模型对同一页面产生了不同判断检查 prompt 是否缺少明确的判断标准在系统提示里加入“只有在出现明显可点击按钮时才执行点击”等约束排查自主行动 Agent 的问题核心思路是先看“页面到底是什么状态”再看“大模型接收到什么信息”最后看“动作执行有没有报错”。很多 Agent 行为异常本质上都是“大模型看到的页面”和“真实页面”不一致导致的。9. 安全、隐私与合规边界这是整篇文章里最需要停下来认真看的部分。“Agent 自动抢课”这种演示做出来很容易但用在哪里、怎么用边界必须清楚。以下几条建议直接照做第一只有在你自己拥有账号、并且该系统的服务条款允许自动化操作的场景下才能做这类测试。很多预约类系统的用户协议明确禁止自动化访问违反后轻则封号重则可能产生法律风险。演示归演示商用前必须先确认授权边界。第二不要编写绕过身份验证、绕过验证码、越权访问数据的代码。这个健身课案例里的 Agent 是在网页正常的操作流程里完成任务它没有“破解”系统而是使用了更快的操作。一旦 Agent 开始尝试绕过安全机制性质就完全不同了这是明确不能做的高风险动作。第三敏感信息要隔离。Agent 运行过程中会接触登录 Cookie、用户令牌、订单信息等数据。这类数据不能写进日志更不能上传到不可信的大模型接口。如果使用云端 API建议在发送前做脱敏只保留执行动作所需的字段。第四Agent 的“失控”风险要提前预防。大模型不是绝对稳定的它可能在某个页面状态下做出超出预期的判断。所以生产环境里必须加行为白名单、最大步骤限制、人工审批节点和紧急停止开关。不要给 Agent 完全自治的权限尤其在涉及支付、隐私数据修改和账号安全操作的环节。第五如果涉及人脸、声音、个人隐私信息需要获得明确授权。这个案例虽然不涉及这些但很多 Agent 项目会导向图像生成、数字人、语音克隆等领域这些场景的合规要求更高授权链条必须完整。10. 总结这个案例最值得学的点这个 “Rogue AI agent hacks gym” 的演示之所以值得关注不是因为它“抢”到了课而是它展示了一条完整的自主行动链路大模型负责理解、决策和判断浏览器自动化负责执行循环机制负责不断修正日志系统负责审计。这个链路是所有真实世界 Agent 应用的共同骨架。如果你想动手验证建议先找一个你自己持有账号、允许自动化测试的网站从最简单的“打开页面-读取信息-点击按钮”开始跑通最小闭环再逐步增加复杂动作和状态判断。第一步测试时直接使用最小 Agent 循环的代码即可不需要一开始就搭建完整框架。最容易踩的坑有三个一是登录态不稳定导致任务跑到一半卡在登录页二是页面选择器变化频繁导致 Agent 感知到的页面和实际不一致三是没有日志任务失败后完全没有线索。把这三个基础问题解决掉Agent 的实用性会大幅提升。后续可以继续扩展的方向包括把 Agent 接入消息通知服务让它在任务完成时自动提醒引入长期记忆存储让 Agent 跨多次任务记住用户偏好和操作习惯增加多 Agent 协作让一个 Agent 负责监控、另一个负责执行。每一层扩展背后都有对应的系统设计问题值得单独写文章展开。