Skip to content

fix: 修复 agent 执行中断场景下 span 状态与输出丢失的 4 类问题 - #300

Merged
raychen911 merged 1 commit into
mainfrom
bugfix/agent-trace-exception-unknown
Aug 19, 2026
Merged

fix: 修复 agent 执行中断场景下 span 状态与输出丢失的 4 类问题#300
raychen911 merged 1 commit into
mainfrom
bugfix/agent-trace-exception-unknown

Conversation

@weimch

@weimch weimch commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

背景

修复 agent 执行中断/异常场景下链路追踪(span)状态错误与输出丢失的 4 类问题。

改动

  1. invocation 初始化异常时 span 显示 unknowntrace_runner()invocation_context 改为 Optional,初始化异常路径传入 None(此时 InvocationContext 尚未构造出来),依赖它的 runner.name 在 None 时跳过,其余业务属性照常写入;runner 初始化失败分支统一调用 trace_runner() 上报错误状态。

  2. call_llm 模型调用错误被记录为成功:模型异常被 retry 层转成 LlmResponse(error_code=...) 后 trace 层未识别 error_code,span 误标成功;现补充检查 llm_response.error_code,标记 span 为失败。

  3. 外部取消(asyncio.CancelledError)时根 span 仍显示成功且缺 partialCancelledError 继承 BaseException 而非 Exception,漏过 except Exception;现单独捕获 CancelledError 标记 span 为错误,并累积已流式的 partial 文本,防止取消时已生成内容丢失。

  4. GeneratorExit 时 agent_run 无部分输出:生成器关闭(aclose/break)触发的 GeneratorExit 分支现会累积并回填 partial 流式文本到 agent_action 中,避免已输出内容丢失。

影响

  • 仅修改 trpc_agent_sdk 下 3 个文件:runners.py / telemetry/_trace.py / agents/_base_agent.py
  • 行为变化:异常/取消/中断场景的 span 状态更准确,流式中断不再丢失已生成文本

1. invocation 初始化异常时 span 缺失 runner 业务属性且显示未知:
   - trace_runner() 的 invocation_context 改为 Optional,初始化异常时传入
     None(此时 InvocationContext 尚未构造),依赖它的 runner.name 在 None
     时跳过,其余业务属性照常写入
   - runner 初始化失败分支统一调用 trace_runner() 上报错误状态

2. call_llm 模型调用错误被记录为成功:
   - 模型异常被 retry 层转成 LlmResponse(error_code=...) 后,trace 层未识别
     error_code,导致 span 误标成功;现补充检查 llm_response.error_code

3. 外部取消(asyncio.CancelledError)时根 invocation span 仍显示成功且缺 partial:
   - CancelledError 继承 BaseException 而非 Exception,漏过 except Exception;
   现单独捕获 CancelledError,标记 span 为错误
   - 累积已流式输出的 partial 文本,防止取消时已生成内容丢失

4. GeneratorExit 时 agent_run 无部分输出:
   - 生成器关闭(aclose/break)触发的 GeneratorExit 分支现会累积并回填
     partial 流式文本到 agent_action 中,避免已输出内容丢失
@helloopenworld

Copy link
Copy Markdown
Contributor

AI Code Review

发现的问题

⚠️ Warning

  • trpc_agent_sdk/runners.py:671-688trpc_agent_sdk/telemetry/_trace.py:159-164:中断时 partial_textlast_event 优先级不一致,多 agent/transfer 场景下抢救的 partial 文本仍会被丢弃

    • trace_runnerlast_event 优先于 partial_textif last_event ... elif partial_text),而 trace_cancellationpartial_text 优先于 last_event(见 tests/telemetry/test_trace.py:434)。当 agent A 产出非 partial 终止事件后 transfer 到 agent B,B 流式产出 partial 期间被 CancelledError/GeneratorExit 中断时,last_non_streaming_event 仍为 A 的事件,trace_partial_text(B 的 partial)被静默丢弃,与本次"不丢失已流式输出"的修复目标相悖。建议在 trace_runner 中与 trace_cancellation 保持一致:中断路径下 partial_text 优先,或当 trace_error_type 指示中断时优先取 partial_text
  • trpc_agent_sdk/runners.py:480:初始化阶段未捕获 asyncio.CancelledError,与主 try 块新增的中断处理不对称

    • 初始化 tryexcept Exception,而 get_session/create_session/register_run 均 await,可能被外部 task.cancel()/wait_for 超时取消。此时 CancelledError 绕过该 except,既不走 trace_runner(invocation_context=None) 标错,也不进主 finally,导致 invocation span 无错误状态、cleanup_run 也不执行(若 register_run 已成功则泄漏注册)。本 PR 专门为主路径补了 CancelledError 分支,初始化路径应同样补一个 except asyncio.CancelledError 调用 trace_runner(..., invocation_context=None, error_type="CancelledError", ...)raise
  • trpc_agent_sdk/telemetry/_trace.py:500-509trace_call_llm 新增的 error_code 回退路径无测试覆盖

    • 新逻辑 effective_error_type = error_type or llm_response.error_code 改变了行为:retry 层吞掉异常并返回带 error_codeLlmResponse 时,现在 span 会被标为 ERROR。现有测试(tests/telemetry/test_trace.py:936)同时传入 error_typeerror_code,未覆盖"仅 llm_response.error_code 有值、error_type=None"这一新触发路径,无法验证回退确实生效且 error.type 取值正确。建议补一条该场景的断言用例。
  • trpc_agent_sdk/runners.py:671trpc_agent_sdk/agents/_base_agent.py:328:新增 asyncio.CancelledError 分支及 partial 文本抢救逻辑缺少测试

    • 现有 tests/test_runner.py:310tests/agents/test_base_agent.py:240 仅覆盖 GeneratorExit,新增的 CancelledError 分支(错误类型 CancelledError/AgentCancelledErrortrace_partial_text/interrupted_partial_text 回填、[INTERRUPTED] 输出)无任何用例。建议补:流式 partial 后触发 CancelledError,断言 trace_runner 收到 error_type="CancelledError"partial_text 非空、runner.output[INTERRUPTED] 开头。

💡 Suggestion

  • trpc_agent_sdk/runners.py:413-416runner.name 在 span 起始处用 self.agent.name 预置,又在 trace_runnerinvocation_context 非空时)用 invocation_context.agent.name 重设,正常路径下两次写入值相同属冗余;仅在 init 失败路径有实际意义。可将预置限定为 init 失败兜底(或加一行注释说明重设会覆盖),减少阅读时的"是否冲突"疑虑,长期维护更清晰。

总结

本 PR 修复了中断场景下 span 状态与流式输出丢失问题,方向正确且与现有 trace_cancellation/_build_interrupted_content 模式基本一致;无 Critical 阻塞问题。主要风险是中断处理覆盖不完整(多 agent transfer 下 partial 仍被丢弃、初始化阶段未处理 CancelledError)以及新增路径缺乏测试,建议合并前补齐。

测试建议

  • 补充 CancelledError 中断用例:runner 与 base agent 各一条,流式产出 partial 后取消,断言 trace_runner/trace_agent 收到正确 error_typepartial_text/runner.output[INTERRUPTED] 前缀的已流式文本。
  • 补充 trace_call_llmllm_response.error_code 有值(error_type=None)的用例,断言 span 被标记为 ERROR 且 error.type 等于 error_code
  • 补充多 agent transfer 后中断用例,验证第二个 agent 的 partial 文本是否落入 trace(用于暴露上文 Warning 1)。

@raychen911
raychen911 merged commit b7d16bb into main Aug 19, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants