写在前面

第一次听说Anthropic的Skills功能时,我和很多同学一样,以为这就是那个“让AI替我写代码”的神器。但当我真正动手实践后才发现,AI工具的真正价值不在于替代思考,而在于放大能力。它让我能把更多精力放在问题分析、方案设计和工程优化上,而不是重复性的体力劳动。
在这里插入图片描述

这篇文章记录了我把问题记录分析流程进行自动化的完整过程。从最初踩的各种坑,到最后找到一个既稳定又省钱的方案,这个过程中我对“什么是好的工程实践”有了更深的理解。谨以此文和大家学习共勉。(文中代码含AI生成,可根据自己的具体需求进行适当调整)

一、问题背景:如何既高效又高质的完成问题记录的分析?

1.1 真实的业务场景

假设某同学在实习期间负责一个B端产品的技术支持工作。每个版本发布后,都需要分析用户反馈的问题记录,找出共性问题,推动产品改进。听起来简单,实际操作起来却相当繁琐:

第一步:登录内网系统

  • 需要VPN+双因素认证
  • 每次分析都要重新登录

第二步:筛选导出数据

  • 按产品、版本、时间范围筛选
  • 导出Excel,经常遇到数据量过大需要分批导出

第三步:人工分析整理

  • 阅读问题记录内容,分类统计
  • 找出高频问题,分析根因
  • 整理成报告发给团队

整个过程需要30-60分钟,而且每周都要重复一遍。

1.2 更深层的痛点

并且随着和不同角色的同事交流,能发现这个问题远比表面复杂:

角色 关注重点 现有方案的痛点
研发同学 具体的技术问题、异常堆栈 需要从大量问题记录中筛选出技术相关的问题
产品经理 共性问题、产品化机会 缺乏系统性的问题聚类分析
技术负责人 跨产品对比、趋势分析 手动整理跨产品数据太耗时
核心矛盾 同一份数据,不同人需要看不同的“切面” 传统的固定报表无法满足这种灵活需求。

1.3 我的初步想法

既然AI擅长数据分析和文本处理,能不能让它帮我完成这个流程?理想情况下:

  1. 自动获取问题记录数据
  2. 智能分类和统计
  3. 生成针对不同角色的分析报告

但从现实来看,这个方法行不通数据在企业内网,AI不能进去。

二、技术探索:踩过的坑与学到的经验

2.1 方案一:让AI“操作浏览器”(Playwright MCP)

我首先想到的是用 Playwright MCP,让 AI 通过 accessibility tree 理解页面结构,自主决策点击、填写、导出等操作。

Playwright MCP(Model Context Protocol) 是微软推出的一个革命性工具,它让大语言模型能够直接"看懂"网页并生成自动化测试脚本。不同于传统的截图识别方式,MCP使用可访问性树来理解页面结构。
在这里插入图片描述

完成相应的配置(Claude Desktop、VS Code、Cursor 等),如果想在代码中使用 MCP Client 连接 Playwright MCP Server,大致的代码结构为:

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';

// 1. 启动 Playwright MCP Server(stdio 模式)
const transport = new StdioClientTransport({
  command: 'npx',
  args: ['-y', '@playwright/mcp@latest'],
});

const client = new Client({ name: 'demo', version: '0.1' }, { capabilities: {} });
await client.connect(transport);

// 2. 通过 MCP 协议让 AI 执行浏览器操作
await client.callTool({ 
  name: 'browser_navigate', 
  arguments: { url: 'https://inner.company.com/workorders' } 
});

// 3. 获取 accessibility tree 快照
const snapshot = await client.callTool({ 
  name: 'browser_snapshot' 
});

// 4. AI 分析 snapshot 后,决定点击哪个元素
await client.callTool({
  name: 'browser_click',
  arguments: { element: '导出按钮', ref: 'e12' }  // 基于 accessibility tree 的引用
});

