feat(run): 子智能体状态实时推送 + 主 run 错误失败不级联取消子 run - #1010
Conversation
|
Codex Review: 预 review 基于 发现 2 个未闭合实际用户路径的问题。
验证:该 head 独立快照执行 |
- 新增 CASCADE_CANCEL_STATUSES,三处收敛点(决策层/finally/终态跳过)统一策略 - 非取消终态 execution tree 未收敛时不强求 cleanup,保持 pending 交 reconcile - cleanup 自身故障仍抛 RuntimeCleanupPendingError 重试 - _publish_subagent_run_update 事件挂父 Run 线程(原用子会话 ID) - 决策记录 + 6 个单测 + 真实 PostgreSQL 集成测试(父 failed 后子仍 running)
|
感谢 review,两个问题都成立,已按反馈收敛(最新 head [P1] 已修复:级联策略在全部收敛点统一确认你的判断:只在 新增模块级
runtime cleanup 策略也随之收敛:
[P2] 已修复:前端消费路径 + 父线程归属
证据
未验证范围:尚未跑「父 Run 失败故障注入」的完整 E2E(依赖 deterministic replay + 真实 worker);上面用真实 PostgreSQL 集成测试覆盖了「回读子 Run 最终状态」这一步,端到端故障注入如需要我可以补。 |
|
Codex Review: 仅供参考:本轮预 review 基于 复查确认:上次指出的 worker finally/终态跳过路径已加策略判断,非取消终态可延迟 runtime cleanup;前端也已新增事件消费。当前仍有两项需处理:
已执行真实函数复现:列表为
另有上次线程归属修正的遗漏:SubagentRunService.start 创建时的事件 仍传 验证:当前 head 快照执行 |
1. chat_service.save_messages_from_langgraph_state 在 complete_run=True 写 completed 时无条件 cancel_active_execution_tree_descendants,误杀仍在跑的异步子 Run。 改为仅 interrupted 才收敛 execution tree,与 run_worker.CASCADE_CANCEL_STATUSES 对齐(此处终态仅 completed/interrupted,chat_service 被 run_worker 反向 import 故内联判断,避免循环依赖)。 2. SubagentRunService.start 创建时推送 subagent_run_update 仍用子线程 ID, 改为父 Run 的 conversation_thread_id,与 worker 启动/终态推送的归属统一。 3. 前端防回退限定在同一 run_id 内:incoming 携带 run_id 却未命中时是全新 run, 不再回退 child_thread_id 匹配,「继续同一子线程」的新 run 不再被旧 run 终态丢弃。 补测试:chat_service completed 不级联、subagent_start 事件挂父线程、 同子线程连续两次 run 不被回退、reconcile 不覆盖新 run。
|
感谢 review,三项都成立,已修复(最新 head [P1] completed 落库路径仍无条件取消子 Run → 已闭合确认根因: 修复:改为仅 新增回归 [P2] child_thread_id 回退匹配把新 run 当旧 run → 已限定同 run_id确认根因: 修复:run_id 存在但未命中即视为全新 run(返回 -1,追加),只有 incoming 没有 run_id(旧的增量形状)才回退 child_thread_id 匹配。状态防回退从此限定在同一 run_id 内,跨 run 的展示取舍交给 新增 2 个用例: SubagentRunService.start 事件线程归属 → 改父线程
新增回归 验证
未验证范围:仍缺「真实父 Agent 完成、后台子 Run 继续执行并落产物」的完整 E2E(依赖 deterministic replay + 真实 worker)。本轮用真实 PostgreSQL 集成测试覆盖「回读子 Run 最终状态」一步;如需要我可补完整故障注入 E2E。 |
现象
主智能体并行调度多个子智能体时,两个体验/健壮性问题:
subagent_await阻塞期间不产生values事件,agent_state冻结,前端面板要等所有子任务一起返回才一次性刷新——子智能体的启动/完成状态更新严重滞后,看不到实时进展。对标
Claude Code 跑多步任务时,每个子步骤的进行/完成状态是实时刷新的,不会等到全部跑完才一次性蹦出来。
机理
values时才被动体现。mark_run_terminal对任何终态都执行cancel_active_execution_tree_descendants,没有区分「用户主动取消」和「错误失败」——用户取消当然该级联,但错误失败只是主 run 自己停了,子 run 还在正常跑,级联取消是误伤。agent_state(HTTP 轮询 / 流式快照)携带的 checkpoint 也可能落后于流式增量,把面板状态回退到旧值。改进方法
后端
run_worker主动向父 run 事件流推subagent_run_update事件(_publish_subagent_run_update),携带serialize_subagent_run_state序列化的子 run 状态,thread_id绑定父线程。mark_run_terminal加cancel_cancel_descendants参数(机制层,默认 True),_finish_run(决策层)只在status in ("cancelled","cancel_requested","interrupted")时传 True。错误终态(failed/completed)不级联,子 run 跑完落库,主 run 续跑时可收割。前端
useAgentRunStream监听subagent_run_update,到达即mergeSubagentRunIntoList合并进agentState.subagent_runs,不等父 graph 的values事件。subagentRuns.js新增状态新鲜度排序(终态 > 取消请求 > 进行中 > 初始),mergeSubagentRunIntoList/reconcileAgentStateSubagentRuns保证「旧 checkpoint 快照不回退流式增量」——在useAgentStreamHandler(流式agent_state事件)和AgentChatComponent.fetchAgentState(HTTP 轮询)两处调用。效果
子智能体的启动/完成状态实时推送到前端,面板不再滞后、也不会被旧 checkpoint 回退;断网/错误导致主 run 失败时,子 run 继续跑完不被误杀,用户主动取消仍正常级联。
验证
subagentRuns.test.js13 个用例(6 个原有 + 7 个新增),覆盖按 run_id/子线程合并、拒绝终态回退、允许 running→cancel_requested 前进、防agent_state回退流式增量。