From 7f038490f18360745bbb4d35c5b305ee53fd3fb6 Mon Sep 17 00:00:00 2001 From: menghao Date: Mon, 3 Aug 2026 11:53:59 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=BF=90=E8=A1=8C=E6=97=B6?= =?UTF-8?q?=E6=81=A2=E5=A4=8D=E8=B7=AF=E5=BE=84=E6=A0=88=E6=BA=A2=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为 pending continuation 与后台主循环增加可取消的 Tokio 任务边界 保留 durable batch 恢复防重和父任务取消语义 补充运行时技术约束与共享排障记录 --- .../agent/runtime_driver/pending_execution.rs | 36 +++++++++++++++++-- docs/project-memory/shared-memory/pitfalls.md | 8 +++++ ...案】AI游戏创作智能体App实施计划-2026-06-24.md | 1 + 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/pending_execution.rs b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/pending_execution.rs index 385d4066b..826ff7701 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/pending_execution.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/pending_execution.rs @@ -1,5 +1,37 @@ use super::*; +async fn run_game_creator_agent_background_task_after_pending_stack_boundary( + root: PathBuf, + agent_id: String, + task: String, + runtime: AgentRuntimeState, + continuation: AgentRuntimeContinuationContext, +) -> AgentBackgroundTaskOutcome { + // Debug builds give both pending execution and the background main loop large poll frames. + // A joined child task prevents those frames from sharing one Tokio worker stack while + // JoinSet still aborts the child if its parent continuation is dropped. + let mut tasks = tokio::task::JoinSet::new(); + tasks.spawn(async move { + run_game_creator_agent_background_task_with_context( + root, + agent_id, + task, + runtime, + continuation, + ) + .await + }); + match tasks + .join_next() + .await + .expect("pending continuation task must exist") + { + Ok(outcome) => outcome, + Err(error) if error.is_panic() => std::panic::resume_unwind(error.into_panic()), + Err(error) => panic!("pending continuation task was cancelled: {error}"), + } +} + pub(crate) async fn continue_game_creator_agent_pending_tool_action( root: PathBuf, agent_id: String, @@ -61,7 +93,7 @@ pub(crate) async fn continue_game_creator_agent_pending_tool_action( usize::try_from(batch.loop_iteration.saturating_sub(1)).unwrap_or(usize::MAX); continuation.context_stalled = false; continuation.applied_steer_cursor = batch.planned_steer_cursor; - let outcome = run_game_creator_agent_background_task_with_context( + let outcome = run_game_creator_agent_background_task_after_pending_stack_boundary( root.clone(), agent_id.clone(), pending.task.clone(), @@ -787,7 +819,7 @@ pub(crate) async fn continue_game_creator_agent_pending_tool_action( return; } } - let outcome = run_game_creator_agent_background_task_with_context( + let outcome = run_game_creator_agent_background_task_after_pending_stack_boundary( root.clone(), agent_id.clone(), pending.task.clone(), diff --git a/docs/project-memory/shared-memory/pitfalls.md b/docs/project-memory/shared-memory/pitfalls.md index 1df822307..db596d07c 100644 --- a/docs/project-memory/shared-memory/pitfalls.md +++ b/docs/project-memory/shared-memory/pitfalls.md @@ -3986,6 +3986,14 @@ - 处理:先以 CAS 单独 commit `queued -> executing`,成功后才调 ToolHost;调用返回后再 commit observation。恢复见到 executing 或 ToolHost 返回 Unknown 时只能进入 reconciliation,不得自动重执行。重复 resume 不得继续增 revision 或重复 event。 - 验证:在“ToolHost 已调用、observation commit 失败”处注入故障,序列化快照并用新 engine 重载;断言重复 resume 后 ToolHost 计数仍为 1,且只有显式 reconcile observation 才恢复 running。 +## 大型 async 状态机不能在同一 Tokio poll 调用栈连续嵌套(2026-08-03) + +- 现象:Supervisor collaboration durable isolated spawn 恢复测试在默认 Tokio worker 栈下稳定 `stack overflow`;单独运行同样失败,提高 `RUST_MIN_STACK` 后通过。 +- 原因:不是业务递归。debug 构建中 pending action continuation、后台 task queue 和 Agent 主循环各自形成大型 async poll frame;恢复路径在同一次 poll 调用链直接进入下一层状态机,累计超过 worker 默认栈。 +- 处理:在 pending continuation 与后台主循环之间建立独立 Tokio task 轮询边界,使 pending poll 先退栈后再轮询主循环。边界必须保留结构化取消语义;当前使用 `JoinSet`,父 continuation 被丢弃时同步 abort 子任务。不得只增大 CI 的 `RUST_MIN_STACK`,否则生产默认栈仍可能崩溃。 +- 验证:失败用例必须在未设置 `RUST_MIN_STACK` 时通过;同时覆盖 policy batch 全组和 pending/cancellation 回归,证明恢复不重复生成 isolated spawn、父任务取消不遗留后台子任务。 +- 关联:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_driver/pending_execution.rs`。 + ## Provider 可扩展不能用一个全局 protocol 枚举代替实例隔离 - 现象:把 `openai_chat / openai_responses / anthropic` 直接当 Provider 身份,注册第二个同协议 endpoint 时发生 ID 冲突;或为方便调用把 API Key、base URL、raw-log 目录放进全局状态,并行请求后日志串目录。 diff --git a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md index 283fa5b6a..87f5971ce 100644 --- a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md +++ b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md @@ -756,6 +756,7 @@ game-project/ - 开发模式可通过本地项目文件面板执行 `file.list/read/write/delete`,普通用户界面不暴露文件面板。 - 2026-07-17 起,同一 Runtime 文档的“V1.32 Runtime 强制 Supervisor 协作合同”作为 mixed swarm 可靠性事实源。项目可用 `.agent/collaboration-policy.json` 约束首波 static/isolated 模式、数量和 required static Agent;Runtime 在任何 child 副作用前整批校验并把合同指纹固化进 Provider batch v2。当前父 run 一旦形成 delivery/group,正式 `project-supervisor` 默认只负责编排、状态认领和验证,不再直接执行项目 mutation;专业 Agent/isolated child 权限与唯一 Supervisor 最终回复边界保持不变。 - 2026-07-17 V1.32 最终代码已完成独立真实 Provider PASS:首批 mixed batch、三 isolated child、Runner 强杀恢复、专业返工、宿主验证、唯一最终回复与零重复/残留/泄漏同时成立。真实报告计数、隔离重试配置和仍待收敛的 tool-plan repair 成本统一以 Runtime 文档 V1.32 章节与共享决策记录为准。 +- 2026-08-03 恢复执行补充约束:pending action continuation 进入后台主循环时必须先跨越独立 Tokio task 轮询边界,不能让 pending executor、task queue 与 Agent 主循环的大型 async poll frame 在同一 worker 调用栈连续嵌套。边界必须随父 continuation 取消子任务并保持 durable action、batch、run/session 身份及恢复防重语义;当前使用 `JoinSet` 承担结构化取消。CI 和生产均使用默认 worker 栈验证,不以提高 `RUST_MIN_STACK` 代替代码边界。 - 2026-07-18 起,同一 Runtime 文档的“V1.34 动态隔离子 Agent writeScopes 命令绕过封堵”作为 isolated child 的现行能力事实源。在 scope-aware OS sandbox 完成前,动态 child 无条件禁用 `project.verify / project.git_commit / command.exec / command.start / command.stdin / preview.start / agent.delegate / agent.spawn_isolated / project.restore / agent.schedule_ready / canvas.asset_generate / task.create / task.update / blackboard.write` 和全部 MCP;原生工具策略统一显示 `denied`,模板、项目 policy 与用户确认均不能放宽。保留固定只读 `command.run_limited`、同身份 `command.output_read / command.poll / command.terminate`、既有预览的 `preview.validate`,以及严格位于 `writeScopes` 内的 `file.write / file.patch / file.delete / project.patchset`。 - V1.34 的新单动作在 confirmation 和 OS launcher 前拒绝;新多 action 原生 batch 只要含一个 denied member 就在独立 pending-action sidecar、confirmation、OS spawn、revision 和任何成员项目副作用前整批 abort,只保留 `aborted / nextActionIndex=0` batch 事实。旧 pending / approval / batch 真正进入执行器时仍重新应用当前 child 边界,旧 executing 未知结果继续进入既有 reconciliation。该安全收紧由恶意 sibling 写入、策略快照、batch、旧 pending 执行器重验和 isolated/mixed/collaboration/provider-batch 回归证明;不因本切片重跑已通过且 isolated mutation 为 0 的 V1.31/V1.32 外部 Provider suite。通用命令只有在后续 scope-aware OS sandbox 对所有后代强制同一 `writeScopes` 并通过独立决策与测试后才可重新评估开放。 - 2026-07-18 起,同一 Runtime 文档的“V1.35 多 ready isolated all-join 原子认领与恢复”作为 `agent.run_status` 同父 run 多 group 认领的现行事实源。Runtime 按 `delegationGroupId` 排序并一次性预取全部 join 锁;任一后续锁忙时保持零 delivery mutation、零 claim sidecar。全锁就绪后,同一 action 的 durable claim journal 按 `prepared -> committed -> observed` 推进;部分 commit 或 Runner 恢复只能复用该 journal 幂等补齐。只认领可完整放入优先 `readyIsolatedJoins` 观察预算的有序前缀,未观察旧 claim 可由后续 action 完整重放,但不创建第二份 isolated claim。每个 claimed delivery 必须由匹配原 action/group 的 journal 覆盖;无 journal 的旧 delivery 每轮只迁移一个原 action,已有 journal 不得扩写或状态倒退,跨 action group 归属冲突失败关闭。成功 observation 写入 pending sidecar 后只能把本轮完整输出的 claim 标记 `observed`,任一未观察或无 journal claim 继续阻断 finalization;每个 group 审计按 `actionId + delegationGroupId` 唯一,并在 Agent DB 锁内修复 torn tail、全量核对后幂等追加。