agent, tools: 支持 OpenAI Responses API、Agent HITL 多轮交互,修复 async generator span 泄漏 - #245
agent, tools: 支持 OpenAI Responses API、Agent HITL 多轮交互,修复 async generator span 泄漏#245pcerypeng wants to merge 1 commit into
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #245 +/- ##
==========================================
Coverage ? 88.47484%
==========================================
Files ? 493
Lines ? 46828
Branches ? 0
==========================================
Hits ? 41431
Misses ? 5397
Partials ? 0 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
AI Code Review确认 — 已删除且未迁移。现在我来检查 现在我来检查一下 现在我来验证 我来检查一个担忧: 现在我来检查 GraphAgent 上下文中的 我还要验证恢复时的 现在我来看一下 我来检查最后一个潜在的实际问题:在
但有一个细微的问题: 我现在已经完全理解了。我来写审查报告。我也要再次检查一下 我来验证一下是否存在导入问题: 现在, await agent_stream.aclose()
interrupt(self._interrupt_payload(current_round))
raise RuntimeError(...)
我来做最后总结。我还要仔细检查一个真正值得关注的问题: 现在我来写审查报告。 发现的问题🚨 Critical无。
|
AI Code Review现在让我检查一下 我已经掌握了所需的所有上下文。让我来撰写审查意见。 发现的问题🚨 Critical
|
AI Code Review确认。现在我来写审查意见。 发现的问题🚨 Critical
|
| "type": "summary_text", | ||
| "text": accumulated_reasoning | ||
| }], | ||
| }) |
There was a problem hiding this comment.
Responses 流式 finally 引用未初始化的 response
response 仅在 try 内部赋值,若 client.responses.create 或 _prepare_responses_api_params 抛错,finally 中 if response is not None 会触发 UnboundLocalError,掩盖原始 API 异常并导致 close_http_client 永不执行(httpx 客户端泄漏)。建议在 try 前初始化 response: Any = None,并将 close_http_client 放入独立 try/except。
AI Code Review我已经充分理解了,可以开始编写审查意见了。多轮恢复中断(interrupt)重放是一个复杂点,但 HITL 测试( 实际上,在 LangGraph 中,当使用 发现的问题🚨 Critical
|
| resume_content: Optional[Content] = None | ||
| if pending_hitl is not None: | ||
| completed_rounds = pending_hitl.get("completed", []) | ||
| for completed in completed_rounds if isinstance(completed_rounds, list) else []: |
There was a problem hiding this comment.
HITL 恢复重放 completed 轮次 interrupt 可能丢失复合 resume 值
恢复路径对 completed 中每个历史轮次调用 interrupt() 再对 current 调用一次,但 Command(resume=...) 仅含最新 pending interrupt id 的响应。completed 轮次依赖 LangGraph 从 checkpoint 重放历史 resume 值;若未持久化 checkpoint(auto_persist=False),重放会再次抛出 interrupt 导致多轮 HITL 卡死。建议显式为 completed 轮次提供历史响应或重放时跳过 interrupt()。
AI Code Review现在让我来撰写评审。 发现的问题
|
1、这是拼写错误修正("desicion" 是 "decision" 的笔误)。该 key 仅在 interrupt.value 非 dict 时触发,实际使用场景极窄。 |
AI Code Review确认: 现在我已经有足够的内容来撰写审查报告了。让我来汇总一下。 发现的问题🚨 Critical
|
AI Code Review我已经彻底理解了。现在我来整理一下审查意见。 发现的问题🚨 Critical
|
| transfer_requested = False | ||
| child_ctx.agent = current_agent | ||
|
|
||
| async for event in current_agent.run_async(child_ctx): |
There was a problem hiding this comment.
AgentNode HITL pending 状态依赖 delta 提交时序,GraphInterrupt 路径下可能不落盘
子 agent 抛出 LongRunningEvent 后写入 parent_ctx.state 的 pending HITL marker(落进 _delta),随后 interrupt() 抛 GraphInterrupt 穿透节点函数,节点不会正常返回,LangGraph 因此不会提交 _delta,marker 不落盘。auto_persist=False 且中断桥事件未携带该 key 时,重启后 _get_pending_hitl 读不到标记,多轮 HITL 重放失败、Command(resume=...) 从 START 重跑。建议显式将该 marker 通过中断桥事件 state_delta 持久化,而非依赖 delta 提交时序。
AI Code Review我已经足够了解了。让我来整理一下审查结论。 基于我对 diff 的分析,以下是我的发现: 发现的问题🚨 Critical
|
| @@ -414,6 +418,9 @@ | |||
There was a problem hiding this comment.
并行合并路径丢失 long-running 工具的错误码导致降级失效
parallel_tool_calls=True 时 _merge_parallel_function_response_events 合并 event 仅搬移 content/actions/timestamp,不搬移 error_code/error_message,合并后错误码变回 None,使 is_tool_execution_error 判错并把参数错误的 long-running 调用误判为正常返回、发出 LongRunningEvent 挂起整图。建议合并时聚合 error_code(任一子事件有错即置错)或保留首个错误码。
AI Code Review我已经充分理解了,现在开始撰写审查结论。 发现的问题🚨 Critical
|
|
|
||
| tc = _make_tool_call(tc_id="tc-1", name="search") | ||
| inp = _make_input(messages=[ | ||
| _make_assistant_message(tool_calls=[tc]), |
There was a problem hiding this comment.
测试方法中混入被删除 LRO 测试的残留代码
diff 把 test_handles_lro_events 改名为新测试时只替换了签名和开头,assert_not_awaited() 之后仍保留原 LRO 测试逻辑(重新构造 lro_event、再次运行并断言 len(events) >= 2),导致一个方法执行两段无关场景。应删除 1718 行 assert_not_awaited() 之后到下一个 def test_ 之前的全部残留代码。
AI Code Review这种合并只选取了第一个错误。这意味着在并行执行中,如果多个工具失败,只会呈现第一个错误。但更重要的是,如果其中一个并行工具是一个报错的 我已经完成了分析。让我记录下关键发现。让我最后确认一下关于 让我再核实一下 现在让我写下审查报告。让我确认一下行号。对于 让我再核实一个潜在的实际问题 —— 即当 让我核实一下是否存在这样的 现在,关于状态同步路径,有一个细微的真正潜在问题:对于图代理的常规(非工具)轮次, 我已经有了足够的发现。让我撰写最终的审查报告。让我确认一下行号。对于 让我再核实一下图节点操作 HITL 路径中一个真正值得注意的问题 —— 即在第 143/148 行恢复期间调用了 但有一个微妙的顺序问题:第 220 行写入了 现在让我写下审查报告。 基于我对 发现的问题
|
AI Code Review我已经获取了所需的所有信息,现在开始编写最终审查报告。 发现的问题
|
AI Code Review发现的问题
|
AI Code Review发现的问题
|
AI Code Review已包含测试。现在让我来写最终评审。 发现的问题
|
AI Code Review请注意第 581 行: 我已经完成了审查,现在开始汇总审查结论。 发现的问题
|
| api_key="your-api-key", | ||
| base_url="https://api.openai.com/v1", | ||
| use_responses_api=True, | ||
| responses_api_params={ |
| # an async generator is cancelled, but try/finally always executes | ||
| # even under CancelledError (PEP 492). | ||
| with tracer.start_as_current_span(f"agent_run [{self.name}]"): | ||
| from opentelemetry import context as otel_context |
| current_handle = partial(stream_handler_adapter, handle) | ||
| for filter in reversed(filters): | ||
| current_handle = partial(filter.run_stream, ctx, req, current_handle) | ||
| async for event in current_handle(): |
| # API can also control reasoning depth. -1 means "automatic" | ||
| # and is left to the model; positive budgets are bucketed | ||
| # into low / medium / high. | ||
| budget = request.config.thinking_config.thinking_budget |
There was a problem hiding this comment.
thinking_config有thinking_level的字段,这里是不是用它比较好?不用这个budget
| # (see trpc_agent_sdk/dsl/graph/_constants.py: STATE_KEY_CHECKPOINTS etc.). | ||
| # Used to detect GraphAgent resume scenarios where the session state must | ||
| # be preserved to avoid overwriting the LangGraph checkpoint. | ||
| _GRAPH_CHECKPOINT_STATE_KEY_PREFIX = "_trpc_graph_checkpoint" |
| await self._session_manager.update_session_state(input.thread_id, app_name, user_id, input.state) | ||
| session = await self._ensure_session_exists(app_name, user_id, input.thread_id, input.state) | ||
|
|
||
| # A GraphAgent tool result is a continuation token, not a fresh |
| len(pending_calls), | ||
| ) | ||
| elif any( | ||
| key.startswith("_trpc_graph_checkpoint") or key == "_trpc_graph_pending_interrupt" |
| from .utils import get_mandatory_args | ||
|
|
||
|
|
||
| class ToolArgumentErrorResponse(dict): |
| # processor (which emits them) and the long-running tool helper (which | ||
| # checks them). Keeping the literals in one place prevents silent drift | ||
| # when new codes are added. | ||
| TOOL_ERROR_CODE_NOT_FOUND = "tool_not_found" |
| break | ||
|
|
||
| if corresponding_call: | ||
| if is_tool_execution_error(tool_event): |
There was a problem hiding this comment.
没太明白这里为啥要做检查,工具执行失败跟LongRunningEvent不是一个概念
There was a problem hiding this comment.
LLM返回工具参数非法,这个问题已经修复了,LlmAgent的相关变更可以去掉,还有使用is_tool_execution_error的部分可以去掉
| # function response. Additionally, replay any *completed* HITL rounds | ||
| # stored in the AgentNode pending state so that LangGraph can resolve | ||
| # their historical interrupts during checkpoint replay — this matters | ||
| # when auto_persist=False and the checkpoint no longer holds the prior |
There was a problem hiding this comment.
按LangGraph接口来说,auto_persist=False时候,这个interrupt是不能用的吧?这里好像不用兼容?
There was a problem hiding this comment.
这块逻辑感觉是:trpc_agent_sdk/dsl/graph/_memory_saver.py的问题,可以看下
| generate_content_config: Optional[GenerateContentConfig] = None, | ||
| http_client_provider_factory: HttpClientProviderFactory = temporary_http_client_provider_factory, | ||
| use_responses_api: bool = False, | ||
| responses_api_params: Optional[Dict[str, Any]] = None, |
There was a problem hiding this comment.
thinking的effort可以从responses_api_params里传
| from ._registry import get_tool_set | ||
| from ._registry import register_tool | ||
| from ._registry import register_tool_set | ||
| from ._load_memory_tool import LoadMemoryResponse, LoadMemoryTool, load_memory, load_memory_tool |
|
Closing as related to #89. |
AI Code Review
现在让我确认一下,没有框架内部状态键使用 我已经完全理解了。让我来写评审意见。 发现的问题
|

agent, tools: 支持 OpenAI Responses API、Agent HITL 多轮交互,修复 async generator span 泄漏
本次 PR 包含三项改进:
OpenAI Responses API 适配 — 新增对 OpenAI Responses API 的流式和非流式
支持,涵盖 reasoning、tool calls、logprobs 等特性。
Agent 节点 HITL(人机多轮交互)机制 — 通过 interrupt bridge 桥接机制,
允许 Agent 节点在运行中暂停等待人工输入,支持多轮审批或修正流程后继续执行。
修复 async generator 中 OpenTelemetry span 泄漏 — 将 start_as_current_span
替换为手动 start_span + attach/detach + try/finally 模式,确保 async
generator 被取消时 span 仍能正确结束。新增防御性 ValueError 捕获,处理
跨 task 清理场景。
此外,本次 PR 还新增了框架级工具错误检测能力:
tool_execution_error),并提供 is_tool_execution_error() 公共辅助函数。
RELEASE NOTES: 新增 OpenAI Responses API 支持、Agent 节点 HITL 多轮交互能力,
修复 async generator 取消时 OpenTelemetry span 泄漏问题。