该方案遇到的问题

问题1:Token消耗爆炸
Playwright MCP 每一步都要把 accessibility tree(页面的语义化结构树,而非完整 HTML)发给 AI 分析。对于一个中等复杂度的页面,accessibility tree 仍可能有 50KB-100KB,转换成 token 就是几万。我实测了一个15步的登录+导出流程,消耗了 22 万 token。这个成本对于个人学习和小团队来说,根本承受不起。

问题2:稳定性仍存挑战
虽然 MCP 用 accessibility tree 摆脱了对 CSS selector 的依赖,但现代 Web 应用的动态特性依然带来问题:下拉菜单的异步加载导致 AI 误判"元素不存在"; 前端改动按钮文案(如"导出"改"下载"),AI 基于语义的理解就会失效。

问题3:调试困难
当脚本失败时,很难定位问题:是 accessibility tree 解析不完整?还是 AI 对页面语义理解错误?还是网络延迟导致状态不同步?需要比对 tree 快照和页面实际状态来排查。

问题4:数据提取局限
accessibility tree 只包含交互元素的文本标签,不包含 DOM 中的结构化数据(价格、ID、class、data属性)从而丢失了大量DOM细节。比如:如果业务需要提取"折扣价¥5999"或"product-id=12345",MCP 无法直接提供,需要额外编写代码注入页面抓取,这增加了复杂度。

在这里插入图片描述

2.2 方案二:更智能的浏览器工具(Agent Browser)

后来我发现 Vercel Labs 推出的 agent-browser ,它针对 AI 操作浏览器做了专门优化:

核心创新:refs 机制
传统方式需要告诉 AI"去找 class 包含 submit 的按钮",agent-browser 会给每个可交互元素打上唯一标签(如 @e1、@e2),AI 直接说"点击 @e2"就行。

agent-browser 的工作流程
agent-browser open https://example.com 
agent-browser snapshot -i
# 输出: @e1 button "Submit", @e2 input "Email"
agent-browser click @e1
agent-browser fill @e2 "user@example.com"

优势:

Token 消耗降低 90%+:只返回可交互元素的精简快照;
稳定性提升:基于语义化引用(refs),不受 CSS class 变动影响;但仍需重新 snapshot 应对动态内容变化;
Rust 编写 + Node.js Daemon:CLI 层用 Rust 实现高性能,底层仍基于 Playwright 控制浏览器;

实测数据对比:

操作 Playwright MCP Agent Browser 效率提升
页面快照 8,000-50,000 tokens 500-800 tokens 90-95%
点击操作 8,000-12,000 tokens 200-300 tokens 95%+
表单填写 8,000-10,000 tokens 300-500 tokens 90%+

注:Playwright MCP 消耗取决于 accessibility tree 的完整度;Agent Browser 消耗取决于可交互元素数量。复杂页面(如大型数据表格)可能偏离上述区间。

但核心问题依然存在
即使是agent-browser,只要是“让AI去操作页面”,行为就一定存在不确定性。AI需要理解页面结构、决策操作路径、处理异常情况,每一步都可能出错。

尝试了以上两种方案,能够得到的结论是:
适合优先上 Playwright MCP 的场景:需要长期保留登录态;要在真实网站里深度浏览要搜索、点详情、看评论、看图;更重视稳定性和可预期性;不希望折腾 profile 和会话管理;希望独立窗口运行,不影响自己正在用的 浏览器。
适合优先试 agent-browser 的场景:操作多、阅读少;页面是后台系统或工具型 Web App;需要更真实的键盘、hover、drag 交互;希望工具返回更轻,尽量省 token;更习惯 CLI / shell 风格的 agent 工作流;愿意自己把 session、profile 这些细节配置好。

2.3 方案三:退回到“固定脚本”(传统自动化)

既然使用AI工具没有那么大的优势,那如果放弃AI操作,用固定的Playwright脚本呢?

