#!/usr/bin/env tsx /** * Priority1 十组随机数字批量询价 — 输出填写内容与报价,便于人工对照官网 * * npm run benchmark:priority1-10cases * $env:PRIORITY1_BATCH_SEED="42"; npm run benchmark:priority1-10cases */ import { randomUUID } from "node:crypto"; import { mkdirSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { formatPriority1InputSummary, generateRandomPriority1Cases, } from "@/workers/rpa/priority1/demo-input"; import { formatPriority1QuotesTable } from "@/workers/rpa/priority1/quote-extract"; import { runPriority1VisualChain } from "./visual-priority1-full-chain"; process.env.RPA_HEADLESS = process.env.RPA_HEADLESS ?? "true"; process.env.RPA_KEEP_BROWSER_OPEN = "false"; process.env.RPA_SLOW_MO_MS = process.env.RPA_SLOW_MO_MS ?? "0"; process.env.RPA_DWELL_MS = process.env.RPA_DWELL_MS ?? "0"; process.env.RPA_QUOTE_WAIT_MS = process.env.RPA_QUOTE_WAIT_MS ?? "120000"; const CASE_COUNT = Math.max(1, Number(process.env.PRIORITY1_BATCH_COUNT ?? 10)); const SEED = Number(process.env.PRIORITY1_BATCH_SEED ?? Date.now()); function formatCaseReport( index: number, result: Awaited>, ): string { const lines: string[] = []; lines.push(`\n${"═".repeat(60)}`); lines.push(`第 ${index + 1} 组 / ${result.caseId} ${result.ok ? "✓ 成功" : "✗ 失败"}`); lines.push("─".repeat(60)); lines.push("【填写内容】"); lines.push(formatPriority1InputSummary(result.demo)); if (result.error) { lines.push(`\n【错误】${result.error}`); } lines.push("\n【页面可见报价】(与官网卡片一致;全量 API 见 quotes.json → allApi)"); lines.push(formatPriority1QuotesTable(result.quotes)); if (result.quotes.length > 0) { lines.push("\n【明细】"); for (const q of result.quotes) { lines.push( ` ${q.rank}. ${q.carrier} (${q.carrierCode || "-"}) $${q.totalUsd.toFixed(2)} ${q.transitDays}天 ${q.serviceLevel || "-"} [${q.source}]`, ); if (q.caboUrl) lines.push(` 详情: ${q.caboUrl}`); } } lines.push(`\n耗时 ${(result.durationMs / 1000).toFixed(1)}s | 截图目录: ${result.outDir}`); return lines.join("\n"); } async function main(): Promise { const runId = randomUUID().slice(0, 8); const batchDir = join(process.cwd(), ".rpa", "visual-priority1-batch", runId); mkdirSync(batchDir, { recursive: true }); const cases = generateRandomPriority1Cases(CASE_COUNT, SEED); const seedPath = join(batchDir, "cases-input.json"); writeFileSync( seedPath, `${JSON.stringify({ runId, seed: SEED, count: CASE_COUNT, cases }, null, 2)}\n`, "utf8", ); console.log(`\n[batch-p1] 十组随机询价 batch=${runId} seed=${SEED}`); console.log(`[batch-p1] 用例输入: ${seedPath}`); console.log(`[batch-p1] 无头=${process.env.RPA_HEADLESS} 报价等待=${process.env.RPA_QUOTE_WAIT_MS}ms\n`); const results: Awaited>[] = []; for (let i = 0; i < cases.length; i += 1) { const c = cases[i]!; console.log(`\n[batch-p1] ▶ 开始 ${c.id} (${i + 1}/${cases.length})`); console.log(formatPriority1InputSummary(c.input)); const result = await runPriority1VisualChain({ demo: c.input, caseId: c.id, runId, outDir: join(batchDir, c.id), keepBrowserOpen: false, }); results.push(result); console.log(formatCaseReport(i, result)); } const pass = results.filter((r) => r.ok && r.quoteCount > 0).length; const summaryPath = join(batchDir, "batch-summary.json"); const summaryMdPath = join(batchDir, "batch-summary.md"); const summary = { runId, seed: SEED, count: CASE_COUNT, pass, fail: CASE_COUNT - pass, finishedAt: new Date().toISOString(), cases: results.map((r) => ({ caseId: r.caseId, ok: r.ok, quoteCount: r.quoteCount, durationMs: r.durationMs, error: r.error, input: r.demo, quotes: r.quotes, outDir: r.outDir, })), }; writeFileSync(summaryPath, `${JSON.stringify(summary, null, 2)}\n`, "utf8"); const mdLines = [ `# Priority1 批量询价对照表`, ``, `- 批次: \`${runId}\``, `- 随机种子: \`${SEED}\`(复现: \`$env:PRIORITY1_BATCH_SEED="${SEED}"\`)`, `- 通过: ${pass}/${CASE_COUNT}`, ``, `请到 [Priority1 公开询价页](https://www.priority1.com/instant-freight-quotes/) 手工填入相同数据核对报价。`, ``, ]; for (let i = 0; i < results.length; i += 1) { const r = results[i]!; mdLines.push(`## ${r.caseId} ${r.ok ? "✓" : "✗"}`); mdLines.push(``); mdLines.push("### 填写内容"); mdLines.push("```"); mdLines.push(formatPriority1InputSummary(r.demo)); mdLines.push("```"); mdLines.push(``); mdLines.push("### 系统提取报价"); if (r.quotes.length === 0) { mdLines.push(r.error ? `失败: ${r.error}` : "(无报价)"); } else { mdLines.push("| # | 承运商 | 金额 USD | 时效 | 来源 |"); mdLines.push("|---|--------|----------|------|------|"); for (const q of r.quotes) { mdLines.push( `| ${q.rank} | ${q.carrier} | $${q.totalUsd.toFixed(2)} | ${q.transitDays}天 | ${q.source} |`, ); } } mdLines.push(``); mdLines.push(`截图: \`${r.outDir}\``); mdLines.push(``); } writeFileSync(summaryMdPath, `${mdLines.join("\n")}\n`, "utf8"); console.log(`\n${"═".repeat(60)}`); console.log(`[batch-p1] 完成 ${pass}/${CASE_COUNT} 组成功提取报价`); console.log(`[batch-p1] JSON 汇总: ${summaryPath}`); console.log(`[batch-p1] Markdown 对照: ${summaryMdPath}`); process.exit(pass === CASE_COUNT ? 0 : 1); } main().catch((e) => { console.error(e); process.exit(1); });