首页 / 文章 / 基于Node.js的无限制浏览器自动化代理

基于Node.js的无限制浏览器自动化代理

为浏览器驱动代理配置明确的工具边界、安全机制以及可操作的日志功能。

4302 词

本指南将重新构建一条可操作的路径,介绍如何使用 Node.js、Playwright、Bright Data 和 Gemini 构建无限制的浏览器自动化 AI 智能体。重点在于那些无需猜测意图即可直接放入代码仓库的契约、检查项以及代码。在修改代码之前,应先明确输入参数、各步骤的负责人以及完成标准。操作人员应当能够从已知的检查点重新运行相应步骤,而无需猜测隐藏的状态。应将此阶段视为输入与经过验证的输出之间的契约:为相关成果命名,定义成功检查标准,并拒绝默许的半完成状态。

简介

在修改代码之前,首先需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应在功能结果旁记录执行时间和成本。提前明确这些信息,可避免在从演示环境过渡到共享环境时出现意外费用。

await page.goto(url)
await page.click(selector)
await page.type(selector, text)

我们正在构建什么

对于我们正在构建的系统,在修改代码之前需明确输入参数、各步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新运行相应步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能开关应集中存放于一个位置,以便操作人员无需查看整个流程即可进行审计。 严格限制工具的架构。过宽的自由文本参数容易引发注入攻击,且会增加审计成本。

前提条件

在修改代码之前,必须先明确输入参数、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。需同时记录正常流程和异常恢复流程。重试机制、人工审核环节以及错误处理都是产品本身的组成部分,而非后续需要补充的功能。要严格限制工具的架构,过宽的自由文本参数容易引发注入攻击,并增加审计成本。在修改代码之前,必须先明确输入参数、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应将这一阶段视为输入与经过验证的输出之间的契约,为相关成果命名、定义成功标准,绝不允许出现无声的半完成状态。

构建 Sikki 项目

在开展 Sikki 项目构建时,应在修改代码之前明确输入参数、各步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新运行相应步骤,而无需猜测隐藏状态。应在功能结果旁记录执行时间和成本。提前明确这些信息,可避免在项目从演示环境过渡到共享环境时出现意外费用。在执行耗时的模型调用后设置检查点,这样重新尝试时就不会重复计算相同的工作量。

步骤 1:创建项目

第一步:在修改代码之前,先创建项目,明确输入参数、该步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能开关应集中存放于一个位置,这样操作人员无需查看整个系统结构即可进行审核。 在执行耗时的模型调用后设置检查点,以避免重复尝试时再次处理相同的工作。

mkdir sikki-agent
cd sikki-agent
npm init -y
npm pkg set type=module
npm install playwright

npm install @google/genai
npm install dotenv
npm install chalk
npm install ora
sikki-agent
 index.js
 .env
 screenshots/
 reports/
 package.json

第二步:获取 Gemini API 密钥

对于第二步“获取 Gemini API 密钥”,在修改代码之前需明确输入参数、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 需同时记录正常流程和异常恢复流程。重试机制、人工审核环节以及错误处理都是产品功能的一部分,而非后续需要补充的内容。 在执行成本较高的模型调用后设置检查点,以避免重试时重复处理相同任务。 对于第二步“获取 Gemini API 密钥”,在修改代码之前需明确输入参数、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 应将此阶段视为输入与验证后输出之间的契约。为相关成果命名,明确成功标准,并杜绝无声的半完成状态。

GEMINI_API_KEY = your_gemini_api_key
BRIGHT_WS_ENDPOINT = //i will show you later

第3步:搭建Bright Data Cloud浏览器基础设施

在执行“第3步:搭建Bright Data Cloud浏览器基础设施”时,需先明确输入参数、该步骤的负责人以及完成标准,然后再修改代码。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应在功能结果旁记录执行时间和成本。提前了解相关情况可避免在从演示环境过渡到共享环境时出现意外费用。应将规划与工具执行分开处理:规划者负责提出方案,执行者负责进行修改,验证者则根据目标检查结果。

BRIGHT_WS_ENDPOINT = "your_endpoint"
chromium.launch()
chromium.connectOverCDP()

第4步:将Playwright与Bright Data连接起来

