题目 1:为什么 FileNotFoundError 通常不适合直接 Retry?
假设 Agent 调用 read_file("config.py") 返回 FileNotFoundError。请解释为什么把同样调用重复三次没有意义,以及更合理的 Fallback 是什么。
FileNotFoundError 通常表示当前路径假设本身错误,而不是短暂环境抖动。如果文件真的不存在,连续重复
read_file("config.py")不会让它凭空出现,因此这类错误通常不应该自动 Retry。更合理的是把错误作为 Observation 放回 Agent,让模型改用list_files()、search_files("config")等工具获取真实路径,再重新调用read_file。这就是“换策略”而不是“重复动作”。
题目 2:判断题——只要 Tool 抛出 Exception,就应该把完整 traceback 直接放进 messages,对吗?
请说明 Traceback 和 Error Observation 在用途上的区别。
不一定。完整 traceback 适合写入 Trace,方便开发者调试和复盘;而给模型的 Error Observation 应优先保留影响决策的结构化信息,例如
error_type、error、retryable、attempts、suggestion。如果把大量 traceback 全塞进 Context,会增加 Token 消耗,还可能让模型抓不住关键错误。两者目标不同:Trace 保存完整证据,Observation 服务下一轮决策。
题目 3:代码阅读——下面 Retry 有什么问题?
while True:
try:
return tool()
except Exception:
continue
请至少指出四个工程问题,并给出正确改造方向。
问题包括:没有最大重试次数,会形成无限循环;对所有 Exception 一视同仁,FileNotFoundError、PermissionError 等不可重试错误也会重复;没有记录 attempts,无法追踪重试成本;没有保留最终错误信息,Agent 无法根据失败继续决策。正确做法是先对异常分类,只有明确 retryable 的异常才自动重试,并设置
max_retries;达到上限后生成结构化 Error Observation,交回 Main Loop。
题目 4:编程实现——写一个带最大重试次数的 Tool Executor
要求只对 TimeoutError 和 ConnectionError 自动重试,其他错误直接返回;结果必须包含 attempts。
#可以这样实现:
from dataclasses import dataclass
from typing import Any, Callable
@dataclass
class ToolResult:
ok: bool
result: Any = None
error_type: str = ""
error: str = ""
attempts: int = 0
def execute_with_retry(
func: Callable[..., Any],
arguments: dict,
max_retries: int = 2,
) -> ToolResult:
retryable_types = (
TimeoutError,
ConnectionError,
)
total_attempts = (
max_retries + 1
)
for attempt in range(
1,
total_attempts + 1,
):
try:
result = func(
**arguments
)
return ToolResult(
ok=True,
result=result,
attempts=attempt,
)
except retryable_types as exc:
if (
attempt
>= total_attempts
):
return ToolResult(
ok=False,
error_type=(
type(exc).__name__
),
error=str(exc),
attempts=attempt,
)
except Exception as exc:
return ToolResult(
ok=False,
error_type=(
type(exc).__name__
),
error=str(exc),
attempts=attempt,
)
return ToolResult(
ok=False,
error_type="Unknown",
error="Unexpected state",
attempts=total_attempts,
)
if __name__ == "__main__":
print(
execute_with_retry(
lambda: "ok",
{},
)
)
#真实项目还可以加入 backoff、jitter、timeout budget,但核心原则已经体现:只重试明确可重试错误,并且次数有限。
题目 5:综合应用——什么时候应该 Fallback,而不是 Retry?
请分别为文件读取、外部 API、数据查询举一个例子,并解释判断标准。查看参考答案
文件读取中,
read_file("config.py")返回 FileNotFoundError,更适合 Fallback 到list_files或search_files,因为路径本身错了;外部 API 出现一次 TimeoutError,更适合先 Retry,因为服务可能短暂抖动;数据查询返回空结果时,如果 SQL 本身执行成功,继续重复同一查询通常没有意义,更适合换维度、换筛选条件或换数据源。判断标准是:同一动作在条件不变时是否有合理概率自然成功,如果没有,就应该换策略。
题目 6:对比分析——Harness Retry 和 Agent Recovery 有什么区别?
请从“是否需要重新调用 LLM”“是否改变 Tool 或参数”“适合的错误类型”三个角度比较。
Harness Retry 通常不需要重新调用 LLM,它是在同一个 Tool、同一组参数下对瞬时错误自动再试,例如 Timeout、ConnectionError。Agent Recovery 则会把错误作为 Observation 重新交给模型,由模型决定换 Tool、换参数、换路径或结束,例如 FileNotFoundError 后改用 list_files。前者适合基础设施短暂失败,后者适合需要语义判断和策略变化的错误。两层配合后既能减少无意义模型调用,也保留 Agent 的动态纠错能力。
题目 7:概念理解——为什么说“自主纠错”不是“无限尝试”?
请从最大重试、最大 Step、错误预算、成本和安全几个角度解释。
自主纠错的目标是让 Agent 在合理边界内从局部失败中恢复,而不是不计成本地一直尝试。Tool 层要有
max_retries,Agent 层要有 MAX_STEPS,还可以设置 max_tool_errors、总超时和 Token / Cost Budget。如果错误持续出现,说明环境异常、策略错误或任务本身不可完成,应明确失败。对于权限和安全错误,更不能靠“再试试”绕过规则。因此成熟 Agent 的自主性始终受 Harness 的资源和安全边界控制。
进一步阅读
- Python Exception Handling 官方文档:重点理解异常类型、捕获范围和自定义 Exception。
- Retry / Exponential Backoff 相关资料:理解瞬时错误为什么适合有限重试,而不是立即失败。
- Resilience Pattern 相关资料:可以继续了解 Retry、Fallback、Circuit Breaker 等容错模式。
- Finite State Machine 相关资料:帮助理解 Tool Error 如何影响 Agent State 和状态流转。
- Observability / Tracing 相关资料:错误恢复必须可追踪,否则 Agent 看似“自愈”,实际上很难复盘。
- Idempotency 相关资料:有副作用 Tool 重试时必须考虑幂等,否则可能重复写入、重复发送或重复扣费。
- Circuit Breaker 相关资料:当外部服务持续失败时,为什么不应该让所有 Run 一直重试。
- Human in the Loop 相关资料:部分错误和高风险恢复动作需要人工介入,而不是全自动继续。
术语表
- 工具异常(Tool Exception):Tool 真正执行过程中产生的异常,例如 FileNotFoundError、TimeoutError、PermissionError。Harness 通常会捕获并转成结构化结果。
- 错误观察(Error Observation):由 Tool Error 转换而来的结构化环境反馈,通常包含 error_type、error、retryable、attempts、suggestion 等信息,用于下一轮 Agent 决策。
- 重试(Retry):在 Tool 和参数保持不变的情况下再次执行,适用于 Timeout、ConnectionError 等可能短暂恢复的错误。
- 回退(Fallback):当前策略不可行时切换另一种完成目标的方式,例如 read_file 失败后改用 list_files、主数据源失败后改用缓存数据源。
- 最大重试次数(Max Retries):一个 Tool Call 在自动恢复阶段允许重复执行的最大次数,用来防止 Tool 内部无限循环和资源失控。
- 可恢复错误(Recoverable Error):虽然当前 Action 失败,但仍存在合理后续路径的错误,例如路径猜错、临时网络异常、查询为空。
- 不可恢复错误(Non-recoverable Error):当前 Run 不应继续自动执行的错误,例如安全策略拒绝、用户取消、超出总预算、超过最大 Step。
- 错误预算(Error Budget):单次 Agent Run 允许发生的 Tool Error 总量或资源预算。超过以后,即使尚未达到 Max Steps,也可以终止任务。
本章总结
这一节,我们让 Agent 第一次真正具备了“失败以后还能继续”的能力。
前面我们已经会:
Tool Call
↓
Tool Execution
↓
Tool Result
这一节进一步把失败也纳入闭环:
Tool Call
↓
Tool Exception
↓
Error Observation
↓
Reason
↓
Recovery Action
最重要的认识是,Tool 失败不等于 Agent 失败。
例如:
read_file("config.py")
↓
FileNotFoundError
这个错误并不是“什么都没得到”。
它告诉 Agent,当前路径假设错了。
于是下一步可以:
list_files
↓
发现 config/config.py
↓
重新 read_file
这就是自主纠错的核心。
我们还明确区分了两种恢复方式:
Retry
适合:
同一个动作下一次可能自然成功
例如:
Timeout
ConnectionError
而:
Fallback
适合:
当前策略本身不对,需要换路线
例如:
FileNotFoundError
↓
list_files
这两个概念非常容易混,但一定要分清。
我们还给 Retry 加了:
max_retries
因为 Tool 内部不能无限重试。
同时 Agent Main Loop 仍然需要:
MAX_STEPS
因为即使单个 Tool 不死循环,Agent 也可能在多个 Tool 之间来回打转。
真正成熟的恢复系统应该至少有:
Tool Exception
Error Observation
Retry
Fallback
Max Retries
MAX_STEPS
Error Budget
而且错误恢复必须可追踪。
如果一个 Agent 最后成功了,但中间偷偷重试 20 次、调用 15 次外部 API,我们仍然需要知道。
这也是后面 Tracing 和 AgentOps 为什么重要。
如果你想验证自己是否真正掌握这一节,可以实现一个最小任务:
用户:
读取 config.py
Step 1:
read_file("config.py")
→ FileNotFoundError
Step 2:
list_files()
→ config/config.py
Step 3:
read_file("config/config.py")
→ success
Step 4:
Finish
如果整个过程不需要人工告诉 Agent第二次该读哪个文件,而是它根据 Error Observation 自己改变动作,那么这一节最核心的“自主纠错”已经跑通了。
下一节我们会继续给 Tool 系统升级。
现在的 Tool 还是一轮一个。
但很多任务里会出现:
同时读取多个独立文件
同时查询多个独立指标
如果一个个串行执行,会很慢。
所以第 11 节,我们会进入,并行 Tool Calling
让 Agent 第一次学会,多个互不依赖的 Tool,可以同时执行。

















请登录后查看评论内容