[Bug]: `WorkflowIdleEvent` is emitted while events sent from outside the run with `handler.ctx.send_event` are still undelivered, so a conte

这通常发生在多个步骤同时等待人工输入、并在同一批里通过 handler.ctx.send_event 从运行外部回传事件的场景:工作流会在外部事件尚未被取走前误发 WorkflowIdleEvent ,此时保存的上下文会丢失该事件。优先排查空闲判定(idle check)是否只覆盖了运行内部发出的事

快速结论:这通常发生在多个步骤同时等待人工输入、并在同一批里通过 handler.ctx.send_event 从运行外部回传事件的场景:工作流会在外部事件尚未被取走前误发 WorkflowIdleEvent,此时保存的上下文会丢失该事件。优先排查空闲判定(idle check)是否只覆盖了运行内部发出的事件,而没有覆盖 receive_queue 中尚未被取走的外部事件。

适用环境:Issue 已确认:LlamaIndex llama-index-workflows 2.26.1,以及 run-llama/llama-agents 在 main 分支的 a457071;Python 3.12;Linux。

最快修复方案:暂无确认的一步修复方案。Issue 已关闭为 #23515 的重复项;据维护者说明,run-llama/llama-agents#808(为 #23515 提交)同样修复了此处的复现脚本,可优先尝试升级到包含该修复的依赖版本。

注意事项:Issue 中未给出确认修复的发布版本号;升级前需自行确认所依赖的 workflows 与 llama-agents 版本是否包含修复。此外,上下文在错误 idle 时刻被保存后的“丢事件”属于数据层面的问题,升级后对旧存档的兼容性未经证实。

问题场景

使用 LlamaIndex 的 workflows(即 llama-index-workflows)构建带人工介入(human-in-the-loop)的工作流:两个步骤同时通过 wait_for_event 挂起等待人类回复;应用侧用 handler.ctx.send_event 一次性回传多个 HumanResponseEvent,其中 first 的回复会让所属步骤再询问一个后续问题(follow-up)。此时事件流会报告后续问题并随即发出 WorkflowIdleEvent,而第二个回复实际上还没有被送达。若应用利用 idle 事件保存上下文(handler.ctx.to_dict()),恢复后工作流会一直等待一个早已发送过的回复,永远无法结束。

报错原文

[Bug]: `WorkflowIdleEvent` is emitted while events sent from outside the run with `handler.ctx.send_event` are still undelivered, so a context saved at that idle loses them

原因分析

Issue 正文指出:_check_idle_state 的设计前提是“没有任何已发送或已返回的事件仍在等待被投递”时才判定为空闲,而 state.deliveries 只追踪从运行内部发出的事件(此部分已由 #23583 处理)。从运行外部经 ExternalContext.send_event 发送的事件会进入适配器的 receive_queue,控制循环每次 tick 只从队列中取一个,因此“已在队列中但仍未被取走”的事件对空闲判定不可见,可能被提前判定为空闲。

维护者后续在评论中确认,此问题与 #23515 为同一根因(重复 issue),并为 #23515 提交的 run-llama/llama-agents#808 可同时修复本复现脚本。结合复现结果推测,根因可能是:外部事件入队后、被控制循环取出投递前,存在一个时间窗口,空闲检查未把队列中的待取事件计入判断。

环境排查

  • 确认 llama-index-workflows 版本是否为 2.26.1 或更早的受影响版本。
  • 确认是否通过 handler.ctx.send_event 而非运行内部 ctx.send_event 投递事件;前者走 ExternalContext 与 receive_queue,正是本问题涉及的路径。
  • 确认是否依赖 WorkflowIdleEvent 做上下文持久化;该事件被提前触发是本问题的直接表现。
  • 确认 Python 版本(Issue 为 3.12)与操作系统运行环境是否与复现一致,排查时可先排除环境差异。
  • 若同时集成 run-llama/llama-agents,记录所用 commit(Issue 为 a457071),便于对照 #808 修复是否已合入。

解决步骤

  1. 先用 Issue 中的最小复现脚本确认本地表现:运行后打印 waiters still open in the saved context,若保存时仍有未决 waiter(如 other),说明命中了该竞态。
  2. 在 handler.ctx.send_event 之后立即调用 handler.ctx.to_dict() 的场景中,暂时避免仅凭单个 WorkflowIdleEvent 就做持久化;可优先尝试在保存前用 collected_waiters 中未解析项做二次校验(此方案为规避手段,Issue 未验证)。
  3. 可优先尝试升级到包含 run-llama/llama-agents#808 对应修复的依赖版本,并重新运行复现脚本,确认 idle 前 other 的 waiter 已不再悬空。
  4. 若仍需在旧版本运行,考虑将同时回传的多个外部事件改为间隔投递(例如等第一个回复引发的后续问题出现后再发送第二个),以缩小队列待取窗口(此方案为推测性规避,未经验证)。
  5. 跟踪上游 #23515 与 #808 的合入与发布状态,修复发布后再移除临时规避逻辑。

验证方法

重新执行复现脚本:在保存上下文之前,两个问题都已被报告,并且空闲事件之后 waiting(saved) 为空——即保存的上下文中没有 resolved_event 为 None 的 collected_waiters;随后从保存的上下文恢复并作答,工作流能正常走完并返回 StopEvent(result="done"),而非永久挂起等待已发送的回复。

参考来源

run-llama/llama_index #23839

关联:#23515(重复根因)、run-llama/llama-agents#808(对应修复)、#23583(此前对运行内部事件投递追踪的修复)。

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 31520

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注