// 固定selector的脚本
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('http://example.com');

// 高度依赖DOM结构的选择器
await page.locator('.data-table > tbody > tr:nth-child(2) > td:nth-child(3) > .action-btn').click();
// UI微调后,这个选择器可能就失效了

await browser.close();

优点:确定性强,执行速度快

缺点

  • 编写成本高,需要深入理解页面DOM
  • 维护成本极高,前端迭代频繁
  • 健壮性差,对异常处理支持有限

2.4 关键观察:SPA架构下的接口复现

在反复尝试中,我注意到一个现象:

现代Web应用(SPA)的数据,本质上都来自接口请求。页面只是“渲染层”,数据在“接口层”。

SPA架构(单页面应用)是一种仅加载一个核心HTML文件的Web应用设计,通过JavaScript动态更新页面内容,无需整页刷新。其核心优势在于流畅的交互体验和高效资源利用,广泛应用于后台系统和移动端应用。
与传统多页面应用(MPA)相比,SPA首次加载资源较多但后续交互更快,适配性强但对SEO优化要求更高。现代前端框架如Vue、React等均以SPA为核心,通过前端路由和异步数据请求实现视图更新。

既然页面能正常渲染并展示了最新的业务数据,说明对应的接口在当前登录态下是可用的,且返回了符合预期的数据结构.

比如可以做个实验:

实验网站:https://jsonplaceholder.typicode.com/
JSONPlaceholder 是一个提在线 REST API 的网站,我们在开发时可以使用它获取一些假数据、假图片。其返回的数据为 JSON 格式,且同时支持 HTTP 和 HTTPS 这两种请求类型。
以 JSONPlaceholder 提供的 /posts/1 接口为例,这是典型的 RESTful API 端点,模拟了真实业务系统中’根据 ID 查询文章’的数据请求。

  1. 在 Edge DevTools 的 Network 面板中,对问题记录列表接口执行 Copy as fetch
  2. 将代码粘贴到Console中执行
  3. 成功获取到 JSON 格式的问题记录数据

具体操作如下:

在这里插入图片描述
右击后选择copy as fetch
通过 DevTools 的'Copy as fetch'功能,可将浏览器已验证成功的请求(含完整 Headers、Cookie 等上下文)一键转换为 JavaScript 代码。这意味着我们无需手动拼接请求参数,直接复用浏览器当前的登录态和请求配置。

在这里插入图片描述

然后将复制的信息粘贴在控制台,然后回车

在这里插入图片描述

然后点击nextwork功能项,可以看到返回的304状态码信息
首次请求返回 200(OK)并缓存数据,再次执行时服务器返回 304(Not Modified),这是 HTTP 缓存优化机制。无论 200 还是 304,均表明成功获取到结构化的 JSON 数据,验证了接口的可用性。

在这里插入图片描述
在这里插入图片描述

或者是在打开该网站后,直接在开发者模式下的控制台中输入

// 返回 JSON 的测试接口
const res = await fetch("https://jsonplaceholder.typicode.com/posts/1");

// 查看状态
console.log("状态码:", res.status);  // 应该是 200

// 解析 JSON 数据
const data = await res.json();
console.log(data);

在这里插入图片描述

有兴趣的话还可以尝试以下代码:

// DevTools生成的fetch代码(headers已脱敏,实际包含完整认证信息)
fetch("https://inner.company.com/api/workorders/list", {
  "headers": {
    "accept": "application/json",
    "content-type": "application/json"
    // 实际还包含:authorization/x-requested-with/user-agent 等
  },
  "body": JSON.stringify({
    "page": 1,
    "pageSize": 50,
    "product": "API Gateway"
  }),
  "method": "POST",
  "credentials": "include" // 自动携带当前登录态的cookie
});

