web-access 深度拆解:一个 Agent Skill 如何定义 Claude Code 的联网与浏览器边界

引子:原生工具是否足够

Claude Code 开箱即带两个联网工具:WebSearch 负责发现,WebFetch 负责提取。能力清单看似完整,但在真实的 Agent 联网任务中,会接连遇到三类典型障碍:

  • 读取微信公众号文章,WebFetch 返回的是「环境异常」的空壳;
  • 访问小红书账号页,正文在 JS 执行后才渲染,curl 仅能取回骨架;
  • 同时调研多个竞品官网,串行 WebFetch 耗时长,且回传的大段 HTML 会将上下文撑爆。

这三类障碍暴露的不是 Claude Code 的能力不足,而是缺少两样东西:一套联网的调度策略,以及一层浏览器能力。原生工具是零件,却没有引擎将其组织起来。

web-access(当前 v2.4.1,作者一泽 Eze)补全的正是这一层:联网策略 + CDP 浏览器代理 + 站点经验积累(CDP 即 Chrome DevTools Protocol,Chrome 浏览器的底层控制协议)。它的特别之处在于,这并非一个「代为联网的脚本」,而是一份指导 Agent 如何思考联网任务的哲学文档,外加一个最小化的 CDP 代理实现(572 行零依赖 Node 脚本)。

本文不做功能罗列,而是从源码层面将其拆透:先阐述设计哲学,再解析 CDP Proxy 的工程实现,最后落到笔者主导的一个 AIGC 内容生产系统——以一个真实生产系统的技术选型与边界识别,印证 web-access 的价值。需要强调的是,本文的真正主角是笔者的工程实践(服务端 axios 爬虫框架、OPRO Prompt 自我进化,OPRO 即 Optimization by PROmpting,基于提示的 Prompt 自动优化算法),web-access 是用来反哺验证选型边界的业界参照。全文只回答一个问题:一个优秀的 Agent Skill,究竟该具备怎样的形态——以及一个工程师如何从自己的生产实践中识别技术边界、并找到解法。


一、设计哲学:Agent Skill 应该如何编写

这是 web-access 最值得剖析的部分,也是读者最易跳过的部分。通读源码后可以确认,恰恰是这部分决定了 skill 的上限——技术实现可以替换(CDP 换成 Playwright、WebSocket 换成长轮询),而哲学不会过时。

1. Skill = 哲学 + 技术事实,不是操作手册

SKILL.md 中的定义直接点题:

Skill = 哲学 + 技术事实,不是操作手册。讲清 tradeoff 让 AI 自己选,不替它推理。

这要求区分两类知识。操作手册将每一步钉死:「第一步打开浏览器,第二步输入网址,第三步点击按钮」。哲学 + 技术事实则相反:告知 Agent 世界的样貌(技术事实),以及面对选择时的权衡准则(哲学),具体动作交由 Agent 自行判断。

web-access 通篇贯彻此路。它不写「遇到小红书就用 CDP」,而是给出一张完整的工具选择表,明确每个工具的适用边界:

场景 工具
搜索摘要或关键词结果,发现信息来源 WebSearch
URL 已知,需要从页面定向提取特定信息 WebFetch(小模型按 prompt 提取)
URL 已知,需要原始 HTML 源码(meta、JSON-LD 等结构化字段) curl
非公开内容,或已知静态层无效的平台(小红书、微信公众号等) 浏览器 CDP
需要登录态、交互操作,或需要像人一样在浏览器内自由导航探索 浏览器 CDP

进而将「何时升级到更根本的获取方式」提炼为一条原则:多次搜索无质的改进时,定位一手来源(官网、原始页面)。Agent 据此面对新任务时,能自主组合工具,而非机械套用步骤。这正是它能处理「未知站点」「意外弹窗」「反爬升级」等手册无法穷举的情况的根本原因。

2. 「像人一样思考」四步法

web-access 的执行框架为四步:拿请求 → 选起点 → 过程校验 → 完成判断。看似朴素,但每一步的设计都针对 Agent 的典型失败模式。

