重构世界模拟器为模块化配方架构,完善创作编排、会话运行时与 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,165 @@
/**
* Worker contextSegments 拼装:按 tier 顺序把黑板 tag 拼成 Markdown。
* 见 docs/context-assembly.md
*/
import type { Blackboard } from "../blackboard/blackboard.js";
import type { BlackboardInputMerge } from "../types/blackboard.js";
import { CONTEXT_BRIEF_TAG } from "../runtime/compress-after-worker.js";
import {
CREATION_ACCEPTED_CONTENT_TAG,
formatAcceptedContentForPrompt,
} from "./creation-units.js";
export type ContextSegmentTier = "static" | "dynamic";
export type ContextSegmentDef = {
id: string;
tier: ContextSegmentTier;
tags: string[];
label?: string;
/** latest | concat | tail_lines_N */
policy?: string;
};
export function parseContextSegments(raw: unknown): ContextSegmentDef[] {
if (!Array.isArray(raw)) return [];
const out: ContextSegmentDef[] = [];
for (const row of raw) {
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
const r = row as Record<string, unknown>;
const id = typeof r.id === "string" ? r.id.trim() : "";
const tier = r.tier === "dynamic" ? "dynamic" : "static";
const tags = Array.isArray(r.tags)
? r.tags.filter((t): t is string => typeof t === "string" && t.trim()).map((t) => t.trim())
: [];
if (!id || tags.length === 0) continue;
out.push({
id,
tier,
tags,
label: typeof r.label === "string" ? r.label.trim() : undefined,
policy: typeof r.policy === "string" ? r.policy.trim() : undefined,
});
}
return out;
}
function applyPolicy(content: string, policy?: string): string {
if (!policy || policy === "latest" || policy === "concat") return content;
const m = /^tail_lines_(\d+)$/.exec(policy);
if (m) {
const n = Number(m[1]);
if (Number.isFinite(n) && n > 0) {
const lines = content.split(/\r?\n/);
return lines.slice(-n).join("\n");
}
}
return content;
}
function readTagContent(
tag: string,
inputs: Record<string, string>,
blackboard: Blackboard,
inputMerge: BlackboardInputMerge,
): string {
if (inputs[tag]?.trim()) return inputs[tag]!.trim();
const items = blackboard.queryByPatterns([tag], inputMerge);
if (items.length === 0) return "";
if (inputMerge === "concat") {
return items.map((i) => i.content).filter(Boolean).join("\n\n");
}
return items[items.length - 1]?.content?.trim() ?? "";
}
function formatSegmentBody(
segment: ContextSegmentDef,
inputs: Record<string, string>,
blackboard: Blackboard,
inputMerge: BlackboardInputMerge,
): string {
const parts: string[] = [];
for (const tag of segment.tags) {
let content = readTagContent(tag, inputs, blackboard, inputMerge);
if (!content) continue;
if (tag === CREATION_ACCEPTED_CONTENT_TAG) {
content = formatAcceptedContentForPrompt(content);
}
content = applyPolicy(content, segment.policy);
if (!content.trim()) continue;
parts.push(content.trim());
}
return parts.join("\n\n");
}
/**
* 按 contextSegments 拼装 user 侧上下文。
* 无 segments 时回退为 JSON inputs兼容旧 skill
*/
export function assembleWorkerContext(params: {
inputs: Record<string, string>;
segments?: ContextSegmentDef[] | null;
blackboard: Blackboard;
inputMerge?: BlackboardInputMerge;
workerId: string;
workerName: string;
outputTags: string[];
}): string {
const inputMerge = params.inputMerge ?? "latest";
const segments = params.segments ?? [];
if (segments.length === 0) {
return JSON.stringify(
{
workerId: params.workerId,
workerName: params.workerName,
outputTags: params.outputTags,
inputs: params.inputs,
instruction:
"根据 SKILL 说明完成任务。若 inputs 含「上下文.定稿摘要」,以终产物为准。" +
"设计.worker集 / 草稿须为 JSON 对象文本。",
},
null,
2,
);
}
const staticSegs = segments.filter((s) => s.tier === "static");
const dynamicSegs = segments.filter((s) => s.tier === "dynamic");
const blocks: string[] = [];
const render = (seg: ContextSegmentDef) => {
const body = formatSegmentBody(seg, params.inputs, params.blackboard, inputMerge);
if (!body) return;
if (seg.label) {
blocks.push(`${seg.label}\n\n${body}`);
} else {
blocks.push(body);
}
};
for (const seg of staticSegs) render(seg);
for (const seg of dynamicSegs) render(seg);
// 定稿摘要:若未在 segments 中声明,仍附在末尾
const brief = params.inputs[CONTEXT_BRIEF_TAG]?.trim();
const briefInSegments = segments.some((s) => s.tags.includes(CONTEXT_BRIEF_TAG));
if (brief && !briefInSegments) {
blocks.push(`## 上下文.定稿摘要\n\n${brief}`);
}
blocks.push(
[
"## 本步任务",
"",
`- workerId: \`${params.workerId}\``,
`- workerName: ${params.workerName}`,
`- outputTags: ${params.outputTags.map((t) => `\`${t}\``).join("、") || "(无)"}`,
"",
"按 SKILL 与上方分区完成任务。标为「只读 / 已定稿」的分区不要擅自改写;只改【本单位】范围。",
"设计.worker集 / 草稿须为 JSON 对象文本(以 `{` 开头)。",
].join("\n"),
);
return blocks.join("\n\n---\n\n");
}