对于第4步:将Playwright与Bright Data连接起来,在修改代码之前需明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放于一个位置,这样操作人员无需查看整个系统结构即可进行审核。 将规划与工具执行分开。规划器负责提出方案,执行器负责实施操作,验证器则根据目标检查结果。

import { chromium }
from "playwright";

const BRIGHT_WS_ENDPOINT = "your_endpoint"

const browser =
await chromium.connectOverCDP(BRIGHT_WS_ENDPOINT
);

const page =
await browser.newPage();
await page.goto(
"https://cnn.com"
);

console.log(
await page.title()
);

在实现之前深入了解SIKKI Agent

在实施之前深入了解 SIKKI Agent 时,应在修改代码前明确输入参数、各步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 需同时记录正常流程与异常恢复路径。重试机制、人工审核环节以及错误处理都是产品本身的组成部分,而非后续需要补充的功能。 将规划与工具执行分开。规划者负责提出方案,执行者负责进行操作,验证者则根据目标检查结果。 在实施之前深入了解 SIKKI Agent 时,应在修改代码前明确输入参数、各步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 应将此阶段视为输入与已验证输出之间的契约。为相关成果命名,明确成功标准,并杜绝无声的局部处理。

完成。

SIKKI 不是爬虫工具

对于 SIKKI 不是爬虫工具这一框架,在修改代码之前需先明确输入参数、各步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行相应步骤,而无需猜测隐藏状态。 应在功能结果旁记录执行时间和成本。提前明确这些信息可避免在从演示环境过渡到共享环境时出现意外费用。 需严格限制工具的架构结构。过宽的自由文本参数容易引发注入攻击,还会增加审计成本。

ProductPriceAcer Nitro V$899ASUS TUF$949MSI Thin$979

SIKKI 的架构设计

对于 SIKKI 的架构而言,在修改代码之前需明确输入参数、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放于一个位置,这样操作人员无需查看整个系统结构即可进行审计。 严格限制工具的架构规范。过宽的自由文本参数容易引发注入攻击,且会增加审计成本。

SIKKI

Strategic Intelligence Commerce Kernel

┌──────────────────────────────┐
│ Browser Layer                │
│ Playwright + Bright Data     │
└──────────────────────────────┘
                │
                ▼
┌──────────────────────────────┐
│ Extraction Layer             │
│ Product                      │
│ Price                        │
│ Rating                       │
│ Seller                       │
│ Availability                 │
└──────────────────────────────┘
                │
                ▼
┌──────────────────────────────┐
│ AI Layer                     │
│ Gemini                       │
└──────────────────────────────┘
                │
                ▼
┌──────────────────────────────┐
│ Analysis Layer               │
│ Cheapest Products            │
│ Market Average               │
│ Competitor Comparison        │
│ Trend Analysis               │
└──────────────────────────────┘
                │
                ▼
        Markdown / CSV
        Dashboard Export

构建提取层

在构建提取层时,应在修改代码之前明确输入参数、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 需同时记录正常流程和异常恢复流程。重试机制、人工审核环节以及错误处理都是产品本身的组成部分,而非后续需要补充的功能。 需严格限制工具的架构。过宽的自由文本参数容易引发注入攻击,还会增加审计成本。 在构建提取层时,应在修改代码之前明确输入参数、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 应将这一阶段视为输入与经过验证的输出之间的契约。为相关输出文件命名,明确成功判定标准,杜绝无声的半完成状态。

const products = await page.evaluate(() => {const items = [];
    document.querySelectorAll('[data-component-type="s-search-result"]')
      .forEach(card => {
        const title =
          card.querySelector('h2 span')?.innerText;
        const price =
          card.querySelector('.a-price .a-offscreen')
          ?.innerText;
        const rating =
          card.querySelector('.a-icon-alt')
          ?.innerText;
        const link =
          card.querySelector('h2 a')
          ?.href;
        if(title && price){
            items.push({
                title,
                price,
                rating,
                link
            });
        }
    });
    return items;
});
console.log(products);
[
 {
   title: "Acer Nitro V Gaming Laptop",
   price: "$899",
   rating: "4.5 out of 5 stars",
   link: "https://..."
 },

{
   title: "ASUS TUF Gaming A15",
   price: "$949",
   rating: "4.4 out of 5 stars",
   link: "https://..."
 }
]

为 SIKKI 赋予智能

