重构世界模拟器为模块化配方架构,完善创作编排、会话运行时与 Web UI,并清理过时技能。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-30 00:39:32 +08:00
parent 2b74c30d36
commit e670a5129c
167 changed files with 22955 additions and 5659 deletions

View File

@@ -0,0 +1,162 @@
import { describe, expect, it } from "vitest";
import { parseWorkerSetYaml } from "../src/skills/worker-set-parse.js";
import { formatWorkerSetForUser } from "../src/skills/worker-set-view.js";
const FULL_SAMPLE = `
version: 1
form_summary: 西幻升级交互,带状态跟踪
play_morphology: action_reaction_loop
workers:
- ref: world-simulator
duty: 世界推进与裁决
when: 每轮用户输入后
context:
static:
- 设计.worker集
- 世界.蓝图.确认稿
dynamic:
- 用户.最新输入
- 运行.事件流
outputs:
- 运行.本轮.裁决
- 运行.事件流
- ref: narrator
duty: 组装用户可见回复
when: 中间产物齐后
presentation:
tone: 西幻
avoid: [空泛公告]
context:
static:
- 叙事.指南.确认稿
dynamic:
- 运行.事件流
outputs:
- 输出.用户展示
instantiate_hints:
invoke: [opening-generator]
tag_flow:
- "用户.最新输入 → 运行.本轮.裁决"
- "运行.本轮.裁决 → 输出.用户展示"
`;
describe("formatWorkerSetForUser", () => {
it("builds Chinese worker cards with context order", () => {
const parsed = parseWorkerSetYaml(FULL_SAMPLE)!;
const view = formatWorkerSetForUser(parsed)!;
expect(view.headline).toContain("西幻");
expect(view.playModeLabel).toBe("行动–反应循环");
expect(view.workers).toHaveLength(2);
expect(view.workers[0].displayName).toBe("世界模拟");
expect(view.creationUnits?.map((u) => u.id)).toEqual(
expect.arrayContaining([
"fixed:aesthetics", // sample 含 narrator.presentation
"worker:world-simulator",
"worker:narrator",
]),
);
// 未写入的示例话题不出现(不是填空表)
expect(view.creationUnits?.some((u) => u.id === "fixed:input_protocol")).toBe(
false,
);
expect(view.workers[0].context.staticTags[0].tag).toBe("设计.worker集");
expect(view.workers[0].writes).toEqual(["运行.本轮.裁决", "运行.事件流"]);
expect(view.workers[0].context.explicit).toBe(true);
expect(view.workers[1].presentation?.[0].label).toBe("语气");
expect(view.tagFlow).toHaveLength(2);
expect(view.designTasks).toHaveLength(1);
expect(view.designTasks[0].id).toBe("opening-generator");
});
it("exposes contextTags as standalone cards with mount targets", () => {
const parsed = parseWorkerSetYaml(`
narrative_guide: 残酷、不有求必应
resident_context:
- id: tone
content: 文风克制
mount: [narrator]
workers:
- ref: world-simulator
duty: 裁决
- ref: narrator
duty: 转述
presentation:
tone: 冷峻
`)!;
const view = formatWorkerSetForUser(parsed)!;
expect(view.contextTags?.length).toBeGreaterThanOrEqual(2);
const guide = view.contextTags?.find((c) => c.id === "fixed:narrative_guide");
expect(guide?.mountSummary).toBe("全部 Worker");
expect(guide?.mounts.map((m) => m.workerId).sort()).toEqual([
"narrator",
"world-simulator",
]);
const tone = view.contextTags?.find((c) => c.id === "resident:tone");
expect(tone?.mounts).toHaveLength(1);
expect(tone?.mounts[0].workerId).toBe("narrator");
expect(tone?.mountSummary).toBe(tone!.mounts[0].workerName);
const aesthetics = view.contextTags?.find((c) =>
c.id.startsWith("fixed:aesthetics"),
);
expect(aesthetics?.mounts[0].workerId).toBe("narrator");
expect(view.workers[1].readsSummary).toMatch(/美学|叙事|常驻/);
});
it("surfaces interaction paradigm, core worker, and rationale", () => {
const parsed = parseWorkerSetYaml(`
form_summary: 囚徒困境旁观
interaction_paradigm: 旁观多角(推断)
core_worker: world-simulator
reasoning: 三个角色需信息隔绝,各用 role-decide陈述用 Markdown不必文学转述。
workers:
- ref: world-simulator
role: core
rationale: 裁决组合结果,兼任结构化陈述。
- ref: role-decide
role: auxiliary
rationale: 仅 3 个参与者,隔绝决策上下文。
merge_considered: 不为每个 NPC 各开一个 worker。
`)!;
const view = formatWorkerSetForUser(parsed)!;
expect(view.interactionParadigm).toContain("旁观");
expect(view.coreWorker).toBe("world-simulator");
expect(view.reasoning).toContain("信息隔绝");
expect(view.workers[0].roleLabel).toBe("核心");
expect(view.workers[0].rationale).toContain("裁决");
expect(view.workers[1].mergeConsidered).toContain("NPC");
});
it("merges default context when explicit context omitted", () => {
const parsed = parseWorkerSetYaml(`
workers:
- ref: variable-update
duty: 跟踪变量
when: 世界机之后
`)!;
const view = formatWorkerSetForUser(parsed)!;
expect(view.workers[0].context.staticTags.some((t) => t.tag.includes("变量.目录"))).toBe(
true,
);
expect(view.workers[0].context.explicit).toBe(false);
expect(view.workers[0].writes).toContain("变量.当前");
});
it("marks design tasks filled when blackboard has tags", () => {
const parsed = parseWorkerSetYaml(`
workers:
- ref: narrator
instantiate_hints:
invoke: [opening-generator]
`)!;
const view = formatWorkerSetForUser(parsed, {
filledTags: ["运行.初始变量", "输出.开场白"],
})!;
expect(view.designTasks[0].status).toBe("filled");
});
it("parses context and outputs fields", () => {
const parsed = parseWorkerSetYaml(FULL_SAMPLE)!;
expect(parsed.workers[0].context?.static).toContain("世界.蓝图.确认稿");
expect(parsed.workers[0].outputs).toContain("运行.事件流");
});
});