公司动态
AI驱动浏览器自动化:从意图到执行的智能网页操作实践
1. 项目概述当AI成为你的浏览器“驾驶员”最近在折腾自动化测试和网页数据抓取的朋友估计没少为浏览器自动化工具头疼。传统的Selenium、Puppeteer虽然强大但写起脚本来定位元素、处理弹窗、等待加载每一步都得小心翼翼代码里充满了各种time.sleep和复杂的XPath/CSS选择器。更别提那些动态加载、反爬机制复杂的现代单页应用了一个不小心脚本就卡死或者抓不到数据。这时候如果有一个工具能让你用自然语言告诉它“去某某网站找到最新的那篇文章把标题和摘要保存下来”然后它就能像真人一样操作浏览器完成任务是不是省心多了bb-browser这个项目就在尝试把这种想象变成现实。它的核心目标很明确让AI特别是大语言模型直接接管和控制浏览器实现基于自然语言指令的、高度智能的网页自动化操作。简单来说bb-browser试图在浏览器和像GPT-4、Claude这样的LLM之间架起一座桥梁。你不再需要编写一行行精确的DOM操作代码而是通过一个“AI智能体”来指挥浏览器。你告诉AI你的意图AI来理解网页结构、决定点击哪里、输入什么、如何翻页并最终提取或操作你需要的信息。这不仅仅是“录制与回放”的升级而是一种根本性的范式转变——从程序驱动转向意图驱动。这个项目适合谁呢首先是那些需要进行大量重复性网页操作的非技术或轻度技术背景的运营、市场、数据分析人员他们可以用最直观的方式实现工作流自动化。其次是开发者尤其是那些面对复杂、多变网页结构的爬虫工程师和测试工程师bb-browser能极大降低脚本的维护成本。最后对于任何想探索“AI智能体”在实际应用中如何与环境这里是浏览器交互的研究者或爱好者这也是一个绝佳的实验平台。2. 核心设计思路构建AI与浏览器的“双向感知”通道bb-browser的设计哲学可以类比为给AI配备了一个“眼睛”和“一双手”并建立了一套高效的沟通机制。它的核心思路不是简单地用API模拟点击而是让AI真正“看到”页面并“思考”后做出行动。2.1 架构拆解从指令到动作的完整闭环整个系统的运行遵循一个清晰的闭环用户指令 - AI解析与规划 - 浏览器状态感知 - AI决策与动作执行 - 结果返回。bb-browser在其中扮演了“中间件”和“执行器”的双重角色。指令输入与AI接口层你通过自然语言下达任务比如“帮我查一下今天纽约的天气”。bb-browser本身不包含AI模型它需要连接一个外部的LLM服务如OpenAI API、Azure OpenAI或本地部署的模型。这一层负责将你的指令、以及后续从浏览器获取的上下文组合成符合模型要求的Prompt发送给LLM并接收LLM返回的“思考过程”和“下一步动作指令”。浏览器状态感知层AI的“眼睛”这是关键的一环。AI不能直接“看”网页。bb-browser需要将浏览器的当前状态转化为AI能理解的文本描述。这通常包括DOM简化与摘要获取当前页面的HTML但全量DOM过于庞大且嘈杂。bb-browser会通过算法提取关键元素生成一个结构化的、包含标签名、ID、类名、可见文本和关键属性如hreftype的简化版页面描述。屏幕截图或可访问性树更高级的感知可能包括截取屏幕截图供多模态模型分析或者获取页面的可访问性树这对于理解复杂视觉组件状态特别有用。当前URL和页面标题提供基本的上下文信息。动作执行层AI的“手”接收到LLM返回的JSON格式动作指令后例如{action: click, selector: #search-button}bb-browser通过浏览器自动化驱动底层可能是Playwright或Puppeteer来精确执行点击、输入文本、滚动、导航等操作。控制与协调层负责管理整个循环。它决定何时向AI请求下一步动作例如每次页面加载完成后如何处理AI输出中的不确定性以及在任务成功、失败或陷入循环时如何终止或提醒用户。2.2 为什么是“意图驱动”而非“脚本驱动”传统自动化是“脚本驱动”的开发者必须预见到所有可能的状态和路径并编写精确的代码来处理。这在静态网站上尚可但在动态Web应用面前显得脆弱。一个前端框架的微小更新就可能让精心编写的选择器失效。bb-browser倡导的“意图驱动”则不同鲁棒性更强AI可以根据当前页面的实际内容动态调整策略。如果预期的按钮没找到它可能会尝试寻找功能相似的文本或元素。开发效率飞跃无需深入学习目标网站的DOM结构用人类语言描述任务即可。处理不确定性能够应对临时弹窗、网络延迟、元素加载顺序变化等意外情况AI可以“等待”或“寻找替代方案”。当然这种模式也带来了新的挑战主要是成本和可靠性。每一次决策都需要调用LLM API产生费用和延迟。同时AI的决策并非100%可靠可能做出不符合预期的操作。因此bb-browser的设计中必须包含对AI决策的约束、验证和容错机制。3. 核心实现细节与实操要点理解了设计思路我们来看看bb-browser具体是如何实现这些能力的。这里我们以一个典型的基于Playwright和OpenAI GPT-4的实现方案为例拆解其核心模块。3.1 环境搭建与基础配置首先你需要一个Python环境。bb-browser通常是一个Python库或一套脚本。# 1. 创建虚拟环境推荐 python -m venv bb-browser-env source bb-browser-env/bin/activate # Linux/macOS # 或 bb-browser-env\Scripts\activate # Windows # 2. 安装核心依赖 pip install playwright openai # 3. 安装Playwright所需的浏览器内核 playwright install chromium # 通常选择Chromium足够接下来是关键的API密钥配置。bb-browser需要与LLM对话所以你必须有一个OpenAI的API密钥或其他兼容API的密钥。# 在你的脚本或环境变量中设置 import os os.environ[OPENAI_API_KEY] sk-your-actual-api-key-here注意永远不要将API密钥硬编码在提交到版本控制系统的代码中。使用环境变量或安全的密钥管理服务是必须遵守的安全准则。3.2 页面状态提取给AI“看”什么这是决定AI决策质量的核心。全量HTML有太多噪音脚本、样式、隐藏元素。一个高效的提取策略是过滤与精简只保留交互性元素如a,button,input,select和重要的文本容器如p,h1-h6,li。移除所有style、script标签和内联样式。提取关键属性对于每个元素提取其id、class、aria-label、placeholder、name、type对于输入框以及其可见文本。可见文本的提取需要递归遍历子节点忽略隐藏元素。生成结构化描述将上述信息组织成一种简洁的格式例如一个元素列表每个元素包含类型、简要描述和唯一标识符。这个标识符可以是精简后的CSS选择器或者是基于其在DOM树中位置的XPath。# 简化示例提取页面关键信息 async def extract_page_state(page): # 获取简化后的DOM元素信息 elements await page.evaluate( () { const items []; // 遍历所有可能交互的元素 document.querySelectorAll(a, button, input, textarea, select, [rolebutton], [rolelink]).forEach(el { if (el.offsetParent null) return; // 跳过隐藏元素 const rect el.getBoundingClientRect(); if (rect.width 0 || rect.height 0) return; // 跳过不可见元素 const text el.innerText || el.value || el.placeholder || el.getAttribute(aria-label) || ; // 生成一个相对稳定的选择器简化版 let selector ; if (el.id) selector #${el.id}; else if (el.className) selector .${el.className.split( )[0]}; else selector el.tagName.toLowerCase(); items.push({ tag: el.tagName.toLowerCase(), selector: selector, text: text.trim().substring(0, 100), // 截断长文本 type: el.type, isVisible: true }); }); return items; } ) # 获取页面标题和URL title await page.title() url page.url return { url: url, title: title, interactive_elements: elements }这个extract_page_state函数返回的字典就是我们将要发送给AI的“世界状态观察报告”。它足够小节省token又包含了AI决策所需的关键信息。3.3 与LLM的交互设计高效的PromptPrompt工程在这里至关重要。我们需要让LLM扮演一个“浏览器操作智能体”的角色并理解我们的任务和它所能执行的动作。def construct_prompt(user_task, page_state, action_historyNone): system_message 你是一个专业的网页浏览器自动化助手。你的任务是理解用户的指令并通过操作浏览器来完成它。 你可以执行以下动作 - click(selector): 点击一个元素。selector是一个CSS选择器。 - type(selector, text): 在输入框内输入文本。 - scroll(direction): 滚动页面direction可以是 up 或 down。 - goto(url): 导航到一个新的URL。 - wait(time_in_seconds): 等待指定秒数。 - extract(description): 根据描述提取当前页面的信息并返回。 - finish(result): 任务完成并返回最终结果。 你每次只能执行一个动作。我会提供当前页面的状态。请基于状态和任务历史决定下一步最合适的动作。 你的回复必须是严格的JSON格式{thought: 你的思考过程, action: 动作名称, args: {参数对象}}。 user_message f 用户任务{user_task} 当前页面状态 URL: {page_state[url]} 标题: {page_state[title]} 可交互元素共{len(page_state[interactive_elements])}个: {chr(10).join([f- [{e[tag]}] 选择器: {e[selector]} 文本: {e[text]} for e in page_state[interactive_elements][:15]])} ...只显示前15个 请决定下一步动作。 if action_history: user_message f\n动作历史{action_history} return [ {role: system, content: system_message}, {role: user, content: user_message} ]这个Prompt明确规定了动作集合、输入输出格式并将复杂的页面状态进行了摘要引导LLM进行结构化输出。action_history的加入有助于AI避免重复操作或陷入循环。3.4 动作解析与执行循环收到LLM的JSON响应后我们需要解析并执行。import json from openai import OpenAI client OpenAI() async def ai_decision_cycle(page, user_task): max_steps 20 action_history [] for step in range(max_steps): # 1. 提取当前页面状态 state await extract_page_state(page) # 2. 构建Prompt并调用LLM prompt construct_prompt(user_task, state, action_history) response client.chat.completions.create( modelgpt-4, # 或 gpt-3.5-turbo messagesprompt, temperature0.1, # 低温度保证输出稳定性 response_format{type: json_object} # 要求返回JSON ) # 3. 解析AI响应 try: ai_output json.loads(response.choices[0].message.content) thought ai_output.get(thought, ) action ai_output.get(action, ) args ai_output.get(args, {}) print(f步骤 {step1}: AI思考 - {thought}) print(f 决定动作 - {action} with {args}) except json.JSONDecodeError: print(AI返回了非JSON格式任务终止。) break # 4. 执行动作 if action finish: print(f任务完成结果{args.get(result, N/A)}) return args.get(result) elif action click: selector args.get(selector) if selector: await page.click(selector) await page.wait_for_load_state(networkidle) # 等待网络空闲 elif action type: selector args.get(selector) text args.get(text) if selector and text: await page.fill(selector, text) elif action goto: url args.get(url) if url: await page.goto(url) await page.wait_for_load_state(networkidle) # ... 处理其他动作 # 5. 记录历史 action_history.append({step: step, action: action, args: args}) # 短暂暂停模拟人类操作间隔 await page.wait_for_timeout(1000) print(达到最大步数限制任务可能未完成。) return None这个循环构成了bb-browser的核心引擎。它不断“观察-思考-行动”直到AI发出finish信号或达到步数限制。4. 实战演练构建一个智能天气查询助手让我们用一个完整的例子将上述模块串联起来实现一个简单的自动化任务“打开百度搜索‘纽约天气’进入第一个结果页提取今天的天气概况。”4.1 主程序搭建import asyncio from openai import OpenAI from playwright.async_api import async_playwright import os import json # 假设 extract_page_state 和 construct_prompt 函数已定义如上 # 假设 ai_decision_cycle 函数已定义如上 async def main(): # 初始化OpenAI客户端 client OpenAI(api_keyos.getenv(OPENAI_API_KEY)) # 启动Playwright浏览器 async with async_playwright() as p: # 使用带界面的浏览器便于调试生产环境可用 headlessTrue browser await p.chromium.launch(headlessFalse, slow_mo100) # slow_mo让动作变慢方便观察 context await browser.new_context(viewport{width: 1280, height: 720}) page await context.new_page() # 定义用户任务 user_task 打开百度首页搜索‘纽约天气’进入第一个搜索结果链接提取今天纽约的天气情况温度、天气状况、风力等。 print(f开始执行任务: {user_task}) # 启动AI决策循环 result await ai_decision_cycle(page, user_task) # 输出最终结果 if result: print(\n 最终提取结果 ) print(result) else: print(\n任务未能完成。) # 保持浏览器打开一段时间供查看然后关闭 await asyncio.sleep(5) await browser.close() if __name__ __main__: asyncio.run(main())4.2 执行过程推演与AI决策分析当你运行这个脚本时AI会如何一步步思考并操作呢我们来推演一下初始状态页面空白about:blank。AI收到任务和空页面状态。它思考“我需要先导航到百度首页。当前页面没有可交互元素最适合的动作是goto百度网址。”动作1AI输出{action: goto, args: {url: https://www.baidu.com}}。脚本执行浏览器打开百度。状态2页面加载完成extract_page_state提取到百度首页的元素包括搜索框#kw和搜索按钮#su。AI思考“我已到达百度首页。用户要搜索‘纽约天气’。我需要先点击搜索框输入文本然后点击搜索按钮。”动作2AI输出{action: click, args: {selector: #kw}}。脚本点击搜索框。动作3AI输出{action: type, args: {selector: #kw, text: 纽约天气}}。脚本输入关键词。动作4AI输出{action: click, args: {selector: #su}}。脚本点击“百度一下”。状态3进入搜索结果页。AI看到很多链接。它思考“用户要求进入第一个结果链接。我需要识别出第一个非广告的搜索结果链接并点击。” 它可能会选择一个包含“天气”字样且看起来是第一个的链接选择器。动作5AI输出{action: click, args: {selector: div.result.c-container h3.t a}}这是一个假设的选择器。脚本点击进入天气详情页。状态4进入天气网站。AI看到温度、天气图标、风力等元素。它思考“我已到达目标页面。现在需要提取今天的天气信息。我可以使用extract动作并描述需要的信息。”动作6AI输出{action: extract, args: {description: 提取今天纽约的温度数字和单位、天气状况如晴、多云、雨、风力等级和风向}}。脚本处理extract动作需要自定义处理函数。它可以基于当前页面的state通过更精确的DOM查询或甚至OCR如果信息在图片里来获取数据然后模拟AI返回一个finish动作。动作7由脚本触发最终脚本组装好提取的信息并调用{action: finish, args: {result: 纽约今天晴气温 15-22°C西北风3-4级。}}。循环结束返回结果。这个过程展示了AI如何将高层次任务分解为一系列低层次浏览器操作。关键在于你作为用户完全不需要知道百度搜索框的ID是#kw也不需要知道搜索结果的选择器是什么。你只需要说出你的意图。5. 进阶技巧与性能优化实战直接使用上述基础框架可能会遇到成本高、速度慢、稳定性差的问题。下面分享一些在实际项目中打磨出来的进阶技巧。5.1 降低LLM调用成本与延迟的策略频繁调用GPT-4是一笔不小的开销。我们可以从几个方面优化使用更便宜的模型进行简单决策并非每一步都需要GPT-4的推理能力。可以设计一个“路由机制”对于简单的、模式化的操作例如“在输入框里输入已知文本”、“点击一个标识非常明确的按钮”使用本地规则或小模型如GPT-3.5 Turbo来判断。只有遇到复杂页面或歧义时才唤醒GPT-4。def decide_model_to_use(page_state, action_history): 根据页面复杂度和历史决定使用哪个模型 if len(page_state[interactive_elements]) 5: # 页面极其简单可以用规则或便宜模型 return gpt-3.5-turbo # 检查是否在重复动作陷入循环 if len(action_history) 3 and action_history[-3:] action_history[-6:-3]: return gpt-4 # 切换更强模型尝试破局 # 默认使用3.5成本低 return gpt-3.5-turbo压缩与摘要页面状态前面提到的extract_page_state已经是压缩。可以更进一步使用LLM本身来对页面状态进行摘要。例如先调用一次快速、便宜的模型如Claude Haiku让它用一句话描述当前页面的主要内容和可操作项再将这个摘要和少量关键元素发送给主决策模型。这能大幅减少Prompt中的token数量。动作批处理允许AI在一次决策中规划一个小的动作序列如“点击A然后在出现的输入框里输入B”而不是严格的一步一请示。这需要AI有更强的规划能力并且需要脚本能够安全地执行序列例如加入元素存在性检查。5.2 提升稳定性和成功率的工程化方法AI会“犯傻”我们需要为它设置护栏。选择器回退与验证AI建议的选择器可能失效。在执行click或type前应添加验证。async def safe_click(page, selector): 安全点击如果选择器无效则尝试备用方案 try: # 先等待并检查元素是否存在且可点击 await page.wait_for_selector(selector, statevisible, timeout5000) await page.click(selector) return True except Exception as e: print(f点击失败 {selector}: {e}) # 回退策略1尝试通过文本内容查找 # 可以从page_state中寻找包含相似文本的元素 # 回退策略2让AI重新决策 return False循环检测与中断AI可能陷入无限循环例如在两个页面间来回点击。需要在action_history中检测重复模式并在检测到时强行中断或向AI发送警告要求它改变策略。def detect_loop(action_history, window5): 检测最近的动作序列是否重复 if len(action_history) window * 2: return False recent [a[action] for a in action_history[-window:]] previous [a[action] for a in action_history[-window*2:-window]] return recent previous人工确认关键步骤对于涉及支付、删除、发布等不可逆操作或者导航到非预期域名的情况可以设置检查点暂停自动化并请求人工确认。这可以通过在Prompt中强调或在代码中插入判断来实现。丰富的基础动作库除了基本的点击输入提供更多动作能增强AI能力。例如screenshot: 截屏并保存用于存档或后续分析。dropdown_select: 处理下拉菜单选择。mouse_hover: 模拟鼠标悬停触发某些动态菜单。execute_js: 执行一段JavaScript代码处理极端情况。 在Prompt中清晰定义这些动作AI在需要时就会调用它们。5.3 针对复杂场景的Prompt增强技巧提供网站特定知识如果你要自动化的网站有特殊结构可以将这些知识注入System Prompt。例如“目标网站的表单提交按钮是一个红色的、类名为.submit-btn的div而不是button。” 这能极大提升AI在该网站上的操作准确性。分阶段任务分解对于非常长的任务如“在这个电商网站上下单购买一台笔记本电脑”不要一次性把整个任务丢给AI。可以人工或用一个高级AI先将其分解为子任务登录-搜索-筛选-查看详情-加入购物车-结算-填写地址-支付然后让bb-browser依次处理每个子任务。这降低了单次决策的复杂度。引入“记忆”或“知识库”让AI能够记住之前步骤中获取的关键信息。例如在登录后记住“用户已登录”在搜索后记住“目标商品ID是12345”。这可以通过在每次传递给AI的Prompt中包含一个简短的“会话记忆”字符串来实现。6. 常见问题、故障排查与实战心得在实际使用bb-browser或自建类似系统的过程中你会遇到各种各样的问题。下面是我踩过的一些坑和总结的排查思路。6.1 AI决策逻辑问题问题现象可能原因排查与解决思路AI反复执行同一无效动作1. 页面状态提取不准确AI未感知到变化。2. Prompt未提供足够的上下文或历史。3. AI陷入局部思维循环。1.增强状态感知在extract_page_state中加入页面URL、标题哈希或关键区域文本的哈希确保AI能感知到页面变化。2.丰富Prompt在Prompt中明确加入最近3-5步的动作历史并提醒AI避免重复。3.引入随机性适当提高LLM的temperature如从0.1调到0.3或当检测到循环时在Prompt中明确指令“你似乎陷入了循环请尝试一个完全不同的操作。”AI选择的选择器永远找不到1. AI生成的CSS选择器过于复杂或脆弱。2. 元素是动态加载的AI决策时元素尚未出现。1.简化选择器生成在extract_page_state中为每个元素提供一个更稳定、简洁的选择器优先id 其次唯一的class组合。并指导AI优先使用你提供的选择器。2.增加等待与重试在执行动作前代码中应加入wait_for_selector或更通用的wait_for_load_state。可以教导AI在动作后使用wait指令。AI不理解任务或执行无关动作1. System Prompt定义不清角色扮演失败。2. 用户任务描述模糊。1.精炼System Prompt用更清晰、强制的语言定义AI的角色、可用动作和输出格式。使用“你必须”、“你只能”等词语。2.任务具体化引导用户给出更具体的指令。例如将“获取产品信息”具体化为“在product-detail这个区域找到class为price的元素和product-title的元素提取它们的文本”。6.2 工程与执行问题问题现象可能原因排查与解决思路脚本运行速度极慢1. 每次循环都调用LLM网络延迟高。2. 页面状态提取太耗时。3.wait_for_timeout设置过长。1.模型路由与缓存如前所述使用便宜模型处理简单步骤。对相同的页面状态可以缓存AI的决策结果。2.优化DOM提取使用更高效的JavaScript片段进行DOM查询避免返回过多数据。可以考虑只提取首屏元素。3.动态等待用wait_for_load_state(‘networkidle’)或wait_for_selector替代固定的sleep减少不必要的等待。在iframe或弹窗中操作失败AI未意识到上下文已切换到iframe或新窗口。1.状态感知增强在extract_page_state中检查并记录当前页面的所有iframe和窗口句柄。2.扩展动作库增加switch_to_frame(selector)和switch_to_window(index)动作并在Prompt中说明。当AI发现目标元素不在主页面时它应主动切换上下文。遇到验证码或强人机验证自动化行为被网站检测到。这是自动化领域的经典难题。bb-browser本身难以解决。可以1.降低检测概率使用slow_mo模拟人类操作间隔随机化等待时间使用真实的浏览器配置文件User-Agent, Cookies。2.人工干预点在流程中设置中断当检测到验证码时暂停脚本通知人工处理处理后再继续。3.集成第三方服务调用专业的验证码识别服务需要额外成本。必须注意此操作需严格遵守目标网站的服务条款和法律法规。6.3 我的实战心得与建议从小任务开始逐步复杂化不要一开始就挑战“自动完成整个电商购物流程”。从“登录某个网站”开始然后是“搜索并抓取列表第一项”逐步增加复杂度。这有助于你调试每一个环节。日志是你的生命线务必详细记录每一个循环AI接收到的页面状态摘要、AI的思考过程和决策、执行的动作、执行后的页面快照或URL。当任务失败时这些日志是唯一能帮你复盘AI“脑回路”的东西。可以考虑将每一步的页面截图保存下来。成本监控至关重要尤其是使用GPT-4时。在代码中加入令牌计数和费用估算功能并在任务开始前给用户一个预估。对于长时间运行的任务设置预算上限。“混合智能”是未来方向纯粹的AI驱动在复杂场景下成本高且不稳定。更实用的架构是“混合智能”规则引擎处理确定性步骤如导航到固定URLAI处理需要理解和决策的模糊步骤如从列表中识别并点击正确的项目。这样在效率和可靠性之间取得最佳平衡。道德与合规先行bb-browser是一个强大的工具。务必用于合法的自动化场景如内部系统测试、公开信息聚合遵守robots.txt、个人效率工具等。避免用于恶意爬虫、刷量、欺诈等行为尊重网站所有者的权益和资源。bb-browser所代表的“AI驱动自动化”范式正在模糊代码指令与自然语言意图之间的界限。它目前可能还不够完美运行速度、成本和稳定性都需要优化。但它指出了一个明确的方向未来的自动化将更侧重于定义“做什么”而非“怎么做”。对于开发者和使用者来说尽早理解并掌握这套思维就是在为下一个效率革命做准备。