立项策划 role brief 按解析器真实线格描述澄清信封
Project CI / Repository checks (pull_request) Failing after 15s
Project CI / Backend tests (pull_request) Failing after 15s
Project CI / Frontend tests (pull_request) Successful in 2m42s
Project CI / Native shell tests (pull_request) Failing after 10m49s

brief 只写「下一行给出严格 JSON 的单题问题」,模型据此输出裸的单题对象,还自创
了一个 answerFormat 字段:

  {"header":...,"prompt":...,"options":[...],"answerFormat":"回复 A、B..."}

解析器要的是 {"questions":[{id,header,question,options}]} 且 deny_unknown_fields,
于是父 run 收到 unknown field `answerFormat`, expected `questions`。此时子 Run
已经终止,信封没有修复通道,整条委派直接停在 needs-reconciliation。模型其余部分
(第N轮·关键决定、A/B/需要原型验证 三项合同)都严格照 brief 执行,说明它跟的就是
brief——两份 prompt 描述了两种形状时,模型跟更具体的那份。

brief 改为逐字给出外壳、四个允许字段、id 的 snake_case 规则和各字段字符上限,并
点名 answerFormat 这个真实踩过的坑。上限数字全部来自 schema 常量,新增用例用
format! 把两边钉在一起,任一边先改都会红;为此把相关常量放宽到 pub(crate)。

这一条只改立项策划自己的 role brief,其它 Agent 的 overlay 不受影响。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-20 11:08:44 +00:00
parent 3bae4cdbb0
commit 5c2d722b12
3 changed files with 44 additions and 7 deletions
@@ -4,7 +4,7 @@
- 当前 run 固定为 `source=agent-delegate``profile=standard`,父 Agent 是 `project-supervisor`。不得伪造、改写或猜测这些 Runtime 身份。
- 你不能委派或调度其他 Agent,不能创建 isolated child,不能调用 MCP、命令、进程、预览、画布、素材生成、写入/补丁/删除工具,也不能改变项目版本或审批事实。
- 你的原生工具目录只应包含 `file.read``file.list` 以及 Runtime 协议控制函数 `update_agent_plan``respond_to_user``user.input_request` 不属于你的工具目录。若需要用户决定,必须以终态信封首行 `AGC_NEEDS_USER_INPUT_V1` 退出本轮,下一行给出严格 JSON 的单题问题,交由 Supervisor 转发
- 你的原生工具目录只应包含 `file.read``file.list` 以及 Runtime 协议控制函数 `update_agent_plan``respond_to_user``user.input_request` 不属于你的工具目录。若需要用户决定,必须以终态信封首行 `AGC_NEEDS_USER_INPUT_V1` 退出本轮,下一行给出严格 JSON 信封 `{"questions":[{ ... }]}`,交由 Supervisor 转发。`questions` 恰好一个元素;元素字段只能是 `id``header``question``options` 四个,多写任何字段(例如 `answerFormat`)或省掉 `questions` 外壳都会被 Runtime 拒收,整条委派随即作废。`id` 是唯一 snake_case(小写字母开头,只含小写字母、数字、下划线);`header` 是决策卡标题,单行且不超过 12 字符;`question` 是决策卡正文,单行且不超过 400 字符;`options` 是 23 个 `{"label": ..., "description": ...}`,label 单行不超过 60 字符、description 单行不超过 240 字符。不要另起一行写答题说明或把选项复述进 `question`,作答方式由 Runtime 自己呈现
- 只有 Runtime 广告并允许 `plan.submit_gdd` 时才可提交 GDD;不要假设未广告的工具存在,也不要把 GDD、审批或下游构建写进普通文本。
## 目标与轮次
@@ -860,6 +860,43 @@ mod tests {
);
}
#[test]
fn project_planning_role_brief_states_the_parser_wire_shape_verbatim() {
// 现场事故:brief 只说「严格 JSON 的单题问题」,模型据此输出裸的单题对象
// 外加自创的 answerFormat 字段,解析器 deny_unknown_fields 直接拒收,
// 子 Run 已终止无从修复,整条委派停在 needs-reconciliation。brief 与
// schema 必须逐字对齐,任一边改了都得让这条用例先红。
let planning = game_creator_agent_runtime_role_overlay_prompt(
GAME_CREATOR_PROJECT_PLANNING_AGENT_ID,
None,
);
assert!(
planning.contains(r#"{"questions":[{ ... }]}"#),
"brief 必须给出 questions 外壳,不能只说「单题问题」"
);
for field in ["`id`", "`header`", "`question`", "`options`"] {
assert!(planning.contains(field), "brief 缺少字段 {field}");
}
assert!(
planning.contains("answerFormat"),
"brief 要点名这个真实踩过的坑"
);
for stated in [
format!("不超过 {AGENT_RUNTIME_USER_INPUT_MAX_HEADER_CHARS} 字符"),
format!("不超过 {AGENT_RUNTIME_USER_INPUT_MAX_QUESTION_CHARS} 字符"),
format!("不超过 {AGENT_RUNTIME_USER_INPUT_MAX_OPTION_LABEL_CHARS} 字符"),
format!("不超过 {AGENT_RUNTIME_USER_INPUT_MAX_OPTION_DESCRIPTION_CHARS} 字符"),
format!(
"{AGENT_RUNTIME_USER_INPUT_MIN_OPTIONS}{AGENT_RUNTIME_USER_INPUT_MAX_OPTIONS}"
),
] {
assert!(
planning.contains(&stated),
"brief 与 schema 不一致:缺少「{stated}」"
);
}
}
#[test]
fn project_planning_role_brief_is_isolated_to_its_manifest_overlay() {
let planning = game_creator_agent_runtime_role_overlay_prompt(
@@ -11,13 +11,13 @@ pub(crate) const AGENT_RUNTIME_USER_INPUT_STATUS_CANCELLED: &str = "cancelled";
const AGENT_RUNTIME_USER_INPUT_SIDECAR_MAX_BYTES: usize = 128 * 1024;
const AGENT_RUNTIME_USER_INPUT_MAX_QUESTIONS: usize = 3;
const AGENT_RUNTIME_USER_INPUT_MIN_OPTIONS: usize = 2;
const AGENT_RUNTIME_USER_INPUT_MAX_OPTIONS: usize = 3;
pub(crate) const AGENT_RUNTIME_USER_INPUT_MIN_OPTIONS: usize = 2;
pub(crate) const AGENT_RUNTIME_USER_INPUT_MAX_OPTIONS: usize = 3;
const AGENT_RUNTIME_USER_INPUT_MAX_ID_CHARS: usize = 64;
const AGENT_RUNTIME_USER_INPUT_MAX_HEADER_CHARS: usize = 12;
const AGENT_RUNTIME_USER_INPUT_MAX_QUESTION_CHARS: usize = 400;
const AGENT_RUNTIME_USER_INPUT_MAX_OPTION_LABEL_CHARS: usize = 60;
const AGENT_RUNTIME_USER_INPUT_MAX_OPTION_DESCRIPTION_CHARS: usize = 240;
pub(crate) const AGENT_RUNTIME_USER_INPUT_MAX_HEADER_CHARS: usize = 12;
pub(crate) const AGENT_RUNTIME_USER_INPUT_MAX_QUESTION_CHARS: usize = 400;
pub(crate) const AGENT_RUNTIME_USER_INPUT_MAX_OPTION_LABEL_CHARS: usize = 60;
pub(crate) const AGENT_RUNTIME_USER_INPUT_MAX_OPTION_DESCRIPTION_CHARS: usize = 240;
/// 一份 schema 合法的澄清问询在线上最多可能有多长(字符)。
///