从这个实验可知:当页面能正常展示数据时,说明对应的接口在当前登录态下已被验证可用。只要浏览器能拿到数据,外部代码也能通过相同接口拿到,且数据格式是结构化的(JSON)而非视觉化的(DOM)
这个实验给了我重要启发与其让AI操作UI,不如让AI执行已经验证过的接口请求。

三、最终方案:Copy as fetch + Playwright CDP + Skill封装

我想到了最近很火爆的Skills,也许可以结合‘Copy as fetch’获取数据与Playwright CDP控制浏览器,再通过Skill封装将代码封装成标准化执行流程。

Skills(技能文件) 是一种结构化的上下文文档,通常以 Markdown 格式编写,放置在项目仓库的特定目录中,专门用来告诉 AI 编程智能体:“在这个项目里,我们是这样工作的。” 想了解Skills的更多细节,可以参考这篇文章:一文搞懂爆火的SKills原理及实践案例

Skills的Skill 框架规范:

在这里插入图片描述

3.1 方案设计

结合前面的探索,可以设计了一个“分层”的解决方案:

核心思想

  • 人做的:首次登录系统(处理 MFA、扫码等复杂认证)、验证最终数据准确性、处理异常边界情况
  • 工具做的:利用 Playwright CDP 连接已登录浏览器,复用 Cookie 会话执行标准化的数据获取脚本(稳定、低成本、可复现)
  • AI做的:基于获取的结构化 JSON 数据进行模式识别、根因分析、生成改进建议报告(发挥 LLM 的推理与生成优势)
    关键技术路径:Copy as fetch(复用浏览器已验证的请求模式)→ Playwright CDP(在保持登录态的页面上下文中执行)→ Skill 封装(标准化执行流程)

3.2 关键技术实现

3.2.1 浏览器端数据获取脚本(Copy as fetch 的代码化复现)

核心逻辑:将 DevTools 中 “Copy as fetch” 得到的请求模式,封装为可在浏览器环境中运行的脚本。通过 credentials: ‘include’ 自动携带当前登录态 Cookie,无需处理复杂的认证逻辑。

// scripts/fetch-workorders.js
// 在已登录浏览器的页面上下文中执行(通过 Playwright CDP 注入)

const CONFIG = {
    API_BASE: '/api/v1/workorders',
    PAGE_SIZE: 100,
    MAX_PAGES: 10,        // 安全限制,防止无限循环
    RETRY_TIMES: 3,
    RETRY_DELAY: 1000
};

/**
 * 带重试机制的 fetch 封装
 * 网络抖动时自动重试,提高稳定性
 */
async function fetchWithRetry(url, options, retryCount = 0) {
    try {
        const response = await fetch(url, options);
        if (!response.ok) {
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
        }
        return response;
    } catch (error) {
        if (retryCount < CONFIG.RETRY_TIMES) {
            console.log(`请求失败,${CONFIG.RETRY_DELAY}ms后重试(${retryCount + 1}/${CONFIG.RETRY_TIMES})...`);
            await new Promise(resolve => setTimeout(resolve, CONFIG.RETRY_DELAY));
            return fetchWithRetry(url, options, retryCount + 1);
        }
        throw error;
    }
}

/**
 * 获取问题记录数据(分页)
 * 完全复用浏览器当前的 Cookie 和 Headers,无需额外认证
 */