拿请求——先定义成功标准:「什么算完成?」这是后续判断的锚点。Agent 最常见的失败是跑偏而不自知,根源正在于缺少明确的完成定义。

选起点——依据任务性质,选择最可能直达的方式作为第一步加以验证,「一次成功最佳,不成功则在过程校验中调整」。它不追求规划最优路径,而追求快速验证、快速纠偏。

过程校验——这是最关键、也最反直觉的一步。原则是:每一步的结果都是证据,而非简单的成功/失败二元信号。SKILL.md 用若干反例强化此点:

  • 搜索未命中,不等于「尚未找对方法」,也可能是「目标不存在」;
  • API 报错、页面缺少预期元素、重试无改善,均在提示「应重新评估方向」,而非「在相同方式上反复重试」;
  • 遇到弹窗或登录墙,先判断其是否真正阻挡目标——「挡住了就处理,没挡住就绕过,内容可能已在 DOM 中,交互只是展示手段」。

这个原则在生产中有大量具象。笔者的一个内容生产项目里,推送文案的主副标题改写 Prompt 就内建了自检流程:「生成标题后立即数字符(含 emoji),如超出 13 字符必须删减至符合要求」「确认无编号、无解释文字」「最终只输出一个标题内容」。每一次生成都被当作证据——超长则回退修正,而非直接采纳。这正是「结果作为证据,发现偏差立即调整」在 Prompt 工程层的落地(后文第五篇会展开这一原则在 OPRO 算法中的极致应用)。

完成判断——对照成功标准确认完成,但「不要过度操作,不为了完整而浪费代价」。

这套框架的价值在于:它交付的是思维纪律,而非步骤。网站会改版、反爬会升级,但「以结果为证据、发现方向错误立即调整」这套纪律始终有效。

3. 目标驱动,而非步骤驱动

四步法背后是一个更根本的立场:目标驱动,而非步骤驱动

这一点在「并行分治」部分体现得最为直接。主 Agent 派子 Agent 调研多个目标时,web-access 明确要求:

子 Agent 的职责是说清楚要什么,仅在必要与确信时限定怎么做。过度指定步骤会剥夺子 Agent 的判断空间,反而引入主 Agent 的假设错误。

它甚至精细到用词层面:写 prompt 时应使用「获取」「调研」「了解」这类描述目标的动词,避免使用「搜索」「抓取」「爬取」这类暗示手段的动词——因为「搜索」会把子 Agent 锚定到 WebSearch,而某些反爬站点需要直接 CDP 访问主站方才有效。

这是一个相当锐利的认知:描述任务的方式,正在潜移默化地限制 Agent 的解空间。 若 skill 作者未意识到这一点,prompt 写得越详细,反而越将 Agent 困于单一手段。

4. 面试视角

这一篇若转化为面试话术,应以本人项目为主语,而非以 web-access 为主语:

我主导过一个多源爬取的 AIGC 内容生产系统,用 axios + cheerio + 设计模式框架解决了 SSR 场景的规模化抓取。在识别出 axios 的三道硬边界(反爬 / 登录态 / 动态渲染)后,我去研究了 eze-is/web-access 的源码——它的 CDP 方案正好覆盖这三道边界。这趟研究最大的收获不是「学会了 CDP」,而是印证了一个设计原则:写 Agent Skill 不是写操作手册,而是写「哲学 + 技术事实」——把世界的约束与选择的 tradeoff 讲清,让 Agent 自行组合。这套目标驱动的四步框架(拿请求 → 选起点 → 过程校验 → 完成判断),与我做工程系统的理念一致:为组件提供清晰的边界与接口,使其自主组合,而非写死流程。

相较「我用过 web-access 爬网页」或「我研究过别人的 skill」,这种回答有两层优势:一是以自己的真实项目为锚(axios 选型 + 边界识别是可验证的产出),二是在更高认知层级理解 Agent 协作的边界(哲学 + 技术事实,而非步骤堆砌)。这恰恰是 AI 工程一类岗位在「AI Agent」考点上真正期待的内容。


二、技术之骨:CDP Proxy 工程拆解

