
1. 项目概述当AI学会“上网冲浪”最近在AI应用开发圈里一个趋势越来越明显让大模型不再只是“纸上谈兵”而是能真正操作电脑、使用浏览器去完成一些需要与真实世界交互的任务。这听起来有点像科幻电影但得益于像opencode和browser-use这类框架的出现它已经变成了我们开发者可以上手实操的现实。我花了几天时间把这两个工具组合起来实实在在地跑通了一个自动化流程整个过程既有踩坑的酸爽也有跑通后的豁然开朗。这篇文章我就来和你复盘一下这次“和AI一起搞事情”的实战经历。简单来说opencode是一个开源的AI智能体Agent开发框架它提供了一套标准化的方式来定义AI的能力Skills和工作流。而browser-use则是一个专门让AI智能体控制浏览器进行自动化操作的库。把它们俩结合就等于给AI装上了眼睛和手让它能看懂网页、点击按钮、填写表单、抓取信息。我这次实战的目标很明确验证这套组合拳的可行性并梳理出一条从环境搭建到任务执行的清晰路径看看AI到底能帮我们自动完成哪些繁琐的网页操作。无论你是对AI应用开发感兴趣的工程师还是想寻找自动化解决方案的产品经理或者单纯是个技术爱好者相信这个结合了前沿框架与具体实操的复盘都能给你带来一些直接的参考和启发。我们不止聊概念更会深入到代码、配置和那些只有亲手做过才会遇到的细节里。2. 核心工具选型与思路解析为什么是opencode加browser-use这个选择背后有我对当前AI智能体开发生态的几点考量。市面上相关的框架和库不少每个都有其侧重点。2.1 为什么选择 opencodeopencode吸引我的地方在于它的“规范性”和“轻量级”。它不像一些庞大的企业级框架那样沉重而是专注于为单智能体Single Agent提供清晰、模块化的开发模式。它的核心是围绕Agent、Skill和Workflow这几个概念构建的。Skill技能这是AI能力的原子单元。比如“读取文件”、“调用某个API”、“进行数学计算”都可以被定义为一个Skill。在opencode中你可以用类似装饰器的方式非常优雅地定义一个Skill这极大地提高了代码的可读性和可维护性。Agent智能体一个Agent就是这些Skills的集合体。你为它配置一个大模型比如GPT-4、Claude或者本地部署的模型然后赋予它一系列Skills它就能根据你的指令自主判断该调用哪个Skill来完成任务。Workflow工作流对于更复杂的任务你可以通过YAML文件来定义一系列步骤形成一个工作流。这适合那些有固定流程的自动化任务。选择opencode意味着我可以用一种结构化的方式去管理AI的能力而不是写一堆零散、难以复用的脚本。这对于后续扩展和维护至关重要。2.2 为什么选择 browser-use让AI操作浏览器听起来简单实则挑战巨大。难点在于如何让AI“理解”网页的复杂结构并做出准确的操作。browser-use在这个问题上提供了一个巧妙的思路。它没有尝试让AI去直接解析原始的HTML DOM那太复杂且不稳定而是为AI提供了一个“简化版”的网页视图。这个视图提取了页面上所有可交互的元素如按钮、输入框、链接的文本描述、类型和关键属性并将其以结构化的方式比如JSON呈现给AI。AI基于这个“摘要”来判断该做什么然后browser-use再将AI的指令如“点击那个写着‘登录’的按钮”翻译成真实的浏览器操作如执行一个JavaScript点击事件。这种“抽象层”的设计非常高明它降低了大模型理解网页的门槛提高了操作的准确性和鲁棒性。相比于直接让AI去啃HTML或依赖坐标点击browser-use的方案显得更加可靠和智能。2.3 组合策略让 opencode 驱动 browser-use我的核心思路是将browser-use封装成一个或多个opencode的 Skill。这样opencodeAgent 就获得了“使用浏览器”这个强大的能力。当用户提出一个涉及网页操作的任务时opencodeAgent 会进行规划判断是否需要调用“浏览器技能”然后以正确的参数去调用它。例如我可以定义一个名为navigate_to_website的Skill其内部使用browser-use打开指定网址再定义一个extract_info_from_page的Skill用来抓取页面特定信息。这样一来一个复杂的任务如“去某某电商网站搜索‘无线鼠标’并列出前三个商品的价格”就可以被分解为导航 - 搜索 - 提取信息每个步骤对应一个清晰的Skill调用。这个架构的优点是解耦和灵活。浏览器操作的细节被隐藏在Skill内部上层的Agent只需要关心任务逻辑。未来如果需要更换浏览器自动化工具或者增加新的网页操作能力都只需要修改或新增对应的Skill即可不会影响整体的Agent逻辑。3. 环境搭建与核心配置详解理论说得再多不如动手搭起来。这部分我会详细记录从零开始搭建整个开发环境的过程并把几个关键的配置坑点讲清楚。3.1 基础环境准备首先你需要一个Python环境建议3.9以上。我习惯使用conda或venv创建独立的虚拟环境避免包冲突。# 创建并激活虚拟环境 python -m venv opencode-browser-env source opencode-browser-env/bin/activate # Linux/Mac # 或 opencode-browser-env\Scripts\activate # Windows接下来安装核心库。这里要注意版本兼容性我使用的是写作时相对稳定的版本。pip install opencode # 安装opencode核心库 pip install browser-use # 安装浏览器自动化库 pip install playwright # browser-use依赖Playwright来驱动浏览器 playwright install chromium # 安装Chromium浏览器驱动这是必须的一步注意playwright install这一步可能会因为网络问题失败。如果遇到可以尝试设置国内镜像源或者耐心多试几次。这是第一个小坑但绕不过去。3.2 opencode 的初始化与模型配置安装好后我们需要初始化一个opencode项目并配置最关键的部分——大模型。# 初始化一个opencode项目如果框架提供了cli工具 # 或者更常见的是我们直接创建一个项目结构。 mkdir my_ai_agent cd my_ai_agent创建一个config.yaml或直接在代码中配置。核心是为Agent指定一个LLM大语言模型。opencode通常支持通过环境变量或配置文件来设置API Key和模型类型。# config.yaml 示例 agent: name: WebAssistant llm: provider: openai # 也可以是 anthropic, azure_openai, 或 local (如 ollama) model: gpt-4-turbo-preview api_key: ${OPENAI_API_KEY} # 建议从环境变量读取在代码中你可以这样加载配置并创建Agentimport opencode from opencode.agent import Agent import os from dotenv import load_dotenv load_dotenv() # 加载.env文件中的环境变量 # 从配置文件或字典创建Agent配置 agent_config { name: WebAssistant, llm: { provider: openai, model: gpt-4-turbo, api_key: os.getenv(OPENAI_API_KEY), base_url: os.getenv(OPENAI_BASE_URL, None) # 如果需要自定义端点 } } my_agent Agent.from_config(agent_config)实操心得模型的选择直接影响智能体的“智商”和成本。对于复杂的网页理解和任务规划GPT-4系列的效果远好于GPT-3.5。如果考虑成本或数据隐私可以探索本地模型如通过Ollama部署的Llama 3、Qwen等但需要评估其指令跟随和规划能力是否足够。一开始验证想法时用GPT-4是效率最高的。3.3 browser-use 的启动与初始化browser-use需要一个浏览器实例来工作。我们需要在代码中启动它并将其与我们的Agent关联起来。from browser_use import Browser, BrowserConfig, Controller import asyncio async def create_browser(): # 配置浏览器例如设置无头模式不显示界面、超时时间等 config BrowserConfig( headlessFalse, # 调试时设为False可以看到浏览器操作生产环境设为True disable_securityTrue, # 有时需要禁用安全策略以访问某些页面 ) browser Browser(configconfig) controller Controller(browser) return controller # 由于browser-use是异步的我们需要在异步上下文中运行 async def main(): controller await create_browser() # ... 后续将controller与opencode skill结合这里的关键是BrowserConfig。headlessFalse在开发时非常有用你能亲眼看到AI在操作什么方便调试。但当你写好一个自动化服务部署到服务器时一定要记得改为headlessTrue。3.4 关键集成将 browser-use 封装为 opencode Skill这是整个集成的核心步骤。我们要创建一个自定义的Skill在其内部使用browser-use的Controller。from opencode.skills import skill from pydantic import Field from browser_use import Controller class BrowserSkill: def __init__(self, controller: Controller): self.controller controller skill( namenavigate_to_url, description导航到指定的网址, input_schema{ url: Field(..., description要访问的完整网址例如 https://www.example.com) } ) async def navigate(self, url: str) - str: 打开一个网页。 try: # browser-use 的 goto 方法 await self.controller.goto(url) # 可以获取当前页面的摘要信息返回给Agent page_state await self.controller.get_page_state() return f成功导航到 {url}。页面标题是{page_state.title} except Exception as e: return f导航到 {url} 时出错{str(e)} skill( nameextract_text_by_selector, description根据CSS选择器提取页面上的文本内容, input_schema{ selector: Field(..., descriptionCSS选择器例如 h1.product-title), all_elements: Field(False, description是否提取所有匹配元素默认只取第一个) } ) async def extract_text(self, selector: str, all_elements: bool False) - str: 从页面提取文本。 # 这里简化处理实际browser-use可能提供更智能的元素定位和内容提取 # 一种方法是使用controller执行JavaScript if all_elements: script fArray.from(document.querySelectorAll({selector})).map(el el.innerText).join(|||) else: script fdocument.querySelector({selector})?.innerText || 未找到元素 result await self.controller.evaluate(script) return result定义好Skill类后我们需要在创建Agent时将这个Skill实例注册进去。async def main(): # 1. 创建浏览器控制器 controller await create_browser() # 2. 创建浏览器Skill实例 browser_skill BrowserSkill(controller) # 3. 创建Agent并注册技能 my_agent Agent.from_config(agent_config) my_agent.register_skill(browser_skill.navigate) my_agent.register_skill(browser_skill.extract_text) # 4. 现在可以给Agent下达任务了 task 请打开百度首页然后搜索一下今天的天气。 response await my_agent.run(task) print(response)这个流程清晰地展示了如何将底层工具的能力通过Skill的形式“嫁接”到AI智能体上形成它可理解和调用的能力。4. 实战任务分解与自动化流程实现环境搭好了技能也定义了现在我们来设计一个具体的实战任务看看AI如何一步步完成。我设计了一个相对经典且实用的场景商品价格监控。具体任务是“去京东网站搜索‘联想拯救者笔记本电脑’获取排名前五的商品名称和价格并计算平均价格。”4.1 任务规划与技能链设计对于这个任务一个合格的Agent应该能自己规划出以下步骤导航打开京东首页 (navigate_to_url)。交互找到搜索框输入关键词“联想拯救者笔记本电脑”并点击搜索按钮。这可能需要一个新的Skill比如input_text_and_submit。等待与筛选等待搜索结果页面加载完成并定位到商品列表区域。信息提取从商品列表中提取前五个商品的名称和价格 (extract_text_by_selector或更专用的extract_product_list)。数据处理解析提取到的文本将价格转换为数字并计算平均值。这可以是一个纯数据处理的Skill如calculate_average_price。我们需要为步骤2和5创建新的Skill并完善步骤4的提取逻辑。4.2 实现交互与数据提取技能首先实现一个更通用的交互技能用于输入文本和点击。skill( nameinput_and_click, description在页面上找到匹配描述的元素输入文本或点击它, input_schema{ element_description: Field(..., description要交互的元素的描述如‘搜索框’、‘登录按钮’), action: Field(..., description要执行的动作type 或 click), text: Field(None, description如果是type动作需要输入的文本) } ) async def interact_with_element(self, element_description: str, action: str, text: str None) - str: 依赖browser-use的AI元素定位能力。 browser-use的controller可以让LLM根据描述去找到元素并操作。 这里假设我们有一个更高级的browser-use方法。 # 注意这是理想化的接口实际browser-use的API可能需要调整 # 一种可能的实现是使用controller的perform_action方法传入自然语言指令 if action click: result await self.controller.ai_click(element_description) return f已点击‘{element_description}’。 elif action type and text: result await self.controller.ai_type(element_description, text) return f已在‘{element_description}’中输入文本{text}。 else: return 无效的动作指令。其次实现一个专门针对京东搜索结果页的商品提取技能。这需要事先分析京东页面的HTML结构。skill( nameextract_jd_products, description从京东搜索结果页提取商品列表信息, input_schema{ max_items: Field(5, description需要提取的最大商品数量) } ) async def extract_jd_products(self, max_items: int 5) - str: 这是一个针对特定网站京东的专用技能。 通过分析京东搜索结果的DOM结构使用CSS选择器精准抓取。 # 京东商品列表项的选择器请注意网站结构可能随时变化这是最大的风险点 product_selector .gl-item name_selector .p-name em # 商品名称 price_selector .p-price i # 商品价格 product_info_list [] # 使用JavaScript批量获取元素信息比多次evaluate效率高 script f (() {{ const items document.querySelectorAll({product_selector}); const results []; const limit Math.min(items.length, {max_items}); for (let i 0; i limit; i) {{ const item items[i]; const nameEl item.querySelector({name_selector}); const priceEl item.querySelector({price_selector}); results.push({{ name: nameEl ? nameEl.innerText.trim() : 未知商品, price: priceEl ? priceEl.innerText.trim() : 未知价格 }}); }} return JSON.stringify(results); }})() try: result_json await self.controller.evaluate(script) import json products json.loads(result_json) formatted_output 提取到的商品信息\n for idx, prod in enumerate(products, 1): formatted_output f{idx}. {prod[name]} - 价格{prod[price]}\n return formatted_output except Exception as e: return f提取商品信息时发生错误{str(e)}。可能是页面结构已变化需要更新选择器。4.3 组装任务与运行Agent现在我们有了导航、交互输入点击、专用提取三个核心技能。我们可以通过一个工作流WorkflowYAML文件来定义整个任务或者直接给Agent一个复杂的自然语言指令让它自己规划。# workflow.yaml (opencode工作流示例) name: jd_price_monitor tasks: - name: open_jd skill: navigate_to_url inputs: url: https://www.jd.com - name: search_product skill: input_and_click inputs: element_description: 搜索框 action: type text: 联想拯救者笔记本电脑 - name: click_search_button skill: input_and_click inputs: element_description: 搜索按钮 action: click - name: wait_for_results # 这里可能需要一个‘等待’技能或者browser-use内部已处理 skill: delay inputs: seconds: 3 - name: extract_products skill: extract_jd_products inputs: max_items: 5在代码中加载并运行这个工作流async def run_workflow(): controller await create_browser() browser_skill BrowserSkill(controller) my_agent Agent.from_config(agent_config) my_agent.register_skill(browser_skill.navigate) my_agent.register_skill(browser_skill.interact_with_element) my_agent.register_skill(browser_skill.extract_jd_products) # 假设opencode支持从YAML加载工作流 workflow Workflow.from_yaml(workflow.yaml) final_result await my_agent.execute_workflow(workflow) print(任务执行结果) print(final_result) # 最后别忘了关闭浏览器释放资源 await controller.close()如果直接使用自然语言指令代码会更简洁但对Agent的规划能力要求更高task 请执行以下操作 1. 打开京东网站 (www.jd.com)。 2. 在搜索框中输入“联想拯救者笔记本电脑”并进行搜索。 3. 等待搜索结果加载完成。 4. 从结果中提取前5个商品的名称和价格。 5. 将结果整理成清晰的列表给我。 response await my_agent.run(task) print(response)无论采用哪种方式最终你都能看到AI自动操作浏览器并返回结构化的商品信息。这个过程充满了成就感也让你直观感受到AI智能体的潜力。5. 避坑指南与效能优化实战在实际操作中理想很丰满现实却会遇到各种骨感的问题。下面是我在实战中踩过的坑以及总结出的优化技巧。5.1 稳定性挑战网页动态加载与元素定位问题现代网页大量使用JavaScript动态加载内容。你可能刚打开页面时想要的商品列表还没加载出来AI就已经开始执行“提取”技能了结果自然是失败。解决方案显式等待在关键步骤如点击搜索后后增加一个“等待”技能。可以是一个简单的asyncio.sleep也可以是更智能的等待某个特定元素出现。重试机制在Skill内部实现重试逻辑。例如提取信息失败时自动等待1秒再试一次最多重试3次。使用 browser-use 的内置等待browser-use的Controller在进行操作如ai_click时通常内部会等待页面稳定。确保你使用的是这些高级方法而不是直接执行原始的、不稳定的JavaScript。5.2 泛化能力网站结构变化问题extract_jd_products这个技能严重依赖京东的CSS选择器。只要京东前端一改版这个技能立刻失效。这是专用技能的最大弱点。解决方案抽象通用提取技能尝试开发不依赖固定选择器的技能。例如利用browser-use提供的页面摘要让AI根据语义如“找到所有看起来像商品价格的数字”来提取。这更接近AI的原生能力但准确率可能稍低。配置化选择器将CSS选择器作为外部配置文件当网站改版时只需更新配置文件而无需修改代码。混合策略对于核心的、稳定的业务如监控自家公司官网可以使用专用技能保证精度对于探索性的、多网站的任务则依赖AI的语义理解能力。5.3 效率与成本优化问题每次操作都让大模型如GPT-4来规划每一步Token消耗大速度慢成本高。解决方案技能粒度设计不要设计过于细碎的技能。比如把“登录”作为一个技能而不是“输入用户名”、“输入密码”、“点击登录”三个技能。让一次模型调用完成一个逻辑上完整的子任务。工作流Workflow优先对于固定流程的任务尽量使用YAML定义的工作流。工作流的执行引擎可以直接调用技能无需大模型反复规划极大节省成本和时间。模型分级在规划阶段使用能力强但贵的模型如GPT-4在执行具体的、定义清晰的技能时可以尝试使用更小、更快的模型如GPT-3.5-Turbo甚至本地小模型如果该技能对逻辑要求不高的话。5.4 错误处理与日志记录问题自动化流程一旦出错如果日志不清晰很难定位问题出在哪一步。解决方案结构化日志在每个Skill的开始和结束以及关键操作点记录详细的日志包括时间戳、技能名、输入参数、输出结果、以及任何错误信息。截图功能在关键步骤失败时自动调用浏览器截图功能保存当时的页面状态。这对于调试元素定位问题 invaluable无比珍贵。异常捕获与友好反馈在Skill内部做好异常捕获不要让底层库的复杂异常直接抛给用户或Agent。应该转化为人类或AI能理解的友好错误信息并提示可能的解决方向。5.5 安全与伦理考量问题自动化工具可能被滥用用于爬虫、刷单等违反网站规则或法律法规的行为。注意事项遵守 robots.txt在开发技能时可以加入对目标网站robots.txt的检查。设置合理的速率限制在Skill或工作流中加入操作之间的延迟模拟人类行为避免对目标服务器造成压力。明确使用边界在项目文档和内部分享中强调该技术应用于提高个人工作效率、进行合规的数据分析等正当场景切勿用于恶意用途。6. 扩展想象还能用这个组合做什么跑通一个价格监控的Demo只是开始。opencodebrowser-use这个组合的想象力空间非常大它本质上是赋予了AI“在图形界面中完成任务”的能力。以下是一些我想到的、值得尝试的方向6.1 自动化办公与数据填报许多内部管理系统或Web版办公软件如OA、CRM、ERP的操作是重复且规律的。你可以训练一个Agent让它每天自动登录系统下载最新的销售报表提取关键数据并填写到另一个汇总系统中。这能将人力从枯燥的“人肉搬运”工作中解放出来。6.2 智能信息聚合与简报生成让Agent每天早上自动打开你常看的几个新闻网站、技术博客、行业报告页面提取标题和摘要然后让另一个文本处理Skill进行总结归纳最终生成一份个性化的每日晨报通过邮件或通讯工具发送给你。6.3 自动化测试与监控对于前端开发者或测试人员可以编写一个Agent让它按照测试用例自然语言描述去操作你的Web应用比如“注册一个新用户”、“将商品加入购物车并结算”。Agent可以记录下操作过程中是否出现错误、页面响应是否正常甚至能对UI元素进行简单的“视觉”校验结合截图和OCR。这比编写传统的UI自动化测试脚本更灵活、更接近自然语言。6.4 交互式学习与助手结合知识库和浏览器能力可以打造一个更强大的研究助手。你可以问它“帮我查查最近三个月关于‘AI智能体’融资的新闻并总结出投资方和金额。” Agent会规划任务打开特定创投媒体网站 - 搜索关键词 - 翻页 - 提取信息 - 整理分析。这比单纯用大模型生成虚构内容要可靠得多。6.5 跨平台自动化桥梁browser-use目前主要针对浏览器但思路可以扩展。理论上任何能提供结构化界面描述的工具都可以被集成。未来或许会出现desktop-use来控制桌面应用mobile-use来控制手机模拟器。opencode作为智能体大脑可以统一调度这些“手”和“眼”实现真正的跨平台、跨应用的复杂自动化。这次实战让我深刻体会到AI智能体开发正在从一个炫技的概念快速落地为可用的工程实践。opencode提供了清晰的架构browser-use解决了关键的“最后一公里”交互问题。虽然目前它们在处理极端复杂、充满变数的网页时仍会“翻车”但对于定义相对清晰、流程化的任务已经展现出惊人的潜力。最大的挑战可能不再是技术本身而是我们如何更好地设计任务、分解步骤并教会AI应对那些不可避免的异常情况。这本身就是一个非常有意思的、人与AI协同进化的过程。