async function fetchWorkorders(params = {}) {
    const allData = [];
    let page = 1;
    let hasMore = true;
    
    console.log('开始获取问题记录数据,参数:', params);

    while (hasMore && page <= CONFIG.MAX_PAGES) {
        console.log(`正在获取第 ${page} 页数据...`);
        
        // 核心:复用浏览器原生的 fetch,自动携带 Cookie
        const response = await fetchWithRetry(
            `${CONFIG.API_BASE}/search`,
            {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'X-Requested-With': 'XMLHttpRequest'
                    // 注意:无需手动设置 Cookie,浏览器自动处理
                },
                credentials: 'include',  // 关键:确保发送当前域的 Cookie
                body: JSON.stringify({
                    page,
                    pageSize: CONFIG.PAGE_SIZE,
                    product: params.product,
                    startDate: params.startDate,
                    endDate: params.endDate,
                    status: params.status || ['open', 'processing', 'resolved']
                })
            }
        );

        const result = await response.json();
        
        if (!result.data || !Array.isArray(result.data.list)) {
            throw new Error('接口返回数据格式不符合预期');
        }

        allData.push(...result.data.list);
        hasMore = result.data.list.length === CONFIG.PAGE_SIZE;
        
        console.log(`${page} 页获取完成,本页 ${result.data.list.length}`);
        page++;
        
        // 防请求过快,避免触发限流
        if (hasMore) {
            await new Promise(resolve => setTimeout(resolve, 500));
        }
    }

    const finalResult = {
        total: allData.length,
        data: allData,
        meta: {
            product: params.product,
            dateRange: [params.startDate, params.endDate],
            fetchedAt: new Date().toISOString(),
            pagesFetched: page - 1
        }
    };

    // 关键:将结果挂载到全局,供外部(Playwright)读取
    window.__WORKORDER_RESULT__ = finalResult;
    console.log(` 数据获取完成,总计 ${allData.length}`);
    return finalResult;
}

// 执行入口:支持通过 URL 参数或全局变量注入配置
const params = window.__SKILL_PARAMS__ || {
    product: new URLSearchParams(location.search).get('product'),
    startDate: new URLSearchParams(location.search).get('start'),
    endDate: new URLSearchParams(location.search).get('end')
};

fetchWorkorders(params).catch(err => {
    console.error('数据获取失败:', err.message);
    window.__WORKORDER_ERROR__ = err.message;
    throw err;
});
3.2.2 Playwright CDP 执行器(连接已登录浏览器)

Playwright 的 connectOverCDP() 可以连接到以 Debug 模式运行的 Chrome 实例,复用其所有页面上下文(包括登录态)。这相当于用代码实现了 “Copy as fetch” 后手动在 Console 粘贴执行的过程,但实现了自动化和结果捕获。

// scripts/cdp-runner.js
// 功能:连接已登录的 Chrome,在页面中执行 fetch-workorders.js,捕获 JSON 结果

const { chromium } = require('playwright');
const fs = require('fs');
const path = require('path');

async function runWithCDP(cdpPort, scriptPath, outputPath, params = {}) {
    console.log(`连接到 Chrome CDP (端口 ${cdpPort})...`);
    
    let browser;
    try {
        // 连接到已运行的 Chrome(用户需提前启动并登录)
        browser = await chromium.connectOverCDP(`http://localhost:${cdpPort}`);
        
        // 获取默认上下文(Default Context),包含用户的手动登录态
        const context = browser.contexts()[0];
        if (!context) {
            throw new Error('未找到浏览器上下文,请确保 Chrome 已正常启动');
        }
        
        // 使用当前已打开的页面(避免新建页面丢失 Cookie)
        const pages = context.pages();
        const page = pages.find(p => p.url().includes('inner.company.com')) || pages[0];
        
        console.log(`当前页面: ${page.url()}`);
        
        // 注入参数到页面全局变量
        await page.evaluate((p) => {
            window.__SKILL_PARAMS__ = p;
        }, params);
        
        // 读取并执行数据获取脚本(等效于在 DevTools Console 中粘贴执行)
        const scriptContent = fs.readFileSync(scriptPath, 'utf-8');
        console.log(` 执行脚本: ${path.basename(scriptPath)}...`);
        
        await page.evaluate(scriptContent);
        
        // 等待异步操作完成(脚本会将结果写入 window.__WORKORDER_RESULT__)
        await page.waitForFunction(() => 
            window.__WORKORDER_RESULT__ !== undefined || window.__WORKORDER_ERROR__ !== undefined, 
            { timeout: 120000 }  // 2分钟超时,应对大数据量
        );
        
        // 提取结果
        const result = await page.evaluate(() => window.__WORKORDER_RESULT__);
        const error = await page.evaluate(() => window.__WORKORDER_ERROR__);
        
        if (error) {
            throw new Error(`脚本执行错误: ${error}`);
        }
        
        // 写入文件供后续 AI 分析
        fs.mkdirSync(path.dirname(outputPath), { recursive: true });
        fs.writeFileSync(outputPath, JSON.stringify(result, null, 2));
        console.log(`结果已保存: ${outputPath}`);
        console.log(` 共获取 ${result.total} 条记录,${result.meta.pagesFetched}`);
        
        return result;
        
    } catch (err) {
        console.error('执行失败:', err.message);
        throw err;
    } finally {
        // 重要:保持浏览器运行(不关闭 Chrome),只断开连接
        if (browser) await browser.close({ runBeforeUnload: false });
    }
}