在为 SIKKI 添加智能功能时,应在修改代码之前明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应在功能结果旁记录执行时间和成本。提前了解这些信息可以避免在从演示环境过渡到共享环境时出现意外费用。在高成本模型调用之后设置检查点,这样重新尝试时就不会重复计算相同的工作量。

const prompt = `You are an ecommerce analyst.
Analyze the following products.
${JSON.stringify(products)}
Return:
1. Cheapest product
2. Average price
3. Best rated product
4. Summary of the market
5. Recommendation
`;

构建最终的 SIKKI 智能体

在构建最终的 SIKKI Agent 时,应在修改代码之前明确输入参数、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放于一个位置,这样操作人员无需查看整个流程即可进行审核。 在耗时的模型调用之后设置检查点,以避免重复尝试时再次处理相同的工作。

步骤 1:创建 Agent 文件

第一步:在修改代码之前,先创建代理文件,明确输入参数、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。需同时记录正常流程与异常恢复路径。重试机制、人工审核环节以及错误处理都是产品本身的组成部分,而非后续需要补充的功能。在执行成本较高的模型调用后设置检查点,以避免重试时重复处理相同任务。第一步:在修改代码之前,先创建代理文件,明确输入参数、该步骤的负责人以及终止条件。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应将此阶段视为输入与验证后输出之间的契约,为相关文件命名,明确成功标准,杜绝无声的半完成状态。

import "dotenv/config"; //to use environment variables from .env file
import fs from "fs"; //to read and write files

import { chromium } from "playwright";
import { GoogleGenAI } from "@google/genai";


//api keys and endpoints from environment variables
const BRIGHT_WS_ENDPOINT = process.env.BRIGHT_WS_ENDPOINT;
const GEMINI_API_KEY = process.env.GEMINI_API_KEY;
//initialize the GoogleGenAI client with the Gemini API key

const ai = new GoogleGenAI({
  apiKey: GEMINI_API_KEY,
});

//the prompt to send to the Gemini API
const prompt = "Write a short introduction to the Gemini API.";

const response = await ai.models.generateContent({
  model: "gemini-2.5-flash",
  contents: prompt,
});

console.log(response.text);

第2步:配置Playwright以连接Bright Data的WebSocket

在执行第2步“配置Playwright以连接Bright Data的WebSocket”时,需先确定输入参数、该步骤的负责人以及结束标准,然后再修改代码。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应在功能测试结果旁记录执行时间与成本。提前明确这些信息可避免在从演示环境切换到共享环境时出现意外费用。应将规划与工具执行分开处理:规划者负责提出方案,执行者负责实施操作,验证者则根据目标检查结果。

//setup Playwright to connect to Bright Data's WebSocket endpoint and navigate to Amazon search results for "gaming laptop"
async function main(){
  const browser = await chromium.connectOverCDP(BRIGHT_WS_ENDPOINT);
  const page = await browser.newPage();

  const query = "gaming laptop";
  const searchUrl = `https://www.amazon.com/s?k=${encodeURIComponent(query)}`;

  console.log("Navigating Bright Data Playwright to:", searchUrl);
  await page.goto(searchUrl, { waitUntil: "domcontentloaded" });

  const title = await page.title();
  console.log("Amazon search page title:", title);
  console.log("Bright Data navigation complete for query:", query);

  await browser.close();
}

main();

第3步:提取产品信息

对于第3步:提取产品信息,在修改代码之前需明确输入参数、该步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能开关应集中存放于一个位置,以便操作人员无需查看整个系统结构即可进行审计。 将规划与工具执行分开。规划器负责提出方案,执行器负责实施变更,验证器则根据目标检查结果。

// Playwright to connect to Bright Data's WebSocket endpoint and navigate to Amazon search results for "gaming laptop"
async function main(){
  const browser = await chromium.connectOverCDP(BRIGHT_WS_ENDPOINT);
  const page = await browser.newPage();

  const query = "gaming laptop";
  const searchUrl = `https://www.amazon.com/s?k=${encodeURIComponent(query)}`;

  console.log("Navigating Bright Data Playwright to:", searchUrl);
  await page.goto(searchUrl, { waitUntil: "domcontentloaded" });

  const products = await page.evaluate(() => {
    const data = [];
    document
      .querySelectorAll('[data-component-type="s-search-result"]')
      .forEach((card) => {
        const title = card.querySelector("h2 span")?.innerText;
        const price = card.querySelector(".a-price .a-offscreen")?.innerText;
        const rating = card.querySelector(".a-icon-alt")?.innerText;
        if (title && price) {
          data.push({
            title,
            price,
            rating,
          });
        }
      });
    return data;
  });

  console.log("Step 3: Extract Product Information");
  console.log(products);

  const title = await page.title();
  console.log("Amazon search page title:", title);
  console.log("Bright Data navigation complete for query:", query);

  await browser.close();
}

