scheduler-gateway/scripts/team-amend-demo.mjs
Liuxinyu176 bbb364cd69 feat: DSH 调度网关(互联网关)v6.0.0 — 四模型合并版 R1+R2+R3
- 零第三方依赖,Node >=20 原生 ESM
- 团队式编排引擎:拆解/路由/执行/审查/合并全真实 LLM
- 阶段心跳、单一权威清单守卫、all-keys-failed 如实上报
- H4 会话视图/amend/watchdog 有界重试/产物区 artifacts.json
- H5 零依赖三栏控制台
2026-10-09 23:20:27 +08:00

90 lines
4.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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);