You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
chajia/scripts/benchmark-priority1-10cases.ts

166 lines
5.7 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/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<ReturnType<typeof runPriority1VisualChain>>,
): 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<void> {
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<ReturnType<typeof runPriority1VisualChain>>[] = [];
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);
});