Appearance
失败和资源生命周期怎么管:Exception、Context Manager 与 Logging
前置:核心接口怎么建模:Protocol、ABC、dataclass 与 Pydantic
核心问题:一次 Tool 失败时,系统怎样同时保留错误语义、释放资源并留下足够日志,而不把所有异常都当成同一种失败?
ToolTransientError 可 retry, ToolPermissionDenied 不可 retry;底层 httpx exception 作为 cause。async with sandbox session() 中 Tool 抛异常时, aexit 仍终止子进程并清理 workspace lease。
Exception hierarchy
异常按 domain 分类并保留 cause/context;上层只捕获能处理的错误,避免 except Exception 后返回 None 隐藏故障。
异常分类应表达调用者能采取的恢复动作,而非只按技术库命名。保留 cause,并避免所有错误最终都变 RuntimeError 。
Python Context Manager (with)
同步/异步 context manager 用 with/async with 绑定资源生命周期,例如 DB connection、trace span、sandbox session。
Logging
结构化 log 至少带 run/trace/node/tool identifiers;不要在字符串里拼 secret/tool payload,敏感数据应按 policy 采集。
日志应结构化包含 run_id/node id/tool 等 correlation 字段,级别和事件语义稳定;不要用日志字符串作为程序状态或审计唯一来源。
Failure Modes
except Exception 后吞掉错误继续当成功
症状: 捕获所有异常只写日志/返回空值。
为什么危险: retry/cancel/error taxonomy 失真。
处理: 定义领域 exception taxonomy,只在能处理的边界捕获转换。
资源清理只写在 Happy Path
症状: 文件/client/span/transaction 出错时没有 finally/context manager。
为什么危险: 连接和文件描述符泄漏。
处理: context manager/AsyncExitStack 把 acquire/release 绑定同一 scope。
如何验证
| Case | 断言 |
|---|---|
| except Exception 后吞掉错误继续当成功 | validation/timeout/cancel 注入后最终 status/error 保持语义。 |
| 资源清理只写在 Happy Path | with 块随机抛异常,资源计数恢复基线且 transaction rollback。 |
自测
- Tool timeout 与参数 validation error 被
except Exception吞掉并统一返回 success,会让上层丢失哪些错误语义? - 一个连接在异常路径没有释放。怎样用 context manager 保证资源生命周期,同时让日志仍保留 run_id、tool_call_id 和原始错误类别?