main();

第4步:让Gemini分析市场

对于第4步:让Gemini分析市场,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 需同时记录正常流程和异常恢复流程。重试机制、人工审核环节以及错误处理都属于产品功能的一部分,而非后续需要补充的内容。 应将规划与工具执行分开:规划者负责提出方案,执行者负责进行操作,验证者则根据目标检查结果。 对于第4步:让Gemini分析市场,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 应将此阶段视为输入与经过验证的输出之间的契约。为相关成果命名,明确成功标准,绝不允许出现无声无息的半完成状态。

const prompt = `You are an ecommerce analyst.
Analyze these products:
${JSON.stringify(products)}
Return:
1. Cheapest Product
2. Average Market Price
3. Best Rated Product
4. Competitor Comparison
5. Final Recommendation
`;
const result = await model.generateContent(prompt);
const analysis =
result.response.text();
### 3. Best Rated Product
There are three products tied for the highest rating of **5.0 out of 5 stars**:
1.  **HP OMEN 16" Gaming Laptop Computer** (Intel Ultra 9 285H, NVIDIA GeForce RTX 5070 8GB, 16GB DDR5, 1TB PCIe SSD, 144Hz IPS WUXGA Display) at **$1,745.99**
2.  **HP OMEN 16 Slim RTX 5070 Gaming Laptop** (Intel Ultra 9 285H, NVIDIA RTX 5070, 32GB DDR5, 1TB SSD, 16" FHD+ Anti-Glare) at **$1,929.00**
3.  **ASUS ROG Strix G16 Gaming Laptop** (Intel Core i7 14650HX, NVIDIA RTX 5060, 32 GB DDR5 RAM, 1 TB SSD, 16" FHD+ LED Display, & Gaming Accessories) at **$1,999.99**

**Analysis:** It's noteworthy that two HP OMEN laptops achieved perfect ratings, both featuring high-end Ultra 9 CPUs and RTX 5070 GPUs.
This suggests strong customer satisfaction with HP's premium gaming line.
The ASUS ROG Strix G16 also achieved a 5.0 rating,
despite having a slightly lower-tier GPU (RTX 5060) than the
HP OMENs in this bracket, possibly due to the included
"Gaming Accessories" and overall perceived value.
The Alienware product has no rating, so it cannot be considered here.

第5步:导出Sikki市场报告

在执行第5步“导出Sikki市场报告”时,需先确定输入参数、该步骤的负责人以及完成标准,然后再修改代码。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应在功能结果旁记录执行时间和成本。提前明确相关内容可避免在从演示环境切换到共享环境时出现意外费用。需严格限制工具架构,过宽的自由文本参数容易引发注入攻击,并增加审计成本。

const report = `
# SIKKI Market Report
## Search Query
${query}

## Products
${JSON.stringify(products, null, 2)}

## Gemini Analysis
${analysis}

Generated: ${new Date().toISOString()}
`;

fs.writeFileSync(reportPath, report, "utf8");
import "dotenv/config"; // use environment variables from .env file
import fs from "fs";

import { chromium } from "playwright";
import { GoogleGenAI } from "@google/genai";

const BRIGHT_WS_ENDPOINT = process.env.BRIGHT_WS_ENDPOINT;
const GEMINI_API_KEY = process.env.GEMINI_API_KEY;

if (!BRIGHT_WS_ENDPOINT) {
  throw new Error("Missing BRIGHT_WS_ENDPOINT environment variable.");
}

if (!GEMINI_API_KEY) {
  throw new Error("Missing GEMINI_API_KEY environment variable.");
}

const ai = new GoogleGenAI({
  apiKey: GEMINI_API_KEY,
});

async function retryWithBackoff(fn, maxRetries = 5, initialDelayMs = 2000) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      const errorMsg = error?.message || error?.status || JSON.stringify(error);
      if (attempt === maxRetries) {
        console.error(`Final attempt failed. Error: ${errorMsg}`);
        throw error;
      }
      const delayMs = initialDelayMs * Math.pow(2, attempt - 1);
      console.log(`Attempt ${attempt} failed: ${errorMsg}`);
      console.log(`Retrying in ${delayMs}ms... (${attempt}/${maxRetries})`);
      await new Promise((resolve) => setTimeout(resolve, delayMs));
    }
  }
}

async function main() {
  let browser;
  try {
    browser = await chromium.connectOverCDP(BRIGHT_WS_ENDPOINT);
    const page = await browser.newPage();

    const query = "gaming laptop";
    const searchUrl = `https://www.amazon.com/s?k=${encodeURIComponent(query)}`;

    console.log("Connecting Playwright to Bright Data...");
    console.log("Navigating to:", searchUrl);

    await page.goto(searchUrl, { waitUntil: "domcontentloaded" });

    const products = await page.evaluate(() => {
      const data = [];
      document
        .querySelectorAll('[data-component-type="s-search-result"]')
        .forEach((card) => {
          const title = card.querySelector("h2 span")?.innerText;
          const price = card.querySelector(".a-price .a-offscreen")?.innerText;
          const rating = card.querySelector(".a-icon-alt")?.innerText;
          if (title && price) {
            data.push({
              title,
              price,
              rating,
            });
          }
        });
      return data;
    });

    console.log(`Extracted ${products.length} products.`);

    const analysisPrompt = `You are an ecommerce analyst.
Analyze these products:
${JSON.stringify(products)}
Return:
1. Cheapest Product
2. Average Market Price
3. Best Rated Product
4. Competitor Comparison
5. Final Recommendation
`;

    console.log("Sending products to Gemini for analysis (with retry logic)...");
    const analysisResult = await retryWithBackoff(async () => {
      return await ai.models.generateContent({
        model: "gemini-2.5-flash",
        contents: analysisPrompt,
      });
    }, 5, 2000);

    const analysis = analysisResult.text || "No analysis text returned.";

    const report = `# SIKKI Market Report

## Search Query
${query}

## Products
${JSON.stringify(products, null, 2)}

## Gemini Analysis
${analysis}

Generated: ${new Date().toISOString()}
`;

    fs.writeFileSync("sikki-report.md", report, "utf8");

    console.log("Step 5: Export the Results");
    console.log("Report generated: sikki-report.md");
    console.log("Final analysis saved. Open sikki-report.md to review the result.");

    await browser.close();
  } catch (error) {
    console.error("Error:", error.message || error);
    if (browser) {
      await browser.close().catch(() => {});
    }
    process.exit(1);
  }
}

main();

第6步:教会SIKKI收集证据

对于第6步:教会SIKKI收集证据,在修改代码之前需明确输入参数、该步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放于一个位置,这样操作人员无需查看整个系统结构即可进行审计。 严格限制工具的架构。过宽的自由文本参数容易引发注入攻击,且会增加审计成本。

const searchScreenshot = path.join(screenshotDir, "search-results.png");
await page.screenshot({ path: searchScreenshot, fullPage: true });
const summaryPath = path.join(screenshotDir, "top-product-summary.png");
await cards[0].screenshot({ path: summaryPath });

第7步:捕获每个产品页面

对于第7步:捕获每个产品页面,在修改代码之前需明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。需同时记录正常流程和异常恢复流程。重试机制、人工审核环节以及错误处理都是产品功能的一部分,而非后续需要补充的内容。需严格限制工具的架构设计,过宽的自由文本参数容易引发注入攻击,并增加审计成本。对于第7步:捕获每个产品页面,在修改代码之前需明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应将此阶段视为输入与验证后输出之间的契约,为相关成果命名、定义成功判定标准,并杜绝无声的半完成状态。

const normalized = normalizeAmazonUrl(p.url, p.title);
await retryWithBackoff(async () =>
 await page.goto(normalized, { waitUntil: "domcontentloaded", timeout: 120000 }),
 3,
 1500
);
await page.waitForTimeout(800);
await page.screenshot({ path: outPath, fullPage: true });

第8步:创建CSV导出文件