归属说明:本篇及第三篇的 CDP 实现、设计均来自 eze-is/web-access 开源项目,笔者从源码层面拆解以学习其工程手法。笔者本人的生产实践是第四篇的 axios 静态抓取——服务端做了规模化,客户端 CDP 边界通过研究 web-access 补全。阅读时请注意区分「业界最佳实践(视野)」与「本人产出(能力)」。

哲学立住之后,本篇进入实现层。web-access 的浏览器能力由 scripts/cdp-proxy.mjs 承担——一个 572 行、零依赖的 Node 脚本(依赖 Node 22 的原生 WebSocket,低版本回退到 ws 模块)。它做的事情可以用一句话概括:在本地起一个 HTTP 服务,把简单的 curl 请求翻译成 Chrome DevTools Protocol 的 WebSocket 命令。

本篇精华(若时间有限,重点看这三点):① Chrome 端口发现的 DevToolsActivePort + UUID wsPath(2);② 同一动作的三种点击机制与可信度分层(3);③ 登录态天然复用(4)。

1. 架构总览

flowchart LR
    A["Agent<br/>curl HTTP"] --> B["cdp-proxy.mjs<br/>localhost:3456"]
    B --> C{"HTTP → CDP 翻译"}
    C -->|Runtime.evaluate| D["执行 JS"]
    C -->|Input.dispatchMouseEvent| E["真实鼠标事件"]
    C -->|DOM.setFileInputFiles| F["文件上传"]
    C -->|Page.captureScreenshot| G["截图"]
    C -->|Target.createTarget| H["新建/关闭 tab"]
    D & E & F & G & H --> I["用户日常 Chrome<br/>天然携带登录态"]

    style B fill:#198038,color:#fff
    style I fill:#0f62be,color:#fff

Agent 侧只需要 curl;复杂的 CDP WebSocket 协议、session 管理、命令-响应配对,全部由 Proxy 吸收。这一层抽象的价值在于:让 Agent 用最朴素的方式(HTTP)获得完整的浏览器控制力。

2. Chrome 端口发现:一个被忽视的精妙设计

Proxy 要控制 Chrome,先得找到 Chrome 的调试端口。最直觉的做法是扫描 9222 等常见端口。但 web-access 没有这么做,而是优先读取 Chrome 写入的 DevToolsActivePort 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async function discoverChromePort() {
// ... 按平台拼接 DevToolsActivePort 路径
const content = fs.readFileSync(p, 'utf-8').trim();
const lines = content.split('\n');
const port = parseInt(lines[0]);
if (port > 0 && port < 65536) {
const ok = await checkPort(port);
if (ok) {
// 第二行是带 UUID 的 WebSocket 路径
const wsPath = lines[1] || null;
return { port, wsPath };
}
}
// ... 回退到扫描 9222/9229/9333
}

这里的关键是 wsPathDevToolsActivePort 的第二行是一个带 UUID 的路径,形如 /devtools/browser/xxx-xxx。当 Chrome 没有通过 --remote-debugging-port 显式启动,而是通过 chrome://inspect 勾选远程调试时,它只接受这个带 UUID 的路径连接——这正是 web-access 不要求用户用命令行参数重启 Chrome 的原因。

更细的一层是端口探测方式。源码注释写得很明白:

1
2
3
4
5
6
7
8
// 用 TCP 探测端口是否监听——避免 WebSocket 连接触发 Chrome 安全弹窗
// (WebSocket 探测会被 Chrome 视为调试连接,弹出授权对话框)
function checkPort(port) {
return new Promise((resolve) => {
const socket = net.createConnection(port, '127.0.0.1');
// ...
});
}

net.createConnection 做 TCP 层探测,而非直接发起 WebSocket 握手。原因是:WebSocket 探测会被 Chrome 判定为调试连接,从而弹出授权对话框,破坏用户体验。这种对「副作用」的敏感,是工程品味的体现。

3. 三种点击机制:同一动作的三种代价

/click/clickAt/setFiles 是 web-access 最具代表性的设计——同一个「点击/交互」动作,提供三种实现,对应不同的可信度与适用场景。

