完善创作节点循环与等待态交互,并大幅打磨 Web 壳与会话运行时。
- 节点循环:配方开局直坐 DAG 第一步;验收后先问下一步意向,再确认开干并可钉编排参数;「按意见修改」只重跑当前节点。 - 询问分流:能力 opening 走说话面引导,可跳过追问按题干去重;追问与产物同轮挂载,避免拆成两段历史。 - 运行时:补强 revision 重跑、创作流程合并/进度指针、工具循环与 worker 执行;新增运行日志与 web:watch。 - 前端:统一等待态文案与底栏主按钮,完善询问卡/产物验收/呈现壳样式与交互。 - 同步世界模拟器模块提示、编排文档与相关测试。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -23,48 +23,15 @@ function buildMainAgentSystemPrompt(
|
||||
? workers.map((w) => `- ${w.id}:${w.description}`).join("\n")
|
||||
: "- (当前 skill 未加载 worker 列表)";
|
||||
|
||||
return `你是写作系统的编排器(Main Agent)。你的职责是调度执行单元(worker),而不是直接创作正文。
|
||||
return `你是创作节点路由器。只读取「设计.创作流程」DAG,并选择下一个要执行的创作节点。
|
||||
|
||||
规则:
|
||||
1. 你不能直接生成小说/文章正文。
|
||||
2. 你不能修改运行状态;statePatchAllowed 必须始终为 false。
|
||||
3. 你只能建议下一步动作:ask_user、run_worker、create_temp_worker、review_blackboard、finish。
|
||||
4. 当信息不足时,使用 ask_user:assessment 是主内容(完备度评价);questions 挂在其下且用户可跳过;一次 1~2 题。
|
||||
5. 当需要执行任务时,使用 run_worker,只指定 workerId。不要指定 inputTags 或 outputTags——Runtime 从 Worker Skill 读取。
|
||||
6. requiresApproval 表示运行 worker 前是否需要用户确认。代笔模式通常为 true。
|
||||
7. run_worker 可选 workerContext:{ "roleId": "A" } 或 { "chance": { "op":"roll", "expression":"2d6" } }(机遇裁定走程序,勿让模型编随机)。
|
||||
8. 你不能把未验收内容当作事实。
|
||||
9. 向用户提问是 worker 的技能(ask_user tool),不是独立 worker。编排器只在调度层提问。
|
||||
10. blackboardIndex 只有 tag 索引,不含正文 content。
|
||||
|
||||
## 调度思维
|
||||
用需求正推:「用户需要 [具体体验/技能] → 调用 [worker/skill] 来 [生成/补充/调整] [什么],以便更好满足用户。」
|
||||
禁止否定式路由:「某模式 / 背景形态 → 不需要某步骤 / 跳过某 worker」。
|
||||
|
||||
反例(禁止):「背景为单一角色,无需世界构建」「不是规则怪谈,跳过 write-rules」「默认上世界模拟套件」
|
||||
正例:「用户已选世界模拟器配方且要网恋对话 → design-flow 排出近期增量步骤(可调味、可后补;有编排参数的步骤须钉 params)→ 用户认可后反复 design-step;需要再补规则/实例 → 再 design-flow 追加同技能并写齐 params → 收成后若需开局 → opening-generator;用户手动进 play」
|
||||
- DAG 不存在或需要追加节点:调用 design-flow。
|
||||
- DAG 中已有下一个可执行节点:调用 design-step。
|
||||
- 只调度 worker,不创作正文,不规划 DAG 之外的流程。
|
||||
|
||||
当前 skill 可用 worker(workerId 必须与下列 id 完全一致):
|
||||
${workerLines}
|
||||
|
||||
## design 优先顺序
|
||||
- 尚无「设计.创作流程」→ **design-flow**(近期 horizon;status=open;未选配方则先请用户选)
|
||||
- 流程已验收且还有未完成步骤 → **design-step**(一次一步;技能由程序注入)
|
||||
- 已列步骤都验收但流程仍 **status=open** → 再 **design-flow**(追加反复步或 closed)
|
||||
- 禁止调度已废弃的 design-core / design-fixed / design-worker / design-refine
|
||||
- 终稿已 accept 且可用 opening-generator、尚无开场产物 → opening-generator
|
||||
- 进 play 由用户手动决定
|
||||
- **禁止**替用户猜测或改选配方
|
||||
- **禁止**一次编排排死全程固定 DAG
|
||||
|
||||
## play 优先顺序(lifecycleStage=play)
|
||||
- 严格按声明管线顺序调度(play_slots 展开序):**auditor(旁观维护)→ perspective? → gm(主世界层)→ narrator(转述)**
|
||||
- auditor / gm / perspective 的 acceptance 多为 continue:跑完立刻调度下一槽,不要中途 ask_user
|
||||
- 仅 narrator(或规格标明 review 的终稿槽)停下来等人看
|
||||
- chance 仅在需要真随机时按需 run_worker,不插入每轮固定序
|
||||
- slots.pendingSideEffectWorkers 非空时优先调度其中 workerId
|
||||
- **禁止**发明未声明的 play ref;**禁止**把旁观维护当成正文作者
|
||||
|
||||
输出必须是 JSON 对象,字段:
|
||||
{
|
||||
"action": "ask_user" | "run_worker" | "create_temp_worker" | "review_blackboard" | "finish",
|
||||
@@ -110,40 +77,21 @@ export class MainAgent {
|
||||
|
||||
export function buildMainAgentUserPrompt(context: MainAgentContext): string {
|
||||
const { session, blackboardIndex, availableWorkers } = context;
|
||||
const stage = String(session.slots?.uiLifecycleStage ?? "design");
|
||||
const pendingSide = session.slots?.pendingSideEffectWorkers;
|
||||
const creationTags = new Set([
|
||||
"设计.创作流程",
|
||||
"创作.当前步骤",
|
||||
"创作.当前单位",
|
||||
"创作.已验收单位",
|
||||
"用户.下一步意向",
|
||||
]);
|
||||
return JSON.stringify(
|
||||
{
|
||||
runtimePhase: session.phase,
|
||||
waitingReason: session.waitingReason,
|
||||
lifecycleStage: stage,
|
||||
flowId: session.flowId,
|
||||
currentStepId: session.currentStepId,
|
||||
currentWorkerId: session.currentWorkerId,
|
||||
acceptanceMode: session.acceptanceMode,
|
||||
slots: session.slots,
|
||||
pendingSideEffectWorkers: pendingSide ?? null,
|
||||
pendingDecision: session.pendingDecision
|
||||
? {
|
||||
id: session.pendingDecision.id,
|
||||
action: session.pendingDecision.action,
|
||||
reason: session.pendingDecision.reason,
|
||||
}
|
||||
: null,
|
||||
pendingArtifactId: session.pendingArtifactId,
|
||||
artifacts: session.artifacts.map((a) => ({
|
||||
id: a.id,
|
||||
workerId: a.workerId,
|
||||
status: a.status,
|
||||
summary: a.summary,
|
||||
outputTags: a.outputTags,
|
||||
})),
|
||||
blackboardIndex,
|
||||
creationDagTags: blackboardIndex.filter((entry) =>
|
||||
creationTags.has(entry.tag),
|
||||
),
|
||||
availableWorkers,
|
||||
instruction:
|
||||
stage === "play"
|
||||
? "play:按声明序 auditor→perspective?→gm→narrator;continue 槽连跑;仅终稿 review 停;pendingSideEffectWorkers 优先;禁止未声明 ref。"
|
||||
: "根据当前状态决定下一步。尚无设计.创作流程 → design-flow;有未完成步骤 → design-step;steps 做完但 status=open → 再 design-flow;禁止旧 design-core/fixed/worker/refine。已 accept 且需开局 → opening-generator。进 play 由用户手动。",
|
||||
instruction: "读取创作 DAG,选择下一个创作节点。",
|
||||
},
|
||||
null,
|
||||
2,
|
||||
|
||||
+13
-71
@@ -6,6 +6,7 @@ import { isMainAgentTerminalTool } from "../types/tools.js";
|
||||
import { MAIN_AGENT_TOOL_DEFINITIONS } from "./tools.js";
|
||||
import { buildMainAgentUserPrompt, parseMainAgentDecision } from "./main-agent.js";
|
||||
import type { MainAgentContext } from "./main-agent.js";
|
||||
import { debugLog } from "../log.js";
|
||||
|
||||
export type ToolLoopHandlers = {
|
||||
readBlackboard: (tags: string[]) => Record<string, string>;
|
||||
@@ -20,7 +21,7 @@ export type ToolLoopHandlers = {
|
||||
onToolCall?: (name: string, detail: string) => void;
|
||||
/** 思维链 / reasoning 流式增量(供 UI 实时展示) */
|
||||
onThinkingDelta?: (delta: string) => void;
|
||||
/** 一轮 LLM 调用结束后的完整思考文本 */
|
||||
/** 一轮 LLM 调用结束后的完整思维链(不含正文) */
|
||||
onThinkingDone?: (text: string) => void;
|
||||
};
|
||||
|
||||
@@ -40,71 +41,17 @@ function buildToolLoopSystemPrompt(
|
||||
? workers.map((w) => `- ${w.id}:${w.description}`).join("\n")
|
||||
: "- (当前 skill 未加载 worker 列表)";
|
||||
|
||||
return `你是写作系统的总管(Main Agent)。你在 running 相位通过 **tool call** 推进流程。
|
||||
return `你是创作节点路由器。只根据「设计.创作流程」DAG 选择下一个创作节点。
|
||||
|
||||
## 能力边界
|
||||
1. 你不能直接生成小说/文章正文。
|
||||
2. 你不能修改运行状态。
|
||||
3. 先用 read_blackboard / list_workers / list_artifacts 收集信息,再决定下一步。
|
||||
4. 终止动作只能用 tool:ask_user、run_worker、review_blackboard、finish。
|
||||
- ask_user:**主内容是 assessment(内容完备度评价)**;questions 挂在其下且可被用户跳过。
|
||||
· assessment(必填):给用户看的评价正文。以【核心体验】为首;按需列维度(内容/人物关系/感官/背景规则/意义主题…),不必凑齐。每维写完备度%、已知、待探;待探用【】标互斥或可组合方向。
|
||||
· questions(必填数组,语义可选):只针对 assessment「待探」里想确认的点;一次 1~2 题。用户可不答、直接让你基于现有信息继续。prompt=明确题干;options=2~4 条**建议示范**;required 默认 false。
|
||||
· reason:调度短句,正推,勿塞长文。
|
||||
· 能推断就别问;已写在 用户.需求 里的不要重复问。
|
||||
5. run_worker 只传 workerId;inputTags/outputTags 由 Runtime 从 Worker Skill 读取。
|
||||
6. requiresApproval=true 时 run_worker 需用户确认后再执行。
|
||||
7. 不能把未验收产物当作已定事实。
|
||||
|
||||
## 调度思维(必须遵守)
|
||||
用 **需求正推**,禁止 **模式否定式** 表述。
|
||||
|
||||
**要这样思考(正推):**
|
||||
「用户需要 [具体体验/能力/产物] → 因此调用 [worker/skill] 来 [生成/补充/调整] [什么],以便更好满足用户。」
|
||||
|
||||
**不要这样思考(反例):**
|
||||
「这是 xxx 模式 / 形态 → 不需要 xxx 步骤 / 跳过 xxx。」
|
||||
「背景为单一角色,无需世界构建。」
|
||||
「不是规则怪谈,跳过 write-rules。」
|
||||
|
||||
示例(好):
|
||||
- 「用户已选配方且要 1v1 网恋 → design-flow 排出近期增量步骤(可调味)→ 认可后反复 design-step;不够再追加。」
|
||||
- 「尚无设计.创作流程 → run_worker(design-flow);有未完成步骤 → design-step;steps 完但 status=open → 再 design-flow。」
|
||||
- 「运行规格已 accept 且声明需要开局 → 调用 opening-generator 写开场白。」
|
||||
|
||||
示例(坏):
|
||||
- 「调度 design-core / design-fixed(已废弃)。」
|
||||
- 「一次 design-flow 排死全程固定 DAG。」
|
||||
- 「跳过流程编排直接写满运行规格。」
|
||||
- 「默认先上世界模拟全套再问用户要什么。」
|
||||
- 「替用户改选 / 猜测配方。」
|
||||
|
||||
在 reasoning / 可见 content 中请用 **正推句式** 写出调度理由;终止 tool 的 reason 字段同样用正推表述。
|
||||
|
||||
## design 阶段提示
|
||||
- 尚无「设计.创作流程」→ **优先 design-flow**(近期 horizon;status=open;未选则先请用户选)。
|
||||
- 流程已验收、还有未完成步骤 → **design-step**(一次一步)。
|
||||
- 已列步骤全验收但 **status=open** → 再 **design-flow**(追加生成规则/具体实例等并钉齐 params,或设 closed)。
|
||||
- 有编排参数的步骤缺必填 params → 先让 design-flow askUser,不要空壳跑 design-step。
|
||||
- 禁止 run_worker(design-core|design-fixed|design-worker|design-refine)——已废弃。
|
||||
- 不要重复问用户「想做什么」——意图已在 用户.需求。
|
||||
- 运行规格已 accept,且可用 worker 含 opening-generator,尚无开场产物 → opening-generator。
|
||||
- 进 play 由用户手动决定。
|
||||
- 未声明开局、用户也未要求开场时,不要硬调 opening-generator。
|
||||
- **禁止**替用户猜测或改选配方。
|
||||
- **禁止**一次编排排死全程固定长链。
|
||||
|
||||
## play 阶段提示(lifecycleStage=play 时优先)
|
||||
- 管线顺序:**auditor(旁观维护)→ perspective? → gm(主世界层)→ narrator**。
|
||||
- auditor/gm/perspective 多为 continue:写完立刻 run 下一槽,不要中途 ask_user。
|
||||
- 只在 narrator(或 review 终稿槽)停给人看。
|
||||
- chance 按需;pendingSideEffectWorkers 非空则优先调度。
|
||||
- 禁止未声明 play ref;旁观维护默认空操作,不是正文作者。
|
||||
1. 用 read_blackboard 读取「设计.创作流程」及其进度 tag。
|
||||
2. 没有 DAG 或 DAG 需要追加节点时,run_worker(design-flow)。
|
||||
3. DAG 已给出下一个可执行节点时,run_worker(design-step)。
|
||||
4. 不创作正文,不改写 DAG,不处理运行状态或游玩流程。
|
||||
|
||||
当前 skill 可用 worker:
|
||||
${workerLines}
|
||||
|
||||
信息不足时可多次 read_blackboard;一旦调用终止 tool,本轮循环结束。`;
|
||||
选定节点后立即调用 run_worker。`;
|
||||
}
|
||||
|
||||
async function completeToolsPreferStream(
|
||||
@@ -172,14 +119,9 @@ function assistantMessageFromToolCalls(
|
||||
};
|
||||
}
|
||||
|
||||
function emitThinkingDone(handlers: ToolLoopHandlers, parts: {
|
||||
reasoning?: string;
|
||||
content?: string | null;
|
||||
}): void {
|
||||
const reasoning = parts.reasoning?.trim() ?? "";
|
||||
const content = parts.content?.trim() ?? "";
|
||||
const combined = [reasoning, content].filter(Boolean).join("\n\n").trim();
|
||||
if (combined) handlers.onThinkingDone?.(combined);
|
||||
function emitThinkingDone(handlers: ToolLoopHandlers, reasoning?: string): void {
|
||||
const text = reasoning?.trim() ?? "";
|
||||
if (text) handlers.onThinkingDone?.(text);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,12 +144,12 @@ export async function runMainAgentToolLoop(
|
||||
const toolTrace: string[] = [];
|
||||
const streamCallbacks: StreamCallbacks = {
|
||||
onReasoningDelta: (delta) => handlers.onThinkingDelta?.(delta),
|
||||
onContentDelta: (delta) => handlers.onThinkingDelta?.(delta),
|
||||
};
|
||||
|
||||
for (let iteration = 1; iteration <= MAX_TOOL_LOOP_ITERATIONS; iteration++) {
|
||||
debugLog("llm", `总管循环 第${iteration}轮`);
|
||||
const result = await completeToolsPreferStream(llm, messages, streamCallbacks);
|
||||
emitThinkingDone(handlers, result);
|
||||
emitThinkingDone(handlers, result.reasoning);
|
||||
|
||||
if (result.toolCalls.length === 0) {
|
||||
if (result.content?.trim()) {
|
||||
|
||||
+1
-148
@@ -22,118 +22,11 @@ export const MAIN_AGENT_TOOL_DEFINITIONS: ToolDefinition[] = [
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "list_workers",
|
||||
description: "列出当前 skill 可调度的 worker id 与说明。",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {},
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "list_artifacts",
|
||||
description: "列出当前会话 worker 产物(id、workerId、status、summary)。",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {},
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "ask_user",
|
||||
description:
|
||||
"向用户追问。assessment 是给用户看的主内容(完备度评价);questions 挂在其下,用户可跳过并请你基于现有信息继续。将暂停等待用户。",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
reason: {
|
||||
type: "string",
|
||||
description:
|
||||
"调度层短理由(正推:用户需要澄清 X → 因此追问)。勿把长文评价写这里。",
|
||||
},
|
||||
assessment: {
|
||||
type: "string",
|
||||
description:
|
||||
"给用户看的内容完备度评价(Markdown)。以【核心体验】为首要;按需列维度(不必凑齐),每维:完备度%、已知、待探。待探用【】标出互斥/可组合方向。评价只服务「用户想要什么」。",
|
||||
},
|
||||
message: {
|
||||
type: "string",
|
||||
description:
|
||||
"兼容旧字段:等同 assessment。优先传 assessment。",
|
||||
},
|
||||
questions: {
|
||||
type: "array",
|
||||
description:
|
||||
"挂在 assessment 下的可选追问(用户可不答)。基于「待探」出题,一次 1~2 题。prompt=明确选择题干;options=建议示范(可改写后采用)。",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
description: "稳定 id,如 q1 / stance",
|
||||
},
|
||||
prompt: {
|
||||
type: "string",
|
||||
description:
|
||||
"题干:引导用户选定一种详细体验倾向(例:「你更倾向于哪种皇帝的享受?」)。",
|
||||
},
|
||||
options: {
|
||||
type: "array",
|
||||
description:
|
||||
"2~4 个建议示范。label 写完整可采纳文案(可先一句场景钩子再点题),不是标签词。",
|
||||
items: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: {
|
||||
type: "string",
|
||||
description: "A/B/C…",
|
||||
},
|
||||
label: {
|
||||
type: "string",
|
||||
description:
|
||||
"建议正文:用户点选后可直接当答案,也可改写。例:「冰冷的、主宰一切的权力感——九重宫阙一言定生死…」",
|
||||
},
|
||||
editable: {
|
||||
type: "boolean",
|
||||
description: "默认 true:点文案可改写,点字母才选中",
|
||||
},
|
||||
},
|
||||
required: ["label"],
|
||||
},
|
||||
},
|
||||
allowOther: {
|
||||
type: "boolean",
|
||||
description: "默认 true:允许 Other 自拟",
|
||||
},
|
||||
required: {
|
||||
type: "boolean",
|
||||
description: "默认 false(可选追问);仅关键阻塞题才 true",
|
||||
},
|
||||
},
|
||||
required: ["prompt", "options"],
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ["reason", "assessment", "questions"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "run_worker",
|
||||
description:
|
||||
"调度 worker。常规只传 workerId。调度 chance(机遇裁定)时传 chance 请求对象(程序掷骰/比点/抽签,禁止让模型编随机)。",
|
||||
description: "执行选中的下一个创作节点。",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
@@ -143,50 +36,10 @@ export const MAIN_AGENT_TOOL_DEFINITIONS: ToolDefinition[] = [
|
||||
type: "boolean",
|
||||
description: "true 时需用户确认后才执行",
|
||||
},
|
||||
roleId: {
|
||||
type: "string",
|
||||
description: "role-decide 等 worker 的当前角色 id",
|
||||
},
|
||||
chance: {
|
||||
type: "object",
|
||||
description:
|
||||
"仅 workerId=chance:{ op:'roll'|'compare'|'draw'|'pick', ... }。例 roll:{expression:'2d6+1'};draw:{pool:['a','b'],count:1}",
|
||||
},
|
||||
},
|
||||
required: ["workerId", "reason", "requiresApproval"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "review_blackboard",
|
||||
description: "向用户说明当前黑板与进度概况,并暂停等待用户回复。",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
reason: { type: "string" },
|
||||
summary: { type: "string", description: "给用户看的概况说明" },
|
||||
},
|
||||
required: ["reason", "summary"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "finish",
|
||||
description: "正常结束当前创作流程。",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
reason: { type: "string" },
|
||||
},
|
||||
required: ["reason"],
|
||||
additionalProperties: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user