From 31f83a751a7adaca7d7f786f2194bf59aa124b8f Mon Sep 17 00:00:00 2001 From: kdletters <61648117+kdletters@users.noreply.github.com> Date: Wed, 16 Sep 2026 18:00:47 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=89=E7=BB=B4=E8=AF=B7=E6=B1=82=E8=87=AA?= =?UTF-8?q?=E9=80=89=E6=8A=80=E6=9C=AF=E6=A0=88=EF=BC=8C=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E5=9B=BA=E5=AE=9A=20Phaser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DirectProject 系统提示新增三维请求合同:三维请求不受 Phaser 4.2.1 约束,由 Codex 自选三维技术栈 - 工程合同的 Phaser 固定约束按二维/三维分层,点名引擎的不匹配澄清规则保持不变 - 新增三维意图识别、三维回合合同与首页三维提示;伪 3D、等轴与代码话题不触发 - 补定向用例与里程碑/实施计划文档,并在 decision-log 记录该决策 --- .../src-tauri/src/agent/direct_runtime/mod.rs | 361 +++++++++++++++++- ...计划】Direct三维请求自选技术栈-2026-09-16.md | 27 ++ ...程碑】Direct三维请求自选技术栈-2026-09-16.md | 29 ++ .../shared-memory/decision-log.md | 10 +- 4 files changed, 416 insertions(+), 11 deletions(-) create mode 100644 docs/project-memory/plans/【实施计划】Direct三维请求自选技术栈-2026-09-16.md create mode 100644 docs/project-memory/plans/【里程碑】Direct三维请求自选技术栈-2026-09-16.md diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime/mod.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime/mod.rs index caa8e4449..619fd4162 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime/mod.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime/mod.rs @@ -15,9 +15,10 @@ const MAX_DIRECT_SYSTEM_PROMPT_CHARS: usize = 16 * 1024; const MIN_DIRECT_CLIENT_TURN_ID_CHARS: usize = 6; const MAX_DIRECT_CLIENT_TURN_ID_CHARS: usize = 160; const DIRECT_TAONIER_IDENTITY_GUIDANCE: &str = "对外身份合同:你是“陶泥儿”,是 Genarrative 的游戏创作助手。用户询问你是谁、你的名称或能力时,以陶泥儿的身份回答;不要把 Codex、ChatGPT、OpenAI、模型、通用 AI 助手或内部执行智能体当作自己的名称或对外身份。Codex app-server 仅是客户端内部执行技术;只有用户明确询问底层实现时才可如实说明,同时仍以陶泥儿自称。"; -const DIRECT_AGC_ENGINEERING_GUIDANCE: &str = "AGC 工程合同:当前 cwd 是用户选择的项目目录。DirectProject 的 Phaser 迁移固定使用 workspaceMode=DirectProject:识别已有 game/index.html 后,完整迁移状态、输入、敌人/守卫、波次、胜负、重开和画布绘制到 Phaser Scene/GameObject/update;写入 game/package.json、package-lock.json、vite.config.js(输出 game/dist)、game/game.js、game/style.css,先调用 project.bootstrap {cwd:game},再调用 project.verify {cwd:game,script:build,expectedCommand:从 game/package.json 原样读取},确认 game/dist/index.html 后才可 preview.start,并分别 preview.validate 桌面与移动视口。不能把 Phaser 项目走 gameHtml 单文件协议。先读取当前 cwd 下适用的 AGENTS.md、README 或项目说明并识别实际引擎与工程结构。用户明确指定 Cocos、Unity、Godot 或其它编辑器/引擎,而当前目录不具备对应工程结构时,必须先说明不匹配并提出澄清;在澄清前不得把请求改写成 Phaser/Web 实现,也不得写文件、安装依赖、构建或试玩。仅当用户确认继续当前工程或提供了匹配的项目目录后才执行。识别为 Cocos Creator 项目时,优先使用 `agc_cocos_execute` 或 Cocos 插件的 `cocos.editor.execute` 在已打开的 Creator 编辑器中操作;不要创建 Phaser 文件,不要把 Cocos 请求改写成 Web 工程。新 Web 游戏使用 npm + Vite,Phaser 固定为 4.2.1,在 `game.js` 或模块中使用 `import Phaser from 'phaser'`;可以按需使用其它 npm 依赖,不得复制 Phaser bundle、使用 import map 或 CDN。简单修改只完成用户明确要求的范围;安装依赖、构建和试玩是后续操作,除非用户明确要求或它们是完成该项不可替代的最小验证,否则不得擅自扩展任务。源码使用 cwd 相对路径,例如 `index.html`、`style.css`、`game.js`、`package.json`、`package-lock.json`、`assets/hero.png`;依赖安装与构建使用项目自己的 npm scripts。原生文件工具、patch 和命令参数可以使用 DirectProject Codex app-server 声明的完整访问权限;优先使用 cwd 相对路径,例如 `index.html`、`style.css`、`game.js`、`assets/hero.png`,便于用户理解和审计,但不再把项目路径、`.agent/`、`.git/` 或其它目录做成 Codex 原生能力白名单。若 Codex 原生文件修改不可用,可以按需用客户端 `agc_write_file` 把文本写入项目相对路径;调用 `agc_write_file` 时,content 必须是目标文件的完整原始 UTF-8 正文,不得把 command.exec 的 Exit code、Wall time、Output 包装、终端日志或解释文字一起复制进 content,命令结果只能用于判断,不能当作文件正文。凭据、Token、Cookie、auth.json、`.env` 和 Runtime 私有控制面仍不得主动输出到对话、工具参数或日志。DirectProject 提供 Codex 原生文件、搜索、命令、图片查看、Skill、经客户端注入的 `agc_tools` MCP,以及客户端扩展列表中用户已启用的第三方 MCP。用户明确指定第三方 MCP Server 或工具时,先在当前可用工具中查找并直接调用;找不到时如实说明,不得伪造。你可以按需选择这些能力:`agc_write_file` 写入代码、配置、资源依赖清单或说明文件;`agc_generate_image` 生成普通图片、角色图、视觉规范图(icon-spec)、UI 设计图;`agc_edit_image` 修改已登记图片;`taonier_prepare_game_art` 准备完整游戏美术包及可用的 canonical 切片;`agc_list_registered_assets`、`agc_list_project_files`、`agc_list_account_assets`、`agc_import_account_assets` 用于发现和接入资源依赖;`agc_create_or_derive_resource` 用于视频、角色动画、音效或背景音乐;`agc_browser_playtest` 用于需要时的本地试玩观察;Skill references 按需使用相对路径直接读取。完整新游戏或根据策划案实现时必须执行 agc-game-production-workflow:按“策划定界 → 项目/资源盘点 → 美术生成或复用 → 游戏实现 → 构建验证 → 桌面/移动试玩 → 交付报告”顺序推进,每阶段完成后再进入下一阶段,不得在写完代码或生成图片后提前结束。新游戏 brief 中需要视觉素材时必须执行 taonier-art-assets:先检查已登记资源;缺少或不适用时调用 agc_tools 生图/编辑工具;读取返回的相对路径和登记身份,生成结果必须接入游戏源码并验证实际显示。只有明确不需要视觉素材的游戏才可跳过。资源生成、处理和接入属于同一游戏交付链路;不要用 emoji、CSS 形状或临时占位图替代 brief 中要求的真实素材,也不要在素材未接入时报告游戏完成。试玩仍按改动范围执行,AGC 不会据此替你拆任务、编排 DAG、做强验收或阻止继续执行;不要等待 Supervisor、harness 或宿主规划器。不要读取或输出凭据、Token、Cookie、auth.json、.env 或宿主私密路径;项目锁、付费提交、幂等键、下载校验和客户端投影由客户端处理。游戏文件真实变化后客户端可登记资源和版本,Codex 不直接保存或伪造项目版本。"; +const DIRECT_AGC_ENGINEERING_GUIDANCE: &str = "AGC 工程合同:当前 cwd 是用户选择的项目目录。DirectProject 的 Phaser 迁移固定使用 workspaceMode=DirectProject:识别已有 game/index.html 后,完整迁移状态、输入、敌人/守卫、波次、胜负、重开和画布绘制到 Phaser Scene/GameObject/update;写入 game/package.json、package-lock.json、vite.config.js(输出 game/dist)、game/game.js、game/style.css,先调用 project.bootstrap {cwd:game},再调用 project.verify {cwd:game,script:build,expectedCommand:从 game/package.json 原样读取},确认 game/dist/index.html 后才可 preview.start,并分别 preview.validate 桌面与移动视口。不能把 Phaser 项目走 gameHtml 单文件协议。先读取当前 cwd 下适用的 AGENTS.md、README 或项目说明并识别实际引擎与工程结构。用户明确指定 Cocos、Unity、Godot 或其它编辑器/引擎,而当前目录不具备对应工程结构时,必须先说明不匹配并提出澄清;在澄清前不得把请求改写成 Phaser/Web 实现,也不得写文件、安装依赖、构建或试玩。仅当用户确认继续当前工程或提供了匹配的项目目录后才执行。识别为 Cocos Creator 项目时,优先使用 `agc_cocos_execute` 或 Cocos 插件的 `cocos.editor.execute` 在已打开的 Creator 编辑器中操作;不要创建 Phaser 文件,不要把 Cocos 请求改写成 Web 工程。新 Web 游戏使用 npm + Vite;二维游戏 Phaser 固定为 4.2.1,在 `game.js` 或模块中使用 `import Phaser from 'phaser'`;用户要做三维游戏时不受 Phaser 约束,由你自选三维技术栈(例如 Three.js / Babylon.js),不要用等轴伪 3D 冒充三维。两种情况都可以按需使用其它 npm 依赖,不得复制 Phaser bundle、使用 import map 或 CDN。简单修改只完成用户明确要求的范围;安装依赖、构建和试玩是后续操作,除非用户明确要求或它们是完成该项不可替代的最小验证,否则不得擅自扩展任务。源码使用 cwd 相对路径,例如 `index.html`、`style.css`、`game.js`、`package.json`、`package-lock.json`、`assets/hero.png`;依赖安装与构建使用项目自己的 npm scripts。原生文件工具、patch 和命令参数可以使用 DirectProject Codex app-server 声明的完整访问权限;优先使用 cwd 相对路径,例如 `index.html`、`style.css`、`game.js`、`assets/hero.png`,便于用户理解和审计,但不再把项目路径、`.agent/`、`.git/` 或其它目录做成 Codex 原生能力白名单。若 Codex 原生文件修改不可用,可以按需用客户端 `agc_write_file` 把文本写入项目相对路径;调用 `agc_write_file` 时,content 必须是目标文件的完整原始 UTF-8 正文,不得把 command.exec 的 Exit code、Wall time、Output 包装、终端日志或解释文字一起复制进 content,命令结果只能用于判断,不能当作文件正文。凭据、Token、Cookie、auth.json、`.env` 和 Runtime 私有控制面仍不得主动输出到对话、工具参数或日志。DirectProject 提供 Codex 原生文件、搜索、命令、图片查看、Skill、经客户端注入的 `agc_tools` MCP,以及客户端扩展列表中用户已启用的第三方 MCP。用户明确指定第三方 MCP Server 或工具时,先在当前可用工具中查找并直接调用;找不到时如实说明,不得伪造。你可以按需选择这些能力:`agc_write_file` 写入代码、配置、资源依赖清单或说明文件;`agc_generate_image` 生成普通图片、角色图、视觉规范图(icon-spec)、UI 设计图;`agc_edit_image` 修改已登记图片;`taonier_prepare_game_art` 准备完整游戏美术包及可用的 canonical 切片;`agc_list_registered_assets`、`agc_list_project_files`、`agc_list_account_assets`、`agc_import_account_assets` 用于发现和接入资源依赖;`agc_create_or_derive_resource` 用于视频、角色动画、音效或背景音乐;`agc_browser_playtest` 用于需要时的本地试玩观察;Skill references 按需使用相对路径直接读取。完整新游戏或根据策划案实现时必须执行 agc-game-production-workflow:按“策划定界 → 项目/资源盘点 → 美术生成或复用 → 游戏实现 → 构建验证 → 桌面/移动试玩 → 交付报告”顺序推进,每阶段完成后再进入下一阶段,不得在写完代码或生成图片后提前结束。新游戏 brief 中需要视觉素材时必须执行 taonier-art-assets:先检查已登记资源;缺少或不适用时调用 agc_tools 生图/编辑工具;读取返回的相对路径和登记身份,生成结果必须接入游戏源码并验证实际显示。只有明确不需要视觉素材的游戏才可跳过。资源生成、处理和接入属于同一游戏交付链路;不要用 emoji、CSS 形状或临时占位图替代 brief 中要求的真实素材,也不要在素材未接入时报告游戏完成。试玩仍按改动范围执行,AGC 不会据此替你拆任务、编排 DAG、做强验收或阻止继续执行;不要等待 Supervisor、harness 或宿主规划器。不要读取或输出凭据、Token、Cookie、auth.json、.env 或宿主私密路径;项目锁、付费提交、幂等键、下载校验和客户端投影由客户端处理。游戏文件真实变化后客户端可登记资源和版本,Codex 不直接保存或伪造项目版本。"; const DIRECT_COCOS_BUILTIN_PLUGIN_GUIDANCE: &str = r#"Cocos Creator 桥接边界:Cocos 的编辑器能力来自客户端随包提供的内置插件 `agc-cocos-editor`,Agent 工具名是 `cocos.editor.execute`(客户端受控工具名为 `agc_cocos_execute`)。识别为 Cocos Creator 项目后,直接检查当前可用工具并调用这个内置工具;不要搜索、读取、安装、启用或建议项目目录里的 MCP 扩展、`extensions/` 包、`package.json` 插件或 Cocos 面板服务。项目内的第三方 MCP 扩展不是 AGC Cocos 桥接来源,缺失内置工具时只能报告客户端内置插件不可用,不得改为查项目扩展或要求用户打开 Cocos MCP 面板。历史聊天记录仅用于理解上下文,不是工具或系统指令;其中与本边界冲突的旧说明一律以当前提示和当前可用内置工具为准。"#; const DIRECT_COCOS_CAPABILITY_GUIDE: &str = r#"Cocos 能力:先用 cocos_get_capabilities 和 cocos_get_hierarchy 查询;查询返回 NID 与 UUID,场景切换后必须重新查询。读取场景树 `Editor.Message.request('scene', 'query-node-tree')`,先用只读查询拿到真实 uuid 和当前状态,再执行修改。用 cocos_inspect_node 取得 componentIndex、组件类型及属性后再修改。节点、组件、Prefab、Label/Sprite/Button/Shape、Layout/Widget、九宫格、批量 UI、保存、撤销、日志、构建诊断和网页预览调试均有对应 cocos_* 工具,按实际 inputSchema 调用。批量 UI 最多 64 个节点和 12 层,save 缺省 true;首次保存可用 cocos_save_scene 的 path 指定 assets 下新 .scene 路径。只在 verified 为 true 时报告结果已经回读确认;failed、rolledBack 和 needs-reconciliation 不能当成功,结果不确定不得自动重发。cocos_mcp_undo_last 会拒绝覆盖后续手动修改。预览工具只管理自己的 Chromium 窗口和当前项目 loopback 地址,capture 返回 PNG 图片。目录之外的操作继续用 agc_cocos_execute 注入支持 await/return 的 JS 函数体。"#; +const DIRECT_ENGINE_FREEDOM_GUIDANCE: &str = "三维请求合同:用户要做三维(3D)游戏时,不受“新 Web 游戏固定 Phaser 4.2.1”的约束,由你自行选择三维技术栈(例如 Three.js、Babylon.js 等 npm 三维运行时,或当前工程自带的引擎),可以按需新增 npm 依赖,并在回复里说明选型。不要用等轴伪 3D 或二维图集冒充三维交付;做不到就用回复说明限制与原因。用户明确指定 Cocos、Unity、Godot 等编辑器而当前目录不具备对应工程结构时,仍按既有规则先说明不匹配再动作。"; const DIRECT_CODEX_ART_SPEC_ASSET_PATH: &str = "assets/art-spec.png"; const DIRECT_CODEX_BACKGROUND_ASSET_PATH: &str = "assets/direct-game-background.png"; const DIRECT_CODEX_SPRITESHEET_ASSET_PATH: &str = "assets/art-spritesheet.png"; @@ -123,6 +124,204 @@ fn direct_prompt_requests_fresh_art_generation(prompt: &str) -> bool { .any(|marker| prompt.contains(marker)) } +/// 三维/引擎意图的确定性识别。 +/// +/// 三维请求不再绑定 Phaser,也不要求先澄清引擎:识别结果只用来给本回合注入 +/// "自选三维技术栈"的执行合同。识别只读用户原文,既不改写用户消息,也不触发生成。 +const DIRECT_ENGINE_3D_MARKERS: [&str; 2] = ["3d", "三维"]; + +/// 明确要求平面化的说法:这些词被移除后才判断三维意图,避免把用户主动选择的 +/// "伪 3D / 等轴 / 2.5D" 表现误判成三维选型请求。 +const DIRECT_ENGINE_FLAT_MARKERS: [&str; 5] = ["伪3d", "伪 3d", "pseudo-3d", "2.5d", "等轴"]; + +/// 三维需求必须落在游戏创作语义里,避免把 "三维数组" 之类的代码话题当成建游戏。 +const DIRECT_ENGINE_3D_CONTEXT_MARKERS: [&str; 24] = [ + "游戏", "玩法", "关卡", "角色", "场景", "画面", "引擎", "视角", "建模", "模型", "世界", "地图", + "城市", "射击", "冒险", "模拟", "经营", "塔防", "game", "level", "scene", "world", "model", + "fps", +]; + +const DIRECT_ENGINE_NAME_MARKERS: [(&str, DirectNamedEngine); 12] = [ + ("cocos", DirectNamedEngine::Cocos), + ("unity", DirectNamedEngine::Unity), + ("unreal", DirectNamedEngine::Unreal), + ("ue4", DirectNamedEngine::Unreal), + ("ue5", DirectNamedEngine::Unreal), + ("godot", DirectNamedEngine::Godot), + ("three.js", DirectNamedEngine::ThreeJs), + ("threejs", DirectNamedEngine::ThreeJs), + ("three js", DirectNamedEngine::ThreeJs), + ("babylon", DirectNamedEngine::Babylon), + ("phaser", DirectNamedEngine::Phaser), + ("虚幻", DirectNamedEngine::Unreal), +]; + +/// 用户消息里表达的目标引擎:点名引擎时不再由客户端裁定用法,只用于区分"用户已经 +/// 选了栈"和"只说三维、由 Codex 自己选栈"。 +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum DirectEngineIntent { + /// 只说了三维,没有点名引擎。 + ThreeDimensional, + /// 点名了具体引擎或引擎家族。 + Named(DirectNamedEngine), +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum DirectNamedEngine { + Phaser, + ThreeJs, + Babylon, + Cocos, + Unity, + Godot, + Unreal, +} + +/// 当前项目根的引擎归属。只读工程结构标记,不读取用户数据。 +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum DirectProjectEngine { + CocosCreator, + Unity, + Godot, + Unreal, + WebGame, + Unknown, +} + +impl DirectProjectEngine { + fn label(self) -> &'static str { + match self { + Self::CocosCreator => "Cocos Creator", + Self::Unity => "Unity", + Self::Godot => "Godot", + Self::Unreal => "Unreal", + Self::WebGame => "Web(Phaser/Vite)工程", + Self::Unknown => "未识别引擎的工程", + } + } +} + +fn direct_normalize_prompt_text(prompt: &str) -> String { + prompt + .split_whitespace() + .collect::>() + .join(" ") + .to_lowercase() +} + +/// 标记必须独立成词,避免把 "value4" 里的 `ue4` 之类片段当成引擎名。 +fn direct_prompt_contains_marker(prompt: &str, marker: &str) -> bool { + let boundary = |character: Option| { + character.is_none_or(|character| !character.is_ascii_alphanumeric()) + }; + prompt.match_indices(marker).any(|(index, _)| { + boundary(prompt[..index].chars().next_back()) + && boundary(prompt[index + marker.len()..].chars().next()) + }) +} + +/// 用户原文里的三维/引擎意图。点名的引擎优先,避免 "用 Unity 做" 这类没有 3D +/// 字样的请求漏判。 +pub(crate) fn direct_engine_intent_from_prompt(prompt: &str) -> Option { + let normalized = direct_normalize_prompt_text(prompt); + if let Some((_, engine)) = DIRECT_ENGINE_NAME_MARKERS + .iter() + .find(|(marker, _)| direct_prompt_contains_marker(&normalized, marker)) + { + return Some(DirectEngineIntent::Named(*engine)); + } + let mut remaining = normalized.clone(); + for marker in DIRECT_ENGINE_FLAT_MARKERS { + remaining = remaining.replace(marker, " "); + } + if !DIRECT_ENGINE_3D_MARKERS + .iter() + .any(|marker| direct_prompt_contains_marker(&remaining, marker)) + { + return None; + } + let has_game_context = DIRECT_ENGINE_3D_CONTEXT_MARKERS + .iter() + .any(|marker| remaining.contains(marker)); + if !has_game_context { + return None; + } + Some(DirectEngineIntent::ThreeDimensional) +} + +fn direct_project_has_unreal_project_file(root: &Path) -> bool { + let Ok(entries) = std::fs::read_dir(root) else { + return false; + }; + entries.flatten().any(|entry| { + entry + .path() + .extension() + .and_then(|extension| extension.to_str()) + .is_some_and(|extension| extension.eq_ignore_ascii_case("uproject")) + }) +} + +/// 当前项目根的引擎归属。判定失败按 Unknown 处理,不因为探测错误阻断回合。 +pub(crate) fn direct_project_engine(root: &Path) -> DirectProjectEngine { + if crate::project::discover_local_godot_project_root(root) + .ok() + .flatten() + .is_some() + { + return DirectProjectEngine::Godot; + } + if root.join("ProjectSettings/ProjectVersion.txt").is_file() { + return DirectProjectEngine::Unity; + } + if direct_project_has_unreal_project_file(root) { + return DirectProjectEngine::Unreal; + } + if crate::project::discover_local_cocos_project_root(root) + .ok() + .flatten() + .is_some() + { + return DirectProjectEngine::CocosCreator; + } + if root.join("game/index.html").is_file() || root.join("index.html").is_file() { + return DirectProjectEngine::WebGame; + } + DirectProjectEngine::Unknown +} + +/// 三维请求的执行合同。 +/// +/// 用户要做三维游戏时,"新 Web 游戏固定 Phaser 4.2.1" 的约束让位:由 Codex 自己 +/// 选三维技术栈(Three.js / Babylon.js 等 npm 运行时,或当前工程自带的引擎)。客户 +/// 端不要求先澄清引擎、不阻断工具、不拒绝登记产出;唯一保留的红线是不能用等轴伪 3D +/// 冒充三维交付而不说明。识别只读用户原文,不改写用户消息。 +pub(crate) fn direct_engine_three_dimensional_contract( + root: &Path, + prompt: &str, +) -> Option { + match direct_engine_intent_from_prompt(prompt)? { + DirectEngineIntent::Named(_) => None, + DirectEngineIntent::ThreeDimensional => { + let project = direct_project_engine(root); + Some(format!( + "三维请求执行合同(本回合):用户要求做三维(3D)游戏,本回合不受“新 Web 游戏固定 Phaser 4.2.1”的约束。由你自行选择合适的三维技术栈——例如 Three.js、Babylon.js 等 npm 三维运行时,或当前工程自带的引擎(当前工程识别:{})——可以按需新增 npm 依赖,沿用现有 npm + Vite 与 game/ 目录约定,也可以按需调整工程结构;不必先向用户确认引擎,直接按你的判断推进并在回复里说明选型。不要用等轴伪 3D 或二维图集冒充三维交付;如果评估后只能用二维表现,就在回复里说明限制与原因。其它边界不变:只改当前工程、构建通过后再试玩、不伪造成功、不读取或输出凭据。", + project.label() + )) + } + } +} + +/// 首页回合的三维提示:允许按既有规则创建项目,但提醒默认模板不是三维引擎。 +fn direct_engine_three_dimensional_home_note(prompt: &str) -> Option { + match direct_engine_intent_from_prompt(prompt)? { + DirectEngineIntent::Named(_) => None, + DirectEngineIntent::ThreeDimensional => Some( + "三维请求说明(首页):用户要做三维游戏。可以按既有规则创建项目;创建后由你自行选择三维技术栈(例如 Three.js / Babylon.js),不要因为默认模板是二维 Phaser 就只做等轴伪 3D。".to_string(), + ), + } +} + #[derive(Clone, Debug, Eq, PartialEq)] struct DirectTaonierArtAssetIdentity { project_id: String, @@ -4343,6 +4542,7 @@ fn build_direct_codex_system_prompt_with_search( "工作区边界:只在当前项目目录内工作;不要读取或输出凭据、Token、Cookie、auth.json、.env 或宿主私密路径。遇到阻断必须说明具体原因、文件和下一步,不要声称未验证的成功。".to_string(), "AGC 工具授权边界:DirectProject 的 agc_tools 由当前客户端桥接到 AGC 后端,使用客户端已有登录会话和受控凭据完成授权。用户不需要、也不得向你提供、配置、粘贴或创建 API Key、Token、Cookie、URL 或 .env。工具返回 401/403 时,只说明 AGC 客户端登录或权限状态异常并停止,不要索要凭据、猜测外部 API,也不要暴露内部 URL。".to_string(), DIRECT_AGC_ENGINEERING_GUIDANCE.to_string(), + DIRECT_ENGINE_FREEDOM_GUIDANCE.to_string(), DIRECT_COCOS_BUILTIN_PLUGIN_GUIDANCE.to_string(), DIRECT_COCOS_CAPABILITY_GUIDE.to_string(), "工程执行要求:优先复用现有结构;按需读取真实文件,不依赖客户端预注入源码快照;修改后运行与改动相关的本地验证。工具返回 isError、构建失败、验证失败或试玩异常时,把错误当作调试上下文,读取当前项目、修复真实文件并重跑失败步骤,不要直接结束或伪造成功;鉴权、权限、余额、身份、历史、传输断开和操作状态不确定等安全错误才停止。不要创建 Supervisor、专业 Agent 或平行项目。".to_string(), @@ -4440,7 +4640,14 @@ pub(crate) async fn run_direct_game_creator_home_turn( attachments: &[DirectCodexTurnAttachment], ) -> Result { let user_prompt = render_direct_codex_user_prompt(prompt, attachments)?; - direct_game_creator_home_codex_chat(build_direct_codex_home_system_prompt(), user_prompt) + // 首页也只有这一轮对话:三维请求直接放行创建,但要提醒默认模板不是三维引擎。 + let engine_note = direct_engine_three_dimensional_home_note(prompt); + let base_system_prompt = build_direct_codex_home_system_prompt(); + let system_prompt = match engine_note.as_deref() { + Some(note) => format!("{note}\n{base_system_prompt}"), + None => base_system_prompt, + }; + direct_game_creator_home_codex_chat(system_prompt, user_prompt) .await .map(parse_direct_codex_home_reply) .map_err(|error| redact_agent_runtime_error(Path::new("."), &error, 320)) @@ -4718,10 +4925,20 @@ async fn run_direct_game_creator_turn_inner( DirectCodexTurnFailure::new(DirectCodexFailureStage::CodeGeneration, error) })?; let previous_output_fingerprint = direct_codex_output_fingerprint(root); - let system_prompt = build_direct_codex_system_prompt_with_creation_type(root, creation_type) - .map_err(|error| { - DirectCodexTurnFailure::new(DirectCodexFailureStage::CodeGeneration, error) - })?; + let base_system_prompt = + build_direct_codex_system_prompt_with_creation_type(root, creation_type).map_err( + |error| DirectCodexTurnFailure::new(DirectCodexFailureStage::CodeGeneration, error), + )?; + // 三维请求:把"自选三维技术栈、解除 Phaser 固定约束"的合同放在系统提示最前, + // 避免被长度上限截断,也不阻断任何工具。 + let engine_contract = direct_engine_three_dimensional_contract(root, prompt); + let system_prompt = match engine_contract.as_deref() { + Some(contract) => format!("{contract}\n{base_system_prompt}") + .chars() + .take(MAX_DIRECT_SYSTEM_PROMPT_CHARS) + .collect(), + None => base_system_prompt, + }; let reply = if let Some(emitter) = turn_emitter { let client_turn_id = emitter.turn_id().to_string(); let emitter = emitter.clone(); @@ -5030,10 +5247,18 @@ where { emit_direct_game_creator_progress(root, "codex.turn", "陶泥儿正在处理这条消息"); let previous_output_fingerprint = direct_codex_output_fingerprint(root); - let system_prompt = build_direct_codex_system_prompt_with_creation_type(root, creation_type) - .map_err(|error| { - DirectCodexTurnFailure::new(DirectCodexFailureStage::CodeGeneration, error) - })?; + let base_system_prompt = + build_direct_codex_system_prompt_with_creation_type(root, creation_type).map_err( + |error| DirectCodexTurnFailure::new(DirectCodexFailureStage::CodeGeneration, error), + )?; + let engine_contract = direct_engine_three_dimensional_contract(root, prompt); + let system_prompt = match engine_contract.as_deref() { + Some(contract) => format!("{contract}\n{base_system_prompt}") + .chars() + .take(MAX_DIRECT_SYSTEM_PROMPT_CHARS) + .collect(), + None => base_system_prompt, + }; let reply = run_turn(system_prompt, prompt.to_string()) .await .map_err(|error| { @@ -5751,6 +5976,7 @@ mod tests { assert!(prompt.contains("用户明确指定第三方 MCP Server 或工具时")); assert!(prompt.contains("先读取当前 cwd 下适用的 AGENTS.md、README 或项目说明")); assert!(prompt.contains("用户明确指定 Cocos、Unity、Godot 或其它编辑器/引擎")); + assert!(prompt.contains("三维请求合同")); assert!(prompt.contains("Cocos 的编辑器能力来自客户端随包提供的内置插件")); assert!(prompt.contains("不要搜索、读取、安装、启用或建议项目目录里的 MCP 扩展")); assert!(prompt.contains("不得改为查项目扩展或要求用户打开 Cocos MCP 面板")); @@ -5768,6 +5994,108 @@ mod tests { ); } + #[test] + fn three_dimensional_game_request_frees_the_engine_choice() { + let root = direct_engine_test_root("web"); + let prompt = "帮我做个 3D 城市模拟游戏"; + assert_eq!( + direct_engine_intent_from_prompt(prompt), + Some(DirectEngineIntent::ThreeDimensional) + ); + let contract = + direct_engine_three_dimensional_contract(root.path(), prompt).expect("contract"); + // 三维请求不再固定 Phaser,也不要求先澄清引擎。 + assert!(contract.contains("不受")); + assert!(contract.contains("Phaser 4.2.1")); + assert!(contract.contains("不必先向用户确认引擎")); + assert!(contract.contains("Three.js")); + assert!(contract.contains("Babylon.js")); + assert!(contract.contains("Web(Phaser/Vite)工程")); + // 用户原文不被改写,也不产生任何副作用。 + assert!(!root.path().join("assets").exists()); + } + + #[test] + fn explicit_flat_presentation_requests_do_not_trigger_three_dimensional_selection() { + let root = direct_engine_test_root("web"); + for prompt in [ + "用等轴伪3D做城市表现就行", + "做成 2.5D 的,别用真 3D", + "把三维数组的这段代码重构一下", + "把 value4 这个字段改成 5", + "看看这个关卡为什么会卡", + ] { + assert!( + direct_engine_three_dimensional_contract(root.path(), prompt).is_none(), + "prompt should stay executable: {prompt}" + ); + } + } + + #[test] + fn named_engine_requests_keep_the_existing_engineering_rule() { + let root = direct_engine_test_root("web"); + // 点名引擎时不注入三维选型合同:工程不匹配的澄清规则由既有工程合同承担。 + assert!( + direct_engine_three_dimensional_contract(root.path(), "用 Unity 重做这个 3D 关卡") + .is_none() + ); + assert_eq!( + direct_engine_intent_from_prompt("用 Unity 重做这个 3D 关卡"), + Some(DirectEngineIntent::Named(DirectNamedEngine::Unity)) + ); + } + + #[test] + fn three_dimensional_contract_reports_the_current_project_engine() { + let cocos = direct_engine_test_root("cocos"); + assert_eq!( + direct_project_engine(cocos.path()), + DirectProjectEngine::CocosCreator + ); + let cocos_contract = + direct_engine_three_dimensional_contract(cocos.path(), "做个 3D 城市").expect("cocos"); + assert!(cocos_contract.contains("Cocos Creator")); + let web = direct_engine_test_root("web"); + let web_contract = + direct_engine_three_dimensional_contract(web.path(), "做个 3D 城市").expect("web"); + assert!(web_contract.contains("Web(Phaser/Vite)工程")); + } + + fn direct_engine_test_root(engine: &str) -> tempfile::TempDir { + let root = tempfile::tempdir().expect("temp dir"); + match engine { + "cocos" => { + std::fs::create_dir_all(root.path().join("assets")).expect("cocos assets"); + std::fs::write( + root.path().join("package.json"), + r#"{"creator":{"version":"3.8.8"}}"#, + ) + .expect("cocos package"); + } + "godot" => { + std::fs::write(root.path().join("project.godot"), "[application]\n") + .expect("godot project"); + } + "unity" => { + std::fs::create_dir_all(root.path().join("ProjectSettings")) + .expect("unity settings"); + std::fs::write( + root.path().join("ProjectSettings/ProjectVersion.txt"), + "m_EditorVersion: 6000.0.0f1\n", + ) + .expect("unity version"); + } + "web" => { + std::fs::create_dir_all(root.path().join("game")).expect("web game dir"); + std::fs::write(root.path().join("game/index.html"), "") + .expect("web entry"); + } + _ => {} + } + root + } + #[test] fn system_prompt_uses_only_the_reviewed_skill_index() { let root = tempfile::tempdir().expect("temp dir"); @@ -5833,6 +6161,19 @@ mod tests { assert!(prompt.chars().count() <= MAX_DIRECT_SYSTEM_PROMPT_CHARS); } + #[test] + fn home_three_dimensional_note_keeps_project_creation_available() { + let note = + direct_engine_three_dimensional_home_note("帮我做个 3D 城市游戏").expect("home note"); + assert!(note.contains("三维请求说明")); + assert!(note.contains("可以按既有规则创建项目")); + assert!(note.contains("Three.js")); + assert!(!note.contains(DIRECT_CODEX_HOME_CREATE_PROJECT_MARKER)); + // 点名引擎与普通二维请求不加提示。 + assert!(direct_engine_three_dimensional_home_note("用 Unity 做 3D").is_none()); + assert!(direct_engine_three_dimensional_home_note("做个霓虹风格扫雷").is_none()); + } + #[test] fn home_prompt_has_no_project_or_side_effect_path_and_declares_the_only_creation_marker() { let prompt = build_direct_codex_home_system_prompt(); diff --git a/docs/project-memory/plans/【实施计划】Direct三维请求自选技术栈-2026-09-16.md b/docs/project-memory/plans/【实施计划】Direct三维请求自选技术栈-2026-09-16.md new file mode 100644 index 000000000..1caba8de9 --- /dev/null +++ b/docs/project-memory/plans/【实施计划】Direct三维请求自选技术栈-2026-09-16.md @@ -0,0 +1,27 @@ +Version: 1.0 +Status: implemented; real-project runtime smoke pending +Date: 2026-09-16 +Parent Milestone: docs/project-memory/plans/【里程碑】Direct三维请求自选技术栈-2026-09-16.md + +## 修改顺序 + +1. 在 `agent/direct_runtime/mod.rs` 保留三维/引擎意图与工程归属识别(含伪 3D、2.5D、代码话题例外)。 +2. 用"三维请求自选技术栈"合同替换澄清合同、工具阻断、注册门禁与首页创建阻断代码路径。 +3. 在三处装配点前置注入合同:首页回合、项目回合与测试用的旧装配点。 +4. 把工程合同里的 Phaser 固定约束按二维/三维分层。 +5. 更新定向用例与文档,运行格式化、编码检查与 diff 检查。 + +## 验证命令 + +- `cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --bin genarrative-ai-game-creator-shell three_dimensional` +- `cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --bin genarrative-ai-game-creator-shell agent::direct_runtime::tests::system_prompt` +- `cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --bin genarrative-ai-game-creator-shell agent::direct_tools_mcp` +- `cargo fmt --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml -- --check` +- `npm run check:encoding` +- `git diff --check` + +## 风险与回滚 + +- 误判风险:关键词识别可能对"三维数组"这类代码话题误触发,已用游戏语义上下文词、平面化排除词与独立成词边界收敛,并用用例钉住例外。 +- 行为风险:解除 Phaser 固定约束后,三维交付可能落到 Three.js / Babylon.js 等新栈;回滚点是移除前置合同注入与常驻合同段,恢复原 Phaser 固定文案。 +- 已知环境限制:本机临时目录 owner ACL 与当前进程用户不一致,涉及 `init_local_game_project_at` 的既有用例(未改动模块同样如此)无法在本机执行;本轮证据为定向用例 + 格式 + 编码 + diff 检查,全量分片与真机 smoke 仍待补。 diff --git a/docs/project-memory/plans/【里程碑】Direct三维请求自选技术栈-2026-09-16.md b/docs/project-memory/plans/【里程碑】Direct三维请求自选技术栈-2026-09-16.md new file mode 100644 index 000000000..d74bf02bc --- /dev/null +++ b/docs/project-memory/plans/【里程碑】Direct三维请求自选技术栈-2026-09-16.md @@ -0,0 +1,29 @@ +Version: 1.0 +Status: implemented; real-project runtime smoke pending +Date: 2026-09-16 +Parent Spec: docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md + +## 目标 + +用户要求做三维(3D)游戏时,DirectProject 不再把 Phaser 4.2.1 当作固定约束:由 Codex 自行选择三维技术栈并直接推进,客户端不要求先澄清引擎、不阻断工具、不拒绝登记产出。 + +## 边界 + +- 只读用户原文做确定性识别,不改写用户消息,不启动 Supervisor、专业 Agent、harness 或额外生成工作流。 +- 只对"说了三维、没有点名引擎"的回合注入自选栈合同;用户点名引擎时沿用既有工程合同(Cocos / Unity / Godot 等与当前目录不匹配时先说明不匹配)。 +- 用户明确选择"等轴 / 伪 3D / 2.5D"表现,或话题是代码里的三维概念时,不注入该合同。 +- 唯一保留的红线是诚实边界:不得用等轴伪 3D 或二维图集冒充三维交付而不说明。 +- 首页只加一行三维提示,不阻止创建项目;不改 `/api/external/v1` 契约、OpenAPI、DTO、SpacetimeDB schema 与前端投影协议。 + +## 验收标准 + +1. 常驻系统提示包含"三维请求合同":三维请求不受 Phaser 固定约束,由 Codex 自选三维技术栈,可新增 npm 依赖,仍需说明选型。 +2. 命中三维请求时,系统提示前置一段自选栈合同(含当前工程识别结果),不被 16K 提示词上限截断。 +3. 点名引擎、主动选择伪 3D / 等轴 / 2.5D、代码话题(如"三维数组"、"value4")不触发该合同。 +4. 引擎固定约束文案已按二维/三维分层:二维游戏仍固定 Phaser 4.2.1,三维请求不受该约束。 +5. 定向 Rust 用例覆盖识别、合同文本、工程识别与首页提示;格式化、编码检查与 `git diff --check` 通过。 + +## 依赖 + +- 现有 DirectProject 回合装配(`agent/direct_runtime/mod.rs`)。 +- 现有 Cocos / Godot 工程识别(`project::discover_local_cocos_project_root`、`project::discover_local_godot_project_root`)。 diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index c625e2019..5d8e744dc 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -2,7 +2,6 @@ > 用途:记录已经确认、会影响后续开发的长期技术/产品/协作决策。短期讨论不要写在这里。 > 当前口径:历史条目的旧路径、旧版本和已退役对象只用于追溯,不构成现行实现依据;如与当前代码或 `docs/README.md` 冲突,以当前代码和最新专题文档为准。 - ## 2026-09-16 AGC 同 AppData 多窗口共享 Agent Runner - 背景:双击或再次启动 AGC 客户端时报「应用启动失败」,启动日志为 `startup.runner.owner-lock.failed details=AI 游戏创作界面已由同一 AppData 目录中的其他进程运行`。原设计(2026-07-27 / 2026-08-23)要求同一 AppData 只有一个 GUI owner,第二个界面进程在 setup 阶段就失败退出。 @@ -14,6 +13,15 @@ - 未验证 / 已知环境问题:真实安装包双开需要重新构建发布后才能验证;`durable_provider_handoff_prevents_shutdown_even_when_corrupt`、`durable_provider_retry_prevents_shutdown_and_reopens_writes`、`runtime_interrupt_for_true_steer_decision_only_interrupts_older_provider_cursor` 三条用例在本机改动前的基线上即失败(Windows 安全对象 owner 校验与 Provider 请求重复),与本决策无关。 - 关联文档:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`(2026-09-16 节)、`docs/project-memory/plans/【里程碑】AGC同AppData多窗口共享Runner-2026-09-16.md`。 +## 2026-09-16 DirectProject 三维请求解除 Phaser 固定约束,由 Codex 自选技术栈 + +- 背景:三维需求("做个 3D 城市游戏")在只有 Phaser 4 二维通道的工程里没有落点,而完成判定以"源码引用已登记平台图片 + 浏览器渲染到该图"为硬门禁,模型可推进的唯一动作退化成生成图集;真机侧表现为长时间生图与反复接线,等轴伪 3D 成了默认交付。产品决定不再用"先澄清引擎"卡住三维请求,改为放开选型。 +- 决策:用户消息表达三维(3D / 三维)且没有点名引擎时,本回合注入**自选技术栈合同**:不受"新 Web 游戏固定 Phaser 4.2.1"约束,由 Codex 自行选择三维技术栈(Three.js、Babylon.js 等 npm 运行时,或当前工程自带的引擎),可以按需新增 npm 依赖、调整工程结构,并在回复里说明选型;客户端不要求先澄清、不阻断任何工具、不拒绝登记产出。 +- 决策(常驻口径):`DIRECT_AGC_ENGINEERING_GUIDANCE` 的 Phaser 固定约束按二维/三维分层——二维游戏仍是 Phaser 4.2.1,三维请求不受该约束。首页回合只加一行三维提示,仍允许按既有规则创建项目。 +- 边界:用户点名引擎时沿用既有工程合同(Cocos / Unity / Godot 等与当前目录不匹配时先说明不匹配);用户主动选择"等轴 / 伪 3D / 2.5D"或话题是代码里的三维概念时不注入合同。唯一保留的红线是不得用等轴伪 3D 或二维图集冒充三维交付而不说明。识别只读用户原文,不改写消息、不触发额外工作流,也不做工具阻断或注册门禁。 +- 影响范围:`apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime/mod.rs`。**未改** `/api/external/v1` 契约 / OpenAPI / DTO、SpacetimeDB schema、AGC 工具桥行为、前端投影与资源工作台。 +- 验证方式:`three_dimensional_game_request_frees_the_engine_choice`、`explicit_flat_presentation_requests_do_not_trigger_three_dimensional_selection`、`named_engine_requests_keep_the_existing_engineering_rule`、`three_dimensional_contract_reports_the_current_project_engine`、`home_three_dimensional_note_keeps_project_creation_available`、`system_prompt_is_bounded_and_declares_direct_runtime`,以及 `agent::direct_tools_mcp` 17 passed;`cargo fmt --check` 通过。本机临时目录 owner ACL 与进程用户不一致,涉及 `init_local_game_project_at` 的既有用例(含未改动模块)在本机无法执行,全量分片与真机 smoke 未在本轮取得。 +- 关联文档:[里程碑](../plans/【里程碑】Direct三维请求自选技术栈-2026-09-16.md)、[实施计划](../plans/【实施计划】Direct三维请求自选技术栈-2026-09-16.md)。 ## 2026-09-16 图标图集自动拆图上限提高到 256 - 背景:AGC 图标图集自动连通域识别在一次生成中识别出 86 个区域,原有 64 片上限在后处理阶段阻断了请求;该上限同时影响 api-server 自动 / 手动切片、SpacetimeDB 批量落库和统一生成结果 item 数量。