news 2026/9/13 20:57:44

OpenAI Agents SDK 流式运行中途怎么取消并拿到已完成结果?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenAI Agents SDK 流式运行中途怎么取消并拿到已完成结果?

OpenAI Agents SDK 流式运行中途怎么取消并拿到已完成结果?

【免费下载链接】openai-agents-pythonA lightweight, powerful framework for multi-agent workflows项目地址: https://gitcode.com/GitHub_Trending/op/openai-agents-python

用 OpenAI Agents SDK 的Runner.run_streamed()启动流式运行后,你可能会遇到这种情况:用户中途点了停止,或者你希望一个多轮任务只执行到当前这一步就不再继续,但已经产出的回复、工具调用结果不能丢。SDK 对这个问题有明确接口:在RunResultStreaming上调用cancel(...)停止运行,然后继续消费result.stream_events()直到迭代器结束,再从result.new_itemsresult.final_output等结果面上取出已完成的部分。下面按实际操作顺序说明两种取消模式、取消后如何取结果、以及如何接着已完成的进度继续对话。

两种取消模式:立即停止,还是等当前轮做完

cancel()接受一个mode参数,两个取值的行为差异直接决定你能拿到多少已完成结果(见 cancel 源码说明):

  • result.cancel(),即默认的mode="immediate":立即停止,取消所有运行中的任务并清空事件队列。适合用户明确中断、不需要保留后续状态的场景。
  • result.cancel(mode="after_turn"):让当前轮(turn)优雅完成后才停止。它会允许 LLM 响应结束、执行完挂起的工具调用、正确保存 session 状态、准确记录 usage,然后在下一次模型调用开始前停下。适合需要保留已完成工作、稍后继续的场景。

两种模式都有一个共同前提:调用cancel()之后必须继续消费stream_events(),让取消和清理流程走完,不要提前break离开循环。这是 Streaming 文档 和 Results 文档 都明确强调的。另外cancel()是幂等的,重复调用不会报错。

操作路径:在事件循环里取消,然后让流排空

Runner.run_streamed()返回RunResultStreaming,事件通过result.stream_events()这个异步迭代器逐个到达。取消的时机就放在消费事件的循环里,按你的条件触发。下面这个示例基于 streaming.md 中的工具调用示例改写:agent 带一个工具,当工具执行完毕(tool_output事件)后触发after_turn取消,随后流自然排空,检查运行状态和已完成的项目:

import asyncio from agents import Agent, Runner from agents.decorators import tool import random @tool def how_many_jokes() -> int: return random.randint(1, 10) async def main(): agent = Agent( name="Joker", instructions="First call the `how_many_jokes` tool, then tell that many jokes.", tools=[how_many_jokes], ) result = Runner.run_streamed(agent, input="Hello") async for event in result.stream_events(): if event.type == "run_item_stream_event" and event.name == "tool_output": # 条件满足:当前轮的工具结果已产出,让这一轮收尾后停止 result.cancel(mode="after_turn") # 注意:不要在这里 break,继续消费直到迭代器结束 # 流结束后,is_complete 反映最终运行状态 print("is_complete:", result.is_complete) for item in result.new_items: print("--", item.type) print("final_output:", result.final_output) if __name__ == "__main__": asyncio.run(main())

触发条件的选择取决于你的业务:可以按事件类型、按轮数、或按外部信号(比如前端停止按钮置位的标志位)。after_turn模式下,SDK 会在当前轮收尾后停下,不会发起下一次模型调用。

如果你确定不需要保留任何状态,只需把上面一行换成默认模式:

result.cancel() # 等价于 result.cancel(mode="immediate")

immediate 模式会立即取消所有任务并清空队列,因此流很快结束,new_items里可能只剩取消前已经送达的项目。

从结果对象取已完成的内容

流排空之后,已完成的成果都在同一个result对象上,取用位置如下(参见 Results 文档):

  • result.new_items:运行中富化后的RunItem列表,是"完成了什么"最完整的答案。常见类型包括MessageOutputItem(助手消息)、ToolCallItemToolCallOutputItem(工具调用及其结果)、ReasoningItemHandoffCallItem等。即使final_output为空,已完成轮次产出的消息和工具结果也会在这里。
  • result.final_output:最后一个运行 agent 的最终输出。流式模式下它在流处理结束前保持None;如果运行在产生最终输出之前就停下了(例如after_turn恰好停在工具轮之后、下一次模型调用之前),它同样是None。所以判断"有没有拿到最终答案"要看这个属性是否为None,不能只看循环是否结束。
  • result.is_completestream_events()迭代器退出后读取它,确认运行是否到达终态。
  • result.context_wrapper.usageafter_turn模式下已完成轮次的 usage 会被准确记录;immediate模式只覆盖已执行的部分。
  • result.current_agent/result.last_agent:取消时运行停在哪个 agent 上,多 agent 场景下判断下一步该用谁。

