#!/usr/bin/env tsx /** * FTL + Expedited 各 3 组随机询价测试 * npm run benchmark:priority1-ftl-expedited */ import { randomUUID } from "node:crypto"; import { mkdirSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { formatPriority1InputSummary, generateRandomExpeditedCases, generateRandomFtlCases, } 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 PER_TYPE = Math.max(1, Number(process.env.PRIORITY1_BATCH_COUNT ?? 3)); const SEED = Number(process.env.PRIORITY1_BATCH_SEED ?? Date.now()); async function main(): Promise { const runId = randomUUID().slice(0, 8); const batchDir = join(process.cwd(), ".rpa", "visual-priority1-ftl-exp", runId); mkdirSync(batchDir, { recursive: true }); const ftlCases = generateRandomFtlCases(PER_TYPE, SEED); const expCases = generateRandomExpeditedCases(PER_TYPE, SEED + 1); const allCases = [...ftlCases, ...expCases]; writeFileSync( join(batchDir, "cases-input.json"), `${JSON.stringify({ runId, seed: SEED, ftlCases, expCases }, null, 2)}\n`, "utf8", ); console.log(`\n[batch-p1-fe] FTL×${PER_TYPE} + Expedited×${PER_TYPE} batch=${runId} seed=${SEED}`); const results: Awaited>[] = []; for (let i = 0; i < allCases.length; i += 1) { const c = allCases[i]!; console.log(`\n[batch-p1-fe] ▶ ${c.id} (${c.input.shipmentType})`); 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(`\n${result.ok ? "✓" : "✗"} ${c.id}`); console.log(formatPriority1QuotesTable(result.quotes)); if (result.error) console.log(`错误: ${result.error}`); } const pass = results.filter((r) => r.ok && r.quoteCount > 0).length; const summary = { runId, seed: SEED, perType: PER_TYPE, pass, fail: allCases.length - pass, finishedAt: new Date().toISOString(), cases: results.map((r) => ({ caseId: r.caseId, shipmentType: r.demo.shipmentType, ok: r.ok, quoteCount: r.quoteCount, error: r.error, input: r.demo, quotes: r.quotes, outDir: r.outDir, })), }; const summaryPath = join(batchDir, "batch-summary.json"); writeFileSync(summaryPath, `${JSON.stringify(summary, null, 2)}\n`, "utf8"); const md: string[] = [ `# Priority1 FTL / Expedited 批量对照`, ``, `- 批次: \`${runId}\``, `- 通过: ${pass}/${allCases.length}`, ``, ]; for (const r of results) { md.push(`## ${r.caseId} (${r.demo.shipmentType}) ${r.ok ? "✓" : "✗"}`); md.push("```"); md.push(formatPriority1InputSummary(r.demo)); md.push("```"); if (r.quotes.length) { md.push("| # | 承运商 | 金额 | 来源 |"); md.push("|---|--------|------|------|"); for (const q of r.quotes) { md.push(`| ${q.rank} | ${q.carrier} | $${q.totalUsd.toFixed(2)} | ${q.source} |`); } } else { md.push(r.error ?? "(无报价)"); } md.push(""); } writeFileSync(join(batchDir, "batch-summary.md"), `${md.join("\n")}\n`, "utf8"); console.log(`\n[batch-p1-fe] 完成 ${pass}/${allCases.length}`); console.log(`[batch-p1-fe] ${summaryPath}`); process.exit(pass === allCases.length ? 0 : 1); } main().catch((e) => { console.error(e); process.exit(1); });