/click——JS 层面点击(el.click()

1
2
3
4
5
6
7
8
const js = `(() => {
const el = document.querySelector(${selectorJson});
if (!el) return { error: '未找到元素: ' + ${selectorJson} };
el.scrollIntoView({ block: 'center' });
el.click();
return { clicked: true, tag: el.tagName, text: ... };
})()`;
await sendCDP('Runtime.evaluate', { expression: js, ... }, sid);

简单快速,覆盖大多数场景。但它是合成事件,不携带真实的用户手势信号,对具备反自动化检测的站点可能无效。

/clickAt——CDP 真实鼠标事件(Input.dispatchMouseEvent

1
2
3
4
5
6
7
8
9
// 先用 eval 拿到元素坐标
const coord = coordResp.result?.result?.value; // {x, y}
// 再派发浏览器级鼠标按下/释放
await sendCDP('Input.dispatchMouseEvent', {
type: 'mousePressed', x: coord.x, y: coord.y, button: 'left', clickCount: 1
}, sid);
await sendCDP('Input.dispatchMouseEvent', {
type: 'mouseReleased', x: coord.x, y: coord.y, button: 'left', clickCount: 1
}, sid);

这是浏览器内核级别的真实手势,能够触发文件选择对话框、绕过部分反自动化检测。代价是步骤更多、速度略慢。

/setFiles——直接设置文件输入(DOM.setFileInputFiles

1
2
3
4
await sendCDP('DOM.setFileInputFiles', {
nodeId: node.result.nodeId,
files: body.files
}, sid);

完全绕过文件对话框,直接把本地文件路径塞进 <input type="file">

三者并列的设计哲学很清楚:同一目标存在多条路径,每条路径的可靠性、副作用、适用边界各不相同。 skill 不替 Agent 决定用哪条,而是把三条路径及其特性如实陈述,让 Agent 依据目标平台的特点自行选择。这与上一篇「哲学 + 技术事实」一脉相承。

4. 登录态天然复用

web-access 不启动独立的浏览器实例,而是直连用户日常使用的 Chrome。这意味着用户已登录的所有站点,对 Agent 而言天然可用——无需配置任何 cookie 或 token。这是它相对 Playwright/Puppeteer(启动全新浏览器实例)的根本优势。

SKILL.md 对登录判断的处理同样克制:打开页面后先尝试获取目标内容,只有当确认内容无法获取且判断登录能解决问题时,才提示用户登录。这避免了对用户的不必要打扰。

5. 并发安全

CDP 是异步的、基于 id 配对的请求-响应协议。Proxy 的实现用一张 pending Map 管理未完成的命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const pending = new Map(); // id -> {resolve, timer}

function sendCDP(method, params = {}, sessionId = null) {
return new Promise((resolve, reject) => {
const id = ++cmdId;
const msg = { id, method, params };
if (sessionId) msg.sessionId = sessionId;
const timer = setTimeout(() => {
pending.delete(id);
reject(new Error('CDP 命令超时: ' + method));
}, 30000);
pending.set(id, { resolve, timer });
ws.send(JSON.stringify(msg));
});
}

每条命令分配自增 id,配 30 秒超时;响应到达时按 id 取回 Promise。这个设计是并发的基石——多个 tab、多个子 Agent 的命令可以交错到达,不会串扰。下一篇会展开它在多 Agent 场景下的应用。


三、并发工程链路:子 Agent 分治

当任务包含多个相互独立的调研目标(同时调研 N 个产品、N 个来源),web-access 鼓励将其分治给子 Agent 并行执行,而非由主 Agent 串行处理。这一篇解析其背后的工程逻辑,并对照笔者某服务端项目的并发实践。

1. 为什么分治

收益有二:速度上下文保护

速度上,多子 Agent 并行,总耗时近似等于单个子任务时长。上下文保护上,抓取回来的原始内容留在子 Agent 内部,主 Agent 只接收摘要——这在 token 成本上意义巨大。

2. tab 级隔离:共享一个 Proxy,无竞态

多个子 Agent 并发操作浏览器,最容易担心的是竞态。web-access 的解法是 tab 级隔离:每个子 Agent 自行创建后台 tab(Target.createTargetbackground: true),通过各自的 targetId 操作各自的 tab,任务结束自行关闭(/close)。

1
2
3
4
5
// /new 端点:创建后台 tab
const resp = await sendCDP('Target.createTarget', { url: targetUrl, background: true });
const targetId = resp.result.targetId;
const sid = await ensureSession(targetId); // 每个 tab 独立 session
await waitForLoad(sid);

所有子 Agent 共享同一个 Chrome 实例、同一个 Proxy 进程,但通过不同 targetId + sessionId 操作不同 tab,天然隔离,无竞态风险。这比每个 Agent 各起一个浏览器实例要轻量得多。

3. 上下文经济学

分治最容易被忽视的价值,是上下文隔离带来的 token 经济性。

抓取一个网页可能回传数千 token 的 HTML;同时调研五个目标,若由主 Agent 串行处理,这些原始内容全部涌入主 Agent 上下文,迅速逼近上限。分治后,原始内容沉淀在子 Agent 内部,主 Agent 只得到每个子 Agent 提炼的摘要——上下文占用从「原始内容之和」降为「摘要之和」。

这与笔者的另一个项目 RTK(Rust Token Killer,对开发类命令做 token 压缩,节省 60-90%)理念同源:在 Agent 时代,上下文是稀缺资源,凡能削减上下文占用的设计,都是高杠杆设计。 web-access 的分治策略,正是这一理念在「联网调研」场景的具体落地。

4. 服务端对照:分批并发与容错

web-access 的并发是「客户端并行调研」(子 Agent 各开 tab)。笔者该服务端项目面对的是「批量 AI 处理」——同一套「并发 + 限流 + 容错」理念,落地形态截然不同,恰好构成对照:

1
2
3
4
5
6
7
8
// 分批并发 + 容错(Promise.allSettled 而非 Promise.all)
for (let i = 0; i < materials.length; i += batchSize) {
const batch = materials.slice(i, i + batchSize);
const results = await Promise.allSettled(
batch.map(material => processMaterial(material))
);
if (i + batchSize < materials.length) await delay(1000); // 批次间限流
}

三个工程要点值得展开:

  1. Promise.allSettled 而非 Promise.all——单条失败不拖垮整批,失败项单独记录后再处理。这是「结果作为证据」在容错层的体现:失败不是终止信号,而是需要单独处理的证据。
  2. 指数退避重试——内部 RPC 框架调用失败时 baseRetryDelay * 2^retryCount,最多 3 次,避免瞬时故障引发雪崩。
  3. upsert + 幂等追踪——多进程并发写进度时用数据库级 upsert 解决冲突,配合 isExecuted 幂等检查避免重复处理。

这套设计与 web-access 的 tab 级隔离形成完整对照:

维度 web-access(客户端) 本项目(服务端)
并发单元 tab(targetId 隔离) 批次(batchSize 切分)
安全机制 隔离(不同 tab 无竞态) 容错 + 幂等(allSettled + upsert)
失败处理 子 Agent 自行重试 指数退避 + 单独记录
限流 tab 数即并发上限 批次间 delay + QPM 控制

同一工程思维的两面:客户端用「隔离」实现并发安全,服务端用「容错 + 幂等」实现并发安全。理解这两种形态的适用场景,比掌握任何一种并发工具更重要。

5. 子 Agent prompt 的写法

如第一篇所述,web-access 要求主 Agent 向子 Agent 描述目标而非步骤。其推荐写法是:

必须在子 Agent prompt 中写 必须加载 web-access skill 并遵循指引,子 Agent 会自动加载 skill,无需在 prompt 中复制 skill 内容或指定路径。

并避免使用暗示手段的动词。这种「目标导向 + 让子 Agent 自主加载能力」的模式,是 Agent 协作从「主从指令」走向「目标委派」的关键转变。


四、实战之肉:从技术选型到边界补全

脱离场景的源码解读容易沦为翻译。这一篇以笔者主导的一个 AIGC 内容生产系统(下称「本项目」)为样本,展示「一个真实生产系统的技术选型,如何与 web-access 形成互补」。

场景 A:AIGC 内容生产系统——一次完整的技术选型与边界识别

本系统的核心链路是:多源爬取(小红书、微信公众号、垂直资讯站、行业站点)→ AI 加工(标签 / 改写 / 生图 / 多轮对话)→ 内容分发。

项目规模(内部数据,面试可口头展开):覆盖 [待填: N 个] 站点源,日处理 [待填: 量级] 素材,笔者主导爬取链路。

技术选型:为什么是 axios + cheerio,而非浏览器自动化

项目文档明确记载了不用 Puppeteer/Playwright 的原因:

  • 运行在 Serverless 函数计算(FaaS)环境,启动浏览器实例冷启动慢、成本高;函数计算单实例内存与执行时长有硬上限([待填: 内存上限 / 时长上限]),浏览器实例常 200MB+,易 OOM 或超时;
  • 目标网站(资讯站、新闻、行业站)多为服务端渲染,静态抓取即可满足;
  • 对少数动态加载场景,通过分析接口直接取数,绕过前端渲染。

这不是技术局限,而是约束下的正确决策。配套的是一套基于设计模式的爬虫框架:

flowchart TD
    Factory["CrawlerFactory<br/>工厂模式"] --> Base["BaseCrawler 基类<br/>模板方法模式"]
    Base --> News["NewsBaseCrawler<br/>新闻站"]
    Base --> Ind["IndustryBaseCrawler<br/>行业站"]
    Base --> Region["RegionBaseCrawler<br/>多区域适配"]
    Strategy["ExtractionStrategy<br/>策略模式<br/>DIRECT_TAB / MAIN_PAGE_SIMULATION"] -.适配.-> Region

BaseCrawler 用模板方法定义 crawl → extractArticleLinks → filterRelevant → scrapeArticles 流程,子类实现具体步骤;CrawlerFactory 按类型创建实例;ExtractionStrategyDIRECT_TAB / MAIN_PAGE_SIMULATION)适配不同站点页面结构。四种设计模式(模板方法 / 工厂 / 策略 / 配置驱动)保证新站点接入只需配置,不改核心代码。 TypeScript 的类型系统与接口契约为这套框架提供了编译期保障。

边界识别:axios 触及不到的地方

axios + cheerio 是服务端静态抓取,它有三个硬边界:

  1. 反爬站点——小红书、微信公众号的公开内容同样被反爬限制,静态层只拿到空壳;
  2. 登录态内容——需要登录才能获取的资源,服务端无登录态;
  3. 动态渲染——JS 执行后才出现的内容,静态层无法触及。

web-access 的补全:客户端 CDP 正好覆盖这三道边界

web-access 的 CDP Proxy 直连用户日常 Chrome:天然登录态(解决边界 2)、真实浏览器渲染(解决边界 3)、跳过静态层直达 DOM(解决边界 1)。

flowchart LR
    subgraph S["服务端 本项目(约束:FaaS / SSR / 高并发)"]
        A1["axios + cheerio<br/>静态抓取"]
        A2["设计模式框架<br/>4 种模式"]
        A3["AI 加工<br/>AI 工作流 / 云 API / Prompt 平台"]
    end
    subgraph C["客户端 web-access(边界补全)"]
        B1["CDP 反爬"]
        B2["登录态复用"]
        B3["动态渲染"]
    end
    A1 -.|"边界1"| B1
    A1 -.|"边界2"| B2
    A1 -.|"边界3"| B3

    style S fill:#198038,color:#fff
    style C fill:#0f62be,color:#fff

这给博客的真正启示:web-access 不是「我研究的别人的 skill」,而是「我基于生产实践识别出 axios 的技术边界后,主动研究的补全方案」。一个工程师的技术视野,体现在他既知道自己选型的理由,也清楚选型的边界,并能找到边界的解法。这正是「技术扩展」考点的本质——不是会用更多工具,而是理解工具的适用边界。

注:项目代码属企业资产无法公开,但技术选型逻辑、设计模式应用、边界识别均有完整内部文档支撑,面试中可逐层展开。

场景 B:Claude Code 工具链中的 web-access

脱离工作项目,web-access 在笔者个人的 Claude Code 工具链中承担「联网与浏览器」这一角色,与其它组件协同:

flowchart TB
    U["复杂任务"] --> F["flow / flow-deep<br/>任务编排"]
    F --> W["web-access<br/>联网与浏览器"]
    F --> S["Serena MCP<br/>语义代码操作"]
    F --> R["RTK<br/>token 压缩"]
    W --> T["联网调研·登录态操作·反爬内容"]

    style F fill:#0f62be,color:#fff
    style W fill:#198038,color:#fff

实际使用场景包括:联网调研时通过 flow 编排多子 Agent 并行调研(用上篇所述的分治策略),核实信息时优先定位一手来源,处理需要登录态的站点时复用 Chrome 登录态。web-access 在这条链路中是一个可被编排的联网能力单元,而非孤立的工具。


五、升华:从站点经验到 OPRO,AI 自我进化的两个面孔

如果前四篇讲的是 web-access「现在能做什么」,这一篇讲的是它「如何越用越强」,并对照笔者项目里的 OPRO 算法。需要先点明主次:OPRO 是笔者本项目的硬核产出(Prompt 空间的自我进化,有真实迭代数据),web-access 站点经验是业界参照(操作策略空间的自我进化)。两者同构,但前者是本文主角,后者用于印证同一范式。

1. web-access 站点经验:操作策略空间的自我进化

SKILL.md 规定:CDP 操作成功完成后,若发现了值得记录的新站点或新模式(URL 结构、平台特征、操作策略),主动写入对应的站点经验文件。文件格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
---
domain: example.com
aliases: [示例, Example]
updated: 2026-03-19
---
## 平台特征
架构、反爬行为、登录需求、内容加载方式等事实

## 有效模式
已验证的 URL 模式、操作策略、选择器

## 已知陷阱
什么会失败以及为什么

这是一个完整的经验闭环:操作 → 沉淀 → 复用。下一次遇到同一站点时,match-site.sh 会根据用户输入自动匹配并注入先验知识:

1
2
3
4
5
6
7
8
9
# 根据用户输入匹配站点经验文件
for f in "$DIR"/*.md; do
domain=$(basename "$f" .md)
aliases=$(grep '^aliases:' "$f" | sed 's/...,/|/g')
patterns="$domain|$aliases"
if echo "$1" | grep -qiE "$patterns"; then
# 输出该站点的经验正文
fi
done

匹配逻辑是 domain + aliases 的正则匹配,命中即把该站点的经验正文注入当前会话。进化的载体是 markdown 文件,驱动是 Agent 的成功操作。

2. 本项目 OPRO:Prompt 空间的自我进化

如果站点经验是 web-access 在「操作策略空间」的自我进化,那么本项目里的 OPRO 算法(Optimization by PROmpting),是同一理念在「Prompt 空间」的自我进化。这是笔者本项目最硬的技术产出——线上效果:Prompt 准确率从 [待填: X%] 提升至 [待填: Y%],迭代实际在第 [待填: N] 轮收敛。核心实现位于 promptEngine/APE/service.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
async runInternal(id, prompt, data, initIteration) {
const initAccuracy = await this.stepRun(prompt, data); // 初始准确率
const iteration = 1000;
let bestScores = 0;
let noOptimizeIteration = 0;

for (let i = 0; i < iteration; i++) { // 最多 1000 轮
if (promptModel?.state >= 2) break; // 任务可中断
const score = await this.iterationRun(/* 内部 20 步搜索 */);
if (score > bestScores) {
bestScores = score;
noOptimizeIteration = 0; // 有优化,重置计数
} else {
noOptimizeIteration++;
}
if (noOptimizeIteration > 50) break; // 早停:连续 50 轮无优化
}
}

