完善配方驱动的创作编排

为可重复技能补充参数校验与展示,统一配方、编排器和执行单元术语。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
moran
2026-07-30 17:59:20 +08:00
parent e670a5129c
commit 6392af54b4
49 changed files with 1671 additions and 626 deletions

View File

@@ -186,10 +186,23 @@ describe("creation-flow", () => {
expect(
catalog?.modules.find((m) => m.name === "具体实例")?.repeatable,
).toBe(true);
expect(
catalog?.modules.find((m) => m.name === "生成规则")?.params?.[0]?.key,
).toBe("target");
expect(
catalog?.modules.find((m) => m.name === "具体实例")?.params?.[0]?.key,
).toBe("rule_id");
const gen = catalog?.modules.find((m) => m.name === "生成规则");
expect(gen?.when).toBeTruthy();
expect(gen?.when_not).toBeTruthy();
expect(gen?.boundary).toBeTruthy();
const block = formatModuleCatalogForAgent(catalog!);
expect(block).toContain("【能力");
expect(block).toContain("美学纲领与交互范式:");
expect(block).toContain("生成规则〔可反复〕");
expect(block).toContain("何时用");
expect(block).toContain("何时不用");
expect(block).toContain("编排参数");
expect(block).not.toContain("设计.美学纲领与交互范式");
});
@@ -203,15 +216,20 @@ describe("creation-flow", () => {
const details = await loadAllRecipeDetails("dialogue/world-simulator");
expect(details.length).toBeGreaterThanOrEqual(2);
expect(details.find((d) => d.id === "world-simulator")?.when).toBeTruthy();
const ws = details.find((d) => d.id === "world-simulator");
expect(ws?.when).toBeTruthy();
expect(ws?.core).toBeTruthy();
expect(ws?.process).toBeTruthy();
expect(ws?.principles).toBeTruthy();
expect(
details
.find((d) => d.id === "world-simulator")
?.seed?.steps.some((s) => s.name === "美学纲领与交互范式"),
ws?.seed?.steps.some((s) => s.name === "美学纲领与交互范式"),
).toBe(true);
expect(details.find((d) => d.id === "world-simulator")?.seed?.status).toBe(
"open",
);
expect(ws?.seed?.status).toBe("open");
const formatted = formatSelectedRecipeForAgent(ws!);
expect(formatted).toContain("核心思路");
expect(formatted).toContain("设计流程");
expect(formatted).toContain("原则");
expect(formatted).not.toContain("调味提示");
});
it("parses selected recipe ref", () => {
@@ -222,11 +240,16 @@ describe("creation-flow", () => {
expect(parseSelectedRecipeRef("")).toBeNull();
});
it("parses recipe yaml with suggested steps", () => {
it("parses recipe yaml with methodology fields", () => {
const detail = parseRecipeYaml(
`
when: 测试适用
hint: 可调味
core: 核心一句话
process:
- 先美学
- 再按缺口选型
principles:
- 正推
brief: 测试 brief
steps:
- name: 美学纲领与交互范式
@@ -235,6 +258,9 @@ steps:
{ id: "t", name: "测试配方", declaration: "测" },
);
expect(detail.when).toBe("测试适用");
expect(detail.core).toContain("核心一句话");
expect(detail.process).toEqual(["先美学", "再按缺口选型"]);
expect(detail.principles).toEqual(["正推"]);
expect(detail.seed?.status).toBe("open");
expect(detail.seed?.steps).toEqual([
{
@@ -243,10 +269,26 @@ steps:
depends_on: [],
},
]);
expect(formatSelectedRecipeForAgent(detail)).toContain("用户已选导演");
expect(formatSelectedRecipeForAgent(detail)).toContain("增量");
const formatted = formatSelectedRecipeForAgent(detail);
expect(formatted).toContain("用户已选配方");
expect(formatted).toContain("核心思路");
expect(formatted).toContain("增量");
});
it("falls back to legacy hint when methodology absent", () => {
const detail = parseRecipeYaml(
`
when: 旧配方
hint: 可调味
steps:
- name: 美学纲领与交互范式
depends_on: []
`,
{ id: "legacy", name: "旧", declaration: "测" },
);
expect(detail.hint).toBe("可调味");
expect(formatSelectedRecipeForAgent(detail)).toContain("调味提示(旧字段)");
});
it("parseRecipeCatalog skips incomplete rows", () => {
const cat = parseRecipeCatalog(`
recipes:
@@ -277,10 +319,12 @@ recipes:
{ selectedRecipeRef: "world-simulator" },
);
expect(selected.worker.outputTags).toContain("设计.创作流程");
expect(selected.promptBody).toContain("【用户已选导演 · 世界模拟器】");
expect(selected.promptBody).toContain("【用户已选配方 · 世界模拟器】");
expect(selected.promptBody).toContain("世界模拟器");
expect(selected.promptBody).toContain("【能力");
expect(selected.promptBody).toContain("美学纲领与交互范式");
expect(selected.promptBody).toContain("核心思路");
expect(selected.promptBody).toContain("何时用");
expect(selected.promptBody).toContain("增量");
expect(selected.promptBody).not.toContain("【导演】用户尚未手动选择");
});
@@ -354,6 +398,72 @@ recipes:
expect(binding?.modulePrompt).not.toContain("最想反复感受到的是什么");
});
it("parses and validates step params from catalog", async () => {
const flow = parseCreationFlow(`{
"status": "open",
"steps": [
{
"id": "生成规则·怪物",
"name": "生成规则",
"params": { "target": "怪物", "lifecycle_intent": "runtime_only" },
"depends_on": []
}
]
}`)!;
expect(flow.steps[0]?.params).toEqual({
target: "怪物",
lifecycle_intent: "runtime_only",
});
const view = formatCreationFlowForUser(flow);
expect(view.steps[0]?.params?.target).toBe("怪物");
const catalog = await loadModuleCatalog("dialogue/world-simulator");
expect(
catalog?.modules.find((m) => m.name === "生成规则")?.params?.some(
(p) => p.key === "target" && p.required,
),
).toBe(true);
expect(validateCreationFlow(flow, catalog).ok).toBe(true);
const missing = parseCreationFlow(`{
"steps": [{ "name": "生成规则", "depends_on": [] }]
}`)!;
const bad = validateCreationFlow(missing, catalog);
expect(bad.ok).toBe(false);
expect(bad.errors.some((e) => e.includes("target"))).toBe(true);
const block = formatModuleCatalogForAgent(catalog!);
expect(block).toContain("编排参数");
expect(block).toContain("target");
});
it("injects step params into design-step prompt", async () => {
const flowRaw = JSON.stringify({
steps: [
{
id: "生成规则·怪物",
name: "生成规则",
params: { target: "怪物", rule_id: "monsters" },
depends_on: [],
},
],
});
const loaded = await loadWorkerSkillWithContext(
"world-simulator",
"design-step",
undefined,
{
flowRaw,
currentStepName: "生成规则·怪物",
acceptedStepNames: [],
},
);
expect(loaded.promptBody).toContain("【本步参数】");
expect(loaded.promptBody).toContain("target: 怪物");
expect(loaded.promptBody).toContain("rule_id: monsters");
expect(loaded.worker.outputTags).toContain("设计.生成规则");
});
it("nextPendingStep respects deps and accepted by id", () => {
const flow = parseCreationFlow(`{
"status": "open",

View File

@@ -21,10 +21,10 @@ describe("display-labels", () => {
it("maps creation unit ids", () => {
expect(displayWorkerLabel("phase:core")).toBe("单位 · 核心");
expect(displayWorkerLabel("fixed:interaction")).toBe("能 · 交互范式");
expect(displayWorkerLabel("fixed:interaction")).toBe("能 · 交互范式");
expect(displayWorkerLabel("fixed:aesthetics-interaction")).toBe(
"能 · 美学纲领与交互范式",
"能 · 美学纲领与交互范式",
);
expect(displayWorkerLabel("worker:narrator")).toBe("演员 · 叙事转述");
expect(displayWorkerLabel("worker:narrator")).toBe("执行单元 · 叙事转述");
});
});