// CLI 入口
const [,, cdpPort = '9222', scriptPath = './scripts/fetch-workorders.js', 
       outputPath = `./.output/workorders-${Date.now()}.json`, paramsJson = '{}'] = process.argv;

const params = JSON.parse(paramsJson);

runWithCDP(cdpPort, scriptPath, outputPath, params)
    .then(() => process.exit(0))
    .catch(() => process.exit(1));
3.2.3 Skill 配置文件(标准化执行流程)
# SKILL.md
name: workorder-analyzer
description: 基于 Copy as fetch + Playwright CDP 的问题记录分析工具。复用已登录浏览器会话,直接调用内网 API 获取数据,规避 AI 操作 UI 的高成本和不稳定性。

version: 1.0.0
author: "你的名字"

## 前置依赖检查
environment:
  node: ">=16.0.0"
  dependencies:
    - playwright: "^1.40.0"

## 执行流程

### 步骤1:启动并登录(人工操作,一次性)
# 终端1:启动 Chrome Debug 模式(保持运行)
macOS:
  /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
    --remote-debugging-port=9222 \
    --user-data-dir="$HOME/chrome-debug-profile" \
    --no-first-run

Windows:
  "C:\Program Files\Google\Chrome\Application\chrome.exe" ^
    --remote-debugging-port=9222 ^
    --user-data-dir="%TEMP%\chrome-debug-profile"

# 在启动的 Chrome 中手动登录问题记录系统(只需做一次,Cookie 会保留)

### 步骤2:执行数据获取(工具自动化)
# 使用 Playwright CDP 连接已登录浏览器,执行数据获取脚本
# 等效于:手动 Copy as fetch → 粘贴到 Console → 执行 → 复制结果
# 但实现了完全自动化

node scripts/cdp-runner.js 9222 \
  scripts/fetch-workorders.js \
  .output/workorders-$(date +%Y%m%d-%H%M%S).json \
  '{"product":"API Gateway","startDate":"2024-01-01","endDate":"2024-03-31"}'

# 验证数据
if [ ! -s .output/workorders-*.json ]; then
  echo "数据获取失败,请检查:1) Chrome是否已登录 2) 网络连接 3) 接口权限"
  exit 1
fi

echo "数据获取成功"

### 步骤3:AI 分析(自动化)
# 读取获取的 JSON 数据,进行 AI 分析
# 这里可以接入 Claude / GPT-4 等,通过 MCP 或 API 调用
cat .output/workorders-*.json | your-ai-cli analyze \
  --template "分析高频问题,提出产品改进建议" \
  --output .output/analysis-report.md

## 数据获取脚本说明
scripts:
  fetch-workorders.js:
    desc: "在浏览器上下文执行的脚本,复用 Cookie 调用 /api/v1/workorders/search"
    params:
      product: "产品名称(如 API Gateway)"
      startDate: "开始日期(YYYY-MM-DD)"
      endDate: "结束日期(YYYY-MM-DD)"
      status: "问题状态数组(默认 open, processing, resolved)"

