fix(agent): validate non-empty retry delays; pin exhaustion semantics

核心算法 #10(Agent Loop):Codex 质量审查跟进,仅加固防御与
可观测,不改变循环语义。
- __init__ 校验 step_retry_delays 非空,空序列直接抛 ValueError
  (P5 fail-fast,避免首次可重试异常时 IndexError 掩盖原始 LLM
  异常、违背方法契约)
- 补耗尽语义窄测试(耗尽后原样抛出最后一次原始异常)与空序列
  构造校验测试
- run() 最终失败日志补异常类型名,便于回溯归因
This commit is contained in:
2026-07-11 08:44:47 -04:00
parent e3184c11f9
commit badfcce4cb
2 changed files with 25 additions and 2 deletions
+5 -2
View File
@@ -73,7 +73,8 @@ class AgentLoop:
max_steps: 最大有效步数(每次成功工具调用计一步)。
max_retries: JSON 解析连续失败的最大容忍次数。
step_retries: LLM 瞬时异常的步级重试次数(不含首次调用)。
step_retry_delays: 步级重试的退避秒数序列,超出部分取末值
step_retry_delays: 步级重试的退避秒数序列,超出部分取末值
不得为空(空序列构造时抛 ValueErrorfail-fast)。
retryable_exceptions: 可重试异常元组,默认 (TimeoutError, OSError)——
ssl.SSLError/ConnectionError 均为 OSError 子类,覆盖穿透
GovernedLLMClient 内部重试栈的瞬时异常;openai API 类异常由
@@ -90,6 +91,8 @@ class AgentLoop:
step_retry_delays: tuple[float, ...] = (20.0, 40.0),
retryable_exceptions: tuple[type[BaseException], ...] = (TimeoutError, OSError),
) -> None:
if not step_retry_delays:
raise ValueError("step_retry_delays 不得为空")
self._llm = llm
self._max_steps = max_steps
self._max_retries = max_retries
@@ -138,7 +141,7 @@ class AgentLoop:
messages, token_usage, session_id=session_id
)
except Exception as e:
logger.error("LLM API 调用失败: {}", e)
logger.error("LLM API 调用失败{}: {}", type(e).__name__, e)
result = LoopResult(
steps=steps,
steps_used=step_count,