每轮迭代内部走 20 步:用当前 Prompt 跑训练数据评分,把历史「Prompt + 准确率」结构化喂给 AI 工作流(<PROMPT><PROMPT_TEXT>...<ACCURACY>...</ACCURACY></PROMPT>),让 AI 基于历史生成更优 Prompt;评分用分批并发(batch=20 + Promise.all),每步结果持久化到 apeStep 表,迭代全程可追溯。进化的载体是数据库,驱动是 AI 的自我优化。

两者的共同本质——回到第一篇的「过程校验」哲学:结果作为证据,驱动系统向更优解进化。 web-access 用「操作是否成功」作为证据进化操作策略,OPRO 用「准确率是否提升」作为证据进化 Prompt。一个是经验驱动的 spec 演进,一个是 metric 驱动的 Prompt 演进,底层都是同一个工程范式:让系统从自己的执行结果中学习。

维度 web-access 站点经验 本项目 OPRO
进化空间 操作策略(URL/选择器/陷阱) Prompt 本身
驱动证据 操作是否成功 准确率是否提升
进化载体 markdown 文件 数据库 + 迭代记录
进化主体 Agent 成功操作 AI 自我优化
终止条件 无(持续积累) 1000 轮上限 / 连续 50 轮无优化早停

3. 这是 Spec Driven Development 在 AI 时代的升级