## 输出规范
output:
  format: "JSON + Markdown"
  files:
    - "workorders-{timestamp}.json": "原始数据(数组格式)"
    - "analysis-report.md": "AI 分析报告(包含统计图表、根因分析、改进建议)"
3.2.4 环境检查脚本(辅助工具)
#!/bin/bash
# scripts/check-env.sh
# 检查执行环境是否满足要求

echo "=================="
echo "环境检查"
echo "=================="

# 检查 Chrome CDP
if lsof -Pi :9222 -sTCP:LISTEN -t >/dev/null 2>&1; then
    echo " Chrome CDP 正在端口 9222 运行"
    # 尝试获取版本
    curl -s http://localhost:9222/json/version 2>/dev/null | grep '"Browser"' || true
else
    echo "Chrome CDP 未运行"
    echo "   请先执行启动命令(见 SKILL.md 步骤1)"
    exit 1
fi

# 检查 Node.js 版本
NODE_VERSION=$(node --version 2>/dev/null || echo "none")
if [[ "$NODE_VERSION" != "v16"* && "$NODE_VERSION" < "v18" ]]; then
    echo "Node.js 版本 $NODE_VERSION 可能过低,建议 v16+"
else
    echo "Node.js 版本: $NODE_VERSION"
fi

# 检查 Playwright
if npm list playwright >/dev/null 2>&1; then
    echo " Playwright 已安装"
else
    echo " Playwright 未安装"
    echo "   执行: npm install playwright"
    exit 1
fi

# 检查脚本文件
if [ -f "scripts/fetch-workorders.js" ] && [ -f "scripts/cdp-runner.js" ]; then
    echo "执行脚本存在"
else
    echo " 执行脚本缺失"
    exit 1
fi

echo ""
echo "环境检查通过,可以开始执行数据获取"

3.3 项目结构

workorder-analyzer-skill/
├── SKILL.md                      # Skill 核心定义与执行流程
├── README.md                     # 项目说明与快速开始指南
├── package.json                  # Node.js 依赖(playwright等)
├── .gitignore                    # 忽略.output/、node_modules/等
│
├── scripts/                      # 执行脚本
│   ├── fetch-workorders.js       # 浏览器端数据获取脚本(Copy as fetch代码化)
│   ├── cdp-runner.js             # Playwright CDP执行器(Node.js)
│   ├── check-env.sh              # 环境检查(Chrome CDP、Node.js、Playwright)
│   └── install-deps.sh           # 一键安装依赖脚本
│
├── config/                       # 配置文件(可选)
│   ├── api-endpoints.json        # API端点配置
│   └── default-params.json       # 默认查询参数
│
├── templates/                    # 报告模板
│   └── analysis-template.md      # AI分析报告模板
│
├── examples/                     # 示例与文档
│   ├── sample-output/            # 示例输出
│      ├── workorders-sample.json
│      └── analysis-report-sample.md
│
└── .output/                      # 实际输出目录
    ├── workorders-latest.json    # 最新数据(软链接或复制)
    └── 20240314-143052/          # 带时间戳的执行目录(每次执行新建)
        ├── metadata.json         # 执行元数据(时间、参数等)
        ├── workorders.json       # 原始数据(从API获取)
        └── analysis-report.md    # AI生成的分析报告

关键约束

本方案要求目标系统提供可访问的 REST API(SPA 架构下的标准做法)。如果系统完全依赖服务端渲染且无 API 暴露,则仍需退回到浏览器自动化方案。

四、工程化反思

4.1 从“让 AI 做一切”到“人与 AI 的合理分工”

AI从风口席卷成海啸,很容易把人打个“措手不及”,在这种大模型百家争鸣的背景下,人类的持续学习和反思显得尤为重要。在探索过程中,我经历了一个重要的认知转变

