Files
writing-agent/tests/context-assembly.test.ts
moranzhi ec474cb946 完善创作节点循环与等待态交互,并大幅打磨 Web 壳与会话运行时。
- 节点循环:配方开局直坐 DAG 第一步;验收后先问下一步意向,再确认开干并可钉编排参数;「按意见修改」只重跑当前节点。
- 询问分流:能力 opening 走说话面引导,可跳过追问按题干去重;追问与产物同轮挂载,避免拆成两段历史。
- 运行时:补强 revision 重跑、创作流程合并/进度指针、工具循环与 worker 执行;新增运行日志与 web:watch。
- 前端:统一等待态文案与底栏主按钮,完善询问卡/产物验收/呈现壳样式与交互。
- 同步世界模拟器模块提示、编排文档与相关测试。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-17 01:28:12 +08:00

169 lines
5.5 KiB
TypeScript
Raw 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.
import { describe, expect, it } from "vitest";
import { Blackboard } from "../src/blackboard/blackboard.js";
import {
assembleWorkerContext,
parseContextSegments,
} from "../src/skills/context-segments.js";
import {
CREATION_ACCEPTED_CONTENT_TAG,
extractUnitContentFromDraft,
formatAcceptedContentForPrompt,
upsertAcceptedUnitContent,
} from "../src/skills/creation-units.js";
import type { ParsedWorkerSet } from "../src/skills/worker-set-parse.js";
import { loadWorkerSkill } from "../src/skills/loader.js";
describe("contextSegments assembly", () => {
it("parses segments from frontmatter shape", () => {
const segs = parseContextSegments([
{ id: "a", tier: "static", tags: ["创作.已验收内容"], label: "## 前情" },
{ id: "b", tier: "dynamic", tags: ["用户.需求"], policy: "latest" },
]);
expect(segs).toHaveLength(2);
expect(segs[0]?.label).toContain("前情");
});
it("assembles markdown with labels when segments present", () => {
const bb = new Blackboard();
bb.write({
tag: CREATION_ACCEPTED_CONTENT_TAG,
content: upsertAcceptedUnitContent(null, {
unitId: "phase:core",
content: { interaction: { user_stance: "单角" } },
summary: "单位 phase:core · 核心",
acceptedAt: "2026-01-01T00:00:00.000Z",
}),
source: "test",
});
bb.write({ tag: "用户.需求", content: "网恋对话", source: "test" });
bb.write({ tag: "创作.当前单位", content: "worker:narrator", source: "test" });
const text = assembleWorkerContext({
inputs: {
[CREATION_ACCEPTED_CONTENT_TAG]: bb.getContentByTag(CREATION_ACCEPTED_CONTENT_TAG)!,
"用户.需求": "网恋对话",
"创作.当前单位": "worker:narrator",
},
segments: [
{
id: "prior",
tier: "static",
tags: [CREATION_ACCEPTED_CONTENT_TAG],
label: "## 【前情提要 · 已定稿】",
},
{
id: "unit",
tier: "static",
tags: ["创作.当前单位"],
label: "## 【本单位】",
},
{
id: "user",
tier: "dynamic",
tags: ["用户.需求"],
label: "## 用户表述",
},
],
blackboard: bb,
workerId: "design-worker",
workerName: "B",
outputTags: ["设计.worker集.草稿"],
});
expect(text).toContain("【前情提要 · 已定稿】");
expect(text).toContain("phase:core");
expect(text).toContain("只读");
expect(text).toContain("【本单位】");
expect(text).toContain("worker:narrator");
expect(text).toContain("网恋对话");
expect(text).not.toMatch(/^\s*\{/);
});
it("revision mode asks the worker to apply sidecar answers and keep the draft", () => {
const bb = new Blackboard();
bb.write({
tag: "设计.美学纲领与交互范式",
content: '{"tone":"warm"}',
source: "worker",
});
const text = assembleWorkerContext({
inputs: {
"用户.修订说明": "再冷一点",
"用户.worker答复": "【追问作答】\n问偏好\n答偏冷",
"设计.美学纲领与交互范式": '{"tone":"warm"}',
},
segments: [
{
id: "user",
tier: "dynamic",
tags: ["用户.worker答复", "用户.修订说明"],
label: "## 用户表述",
},
],
blackboard: bb,
workerId: "design-step",
workerName: "创作 · 美学纲领与交互范式",
outputTags: ["设计.美学纲领与交互范式"],
});
expect(text).toContain("待改底稿");
expect(text).toContain("用户.worker答复");
expect(text).toContain("偏冷");
expect(text).toContain("再冷一点");
});
it("falls back to JSON inputs without segments", () => {
const bb = new Blackboard();
const text = assembleWorkerContext({
inputs: { "用户.需求": "x" },
segments: [],
blackboard: bb,
workerId: "x",
workerName: "x",
outputTags: [],
});
expect(JSON.parse(text).inputs["用户.需求"]).toBe("x");
});
it("design-flow skill loads contextSegments", async () => {
const w = await loadWorkerSkill("world-simulator", "design-flow");
expect(w.contextSegments?.length).toBeGreaterThan(0);
expect(w.outputTags).toContain("设计.创作流程");
});
});
describe("accepted unit content", () => {
it("extracts worker slice and upserts last accept", () => {
const parsed: ParsedWorkerSet = {
interaction: {
user_stance: "单角",
system_role: "世界",
output: "叙事",
},
workers: [
{ ref: "narrator", duty: "转述", rationale: "可读", acceptance: "review" },
],
};
expect(extractUnitContentFromDraft(parsed, "phase:core")).toMatchObject({
interaction: { user_stance: "单角" },
});
expect(extractUnitContentFromDraft(parsed, "worker:narrator")).toMatchObject({
ref: "narrator",
duty: "转述",
});
const v1 = upsertAcceptedUnitContent(null, {
unitId: "worker:narrator",
content: { ref: "narrator", duty: "旧" },
acceptedAt: "2026-01-01T00:00:00.000Z",
});
const v2 = upsertAcceptedUnitContent(v1, {
unitId: "worker:narrator",
content: { ref: "narrator", duty: "新" },
acceptedAt: "2026-01-02T00:00:00.000Z",
});
const prompt = formatAcceptedContentForPrompt(v2);
expect(prompt).toContain("新");
expect(prompt).not.toContain('"duty": "旧"');
});
});