ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

WebMCP:让AI Agent在网页上从“猜按钮”变“读说明书”

WebMCP:让AI Agent在网页上从“猜按钮”变“读说明书” ChatGPT 浏览器上线 Agent 模式之后我做的第一件事就是把它丢去帮我订机票、查参数、填表。结果两天下来最大的感受是它干活确实勤快但手是真的飘——订酒店能点到「取消预订」查资料能点进「广告位」填表能把「确认密码」和「密码」填反。所有问题的根源就一句话Agent 在网页上全靠猜。WebMCP 最近开始在这类浏览器里落地思路是把网页上「哪里能点、点了会怎样、要填什么格式」写成机器可读的声明Agent 先读说明再动手不用再边看边猜。这篇文章结合我自己的实测把 WebMCP 的定位、原理、接入步骤和踩坑经验完整梳理一遍想搞 Agent 开发、做前端接入或者只是被蠢操作气到过的普通用户都能从中找到有用的东西。多说一句WebMCP 目前还在快速演进不同浏览器实现里的字段名可能略有差异但核心思路是稳定的。我下面描述的是当前主流实现的设计逻辑你拿到具体版本时以实际文档为准。1. Agent 为什么总在「猜按钮」问题出在网页没长「说明书」1.1 一次让我印象深刻的翻车现场上周我想测试 ChatGPT 浏览器在 Agent 模式下能不能独立完成一张国内机票的预订。站点我带路账号提前登录好任务描述写得很清楚「订下周一早上从北京到上海的最早航班」。整个过程我看它一路操作行云流水打开首页、选单程、填城市、选日期眼看就要到提交订单那一步。然后它停了。屏幕上出现了两个长得很像的按钮一个是「提交订单」另一个是「保存查询条件」俩按钮都是蓝色圆角矩形相距不到 40 像素。Agent 的视觉模型犹豫了大概两秒直接点中了后者。页面刷新返回航班列表一切白干。我把浏览器控制台和网络请求日志翻出来复盘发现 Agent 判断按钮的逻辑是「找一个外形像按钮、包含关键语义词的元素」。这本质上是一种概率推理不是确定性操作。页面只要出现文案相近、位置重叠、布局混乱的元素它就面临多选题一旦选错就是一次无效操作。后来我在开发者社区里看到有人专门统计过这类纯视觉驱动的 Agent 在复杂表单页上的首次点击成功率往往不到七成。更麻烦的是很多网页为了美观把按钮做成图标、把可点击区域做成 div、把悬浮层放在按钮上方视觉模型在这种页面上的表现会进一步下降。问题不是大模型不够聪明而是网页本身没有给 Agent 提供任何「可操作」的语义信息。1.2 为什么大模型这么聪明还是搞不定一个按钮这里要聊一个概念叫 GUI grounding就是把自然语言指令对应到界面上的具体操作目标。人类做这件事靠的是经验和约定俗成我们知道红色按钮一般是危险操作知道右上角的 x 是关闭知道「立即购买」和「加入购物车」不一样。但这些知识是模型在训练数据里通过大量像素学来的属于归纳不是推理。网页对于纯视觉 Agent 来说本质上是三个层面从像素到语义再到操作像素层面按钮在屏幕上的 xy 坐标会随窗口大小、滚动位置、设备分辨率变化。语义层面这个按钮叫什么、点击后会发生什么模型需要结合周边文本猜测。操作层面点击之后是打开新页、提交数据还是触发弹窗需要依赖 DOM 结构和事件机制来确认。传统的视觉方案把这三个层面全部交给模型一步到位瞎猜WebMCP 则是把语义和操作层面单独拿出来用结构化数据显式声明。就像一个陌生人到你家做客与其让他猜哪个开关控制哪盏灯不如直接在开关旁边贴好标签。这就是整个问题的核心解法把「猜」变成「读」。1.3 这条老路以前为什么没人走通有人可能会问这个问题存在很多年了为什么现在才有人系统性地解决早期做过类似的尝试比如给网页写专门的自动化脚本、用 AI 视觉锚点做元素识别、或者让网页提供一套私有 API 给 Agent 调用。问题在于脚本方案和私有 API 都是一对一适配每个站点都要单独开发维护成本高到离谱。视觉识别方案则受限于模型能力且很难让站点所有者主动参与优化。WebMCP 的聪明之处在于它开创了一种「双赢」模式网页所有者写一份配置文件就能让自家站点在 Agent 浏览器里获得更高的操作成功率Agent 浏览器呢则减少无效点击、降低服务器压力还能提升用户体验。这种「站长主动适配 Agent」的思路是以前所有方案都没有做到的。同时它保留了回退机制就算某个站点没有配置 WebMCPAgent 照样可以退回视觉猜的方式运作不会彻底罢工。2. WebMCP 是什么把网页升级成 Agent 的「操作说明书」2.1 WebMCP 和 MCP 到底什么关系社区里第一次听到 WebMCP 的人第一反应都是问它和 MCP 是什么关系MCPModel Context Protocol解决的是「Agent 如何调用本地或远程工具」的问题比如读文件、查数据库、调 API。它面向的是工具层通信两边通常是客户端和 MCP Server。WebMCP 的思路一脉相承但战场完全不同它解决的是「Agent 在一个陌生网页里如何进行界面操作」的问题通信两边是浏览器和网页本身。我给一个比较形象的对比维度MCPWebMCP解决的核心问题Agent 怎么调用工具能力Agent 怎么操作网页界面通信对象客户端 → MCP ServerChatGPT 浏览器 → 网页声明文件主要载体JSON-RPC 协议JSON 配置文件 运行时指令类比给 Agent 装上操作系统的 API给网页配上给 Agent 看的地图回退机制工具不可用则取消任务声明缺失则退回视觉猜测模式如果你的 Agent 任务里既有工具调用又有界面操作那 MCP 和 WebMCP 通常会同时出现先用 MCP 查数据再用 WebMCP 去页面上点击操作。它们不是竞争关系而是互补关系。我在实际项目里的体验是两个协议各自管好自己那一层架构反而清爽调试的时候定位问题也更快。2.2 三方握手网页、浏览器与 Agent 怎么配合WebMCP 的工作机制可以拆成三个角色网页站点、浏览器、Agent 模型。整个交互过程像一次三方握手。第一步网页站长在站点根目录放一份配置文件通常是/.well-known/webmcp.json类似于 PWA 的 manifest 文件。内容描述这个页面上有哪些可操作元素每个元素对应什么动作、什么参数、什么权限。第二步ChatGPT 浏览器在用户打开页面时自动请求这份文件。注意这里不是 Agent 主动去读而是浏览器层面自动加载并解析对用户和 Agent 都是透明的。第三步浏览器把解析出来的动作列表注册到当前页面的「可操作元素表」里。Agent 在执行任务时不再需要自己去扫描整个页面找按钮而是先查这张表拿到元素引用后调用浏览器的原生事件接口完成点击、输入等操作。这套三方握手的价值在于网页所有者可以提供权威信息浏览器提供稳定执行环境Agent 只需要专注理解任务和做决策。三个角色各司其职不再互相越界。2.3 核心声明文件长什么样我结合自己接入的一个订票页面写了个简化版的webmcp.json{ name: demo-booking, version: 0.1.0, scope: https://example.com/booking/*, actions: { submit_order: { selector: #checkout-submit, description: 提交当前订单并进入支付页, confirm: true, permissions: [click, observe] }, select_flight: { selector: .flight-row[data-typeresult] .select-btn, description: 在航班列表中选择指定班次, params: { flight_no: { type: string, description: 航班号, required: true } }, permissions: [click] }, fill_passenger: { selector: #passenger-name, description: 填写乘客姓名, params: { name: { type: string, source: user } }, permissions: [input] } }, guards: { require_login: true, timeout_ms: 30000 } }解释几个关键字段selector标准 CSS 选择器浏览器用它定位 DOM 元素。这是整个声明文件里最容易写错的部分后面我会专门讲踩坑经验。description给 Agent 看的动作说明。模型通过它理解「点了这个按钮会有什么后果」是决定是否选择该动作的重要依据。confirm标记该动作是否有破坏性。submit_order涉及支付设成true后浏览器会在执行前弹一次确认窗让用户拍板。params声明这个动作需要什么参数。Agent 会从用户输入、页面上下文或历史对话里收集这些参数收集不齐宁可追问也不会瞎填。permissions限制动作能干什么遵循最小权限原则。guards全局护栏比如是否要求登录、最长执行时间防止 Agent 在一个页面上无限重试。看到这个结构你就明白了WebMCP 做的不是「让 Agent 变得更聪明」而是「让 Agent 拿到的信息更可靠」。它把原本需要模型临场判断的部分变成了确定性声明这是准确率提升的根本原因。3. WebMCP 在 ChatGPT 浏览器中的落地链路3.1 浏览器如何发现并加载声明我第一次在 ChatGPT 浏览器里验证 WebMCP 是否生效时心里是没底的因为整个过程完全没有 UI 提示。后来排查清楚了整个加载链路浏览器导航到某个站点时它会在页面主文档响应完成后自动向/.well-known/webmcp.json发起一个带 CORS 的 GET 请求。这个路径是固定的站长只需要把文件放到站点根目录对应路径不用在 HTML 里加任何 meta 标签。请求成功后浏览器会做三层校验域名校验声明文件里的scope字段必须和当前页面域名匹配防止别人把配置放到自己的站点上来劫持操作。HTTPS 校验所有 WebMCP 操作必须走 HTTPS这是硬性要求避免配置被中间人篡改。结构校验字段类型、权限枚举、选择器语法都会做一遍解析不符合规范的部分会被丢弃并给出控制台警告。校验通过后配置里的动作会被注册到一个内存表里和当前页面的 DOM 元素建立引用关系。这里有个细节如果页面是前端框架动态渲染的配置可能在 DOM 挂载之前就解析完了。主流浏览器实现会在首次解析后监听 DOM 变化一旦发现选择器匹配到新元素就自动补注册不过这个机制在不同实现里表现差异很大后面踩坑部分我会细说。3.2 从自然语言任务到结构化动作配置文件加载成功只是第一步Agent 真正要面对的是怎么把用户的自然语言指令变成一连串动作序列。这个转换过程我拆开来看一下。假设用户说「帮我订下周一早上从北京到上海最早的一班飞机」。Agent 收到任务后首先通过 LLM 理解意图拆出实体出发地北京、目的地上海、时间下周一早上、偏好最早班。接下来Agent 拿到当前页面的 WebMCP 动作表要做的是动作编排。它看到有哪些可用动作select_flight、submit_order、fill_passenger。它不关心两个按钮长什么样子、在什么位置只看每个动作的description和params然后规划出执行顺序先select_flight再fill_passenger最后submit_order。关键在于select_flight动作声明了一个flight_no参数。Agent 知道参数来源可以是用户输入所以在执行前会先确认航班号。如果用户没有指定具体航班Agent 就需要从页面上读取航班列表——此时它可以用「observe」权限读取列表内容然后挑出最早的班次再执行点击。这整个过程比视觉猜按钮多了一个「读取结构化动作定义」的环节但换来的是执行路径的可预测性。3.3 执行回环守卫、反馈、降级动作编排完成接下来是实际执行。WebMCP 的执行不是一个单次点击而是一个带反馈的闭环。我总结它有四个阶段执行前守卫。浏览器检查登录状态、超时时间、confirm 标记。如果当前动作涉及支付且没有登录Agent 会先引导用户登录而不是强行点击。元素定位与状态检查。浏览器通过选择器找到 DOM 元素检查它是否可见、是否可用、是否在视口内。页面需要滚动才能看到目标时浏览器会先执行滚动操作。事件派发。浏览器通过原生 DOM 事件机制派发 click、input 等事件等效于真实用户操作。这里要说明一下它不是模拟鼠标坐标而是直接触发事件所以 React、Vue 这类框架也能正常响应。结果验证。执行完动作后浏览器会重新抓取页面状态对比 URL、可见文本、网络请求变化等信号判断动作是否真的发生了预期效果。如果页面没变化Agent 会进入重试策略先重新定位元素再尝试一次最后才降级到视觉猜测模式。这个降级机制非常关键。它保证了 WebMCP 不是一段软弱的新尝试而是原有能力的增强。我在测试中见过不少场景明明配置的是 A 按钮但页面某个角落弹了个遮罩层盖住了目标选择器依然能定位到元素但点击事件被拦截。这种时候 Agent 会收到「元素不可交互」的反馈然后自动切换到视觉模式去处理遮罩层。换句话说WebMCP 和视觉能力不是替代关系二十互补关系一个负责稳一个负责兜底。4. 手把手把一个旧网页接入 WebMCP4.1 先判断哪些页面值得接聊完原理直接进入实操。但动手写配置文件之前我建议你先做一轮取舍。不是所有页面都值得接入 WebMCP判断标准就三条页面操作是否高频。比如下单、报价、续费、查单这些页面每天有大量重复操作Agent 每点错一次都是实际损失。页面交互是否复杂。包含多个步骤的表单、需要选择多个条件的查询页这类页面视觉猜按钮的出错率明显偏高收益也最大。是否为 Agent 目标场景的主路径。如果你的用户习惯用聊天的方式让 AI 代操作那这些页面必须优先接入。相反首页、文章详情页、纯内容展示页这些简单页面视觉猜模型一般不会出错接入 WebMCP 属于白费功夫。维护一份配置文件是有成本的选择器挂在页面改版时也需要跟着更新把资源花在最痛的点上才是正道。4.2 写第一份 webmcp.json 的三步走选好页面后我先用一个电影票预订页做例子走一遍完整流程。第一步在项目根目录创建/.well-known/webmcp.json。如果用的是静态站点托管确认构建工具不会丢弃以点开头的目录。有些前端脚手架默认忽略隐藏文件这一步特别容易踩。第二步只定义三个最核心的动作别一上来就把整个页面所有元素全塞进去。比如电影票页先写选择影院、选择场次、确认支付这三个就能覆盖完整链路{ name: cinema-booking, version: 0.1.0, scope: https://cinema.example.com/*, actions: { pick_cinema: { selector: .cinema-card[data-id${cinema_id}], description: 选择指定影院, params: { cinema_id: { type: string, required: true } }, permissions: [click, observe] }, pick_session: { selector: .session-list li[data-session${session_id}] .book-btn, description: 选择指定场次并进入选座, params: { session_id: { type: string, required: true } }, permissions: [click] }, confirm_payment: { selector: #pay-confirm-btn, description: 确认支付并完成订单, confirm: true, timeout_after_click: 20000, permissions: [click] } } }第三步逐个验证选择器。打开浏览器 DevTools在 Console 里执行document.querySelector确认返回的不是 null。这里有个细节我特别提一下如果页面是异步渲染的直接执行可能查不到要在网络请求完成后再执行一次。4.3 本地模拟器不等浏览器发布也能验证WebMCP 配置文件写好后如果不想反复打开 ChatGPT 浏览器去验证可以用一个轻量的本地脚本先跑一遍选择器检查。我自己常用的方式是用 Node.js 配合 JSDOM 模拟页面解析import { JSDOM } from jsdom; import webmcp from ./.well-known/webmcp.json assert { type: json }; const dom await JSDOM.fromURL(https://cinema.example.com/booking); const { document } dom.window; for (const [actionName, action] of Object.entries(webmcp.actions)) { const el document.querySelector(action.selector); const status el ? OK : MISSING; console.log(${status} - ${actionName} - ${action.description}); if (el) { console.log( 命中元素: ${el.tagName.toLowerCase()} class${el.className}); } }这个脚本的价值不在于模拟浏览器的完整行为而在于快速发现选择器张冠李戴的低级错误。我通常在部署前跑一遍基本能拦下一半的配置问题。如果你用的是 Puppeteer还可以进一步模拟真实点击事件验证事件是否被页面框架正确接收。4.4 发布前用 DevTools 做三轮检查代码部署上去之后别急着让 Agent 去跑。我在浏览器里做三轮固定检查第一轮看网络面板。重新加载页面找到webmcp.json请求确认返回 200而不是 404 或者 301 跳转。如果看到重定向浏览器很可能不会继续请求因为信任模型基于最终 URL。第二轮看控制台警告。Mozilla 官方实现里有个细节每个动作的选择器如果匹配不到任何元素会在控制台打一条警告格式大概是WebMCP: action pick_session selector did not match any element。这些警告就是配置健康度的体检报告。第三轮是登出状态检查。用无痕窗口打开页面确认配置在未登录状态下不会暴露敏感操作。比如未登录时支付按钮根本不存在那这个动作应该在服务端判断会话状态后返回一份仅含可见动作的配置而不是直接返回全量配置。5. 踩坑记录WebMCP 上线时的典型问题与排查思路5.1 配置不生效先查这五个地方我在接入过程中遇到过不少「配置明明放了但 Agent 就是不按配置走」的情况。排查顺序基本固定路径不对。最常见的是把文件放到了public/webmcp.json但服务端没有把它映射到/.well-known/webmcp.json这个路径。静态托管服务一般会自动处理.well-known目录自己部署的 Nginx 就需要加一条 location 配置。被缓存卡住。浏览器和 CDN 对 JSON 文件的缓存策略可能很激进。我在测试时经常改完配置页面刷新后拿到的还是旧版本。建议在响应头里加上Cache-Control: no-cache至少要在开发调试阶段这么做。HTTPS 缺失。WebMCP 强制要求 HTTPS。如果是本地测试环境用http://localhost浏览器一般会放宽限制但一旦挂到测试服务器上必须确认证书有效自签名证书也不行。域名不匹配。scope字段写的是example.com但实际访问的是www.example.com浏览器会判定为不匹配而直接丢弃配置。这个坑特别隐蔽因为配置请求本身是成功的只是校验不过。CORS 头缺失。浏览器请求配置时会带上特定的 Origin响应必须允许该 Origin 访问。如果 CDN 配置了严格的 CORS 白名单记得把浏览器的 Origin 加进去。5.2 动态页面里的选择器说挂就挂的三种情况选择器是 WebMCP 配置里最脆弱的部分。我归纳了三种最容易翻车的页面形态。第一种是随机 ID。后端渲染时给元素生成了idflight-abc123这样的随机值每次请求都变。这种不能写死在选择器里要么让前端加稳定的>
返回列表