最初的误区:试图让 AI 完成所有事情——登录、导航、数据提取、分析。

最终的方案

  • 人做的:登录(涉及安全权限)、验证数据准确性、审核分析报告
  • 工具做的:数据获取的执行(稳定、低成本)
  • AI 做的:数据分析、报告生成(发挥 AI 优势)

延伸这种思路可知: 在这样的时代里,路径依赖是最大的风险。面对持续发生的变化,最重要的,是始终保持乔布斯所说的那种“初学者心态”,不断地去重新理解,并不断尝试与 AI 的新协作方式。

4.2 稳定性优先的工程原则

在企业环境中,稳定性比炫酷更重要。我的方案选择"接口直调"(直接调用已验证的 REST API)而非"AI 操作浏览器"(基于 accessibility tree 的 DOM 操作),核心考量就是稳定性

稳定性来源

  • 接口契约相对稳定(相比 UI)
  • 登录态由浏览器原生维护
  • 请求脚本可人工验证
  • 错误处理和重试机制
  • 输出可追溯(时间戳目录)

4.3 可维护性的长期价值

一个好的工程方案,不仅要解决当前问题,还要考虑长期维护:

可维护性设计

  • 纯文本配置,易于版本管理
  • 模块化脚本,职责清晰
  • 时间戳组织输出,便于追溯
  • 检查脚本前置,快速定位问题
  • 详细的注释和文档

五、总结与展望

通过方案的不断改进和探索,让我对“软件工程师”这个角色的理解发生了根本变化:工程师不再只是一个写代码的人,而是变成一个定义软件,并对结果负责的人。 当然,目前我和 AI 的协作方式还谈不上成熟。我仍然需要和它一起确定一个具体的架构、定义模块的边界,并持续检查代码在实践中的可行性,这部分判断工作依然消耗着大量精力。但是我一直相信,随着更成熟的产品形态出现:尤其是更可靠的流程和测试机制,这部分判断成本本身,也很可能会被系统性地消化掉。

5.1 方案成果

通过这个 Skill,原本需要 30-60 分钟的人工流程:

  • 打开内网系统
  • 多次筛选、导出
  • 人工分析、整理

现在变成了 5-10 分钟的自动化流程:

  • 一条命令触发 Skill
  • 自动完成数据采集与分析
  • 输出结构化分析报告,人工快速审核后应用

更重要的是,这个 Skill 具有:

  • 可复用性:不同产品、不同版本都能使用
  • 可扩展性:轻松适配新的分析维度
  • 稳定性:基于接口而非 UI,健壮性高
  • 低成本:仅分析环节使用 AI,整体成本降低 95%+

5.2 核心收获

技术层面

  • 深入理解了 SPA 架构下接口与渲染的关系
  • 掌握了 Chrome DevTools Protocol 的高级用法
  • 实践了“稳定性优先”的工程化设计
  • 学会了成本控制和性能优化

思维层面

  • 从“AI 能做什么”转变为“什么问题适合用 AI 解决”
  • 认识到工程化思维在 AI 时代依然重要
  • 体会到“简单方案”有时比“复杂方案”更有价值
  • 培养了成本意识和可维护性意识

5.3 未来优化方向

当然,这个 Skill 还有进一步优化的空间:

  1. 数据缓存机制:对于不频繁变化的历史数据,引入缓存减少重复请求
  2. 增量分析:只分析新增问题记录,与历史分析结果合并
  3. 可视化输出:生成图表而非纯文本报告
  4. 定时任务:结合 cron 实现定时自动分析
  5. 多 Skill 协作:将数据获取、分析、报告生成拆分为独立的子模块/Agent,通过标准 JSON 接口协作,实现更灵活的组装

附录

参考项目地址

https://github.com/microsoft/playwright-mcp
https://github.com/heimanba/order-analysis-skill/tree/main

相关资源

参考文章

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