#!/usr/bin/env node /** * R6 H4-2 现场演示:中途改任务 amend(真实 LLM 编排)。 * run 启动后并发轮询 plan.json:一旦计划落盘,定位 README 写作子任务(按 expectedFile 匹配, * 不硬编码 st 编号——planner 每次编号可能不同),在该子任务被派发前投递 amend; * 引擎在派发它之前由真实 LLM 把改动合并成修正指令再派发。 * 结束校验:①收件箱状态 applied ②会话流含 amend 消息 ③该子任务最终产物确实反映改动。 * 用法:npm run amend-demo */ import { mkdirSync, readFileSync, readdirSync, existsSync } from "node:fs"; import { resolve, dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; import { TeamOrchestrator } from "../src/team/orchestrate.js"; import { HandoffInbox } from "../src/team/conversation.js"; const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); const GOAL = [ "用 Node.js(零第三方依赖)做一个 Markdown 词频统计命令行小工具,团队分工完成三件事:", "①编写可运行的实现脚本 wordcount.mjs:读取 .md 文件,按词统计中文/英文词频,支持 --top=N 参数输出前 N 个高频词,并能对 samples 目录里的样例真实跑出结果;", "②编写 README.md:含功能说明、用法示例(node wordcount.mjs --top=5 samples/sample.txt)、输出格式说明;", "③准备 samples/sample.txt:一份用于演示的 Markdown 样例文本(含若干重复词)。", ].join(""); const AMEND = "请在 README.md 中额外增加一个『故障排查(FAQ)』小节,至少包含:遇到 429 限流时应如何指数退避重试。"; const orch = new TeamOrchestrator({ root: ROOT }); const runId = `run-amend-${Date.now().toString(36)}`; const evDir = resolve(ROOT, "evidence", "team-orch", runId); mkdirSync(evDir, { recursive: true }); // 并发:一边跑编排,一边等 plan.json 落盘后精准投递 amend 到 README 子任务 let amendReq = null; const watcher = (async () => { const planFile = join(evDir, "plan.json"); for (let i = 0; i < 600; i++) { if (existsSync(planFile)) { try { const plan = JSON.parse(readFileSync(planFile, "utf8")); const readmeSub = plan.subtasks.find((s) => /readme/i.test(String(s.expectedFile)) || /README/.test(String(s.expectedArtifact))); if (readmeSub) { const inbox = new HandoffInbox(evDir); amendReq = inbox.add({ kind: "amend", subtaskId: readmeSub.id, change: AMEND, from: "amend-demo" }); console.log(`[amend-demo] plan 落盘,定位 README 子任务 ${readmeSub.id},已投递 ${amendReq.id}:${AMEND}`); return; } } catch { /* plan 可能写一半,下轮再试 */ } } await sleep(1000); } console.log("[amend-demo] 警告:未能在时限内定位 README 子任务投递 amend"); })(); const t0 = Date.now(); const result = await orch.run(GOAL, { runId }); await watcher; const wall = Date.now() - t0; // ---- 验收 ---- const problems = []; const targetSid = amendReq?.subtaskId; if (!amendReq) problems.push("amend 未投递(未定位到 README 子任务)"); const inboxAfter = new HandoffInbox(evDir); const rec = inboxAfter.items.find((x) => x.id === amendReq?.id); if (rec?.status !== "applied") problems.push(`amend ${amendReq?.id} 未被消费(status=${rec?.status})`); const conv = JSON.parse(readFileSync(join(evDir, "conversation.json"), "utf8")); if (!conv.some((m) => m.kind === "amend")) problems.push("会话流缺少 amend 消息"); let reflected = false; const scanned = []; if (targetSid) { const dir = resolve(ROOT, "out", runId, targetSid); if (existsSync(dir)) { for (const f of readdirSync(dir)) { scanned.push(`${targetSid}/${f}`); if (/故障排查|FAQ|429/.test(readFileSync(join(dir, f), "utf8"))) reflected = true; } } } if (!reflected) problems.push(`README 子任务产物未反映 amend(扫描:${scanned.join(",") || "无"})`); console.log("\n===== amend-demo 验收 ====="); console.log(`runId=${runId} verdict=${result.verdict}/${result.finalScore} 耗时=${wall}ms;amend 目标=${targetSid}`); console.log(`amend 状态=${rec?.status};LLM 修正指令=${rec?.correctedInstruction || rec?.summary || "(无)"}`); console.log(`扫描产物:${scanned.join(", ") || "无"};改动已反映=${reflected}`); if (problems.length) { console.error("[amend-demo] 未闭环:\n - " + problems.join("\n - ")); process.exit(2); } console.log("[amend-demo] 闭环成功 EXIT=0(amend 被真实接收并反映到最终产物)"); process.exit(0);