把这两者抽象出来,它们正是 Spec Driven Development 的两种进化形态:

  • Spec 的生成:web-access 从成功操作沉淀 spec,OPRO 从迭代历史沉淀最佳 Prompt;
  • Spec 的复用:下次遇到同类问题,先加载对应 spec,而非从零探索;
  • Spec 的演进:spec 标注日期当作「可能有效的提示」,失效时回退并更新。

与传统 Spec Driven Development(先写规格再实现)相比,这两种形态是经验/数据驱动的 spec 演进——spec 不是预先设计的,而是从实践中生长出来的,天然适配「世界在变化」这一现实(反爬升级、网站改版、模型迭代)。而且比传统更进一步:spec 的生成者从「人」扩展到了「人 + AI」。

4. 对自研 skill 的启发

这套「结果驱动自我进化」的范式,给笔者自研 skill(如 flow 任务编排、RTK token 压缩)提供了一个可借鉴的方向:一个 skill 若只提供能力而不沉淀经验,每次使用都从零开始;若引入经验闭环,则能越用越强。 经验闭环,是 skill 从「能用」迈向「越用越强」的关键机制——这正是 web-access 与 OPRO 共同指向的结论。


结语:从工具使用者到工具设计者

web-access 的三层结构——设计哲学、CDP 工程、经验闭环——恰好构成一个完整的 Agent Skill 样本。哲学定义了「如何思考」,工程定义了「能做什么」,经验闭环定义了「如何进化」。而将其与该项目并置,又能看到「客户端隔离并发 / 服务端容错并发」「站点经验进化 / OPRO Prompt 进化」的同构映射——同一套工程思维,在不同约束下的多种落地

