立项策划根 run 的上下文层不再列出专业角色目录

共享 runtime 合同里的「agent.spawn_isolated 合法 templateAgentId」静态模板目录
会把 design-director 等全部专业角色名拼进 plan 根 Supervisor 的 system prompt。
plan 根的 agent.spawn_isolated 已被 M1A-4 无条件拒绝,这份目录因此没有任何可执行
语义,此前按「死文本」处理。

实测证伪了这个判断:目录第一个名字就是 design-director,Supervisor 首轮据此发起
agent.delegate,被执行层以 plan-root-child-target-unsupported 拒绝后未能自行改回
project-planning,整个 run 空转到 loop 预算耗尽、零产物。漏掉的推理是目录虽为
spawn 而设,模型会把里面的名字挪去当 delegate 目标。

改为在上下文层删掉目录本身,即第 19 节第 2 条要求的第二层,不替代执行层硬拒。
只删 spawn_isolated 专属的两段;isolatedAgentContract 讲的是 expectedArtifacts 与
writeScopes,agent.delegate 同样要用,逐字保留。

覆盖:plan 根 prompt 遍历 GAME_CREATOR_AGENT_GROUP_DEFINITIONS 断言不含任何角色
task_id(新增角色自动进覆盖范围);非 plan source 与其余 agent 逐字等于未收窄的
合成结果且目录完整。前者已变异验证。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 17:33:43 +00:00
parent 938b7ce654
commit d284639903
@@ -530,7 +530,12 @@ pub(crate) fn game_creator_agent_runtime_tool_plan_system_prompt_for_agent(
if agent_id == GAME_CREATOR_PROJECT_PLANNING_AGENT_ID {
return game_creator_project_planning_tool_plan_system_prompt();
}
let prompt = game_creator_agent_runtime_tool_plan_system_prompt();
// 判据里带上 agent_id`source` 只有 Supervisor 自己的 run binding 会填成
// plan(见 provider_request_builders.rs),其余角色恒为空串,这里再要求一次
// 是为了让「谁能收窄」这件事在本函数内自证,不依赖调用方的取值纪律。
let plan_root = agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID
&& agent_runtime_supervisor_source_is_plan(source);
let prompt = game_creator_agent_runtime_tool_plan_system_prompt_for_plan_root(plan_root);
if agent_id == "design-foundation" {
return game_creator_design_foundation_tool_plan_prompt(
&prompt,
@@ -673,14 +678,31 @@ pub(crate) fn game_creator_agent_runtime_role_overlay_prompt(
}
pub(crate) fn game_creator_agent_runtime_tool_plan_system_prompt() -> String {
game_creator_agent_runtime_tool_plan_system_prompt_for_plan_root(false)
}
/// plan 根 run 的 `agent.spawn_isolated` 已被 M1A-4 无条件拒绝,因此共享
/// runtime 合同里那份「合法 templateAgentId」静态模板目录对它没有任何可执行
/// 语义,只剩下把 design-director 等全部专业角色名摆在 Provider 眼前的副作用。
/// 该目录第一个名字就是 `design-director`,实测中 Supervisor 会照着它发起
/// `agent.delegate`,被执行层拒绝后再也没能自行改回 `project-planning`。
/// 因此这里在上下文层直接删掉目录本身——这正是第 19 节第 2 条要求的第二层,
/// 不替代执行层硬拒。
fn game_creator_agent_runtime_tool_plan_system_prompt_for_plan_root(plan_root: bool) -> String {
#[cfg(target_os = "linux")]
const CURRENT_PLATFORM_IS_LINUX: bool = true;
#[cfg(not(target_os = "linux"))]
const CURRENT_PLATFORM_IS_LINUX: bool = false;
game_creator_agent_runtime_tool_plan_system_prompt_for_platform(CURRENT_PLATFORM_IS_LINUX)
game_creator_agent_runtime_tool_plan_system_prompt_for_platform(
CURRENT_PLATFORM_IS_LINUX,
plan_root,
)
}
fn game_creator_agent_runtime_tool_plan_system_prompt_for_platform(linux: bool) -> String {
fn game_creator_agent_runtime_tool_plan_system_prompt_for_platform(
linux: bool,
plan_root: bool,
) -> String {
let tool_catalog = agent_runtime_native_executable_tools().join("");
let prompt_header = format!(
"你正在使用 Genarrative AI 游戏创作多智能体 Runtime。你必须直接调用当前请求广告的原生函数:复杂任务首次拆解、实际进度变化、steer 调整顺序或最终收束时调用 update_agent_plan,并提交 explanation 与完整 steps;无需更新时不要调用 update_agent_plan。steps 只允许 pending、in_progress、completed 且同时最多一个 in_progress;已完成步骤必须保留且不得回退,所有必要步骤 completed 前不得调用 respond_to_userRuntime 不会按工具动作下标代替你更新进度。只能请求以下 Runtime 当前注册的原生可执行工具:{tool_catalog}。MCP 工具仅以当前请求提供的动态目录为准。"
@@ -694,7 +716,15 @@ fn game_creator_agent_runtime_tool_plan_system_prompt_for_platform(linux: bool)
let platform_section = runtime_prompt_platform_section_id(linux);
render_runtime_prompt_composition(RUNTIME_PROMPT_RUNTIME_COMPOSITION, |marker| match marker {
"$header" => Some(&prompt_header),
"$isolatedAgentTemplates" => Some(&isolated_agent_templates),
// 只删 spawn_isolated 专属的两段。`isolatedAgentContract` 讲的是
// expectedArtifacts 与 writeScopes`agent.delegate` 同样要用,plan 根
// 委派 project-planning 时就带 expectedArtifacts,必须逐字保留。
"isolatedTemplateCatalogIntro" if plan_root => Some(""),
"$isolatedAgentTemplates" => Some(if plan_root {
""
} else {
&isolated_agent_templates
}),
"$platform" => Some(required_runtime_prompt_section(platform_section)),
_ => None,
})
@@ -947,8 +977,10 @@ mod tests {
#[test]
fn runtime_prompt_selects_exactly_one_manifest_platform_variant() {
let default_prompt = game_creator_agent_runtime_tool_plan_system_prompt_for_platform(false);
let linux_prompt = game_creator_agent_runtime_tool_plan_system_prompt_for_platform(true);
let default_prompt =
game_creator_agent_runtime_tool_plan_system_prompt_for_platform(false, false);
let linux_prompt =
game_creator_agent_runtime_tool_plan_system_prompt_for_platform(true, false);
let default_section =
required_runtime_prompt_section(RUNTIME_PROMPT_PLATFORM_DEFAULT_SECTION).trim();
let linux_section =
@@ -1268,12 +1300,16 @@ mod tests {
/// system prompt 不拼 supervisorIntro 与 $visualContract 这两个 section
/// ——两段都在暗示存在可并行委派的专业组,而策划链路的合同是只能委派
/// project-planning 一个子 Agent。这里用两段各自最短的、只出现在该 section
/// 里的独有句子做断言,不用裸的 agent-id 字面量`$base`(共享 runtime
/// 合同)里的 isolated agent 模板目录本就会列出 art-director /
/// design-foundation / art-asset-plan / code-prototype 等全部模板 taskId
/// 供 agent.spawn_isolated 使用,这与本次改动无关,也不构成越权——plan 根
/// 已经在执行层被 A2 无条件拒绝 agent.spawn_isolated(见 delegation.rs),
/// 所以这份共享目录残留只是死文本,不是可利用的委派入口。
/// 里的独有句子做断言,不用裸的 agent-id 字面量
///
/// **订正(2026-08-20**:本注释原先记着 `$base` 里的 isolated agent 模板
/// 目录「只是死文本,不是可利用的委派入口」,理由是执行层已无条件拒绝
/// `agent.spawn_isolated`。该推理只覆盖了 spawn 这一条路径,漏掉了 Provider
/// 会把目录里的名字挪去当 `agent.delegate` 的目标:实测 Supervisor 首轮就
/// 委派了目录里排第一的 `design-director`,被执行层拒绝后未能自行改回
/// `project-planning`,整个 run 空转到 loop 预算耗尽、零产物。目录现已在
/// plan 根的上下文层删除,覆盖断言见
/// `plan_root_supervisor_prompt_drops_the_specialist_role_catalog`。
#[test]
fn plan_root_supervisor_prompt_drops_intro_and_visual_contract_sections() {
for editor_api_key_is_configured in [false, true] {
@@ -1316,6 +1352,89 @@ mod tests {
}
}
/// plan 根 Supervisor 的完整 system prompt 里不得再出现任何专业组角色名。
/// 断言遍历 `GAME_CREATOR_AGENT_GROUP_DEFINITIONS` 而不是列举几个字面量,
/// 这样将来新增角色自动进入覆盖范围,不依赖有人记得回来补这条。
#[test]
fn plan_root_supervisor_prompt_drops_the_specialist_role_catalog() {
let plan_prompt = game_creator_agent_runtime_tool_plan_system_prompt_for_agent(
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
AGENT_RUNTIME_SUPERVISOR_PLAN_SOURCE,
);
for group in GAME_CREATOR_AGENT_GROUP_DEFINITIONS {
for role in group.roles {
assert!(
!plan_prompt.contains(role.task_id),
"plan 根 prompt 不应出现专业角色名 {}{plan_prompt}",
role.task_id
);
}
}
assert!(
!plan_prompt
.contains(required_runtime_prompt_section("isolatedTemplateCatalogIntro").trim()),
"plan 根 prompt 不应保留 spawn_isolated 静态模板目录抬头"
);
// agent.delegate 同样按这段填 expectedArtifacts / writeScopesplan 根
// 委派 project-planning 时要用,删目录不能把它一起删掉。
assert!(
plan_prompt.contains(required_runtime_prompt_section("isolatedAgentContract").trim()),
"plan 根 prompt 必须保留 expectedArtifacts / writeScopes 合同"
);
}
/// 收窄只对 plan 根 Supervisor 生效。其余 source 与其余 agent 的共享
/// runtime 合同必须逐字等于「基础 prompt 未被收窄」时的合成结果,且模板
/// 目录仍然完整——`agent.spawn_isolated` 在那些链路上是真能用的。
#[test]
fn only_the_plan_root_supervisor_loses_the_specialist_role_catalog() {
let intact_base = game_creator_agent_runtime_tool_plan_system_prompt();
for (agent_id, source) in [
(GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID, ""),
(
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
AGENT_RUNTIME_SUPERVISOR_GUI_SOURCE,
),
(
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
AGENT_RUNTIME_SUPERVISOR_CLI_SOURCE,
),
(
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
AGENT_RUNTIME_SUPERVISOR_GAME_CHAT_SOURCE,
),
(
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
"project-supervisor-plan-forged",
),
// 专业角色自己即便被伪造成 plan source 也不该被收窄。
("code-prototype", AGENT_RUNTIME_SUPERVISOR_PLAN_SOURCE),
("quality-review", ""),
] {
let actual =
game_creator_agent_runtime_tool_plan_system_prompt_for_agent(agent_id, source);
let expected = if agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID {
game_creator_project_supervisor_tool_plan_prompt(
&intact_base,
editor_api_key_is_configured(),
source,
)
} else {
intact_base.clone()
};
assert_eq!(
actual, expected,
"agent={agent_id} source={source} 不应被 plan 根收窄逻辑改变"
);
assert!(
actual.contains(
required_runtime_prompt_section("isolatedTemplateCatalogIntro").trim()
),
"agent={agent_id} source={source} 必须保留静态模板目录"
);
}
}
/// gui/cli/game-chat 等非 plan source 的 Supervisor system prompt 必须与
/// M1A-4 之前逐字相同:直接用既有 section 常量手工拼出改动前的合成结果,
/// 逐字比对,防止上面的 plan 根分支误伤这条现役路径。