还有一个容易踩的坑:如果后台运行循环在产生任何流事件之前就失败了(例如沙箱初始化早期出错),异常不会从stream_events()里重新抛出。消费完流之后检查result.run_loop_exception,非None就表示存在这种静默失败,见 run_loop_exception 文档:

if result.run_loop_exception: raise result.run_loop_exception

如果运行配置了session=...after_turn取消会把已完成轮次的项目写入 session,之后可以带同一个 session 直接Runner.run(agent, "继续"),历史仍在。

取消之后接着原来的进度继续

after_turn停在工具轮之后时,当前用户轮其实还没有被模型"回答完"。streaming.md 给出的接续方式分三种情况:

  1. 手动维护历史的情况:用result.to_input_list(mode="normalized")取规范化输入,然后重新运行result.last_agent并用这份输入继续,这样是接着未完成的用户轮走,而不是立刻追加一条新的用户轮。

  2. 新用户输入先到达的情况:把已排空的结果转成状态再入队,见 Add input before resuming:

    state = result.to_state() state.add_input("Also keep the generated report in the project folder.") result = Runner.run_streamed(agent, state)

    暂存的输入会在下一次模型调用前被立刻接纳,并且随RunState序列化。

  3. 运行是停在工具审批上的情况:不要把审批暂停当成新轮次。先消费完stream_events(),检查result.interruptions,通过result.to_state()批准或拒绝后再用Runner.run_streamed(agent, state)恢复。完整流程见 human-in-the-loop 指南。

边界与验证小结

  • 必须排空流stream_events()迭代器结束前,运行不算完成,final_outputinterruptions等汇总属性可能还在收尾,after_turn下 session 持久化和历史整理也可能在最后一个可见 token 之后才结束。
  • final_outputNone不等于什么都没完成:它只说明最终输出没有产生(停在工具轮之后、或暂停在审批上),已完成的消息和工具结果仍从new_items取。
  • immediate 模式会清空队列:取消后已入队但未消费的事件会被丢弃,能拿到的完成内容以取消前已送达事件和new_items为准。
  • 验证取消是否按预期生效,看三处即可:stream_events()循环是否如预期退出、result.is_complete是否为Trueresult.new_items是否包含你预期的已完成项目类型。仓库里 tests/test_soft_cancel.py 对after_turn的各行为(等待当前轮完成、执行挂起工具、保存 session、阻止下一轮开始、幂等性)有一组可直接参考的断言用例。

更多流式事件类型(raw_response_eventrun_item_stream_eventagent_updated_stream_event)的说明见 docs/streaming.md,结果面各属性的完整对照表见 docs/results.md。

【免费下载链接】openai-agents-pythonA lightweight, powerful framework for multi-agent workflows项目地址: https://gitcode.com/GitHub_Trending/op/openai-agents-python

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 20:56:13

8款小众桌面蓝牙音箱实测:选型逻辑与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 20:54:50

TensorFlow C++ 图像分割部署:从 SavedModel 导出到高效推理实践

简介:面向需要在 C 工程中直接调用 TensorFlow 库完成图像分割的开发者,这份资源专门整理了与常见图像分类不同的调用方法,避免在张量维度、输入输出处理上反复踩坑。分类任务通常输出一个类别标号,而分割需要逐像素预测&#xff…

作者头像 李华
网站建设 2026/9/13 20:53:10

51单片机差分气压测漏仪设计与调试全指南

简介:本资源是一套完整的基于51单片机的测漏仪嵌入式系统设计资料,面向电子类专业学生、单片机初学者及硬件开发入门者,解决气体/液体泄漏检测类课程设计、毕业设计或小型工业监测项目落地难题。压缩包共22个文件,768KB&#xff0…

作者头像 李华
网站建设 2026/9/13 20:51:31

车载工控系统落地方法论:宽温实时+三防设计+量产闭环

1. 为什么“车载工控”在2026年突然成了硬通货?你可能刚刷到某条短视频:一辆矿用自卸车在零下35℃的戈壁滩上连续作业72小时,仪表盘无重启、CAN总线无丢帧、边缘AI识别模块持续输出障碍物热力图——弹幕飘过一句:“这哪是车&#…

作者头像 李华
网站建设 2026/9/13 20:50:36

ECG信号HHT时频分析与Matlab实现

1. 心电图信号时频分析的核心挑战在生物医学信号处理领域,心电图(ECG)信号分析一直是个经典而复杂的课题。传统ECG分析主要依赖时域特征提取(如R波检测)和频域变换(如傅里叶分析),但这些方法对非平稳信号的…

作者头像 李华
网站建设 2026/9/13 20:49:55

JavaScript防抖与节流原理、实现及场景选型指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华