从面试视角收束,这个样本同时覆盖了 AI 工程一类岗位考察的四个维度:

  • AI Agent:目标驱动的四步框架,以及 prompt 用词对解空间的隐性约束;
  • Playwright / web-access 使用:CDP Proxy 的端口发现、三种点击机制、登录态复用;
  • 并发工程链路:子 Agent 分治(客户端 tab 隔离)对照服务端分批容错(allSettled + upsert + 幂等);
  • Spec Driven Development:站点经验的经验驱动 spec 演进,与 OPRO 的 Prompt 自我进化同构。

但比覆盖考点更重要的是它传递的一个判断:在 Agent 时代,工程师的竞争力正在从「使用工具」转向「设计工具」——设计 Agent 如何思考、如何协作、如何进化,并理解每一种技术选型的边界与解法。 web-access 与本项目共同提供了一份从源码可验证的范本,而这,正是研究它们的全部价值所在。

文章作者: MichaelMao
文章链接: http://michaelmaomao.github.io/2026/06/10/web-access%E6%B7%B1%E5%BA%A6%E6%8B%86%E8%A7%A3-Agent-Skill%E7%9A%84%E8%81%94%E7%BD%91%E4%B8%8E%E6%B5%8F%E8%A7%88%E5%99%A8%E8%BE%B9%E7%95%8C/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 MMao
我要吐槽下