import assert from "node:assert/strict"; import { readFileSync, writeFileSync } from "node:fs"; import { forwardMixtures, forwardMergedRange, toImprovement } from "../../src/rules/index.ts"; import { readRun, type J } from "./py.ts"; // 只读复核选批记录的合并分位数;不调用模型或启动研究。 // node eval/route/verify-ranges.ts <输出json> const [mapPath, output] = process.argv.slice(2); if (mapPath === undefined || output === undefined) throw new Error("需要运行映射和输出路径"); const runs = Object.keys(JSON.parse(readFileSync(mapPath, "utf8"))); const results = runs.map((run) => { const rows = readRun(run); const batch = rows.find(([kind, c]) => kind === "decision" && c.decision === "batch")?.[1]; if (batch === undefined) return { run, status: "无第1轮" }; const predictions = rows.filter(([kind, c]) => kind === "submission" && c.kind === "secondPrediction").map(([, c]) => c); const candidates = batch.output.candidates.map((c: J) => ({ id: c.id, ranges: predictions.filter((p) => p.target === c.id).map((p) => ({ researcher: p.author, range: p.content.range })), })); // 读取选批输入里的基线,不由预测反推。 const base = batch.input.baseline; assert.equal(typeof base, "number", "选批输入应保存基线分"); const mixtures = forwardMixtures(candidates, batch.output.params.lambda); const checked = candidates.map((c: J, i: number) => { const computed = forwardMergedRange(toImprovement(mixtures[i]!, base, "maximize"), base, "maximize"); const recorded = batch.output.candidates[i].merged; assert.deepEqual(computed, recorded); return { id: c.id, ranges: c.ranges, recorded, computed, equal: true }; }); return { run, baseline: base, lambda: batch.output.params.lambda, candidates: checked }; }); writeFileSync(output, JSON.stringify(results, null, 2));