import { readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { entriesIn, entryDecision, ofKind, readLedger, sameAssignment, stages, type Assignment } from "../lib/ledger.ts"; import { replayModels, startReplay, type ReplayScript } from "../lib/replay.ts"; import { launch, newEvalDir, writeConfig } from "../lib/study.ts"; // 把参考项目 16-real-1 的第零轮原样交给新 flow 回放一遍(任务书 12 第 5.3 节):基线照样用真 Docker 跑, // 三位研究者由回放假模型按当年的先后交提交。跑完核对分配是否和当年完全相同,列出被退回的提交和没交出去的提交。 // node eval/round-zero/replay.ts <名字> // 研究在 <检验目录>/study;之后用 quality.ts 打分。 type Script = ReplayScript & { expected: Assignment[]; expectedEntries: string[] }; const name = process.argv[2]; if (name === undefined) throw new Error("要写检验名字,例如 r0-replay-16"); const script = JSON.parse(readFileSync("eval/round-zero/replay-16-real-1.json", "utf8")) as Script; const out = newEvalDir(name); const replay = await startReplay(script, join(out, "pi-agent"), 0); const researchers = Object.keys(script.models).sort(); const config = writeConfig("eval/round-zero/research.json", join(out, "research.json"), name, replayModels(researchers, "jev-latest")); const studyDir = join(out, "study"); const { outcome } = await launch(config, studyDir, replay.env).done; await replay.close(); const records = readLedger(studyDir); const decision = entryDecision(records); const got = decision === undefined ? null : decision.output; const entries = entriesIn(records, new Set(stages(records).filter((s) => s.round === 0).map((s) => s.stageId))).map((e) => e.id); // 入口全体按编号比(账本里按各人最后一版清单的先后排,当年的清单按编号排);分配按研究者、入口、志愿三样比,不看对象里键的先后。 const sameEntries = JSON.stringify([...entries].sort()) === JSON.stringify([...script.expectedEntries].sort()); const same = got !== null && sameAssignment(got, script.expected); const report = { outcome, assignment: got, expected: script.expected, sameAsReference: same, entries, expectedEntries: script.expectedEntries, sameEntriesAsReference: sameEntries, rejections: ofKind(records, "rejection").map((r) => ({ seq: r.seq, stageId: r.content.stageId, author: r.content.author, kind: r.content.kind, reason: r.content.reason })), delivered: replay.delivered, notDelivered: replay.remaining(), }; writeFileSync(join(out, "replay-report.json"), `${JSON.stringify(report, null, 2)}\n`); console.log(`\n结局:${JSON.stringify(outcome)}`); console.log(`分配:${got === null ? "没有写出分配决定" : got.map((a) => `${a.researcher} → ${a.entry}(第 ${a.rank} 志愿)`).join(";")}`); console.log(`当年:${script.expected.map((a) => `${a.researcher} → ${a.entry}(第 ${a.rank} 志愿)`).join(";")}`); console.log(`分配和当年相同:${same ? "是" : "否"};入口全体和当年相同:${sameEntries ? "是" : "否"}(${entries.join("、")})`); console.log(`退回 ${report.rejections.length} 份${report.rejections.map((r) => `\n ${r.author} ${r.stageId} ${r.kind}:${r.reason}`).join("")}`); console.log(`没交出去的:${JSON.stringify(report.notDelivered)}`); console.log(`报告:${join(out, "replay-report.json")};研究目录:${studyDir}`);