将策划 V2 既有阻断校验回灌给 Provider

把当前 question/GDD 硬校验契约写入 V2 system prompt,不扩大校验范围或新增门禁

校验失败的一次重试改为列出具体阻断原因并要求逐项修复

同步 Runtime V2 技术方案和项目决策记录
This commit is contained in:
2026-09-04 07:40:36 +00:00
parent f35ec812d9
commit 45568797f9
3 changed files with 27 additions and 3 deletions
@@ -625,6 +625,21 @@ GDD 输出必须严格使用下面的字段名和层级;不要增加其它字
}
}
decisions[] 每项只能有 id、topic、state、answerSource、round、answerSummaryanswerSource 可以省略,但不能使用 decision、question、answer 或其它字段。state 只能是 confirmed、assumption_pending、prototype_pending;没有被用户明确决定、由 Agent 根据上下文补出的内容使用 assumption_pending,并将 answerSource 记为 agent_inferred。prototype_pending 决定必须在 prototypeValidationItems 中有同 id 的 question、microPrototype、observation、passCriterion。
以下是当前 Runtime 已存在的输出硬校验契约;不是新增门禁。返回前请逐项检查,避免因类型、数量、长度或额外字段导致输出被拒收:
- 所有 JSON 字段名必须使用上面给出的 camelCasequestion、选项、GDD 及其嵌套对象不能增加未列出的字段。所有受校验的文本字段必须首尾无空白,不得包含回车、NUL、DEL 或其它不允许的控制字符。
- question 必须是对象,且只能包含 id、header、question、optionsid 必须是小写 snake_case、132 个字符;header 是 1~120 个字符的非空字符串;question 是 1~400 个字符的非空字符串;options 必须是 2~4 项数组。
- 每个 question option 必须是对象且只能包含 label、descriptionlabel 必须非空、180 个字符,description 必须非空、1400 个字符;所有 label 不能重复。
- GDD 的 schemaVersion 必须严格为 plan-gdd.v2game、decisions、prototypeValidationItems 必须存在且类型正确;game 不要输出 platformFacts(由 Runtime 注入固定平台事实)。
- game.title 为 180 个字符;genre.primary 为 140 个字符,genre.fusion 可以为 null,否则必须是 1~40 个字符的字符串。
- artStyle.visualType 为 180 个字符;keywords 必须是 3~5 个互不重复的字符串,每项 1~32 个字符;moodAndColor、mvpArtBoundary 各为 1400 个字符;oneLiner 必须是 4590 个字符。
- pillars 必须是 2~4 项;每项 name 必须唯一且为 140 个字符,playerFeel 和 mechanism 各为 1240 个字符,decisionState 只能是 confirmed、assumption_pending、prototype_pending。
- coreLoop 必须是 4~8 个非空字符串,每步 1~120 个字符;targetUsers 的 coreUsers、preferences、sessionLength 各为 1240 个字符,referenceGames 必须是数组且最多 5 项,每项为 1~80 个字符。
- mvpSystems 必须是 36 项;每项 system 必须唯一且为 140 个字符,minimalFunction、whyRequired、verifyMethod 各为 1240 个字符,decisionState 只能使用上述三种状态。
- outOfScope 必须是 1~12 个互不重复的字符串,每项 1~80 个字符;creatorTips 的 doFirst、deferForNow、howToVerify、expandWhen 各为 1400 个字符。
- decisions 必须是 1~64 项且 id 不能重复;第一项必须是唯一的 initial-requeststate 必须为 confirmed、round 必须为 0。其它 id 必须以小写字母开头,只能使用小写字母、数字和连字符,长度 1~64;topic 为 1~120 个字符;state 只能使用上述三种状态;round 必须是非负整数。
- decisions[].answerSummary 为非空字符串;initial-request 最多 4001 个字符,其它决定最多 400 个字符。answerSource 可以省略或使用任意值,Runtime 不因其缺失/未知值单独拒收;不要使用 V1 的 default_pending / default 语义。
- prototypeValidationItems 必须是数组,最多 3 项;没有 prototype_pending 决定时必须为空;有 prototype_pending 决定时必须与这些决定按 id 一一对应,不能多也不能少。每项 id 只能使用小写字母、数字和连字符,长度 1~64,且 question、microPrototype、observation、passCriterion 都必须是 1400 个字符的非空字符串。
"#;
let system = platform_llm::LlmMessage::system(format!(
"你是 Planning Session V2 的立项策划 Agent。当前会话 {},回合 {}。只返回一个合法 JSON object,不要 Markdown 代码围栏、解释文字、Supervisor、子 Agent、委派或验收协议。\n\n{}\n\n出稿前必须先确认三项核心闭环信息:玩家核心行为(玩家每一局反复做什么)、单局目标/核心循环(怎样算完成一局)、MVP 制作边界(首个可玩版本做什么、不做什么)。只要其中一项仍然只是 Agent 推断、没有出现在用户需求或用户回答中,就继续只问一个最关键的问题,不能用 assumption_pending 代替用户确认;主题包装、美术、数值和次要系统可以先使用 assumption_pending,并将 answerSource 记为 agent_inferred。不要重复已经回答的问题。\n\n如果需要向用户确认,只返回 {{\"kind\":\"question\",\"question\":{{\"id\":\"snake_case_id\",\"header\":\"当前要决定:...\",\"question\":\"...\",\"options\":[{{\"label\":\"方案 A\",\"description\":\"...\"}},{{\"label\":\"方案 B\",\"description\":\"...\"}}]}}}}。如果信息足够或问题数已达到上限,只返回上面完整形状的 {{\"kind\":\"gdd\",\"gdd\":...}}。GDD 必须完整填写所有字段,不要省略字段。\n\n{}",
@@ -846,10 +861,11 @@ where
Ok(output) => break output,
Err(detail) if policy_retry < 1 => {
policy_retry = policy_retry.saturating_add(1);
let retry_detail = detail.replace('\n', "\n- ");
attempt_prompt = format!(
"{}\n\n【Runtime上一次输出未通过策划协议:{}。请只返回合法 JSON;当前只允许返回合法 question 或完整 GDD,不要输出解释文字。{}",
"{}\n\n阻断校验失败】Runtime 拒绝了上一次输出,具体原因如下:\n- {}\n请针对以上原因逐项修复,保持未涉及内容不变,只返回合法 JSON;当前只允许返回合法 question 或完整 GDD,不要输出解释文字。{}",
prompt,
detail,
retry_detail,
if start.session.question_limit.is_some_and(|limit| {
start.session.question_count >= limit
}) {
@@ -35,6 +35,14 @@
- 验证方式:V2 解析 `assumption_pending` 不报错并落盘为 `assumption_pending/agent_inferred``default_pending` 不作为 V2 合法状态;核心三项未确认时提示词要求继续问询;相关 Rust/TS 定向测试、类型和编码检查通过。
- 关联文档:`docs/technical/【技术方案】策划会话RuntimeV2接入与旧链路退役-2026-09-03.md``apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/planning_policy_v2.rs`
## 2026-09-04 PlanningSessionRuntime V2 将既有输出阻断原因回灌给 Provider
- 背景:原型已将会导致输出拒收的字段、类型、数量和长度契约写入提示词,并在校验失败重试时回灌具体原因;生产 V2 仍只有简要 GDD 形状提示,模型可能重复犯同一结构错误。
- 决策:生产 V2 只同步当前已经存在的 question/GDD 校验契约到 Provider system prompt,并在现有一次重试中明确列出本次阻断原因、要求逐项修复;不扩大校验范围、不新增门禁、不增加重试次数,也不把 `answerSource` 变成阻断条件。
- 影响范围:`planning_session_v2.rs` 的 Provider prompt 与现有非法输出重试提示;`planning_policy_v2.rs` 校验逻辑、问询上限和持久化契约不变。
- 验证方式:运行 Planning V2 定向 Rust 测试、`cargo fmt --check``git diff --check`,确认提示词构造和现有校验路径通过;不改变既有校验结果。
- 关联文档:`docs/technical/【技术方案】策划会话RuntimeV2接入与旧链路退役-2026-09-03.md``apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/planning_session_v2.rs`
## 2026-09-03 AGC 登录 route event 使用 handler 已验证主体归属
- 背景:登录请求进入时尚未拥有 `AuthenticatedAccessToken`,通用 tracking middleware 无法从响应 extensions 归属登录成功用户;将 AGC marker 直接写入按用户/业务日幂等的 `daily_login` 又会受到不同来源登录顺序影响。
@@ -717,7 +717,7 @@ hydrate_planning_session_v2
- 对已存在 V2 Session 的项目,打开项目时先 hydrate V2;没有 V2 authority 的旧项目继续走旧读取路径,避免误把旧项目数据当成 V2。
- P3 已完成;旧会话 `legacy_retired` 封存、入口彻底关闭和真实 Provider/UI 全链路回归仍属于 P4/P5。
P3 首轮人工测试暴露的问题已在进入 P4 前修正:做方案创建工作区不再额外调用自动项目命名 Provider;策划等待态立即显示处理中提示;V2 GDD 提示词明确给出完整嵌套字段和 `decisions[]` 契约;失败结果不重复渲染,GDD 结构错误给出可操作的重试提示。严格解析和失败不落盘成功产物的规则保持不变。
P3 首轮人工测试暴露的问题已在进入 P4 前修正:做方案创建工作区不再额外调用自动项目命名 Provider;策划等待态立即显示处理中提示;V2 GDD 提示词明确给出完整嵌套字段和 `decisions[]` 契约;当前已存在的 question/GDD 硬校验(字段类型、数量、长度、额外字段和 prototype_pending 对应关系)同步写入 Provider 提示词,不新增校验范围或门禁;输出校验失败的重试提示携带具体阻断原因,要求逐项修复;失败结果不重复渲染,GDD 结构错误给出可操作的重试提示。严格解析和失败不落盘成功产物的规则保持不变。
### P4:灰度、真实 Provider 与回归验收