
如果你正在开发或使用 AI agent 自动完成代码修改、页面调整、配置变更你一定遇到过同一个困扰agent 说它完成了任务但你怎么确认它到底改了什么尤其是当前 AI 编程工具爆发式增长agent 可以自主读写文件、执行命令、甚至操作浏览器。黑盒式的工作方式让“信任”成为最大瓶颈。SightDiff 这个项目提供了一种很直观的解法在 agent 执行任务前先截取基线画面任务结束后再截取结果画面然后把两张图做成滑块对比让用户一眼看出改动前后差异。这篇文章不只是介绍 SightDiff 的思路而是围绕它的核心机制带大家从零实现一个轻量的 “AI agent 变更可视化验证工具”。无论你是想给自己开发的 agent 加一层可视化审计还是想在项目中复用 before/after 对比能力本文都可以直接参考。1. 为什么 AI agent 需要前后对比视觉证明1.1 黑盒执行带来的信任问题AI agent 与普通脚本最大的区别在于“自主决策”。它可以自己规划步骤、调用工具、修改文件、执行命令。能力越强不可控感越强。举个实际场景团队在 CI 流程中接入了一个 AI agent让它自动修复前端样式 bug。agent 运行了 10 分钟最终输出“修复完成”。但没人知道它到底改了哪些样式、是否影响了其他页面、有没有引入新的视觉问题。如果人工逐页检查成本极高如果信任 agent 的输出又可能把问题带到生产环境。这种场景下单纯依赖 agent 的自述报告是不够的。需要一种“眼见为实”的验证方式。1.2 SightDiff 做了什么事情SightDiff 的定位非常清晰给 AI agent 的行为加上“视觉凭证”。工作流程大致可以拆解为三个阶段Before 状态采集在 agent 开始工作之前对目标页面或项目进行截图/快照。Agent 执行AI agent 按任务描述开始修改代码、调整页面或执行命令。After 状态采集与对比agent 执行结束后再次截图然后把 before 和 after 通过滑块或 diff 图的方式并排展示。用户只需要拖动滑块就能直观看到 agent 的改动。这种形式比文字报告更有说服力也比纯 git diff 更容易被非技术角色理解。1.3 适用场景AI 编程助手的效果验收agent 改完页面后自动截图对比确认视觉层面是否符合预期。Web 自动化回归测试agent 操作浏览器后对比操作前后的页面变化。配置管理审计agent 修改了配置文件通过渲染效果或导出的对比图确认影响范围。Agent 安全沙箱监控监控 agent 是否在非授权范围内产生了意外改动。前后端联调接口或组件变更后快速生成视觉差异报告供团队评审。2. 技术原理与整体方案设计2.1 三种方案对比实现“before/after 视觉证明”有几种不同技术路线实际项目要根据需求选择。方案原理优点缺点适用场景像素级截图对比对页面进行截图逐像素计算差异直观、适合视觉验证对动态内容和环境敏感页面视觉变更检测DOM/结构对比对比 HTML 结构树变化精确到节点、可忽略渲染差异无法发现纯 CSS 视觉变化结构变更审计文件级 diff对比源码文件变化类似 git diff精确、可追溯非技术角色难以理解代码变更审查SightDiff 的核心思路是第一种像素级截图对比 可视化滑块展示。这是最直观、门槛最低的方案。2.2 整体架构我们实现的最小闭环架构如下[Before 截图模块] → [AI Agent 模拟执行] → [After 截图模块] ↓ [pixelmatch 像素对比引擎] ↓ [HTML 展示页面生成器] ↓ [浏览器打开 before/after 滑块页面]几个模块的职责截图模块使用无头浏览器Playwright对目标页面进行统一截图。Agent 执行器在本文示例中用一个模拟脚本代替实际项目中可以接入任意 agent 工具。像素对比使用 pixelmatch 库对比两张图片输出差异图和差异数量。展示页面生成本地 HTML 文件包含滑块对比、差异热力图、差异统计信息。2.3 技术选型模块选择说明运行环境Node.js 18Playwright 和 pixelmatch 都基于 Node 生态浏览器控制Playwright跨浏览器、截图稳定、适合自动化像素对比pixelmatch体积小、算法成熟、可用于自动化测试图片处理sharp 或 pngjs统一尺寸和格式展示页面原生 HTML CSS JS无需构建工具开箱即用3. 环境准备与项目结构3.1 环境要求本文示例在以下环境中验证通过操作系统macOS / Windows / Linux 均可Node.js建议 18 或更高版本浏览器ChromePlaywright 会下载对应浏览器内核版本不需要和你完全一致思路通用。如果本地已安装 Node.js 和 npm可以直接开始。3.2 初始化项目mkdir sightdiff-demo cd sightdiff-demo npm init -y3.3 安装依赖npm install playwright pixelmatch pngjs如果是在中国大陆网络环境Playwright 下载浏览器可能会比较慢。可以设置国内镜像例如PLAYWRIGHT_DOWNLOAD_HOSThttps://npmmirror.com/mirrors/playwright npx playwright install chromium安装完成后检查 Playwright 是否可用npx playwright --version3.4 项目结构sightdiff-demo/ ├── agent/ │ └── mock-agent.js # 模拟 AI agent 执行改动 ├── lib/ │ ├── screenshot.js # 截图模块 │ ├── diff.js # 像素对比模块 │ └── reporter.js # HTML 报告生成模块 ├── output/ # 生成结果输出目录 ├── pages/ │ └── demo-page.html # 用于对比的测试页面 ├── index.js # 主流程入口 └── package.json后面每个文件都会给出完整实现。4. 核心功能实现4.1 准备一个测试页面为了验证 before/after 对比效果我们先准备一个静态测试页面。它包含一个标题、一段说明文字、一个按钮和一个卡片区域。文件路径pages/demo-page.html!DOCTYPE html html langzh-CN head meta charsetUTF-8 titleSightDiff Demo Page/title style body { font-family: PingFang SC, Microsoft YaHei, sans-serif; max-width: 800px; margin: 40px auto; padding: 0 24px; background: #f5f6fa; color: #2f3542; } .card { background: #ffffff; border-radius: 12px; padding: 24px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); } .btn { background: #1e90ff; color: #fff; border: none; padding: 10px 20px; border-radius: 8px; font-size: 16px; cursor: pointer; } .btn:hover { opacity: 0.85; } /style /head body h1AI Agent 可视化验证/h1 div classcard p这是一个演示页面用于展示 SightDiff 的 before/after 对比能力。/p button classbtn点击按钮/button /div /body /html这个页面很简单但足以演示视觉差异。接下来模拟 agent 对页面样式进行修改。4.2 编写截图模块截图模块负责在 agent 执行前后分别截取页面状态。文件路径lib/screenshot.jsconst { chromium } require(playwright); const path require(path); const fs require(fs); /** * 对指定 URL 或本地 HTML 文件进行截图。 * param {string} targetUrl 目标地址支持 http:// 或本地文件路径 * param {string} outputPath 截图输出路径 * param {number} width 浏览器视口宽度 * param {number} height 浏览器视口高度 */ async function capturePage(targetUrl, outputPath, width 1280, height 800) { const browser await chromium.launch({ headless: true, args: [--no-sandbox, --disable-setuid-sandbox] }); try { const page await browser.newPage({ viewport: { width, height }, deviceScaleFactor: 1 }); // 等待网络空闲确保页面资源加载完成 await page.goto(targetUrl, { waitUntil: networkidle, timeout: 30000 }); // 确保页面没有滚动距离保证截图一致性 await page.evaluate(() window.scrollTo(0, 0)); // 等待一小段时间等待字体和动画稳定 await page.waitForTimeout(500); const absolutePath path.resolve(outputPath); fs.mkdirSync(path.dirname(absolutePath), { recursive: true }); await page.screenshot({ path: absolutePath, fullPage: false }); console.log([截图完成] ${absolutePath}); } finally { await browser.close(); } } module.exports { capturePage };这里有几个注意事项waitUntil: networkidle可以尽量保证动态资源加载完成避免截图时页面还在加载。deviceScaleFactor: 1保证了不同机器上截图尺寸一致。如果你的页面适配了高分屏可以按需调整。截图前执行window.scrollTo(0, 0)是为了避免上一次访问时的滚动位置影响结果。waitForTimeout(500)是一个经验值主要用于等待字体加载和简单动画结束。实际项目建议去掉硬编码等待改为监听页面稳定状态。4.3 编写模拟 AI Agent真实项目中这里会调用你正在使用的 agent 工具。为了聚焦流程演示我们用脚本模拟“AI agent 自动修改了页面主题色和按钮文案”。文件路径agent/mock-agent.jsconst fs require(fs); const path require(path); /** * 模拟 AI Agent 对页面进行改动。 * 实际项目中这里可以替换为对任何 agent 工具的调用。 * param {string} htmlPath 要修改的 HTML 文件路径 */ async function mockAgentRun(htmlPath) { console.log([Agent] 开始模拟 AI agent 执行任务修改页面主题色...); const absPath path.resolve(htmlPath); let html fs.readFileSync(absPath, utf-8); // 模拟 agent 将按钮背景色从蓝色改为绿色 html html.replace(#1e90ff, #2ed573); // 模拟 agent 修改按钮文字 html html.replace(点击按钮, 已由 Agent 修改); // 模拟 agent 调整卡片阴影 html html.replace(rgba(0, 0, 0, 0.08), rgba(46, 213, 115, 0.3)); fs.writeFileSync(absPath, html, utf-8); console.log([Agent] 页面已修改完成); return true; } module.exports { mockAgentRun };在实际项目中这个文件可以改成调用 OpenAI Function Calling、LangChain Agent、自研 Agent 框架等。核心是保证 agent 执行前后我们都能拿到一张可对比的截图。4.4 编写像素对比模块像素对比使用 pixelmatch 库。它的作用是对比两张 PNG 图片并生成一张差异可视化图片。文件路径lib/diff.jsconst fs require(fs); const path require(path); const { PNG } require(pngjs); const pixelmatch require(pixelmatch); /** * 对比两张 PNG 图片生成差异图并返回统计信息。 * param {string} beforePath 执行前截图 * param {string} afterPath 执行后截图 * param {string} diffOutputPath 差异图输出路径 */ async function compareImages(beforePath, afterPath, diffOutputPath) { const beforeImg PNG.sync.read(fs.readFileSync(beforePath)); const afterImg PNG.sync.read(fs.readFileSync(afterPath)); // 如果尺寸不一致需要统一尺寸这里直接输出错误提示 if (beforeImg.width ! afterImg.width || beforeImg.height ! afterImg.height) { throw new Error(图片尺寸不一致: before${beforeImg.width}x${beforeImg.height}, after${afterImg.width}x${afterImg.height}); } const { width, height } beforeImg; const diffImg new PNG({ width, height }); // threshold 取值范围是 0~1约大对颜色差异越不敏感 const numDiffPixels pixelmatch(beforeImg.data, afterImg.data, diffImg.data, width, height, { threshold: 0.1, includeAA: true, alpha: 0.8, diffColor: [255, 0, 0], // 差异像素标记为红色 diffColorAlt: [255, 165, 0] // 抗锯齿差异标记为橙色 }); fs.mkdirSync(path.dirname(diffOutputPath), { recursive: true }); fs.writeFileSync(diffOutputPath, PNG.sync.write(diffImg)); const totalPixels width * height; const diffPercent (numDiffPixels / totalPixels * 100).toFixed(4); console.log([对比完成] 差异像素数: ${numDiffPixels}); console.log([对比完成] 差异占比: ${diffPercent}%); return { diffPixels: numDiffPixels, totalPixels, diffPercent: parseFloat(diffPercent), width, height }; } module.exports { compareImages };pixelmatch 的几个关键参数threshold两个像素的色差阈值0 表示任何差异都会被标记1 表示几乎不标记。建议使用 0.1 到 0.2。includeAA是否检测抗锯齿边缘差异。如果页面用了大量圆角和渐变建议保留 true。diffColor普通差异像素的标记颜色。diffColorAlt检测到抗锯齿相关的像素使用的颜色。如果截图尺寸不一致pixelmatch 会报错或不执行。实际项目里可以在截图模块中统一 viewport或者在对比前用 sharp 做缩放对齐。4.5 编写 HTML 报告生成器这是最接近 SightDiff 核心体验的模块。它会生成一个包含滑块对比、差异信息和统计面板的 HTML 文件。文件路径lib/reporter.jsconst fs require(fs); const path require(path); const { execSync } require(child_process); /** * 生成 before/after 对比报告。 * param {object} stats 对比统计信息 * param {string} beforeImg 相对 output 目录的 before 图路径 * param {string} afterImg 相对 output 目录的 after 图路径 * param {string} diffImg 相对 output 目录的 diff 图路径 * param {string} outputDir 输出目录 * returns {string} 报告文件路径 */ async function generateReport(stats, beforeImg, afterImg, diffImg, outputDir) { // 将图片转换为 base64 嵌入 HTML方便单文件分享 const beforeBase64 base64Encode(path.join(outputDir, beforeImg)); const afterBase64 base64Encode(path.join(outputDir, afterImg)); const diffBase64 base64Encode(path.join(outputDir, diffImg)); const html !DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleSightDiff 对比报告/title style * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: PingFang SC, Microsoft YaHei, sans-serif; background: #0f1115; color: #e8e8e8; min-height: 100vh; padding: 24px; } .container { max-width: 1200px; margin: 0 auto; } .header { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px; background: #1a1d24; border-radius: 12px; margin-bottom: 24px; } .header h1 { font-size: 20px; } .badge { background: #2ed573; color: #fff; font-size: 13px; padding: 6px 14px; border-radius: 20px; font-weight: 600; } .stats-panel { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 24px; } .stat-card { background: #1a1d24; padding: 20px; border-radius: 12px; text-align: center; } .stat-value { font-size: 32px; font-weight: 700; margin-bottom: 8px; color: #fff; } .stat-label { font-size: 14px; color: #9aa0a6; } .compare-box { position: relative; width: 100%; height: 600px; background: #000; border-radius: 12px; overflow: hidden; border: 1px solid #2a2d36; margin-bottom: 24px; cursor: ew-resize; } .compare-box img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: contain; user-select: none; pointer-events: none; } .compare-box .after-img { clip-path: inset(0 0 0 50%); } .compare-box .divider-line { position: absolute; top: 0; bottom: 0; left: 50%; width: 2px; background: #fff; box-shadow: 0 0 12px rgba(255,255,255,0.6); transform: translateX(-50%); } .compare-box .label { position: absolute; top: 16px; font-size: 13px; padding: 6px 12px; border-radius: 6px; background: rgba(0,0,0,0.7); color: #fff; } .label-before { left: 16px; } .label-after { right: 16px; } .diff-section { background: #1a1d24; border-radius: 12px; padding: 24px; } .diff-section h2 { font-size: 18px; margin-bottom: 16px; } .diff-section img { width: 100%; border-radius: 8px; } .meta { margin-top: 16px; font-size: 13px; color: #9aa0a6; } /style /head body div classcontainer div classheader h1SightDiff 对比报告/h1 span classbadgeAI Agent 完成/span /div div classstats-panel div classstat-card div classstat-value${stats.diffPixels.toLocaleString()}/div div classstat-label差异像素/div /div div classstat-card div classstat-value${stats.diffPercent}%/div div classstat-label差异占比/div /div div classstat-card div classstat-value${stats.width} × ${stats.height}/div div classstat-label图片尺寸/div /div /div div classcompare-box idcompareBox img classbefore-img src${beforeBase64} altBefore img classafter-img src${afterBase64} altAfter div classdivider-line iddividerLine/div span classlabel label-beforeBefore/span span classlabel label-afterAfter/span /div div classdiff-section h2像素差异热力图/h2 img src${diffBase64} altDiff p classmeta红色区域表示前后差异明显的像素位置。阈值 threshold0.1。/p /div /div script const box document.getElementById(compareBox); const afterImg box.querySelector(.after-img); const divider document.getElementById(dividerLine); function updateDivision(x) { const rect box.getBoundingClientRect(); let percent ((x - rect.left) / rect.width) * 100; percent Math.max(0, Math.min(100, percent)); afterImg.style.clipPath inset(0 0 0 percent %); divider.style.left percent %; } box.addEventListener(mousemove, (e) updateDivision(e.clientX)); box.addEventListener(touchmove, (e) { e.preventDefault(); updateDivision(e.touches[0].clientX); }); /script /body /html; const reportPath path.join(outputDir, report.html); fs.writeFileSync(reportPath, html, utf-8); console.log([报告生成] ${reportPath}); return reportPath; } function base64Encode(filePath) { return data:image/png;base64, fs.readFileSync(filePath).toString(base64); } /** * 自动打开报告macOS / Linux / Windows 分别处理 */ function openReport(reportPath) { try { if (process.platform darwin) { execSync(open ${reportPath}); } else if (process.platform win32) { execSync(start ${reportPath}); } else { execSync(xdg-open ${reportPath}); } } catch (err) { console.log(报告已生成请手动打开: reportPath); } } module.exports { generateReport, openReport };这个报告页有几个亮点滑块对比鼠标在图片区域移动时after 图层的裁剪区域会实时变化实现类似 SightDiff 的交互体验。图片 base64 内嵌生成的 HTML 是单文件可以直接发给同事查看不需要额外携带图片。统计卡片差异像素数量、差异百分比、图片尺寸一目了然。4.6 编写主流程入口完成所有模块后用主流程把它们串起来。文件路径index.jsconst path require(path); const { capturePage } require(./lib/screenshot); const { compareImages } require(./lib/diff); const { generateReport, openReport } require(./lib/reporter); const { mockAgentRun } require(./agent/mock-agent); const DEMO_PAGE path.join(__dirname, pages/demo-page.html); const OUTPUT_DIR path.join(__dirname, output); const BEFORE_SHOT before.png; const AFTER_SHOT after.png; const DIFF_SHOT diff.png; async function main() { console.log( SightDiff Demo 开始 ); // 1. Before 截图 await capturePage(file:// DEMO_PAGE, path.join(OUTPUT_DIR, BEFORE_SHOT)); // 2. 模拟 AI agent 执行 await mockAgentRun(DEMO_PAGE); // 3. After 截图 await capturePage(file:// DEMO_PAGE, path.join(OUTPUT_DIR, AFTER_SHOT)); // 4. 像素级对比 const stats await compareImages( path.join(OUTPUT_DIR, BEFORE_SHOT), path.join(OUTPUT_DIR, AFTER_SHOT), path.join(OUTPUT_DIR, DIFF_SHOT) ); // 5. 生成并打开报告 const reportPath await generateReport(stats, BEFORE_SHOT, AFTER_SHOT, DIFF_SHOT, OUTPUT_DIR); openReport(reportPath); console.log( SightDiff Demo 结束 ); } main().catch((err) { console.error(执行失败:, err); process.exit(1); });主流程的顺序很重要先 Before 截图再执行 agent再 After 截图。如果顺序颠倒对比就没有意义了。5. 运行与验证5.1 启动项目运行命令node index.js5.2 预期输出正常执行时终端会输出类似下面的日志 SightDiff Demo 开始 [截图完成] /Users/xxx/sightdiff-demo/output/before.png [Agent] 开始模拟 AI agent 执行任务修改页面主题色... [Agent] 页面已修改完成 [截图完成] /Users/xxx/sightdiff-demo/output/after.png [对比完成] 差异像素数: 12834 [对比完成] 差异占比: 1.2345% [报告生成] /Users/xxx/sightdiff-demo/output/report.html SightDiff Demo 结束 5.3 查看产物进入output/目录可以看到四个文件文件作用before.pngagent 执行前页面截图after.pngagent 执行后页面截图diff.png像素差异热力图红色区域表示变化report.html可交互的对比报告浏览器打开即可查看打开report.html后在图片区域左右移动鼠标就能看到之前构建的滑块对比效果。差异热力图中button 背景色变化区域会被标记出来。6. 接入真实 AI agent 工作流上面的示例使用的是模拟 agent实际接入时通常有三种方式。6.1 命令行包装方式如果你的 agent 提供 CLI 接口可以用 shell 脚本或 Node.js 直接包装我们的主流程# 示例capture-before.sh 逻辑 node lib/screenshot.js https://example.com product-page.png # 执行真实 agent ai-agent --task 修改 product page 按钮样式 # 示例capture-after.sh 逻辑 node lib/screenshot.js https://example.com product-page-after.png node lib/diff.js product-page.png product-page-after.png diff.png这种方式侵入性最小不要求 agent 本身有任何改造。6.2 CI/CD 集成方式可以在 GitHub Actions 或 GitLab CI 中增加一个可视验证任务。每次 agent 提交代码后自动部署到预发布环境执行截图对比并把报告作为 Artifact 保存。以 GitHub Actions 为例name: ai-agent-visual-check on: push: branches: [ main ] jobs: visual-diff: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - uses: actions/setup-nodev4 with: node-version: 20 - name: Install dependencies run: npm install - name: Install Playwright browsers run: npx playwright install --with-deps chromium - name: Run agent and generate diff run: node index.js - uses: actions/upload-artifactv4 with: name: sightdiff-report path: output/这里建议把output/目录加入.gitignore避免把大体积截图提交到仓库。6.3 自定义 Agent 能力扩展方式如果你的 agent 是自己开发的可以在 agent 的编排循环中直接调用截图和对比模块。可以把captureSnapshot()定义为一个 Tool让 agent 在“变更前”“变更后”主动调用。这样 agent 自身就能在任务结束时附上一份视觉证明。7. 常见问题与排查7.1 问题速查表问题现象常见原因解决思路截图全黑或空白页面加载失败或 waitUntil 策略不当将networkidle改为load增加等待时间图片尺寸不一致viewport 配置变化或页面动态加载内容在截图模块中固定 viewport 和 deviceScaleFactor差异像素过多页面存在动态加载、动画、时间戳等待页面稳定后再截图或调大 threshold差异像素为 0agent 实际上没有修改页面或对比对象错误检查页面文件是否被真实修改确认 before/after 路径报告 HTML 体积过大截图 base64 内嵌导致可将图片放在外部目录或压缩截图质量Playwright 安装失败网络问题或缺少系统依赖配置镜像执行npx playwright install --with-deps chromium7.2 动态页面的对比坑如果你的目标页面包含轮播图、倒计时、图表动画等动态内容直接做像素对比会产生大量误报。建议采用以下策略固定网络环境拦截外部请求或使用本地 mock。固定时间在固定时间窗口截图。增加稳定等待等待元素和动画结束。使用 IntersectionObserver 或自定义全局状态标记页面渲染完成后通过window.__SIGHTDIFF_READY__标记通知截图程序。7.3 文件写入权限问题在 CI 环境中如果 agent 对页面文件的修改导致EPERM: operation not permitted。常见原因是仓库目录只读。可以给工作目录加写权限或者把构建产物放在临时目录而非源码目录。8. 工程化最佳实践与安全边界8.1 截图纳入定时任务不要只在 agent 发布时做一次对比。对于长期运行 agent 任务的团队建议每天或每次任务执行后自动截图入库。这样可以在矩阵中查看不同时间点的视觉变化趋势快速定位“是哪一次 agent 变更导致页面样式异常”。8.2 合理设置 thresholdthreshold 决定了像素对比的灵敏度。视觉回归要求较严格可以使用0.05 ~ 0.1。快速巡检场景可以使用0.15 ~ 0.2。如果页面本身有轻微噪声如视频背景、粒子效果建议调大。需要注意的是threshold 只是“色差容差”并不能去掉所有误报。8.3 差异报告可追溯每次生成报告时建议把以下信息写入 JSON 文件{ timestamp: 2025-06-30T12:00:00Z, taskId: task_xxxx, agentName: mock-agent, diffPixels: 12834, diffPercent: 1.2345, beforeImage: before.png, afterImage: after.png, diffImage: diff.png }这样后续可以通过taskId反查某次 agent 任务的可视化结果。8.4 敏感信息与安全边界在真实项目中页面截图可能包含用户数据、内部系统信息、密钥等敏感内容。用 AI agent 操作页面并截图时必须注意始终在测试环境中执行截图不要对生产环境页面直接进行未经授权的操作。涉及权限操作时遵循最小权限原则agent 只允许访问任务需要的资源。如果截图保存到对象存储或 CI 制品平台确认访问权限是私有的。不要在报告 HTML 中内嵌包含敏感信息的原始截图后将报告公开分享。8.5 性能优化对长页面只截取首屏可以大幅降低资源开销。如果页面数量较多使用并发截图时要注意 CPU 和内存限制建议限制并发数为 3 到 5。图片对比前可以缩小到固定宽度比如统一缩放到 1024px 宽减少计算量。9. 动手扩展从演示到生产本文已经实现了一个完整可运行的 SightDiff 风格工具。你可以在此基础上继续扩展接入真实的 AI agent比如通过命令行调用你的 agent 工具或者用 Node.js 直接调用 agent 的函数接口。增加视频录制的 before/after 对比而不是只截静态图。对多页面、多路由进行批量视觉对比生成一个总览报告。把报告推送到钉钉、飞书或邮件方便团队在 agent 任务完成后直接收到验收结果。把对比结果作为 agent 任务是否通过的“判定条件”当差异占比超过阈值时自动拦截发布。AI agent 的自动化能力越强人类对它的审查需求就越强烈。SightDiff 这种可视化 before/after 证明方式给 agent 的行为增加了一层可验证、可回溯、可展示的“证据链”。在一个 agent 越来越多参与到代码编写、页面修改、配置调整的时代这种能力会越来越重要。你可以先把示例跑通理解整个流程再结合自己团队的实际 agent 工具做适配。代码不算复杂但解决的是真实的信任与审计问题。