章末自测

题目 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,可以同时执行。

© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
评论 抢沙发
头像-大赛人网
欢迎您留下宝贵的见解!
提交
头像-大赛人网

昵称

取消
昵称

    请登录后查看评论内容