diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/design_runtime.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/design_runtime.rs index c0bc27a75..c7bdbec88 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/design_runtime.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/design_runtime.rs @@ -1451,6 +1451,38 @@ mod tests { assert!(session.history.is_empty()); } + #[tokio::test(flavor = "current_thread")] + async fn scripted_design_provider_retry_clears_previous_reasoning_attempt() { + let (_temp, root, _resources) = init_design_project(); + let mut session = new_design_session("design-fake", "quality"); + begin_design_turn(&mut session, "turn-reasoning-retry"); + let mut response = fake_response("reasoning-retry", "重试后的正文", Vec::new()); + response.reasoning = "重试后的推理".into(); + let _fake = fake_provider::install( + vec![ + Err(platform_llm::LlmError::Upstream { + status_code: 503, + message: "busy".into(), + }), + Ok(response), + ], + 1, + ); + let mut events = Vec::new(); + let response = + request_scripted_design_provider(&root, &mut session, &mut |event| events.push(event)) + .await + .expect("scripted retry provider"); + + let reasoning_events = events + .iter() + .filter_map(|event| event.reasoning_text.as_deref()) + .collect::>(); + assert_eq!(reasoning_events, vec!["", "", "重试后的推理"]); + assert_eq!(response.text, "重试后的正文"); + assert!(session.history.is_empty()); + } + #[tokio::test(flavor = "current_thread")] async fn fake_provider_walks_five_phases_and_enters_consultant() { let (_temp, root, resources) = init_design_project(); diff --git a/docs/project-memory/plans/【实施计划】reasoning最终回归与验收-2026-09-14.md b/docs/project-memory/plans/【实施计划】reasoning最终回归与验收-2026-09-14.md new file mode 100644 index 000000000..a45d2a593 --- /dev/null +++ b/docs/project-memory/plans/【实施计划】reasoning最终回归与验收-2026-09-14.md @@ -0,0 +1,50 @@ +# 【实施计划】reasoning 最终回归与验收 + +| 字段 | 值 | +| --- | --- | +| Milestone | `docs/project-memory/plans/【里程碑】Provider推理与正文分离及策划Agent展示-2026-09-14.md` | +| Status | awaiting-review | +| Owner | Codex | + +## 一句话交付结果 + +用确定性回归、文档同步和门禁证据确认 Provider reasoning 旁路不会改变正文、工具调用、GameAgent 或策划会话持久化行为。 + +## 修改边界 + +本轮只补最终验收所需的测试、当前权威文档和证据记录;不新增协议字段、不扩展真实 Provider、不修改公开 API、SpacetimeDB schema、GameAgent 消费逻辑或 UI 组件。 + +## 实施顺序 + +1. 核对并补齐 platform-llm 的 reasoning 解析、正文隔离、工具调用共存、默认关闭和解析异常降级测试。 +2. 核对并补齐策划 Runtime 的 reasoning 事件、重试清理、错误清理和不写入 history 的测试证据。 +3. 核对 GameAgent、Direct/Codex 与 response stream 只消费正文的回归证据,修正过时的 crate README 语义。 +4. 更新主技术方案与里程碑状态,整理证据矩阵,运行最终门禁;完成后删除本轮及已完成的临时实施计划。 + +## 验收标准 + +- Responses 和 Chat 的 reasoning 增量/终态均有确定性测试。 +- reasoning 永不进入正文、工具参数、正式 assistant message 或策划 history。 +- reasoning 与工具调用共存、无 reasoning 默认行为、reasoning 解析异常降级均有证据。 +- 策划 Runtime 的新回合、重试、项目切换和失败路径不会残留旧 reasoning。 +- GameAgent、Direct/Codex 和 response stream 的正文回归通过。 +- `cargo test`、`cargo check`、TypeScript、文档索引、编码和 diff 门禁通过;真实 Provider smoke 明确记录为未验证。 + +## 风险与回滚点 + +- 若补测试暴露正文或工具回归,优先回退调用方接线,不改变默认关闭契约。 +- 若文档与实现不一致,以源码和测试为准同步修正文档。 +- 真实 Provider 未连接时不宣称端到端协议 fidelity,只报告固定 fixture 和单元测试证据。 + +## 本轮验收证据 + +| 证据 | 结果 | +| --- | --- | +| `cargo test --manifest-path server-rs/Cargo.toml -p platform-llm` | PASS,152 个单元测试;1 个真实 Provider smoke 按需 `#[ignore]` | +| `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --bin genarrative-ai-game-creator-shell design_runtime` | PASS,10 个策划 Runtime 测试 | +| `cargo check --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --tests` | PASS,既有编译警告未升级为错误 | +| `npx tsc -p apps/ai-game-creator-shell/tsconfig.json --noEmit` | PASS | +| `cargo fmt --all --manifest-path server-rs/Cargo.toml -- --check`、AGC Tauri 同命令 | PASS | +| `npm run check:encoding`、`npm run check:doc-index`、`git diff --check` | PASS | + +未验证项:真实 Provider 和浏览器运行时 smoke;`ai-game-creator-shell:typecheck` 中的 `check-config.mjs` 仍受当前 Windows 沙箱私有 DACL 限制,已用独立 TypeScript 检查替代类型门禁。 diff --git a/docs/project-memory/plans/【里程碑】Provider推理与正文分离及策划Agent展示-2026-09-14.md b/docs/project-memory/plans/【里程碑】Provider推理与正文分离及策划Agent展示-2026-09-14.md index d24ade5f6..44322109d 100644 --- a/docs/project-memory/plans/【里程碑】Provider推理与正文分离及策划Agent展示-2026-09-14.md +++ b/docs/project-memory/plans/【里程碑】Provider推理与正文分离及策划Agent展示-2026-09-14.md @@ -3,7 +3,7 @@ | 字段 | 值 | | --- | --- | | Version | 1.0 | -| Status | proposed | +| Status | awaiting-review | | Date | 2026-09-14 | | Parent Spec | `docs/technical/【技术方案】策划Agent生产迁移与工作区浏览-2026-09-10.md` | | Related Issue | `GenarrativeAI/Genarrative#331` | @@ -131,6 +131,20 @@ Responses 的原生 output 仍按当前方式保留,用于后续 Responses 会 - 策划 Runtime 事件映射和 UI 生命周期; - GameAgent 正文与工具调用回归。 +## 第五轮验收证据 + +| 验收面 | 证据 | 结果 | +| --- | --- | --- | +| Chat / Responses reasoning 解析 | `cargo test --manifest-path server-rs/Cargo.toml -p platform-llm` | PASS,152 个单元测试;含字段、content part、SSE 增量、终态快照和正文隔离 | +| reasoning 与工具调用共存 | `responses_response_captures_reasoning_alongside_tool_call`、既有 Chat/Responses 流式工具测试 | PASS | +| 默认关闭与请求兼容 | `run_request_defaults_to_openai_responses_api_kind`、`reasoning_capture_switch_does_not_change_provider_request_body` | PASS | +| 策划 Runtime 生命周期 | `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --bin genarrative-ai-game-creator-shell design_runtime` | PASS,10 个测试;含事件映射、history 隔离、重试清理和失败清理 | +| GameAgent / Direct/Codex 正文回归 | `cargo check --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --tests` 与现有 response stream / direct tests 编译 | PASS;新增字段未进入正文消费路径 | +| 前端与文档门禁 | `npx tsc -p apps/ai-game-creator-shell/tsconfig.json --noEmit`、`npm run check:encoding`、`npm run check:doc-index`、`git diff --check` | PASS | +| 格式门禁 | `cargo fmt --all --manifest-path server-rs/Cargo.toml -- --check`、AGC Tauri 同命令 | PASS | + +真实 Provider、浏览器运行时 smoke 和 `check-config.mjs` 的 Windows 私有 DACL 路径本轮未验证;前者需要凭据和运行环境,后者受当前沙箱权限限制,不能据此扩大验收结论。 + ## 契约与持久化策略 - 不修改 HTTP API、OpenAPI、SpacetimeDB schema 或生成绑定。 diff --git a/docs/technical/【技术方案】策划Agent生产迁移与工作区浏览-2026-09-10.md b/docs/technical/【技术方案】策划Agent生产迁移与工作区浏览-2026-09-10.md index 4d0bc94fe..7c7eb7fdf 100644 --- a/docs/technical/【技术方案】策划Agent生产迁移与工作区浏览-2026-09-10.md +++ b/docs/technical/【技术方案】策划Agent生产迁移与工作区浏览-2026-09-10.md @@ -346,8 +346,8 @@ UI 使用“批准”和“继续修改”两个文字按钮,分别配 Lucide 开发构建的策划工作区页头在“刷新”旁提供“快速准备做成游戏测试”按钮。该入口与策划 Debug 日志共用 `GENARRATIVE_AGC_DESIGN_DEBUG=1` 开关:开关未启用时按钮不显示,命令也不可执行。入口仅进行本地 fixture 和会话状态写入,不调用 Provider;完成后自动刷新文件树与阶段,通过 `design-agent-update` 状态事件同步右侧审批/阶段操作区。随后仍需点击正常的“做成游戏”按钮执行资产登记与运行时切换。 -## 15. 策划 Agent reasoning 展示现状 +## 15. 策划 Agent reasoning 展示 -右侧栏已预留策划 Agent 的 `reasoningText` 事件字段和默认折叠的展示样式,但当前 Provider 解析链仍会过滤 reasoning 内容,尚未向策划 Runtime 产出该字段。因此现阶段只展示用户可见正文和工具状态;reasoning 折叠区在没有数据时不会出现。 +策划 Agent 的 Provider 请求显式开启 `capture_reasoning`,共享 `platform-llm` 将 Chat / Responses 的 reasoning 通过独立字段旁路传递,策划 Runtime 映射为已有 `DesignEvent.reasoningText`,前端复用右侧栏默认折叠的思考过程展示。正文、工具调用参数、正式 assistant message 和会话 history 继续使用原有字段;GameAgent、Direct/Codex 与通用 response stream 保持只消费正文的行为。 -后续若补充 reasoning,需要在策划 Agent 专用 Provider 解析层接入,不能直接修改共享 Provider 以免影响 GameAgent。 +reasoning 捕获默认关闭。新回合和 Provider 重试会先清空同一响应槽的临时 reasoning,失败路径也会清理,避免旧内容残留。该能力不新增公开 API、SpacetimeDB 字段或独立 UI 组件。 diff --git a/server-rs/crates/platform-llm/README.md b/server-rs/crates/platform-llm/README.md index 5d014f862..576aa0306 100644 --- a/server-rs/crates/platform-llm/README.md +++ b/server-rs/crates/platform-llm/README.md @@ -19,7 +19,7 @@ 2. `OpenAiChat`、`OpenAiResponses` 与 `Anthropic` 三类 API kind 都支持 JSON 请求、非流式响应和 SSE 流式响应;默认 API kind 仍为 `OpenAiResponses`。 3. 三类协议都使用统一的 `function_tools` / `tool_choice` 输入和 `LlmRunResponse.tool_calls` 输出。Anthropic 请求使用顶层 `tools[].input_schema` 与对象形态 `tool_choice`;Anthropic URL 默认在 base URL 后拼 `/v1/messages`,如果 base URL 已以 `/v1` 结尾则只拼 `/messages`。 4. Anthropic 当前仍不支持 `web_search`、图片内容和纯 system 消息;至少需要一条非 system 文本消息。角色动画、图片、视频、资产轮询仍留在其他平台适配和业务模块任务里。 -5. 流式 `on_delta` 只发送文本增量与完成原因;工具调用增量在 crate 内按 slot 聚合,完整调用只从最终 `LlmRunResponse.tool_calls` 读取。上下文管理、后台执行和业务状态不写回本 crate。 +5. 流式 `on_delta` 发送正文增量、可选的独立 reasoning 增量与完成原因;reasoning 只有在 `LlmRunRequest.capture_reasoning=true` 时才累计,默认关闭。工具调用增量在 crate 内按 slot 聚合,完整调用只从最终 `LlmRunResponse.tool_calls` 读取。reasoning 不进入 `delta_text`、`accumulated_text`、正式 assistant message 或工具参数;上下文管理、后台执行和业务状态不写回本 crate。 6. 支持按 provider 打标签,但不把业务 prompt、SSE 转发和模块状态写回本 crate。 7. `DashScope` 当前只通过“调用方显式提供兼容文本网关 base url”的方式接入,不复用图像 API。 8. 角色动画、图片、视频、资产轮询仍留在后续 `platform-llm` / `platform-oss` / 业务模块任务里另行实现。 diff --git a/server-rs/crates/platform-llm/src/lib.rs b/server-rs/crates/platform-llm/src/lib.rs index cd7d08da5..8bfb685e8 100644 --- a/server-rs/crates/platform-llm/src/lib.rs +++ b/server-rs/crates/platform-llm/src/lib.rs @@ -5332,6 +5332,8 @@ mod tests { .expect("tool call should keep the response valid without visible content"); assert_eq!(response.text, ""); + assert!(response.reasoning.is_empty()); + assert_eq!(response.tool_calls.len(), 1); } #[test] @@ -5406,6 +5408,28 @@ mod tests { assert_eq!(response.reasoning, "先判断。再回答。"); } + #[test] + fn responses_response_captures_reasoning_alongside_tool_call() { + let response = parse_responses_response_with_capture( + LlmProvider::OpenAiCompatible, + "fallback-model", + true, + r#"{"id":"resp_reasoning_tool","output":[{"type":"reasoning","summary":[{"type":"summary_text","text":"先分析工具需求。"}]},{"type":"message","content":[{"type":"output_text","text":"我先查询。"}]},{"type":"function_call","call_id":"call_lookup","name":"lookup","arguments":"{\"query\":\"项目\"}"}],"status":"completed"}"#, + ) + .expect("Responses reasoning plus tool call should parse"); + + assert_eq!(response.text, "我先查询。"); + assert_eq!(response.reasoning, "先分析工具需求。"); + assert_eq!( + response.tool_calls, + vec![LlmToolCall { + id: "call_lookup".to_string(), + name: "lookup".to_string(), + arguments: r#"{"query":"项目"}"#.to_string(), + }] + ); + } + #[test] fn stream_events_capture_reasoning_delta_and_terminal_snapshot() { let chat = parse_sse_event_block(