对于第8步:创建CSV导出文件,在修改代码之前需明确输入参数、该步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应在功能结果旁记录执行时间和成本。提前了解这些信息可以避免在从演示环境切换到共享环境时出现意外费用。在耗时的模型调用之后设置检查点,这样重新尝试时就不会重复计费。

const csvLines = ["TITLE,PRICE,RATING,URL"];
enriched.forEach((p) => {
 const safe = (s) => `"${String(s || "").replace(/"/g, '""')}"`;
 csvLines.push([safe(p.title), safe(p.price), safe(p.rating || ""), safe(p.url || "")].join(","));
});
fs.writeFileSync(csvPath, csvLines.join("\n"), "utf8");

第9步:生成仪表板摘要

对于第9步“生成控制面板摘要”,在修改代码之前需明确输入参数、该步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放于一个位置,以便操作人员无需查看整个系统结构即可进行审计。 在执行耗时的模型调用后设置检查点,这样重新尝试时就不会重复计算相同的工作。

const priced = enriched.filter((p) => !isNaN(p.priceValue));
const cheapestObj = priced.length ? priced.reduce((a, b) => (a.priceValue <= b.priceValue ? a : b)) : null;
const avgPriceVal = priced.length ? (priced.reduce((s, p) => s + p.priceValue, 0) / priced.length).toFixed(2) : “N/A”;
const bestRatedObj = enriched.slice().sort((a, b) => {
 const ra = parseFloat((a.rating || “”).split(“ “)[0]) || 0;
 const rb = parseFloat((b.rating || “”).split(“ “)[0]) || 0;
 return rb — ra;
})[0] || null;
const dashboard = [];
dashboard.push(“==================================”);
dashboard.push(“SIKKI MARKET DASHBOARD”);
dashboard.push(“==================================”);
dashboard.push(`Query: ${query}`);
dashboard.push(`Products Extracted: ${enriched.length}`);
dashboard.push(`Cheapest Product: ${cheapestObj ? `${cheapestObj.title} | ${cheapestObj.price}` : “N/A”}`);
dashboard.push(`Average Price: ${avgPriceVal === “N/A” ? “N/A” : `${avgPriceVal}`}`);
dashboard.push(`Best Rated: ${bestRatedObj ? `${bestRatedObj.title} | ${bestRatedObj.rating || “N/A”}` : “N/A”}`);
dashboard.push(“\nTop 5 Products:”);
enriched.slice(0, 5).forEach((p, i) => {
 dashboard.push(`${i + 1}. ${p.title} | ${p.price} | ${p.rating || “N/A”} | ${p.url || “N/A”}`);
});
fs.writeFileSync(dashboardPath, dashboard.join(“\n”), “utf8”);

下一步该做什么

在规划后续步骤时,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 需同时记录正常流程与异常恢复路径。重试机制、人工审核环节以及错误处理都是产品功能的一部分,而非后续需要补充的内容。 在执行成本较高的模型调用后设置检查点,以避免重试时重复处理相同工作。 在规划后续步骤时,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 应将此阶段视为输入与验证后输出之间的契约。为相关成果命名,明确成功判定标准,杜绝无声的半完成状态。

结论

总结而言,在修改代码之前,需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应在功能结果旁记录执行时间和成本。提前明确这些信息可避免在从演示环境过渡到共享环境时出现意外费用。应将规划与工具执行分开:规划者负责提出方案,执行者负责进行修改,验证者则根据目标检查结果。

操作检查清单

在制定操作检查清单时,同样需要在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。

应优先选择小型、可测试的单元,而非结构复杂的脚本。当某个步骤出现故障时,故障原因应能明确指向某个具体责任模块,而非整个复杂的流程体系。

在昂贵的模型调用之后设置检查点,这样重试时就不会重复计算相同的工作。

明确事件循环的边界:哪些内容会阻塞线程,哪些内容会在内核处等待。

编写简短的操作手册:包括如何轮换密钥、如何清空队列以及如何回滚最近的更改。

将配置信息置于应用程序代码之外。环境文件、密钥存储和功能开关应集中存放,以便操作人员无需查看整个系统结构即可进行审计。

在升级技术栈之前,先冻结现有版本,为关键流程记录完整的操作日志,并确认回滚步骤。共享环境需要设置速率限制、租户验证机制,以及明确的密钥轮换负责人。与其追求花哨的一次性演示,不如注重扎实的可靠性。