跳到主要内容

优雅的工具调用失败:你的 Agent UI 缺失的错误契约

· 阅读需 12 分钟
Tian Pan
Software Engineer

你见过的每一个 Agent 演示都以干净的结果收尾。工具调用返回了模型预期的数据,响应在两秒内到达,最终答案清晰准确。那是演示。生产环境则是另一回事。

在生产环境中,工具会超时。API 会返回 403,因为某个服务账户上周二被轮换了。第三方数据丰富端点返回 200,但响应体写着 {"status": "degraded", "data": null}。OAuth 令牌在周六凌晨 3 点过期。这些不是边缘案例——这是任何与真实世界交互的 Agent 的正常运行状态。失败模式是可预见的。问题在于,大多数 Agent 架构将它们视为事后补救,而大多数 Agent UI 根本没有向用户传达这些失败的词汇。

缺失的错误契约

当你构建 REST API 时,你会考虑完整的响应范围:200、400、401、403、404、429、500、503。每个状态码都承载着语义契约。调用方知道如何处理每一种情况。整个 Web 都建立在这套共享词汇之上。

Agent 工具调用没有等效的约定。大多数框架中的工具要么返回成功值,要么抛出异常。如果框架将异常转换为观察字符串,LLM 可以看到该异常消息——但前提是有人记得配置了 handle_tool_error。在 Agent 要么恢复要么放弃之前,用户什么也看不到。没有状态分类,没有重试语义,没有对用户可见的进度状态。

这种缺失的契约正是工具调用失败感觉比 API 失败糟糕得多的原因。当网络请求失败时,浏览器会显示合理的错误页面。当 Agent 工具调用在推理过程中失败时,Agent 要么无声地停滞,要么幻觉出一个恢复方案,要么给出一条令人困惑的最终消息,没有任何关于出了什么问题或用户下一步可以做什么的上下文。

解决方案不纯粹是技术问题——它是一个横跨工具实现、Agent 运行时和用户界面的设计问题。

处理前先分类

第一个架构决策是大多数团队跳过的:在决定如何响应之前对错误进行分类。

并非所有工具失败都是平等的。两大类别至关重要:

瞬时失败是那些可能通过重试解决的:速率限制(429)、网络超时、临时服务降级、上游 API 过载。这些值得用指数退避重试——通常在放弃前延迟 1 秒、2 秒和 4 秒进行三次尝试。对瞬时失败重试超过三次通常是浪费;如果三次尝试后仍未解决,接下来三十秒内可能也不会解决。

永久性失败是那些重试无法修复的:无效凭证(401)、权限拒绝(403)、格式错误输入导致的验证错误(400)以及超出上下文限制。重试这些会消耗令牌和时间,同时让用户等待一个无法改变的结果。正确的响应是快速失败,给出清晰的解释,然后要么回退到替代方案,要么升级给用户。

工具应在返回值中明确编码这种分类。携带 error_type 字段的错误响应——例如 RateLimitErrorPermissionDeniedErrorValidationErrorServiceUnavailableError——为 Agent 运行时提供了智能路由失败所需的信息,而不是应用一刀切的重试策略。

LLM 也能从这种结构中受益。像"日历 API 返回 403:Agent 的服务账户没有对此日历的写入权限"这样的消息给了模型足够的上下文来决定是向用户寻求澄清、尝试不同方法,还是诚实地报告问题。原始的 Python 回溯什么可操作的信息都没有传达。

系统性失败的熔断器

单独的重试逻辑是不够的。当上游提供商经历中断时,每个请求都会失败。没有熔断器,每个触及该提供商的 Agent 会话都会排队并依次超时,消耗延迟预算,让用户等待不会到来的结果。

熔断器模式——直接借鉴自分布式系统——在滚动窗口内跟踪失败率。当失败超过阈值时,熔断器打开并立即开始阻止新请求,而不是让它们尝试并失败。在配置的冷却期后,熔断器进入半开状态,用单个探测请求进行测试。如果成功则关闭;否则保持打开。

对于 Agent 工具,这意味着下游服务的失败应在毫秒内呈现给用户("搜索工具当前不可用"),而不是在 30 秒的级联超时之后。用户接受"这个工具现在宕机了"比看着加载转圈半分钟后得到一个令人困惑的错误要容易得多。

每个工具需要跟踪的关键数据:失败计数、最后失败时间戳、熔断器状态和恢复探测结果。大多数 Agent 框架不提供内置的熔断器支持——这是你在工具包装层添加的插桩。

部分结果优于没有结果

Agent 设计中最有价值的 UX 决策之一是选择在无法返回所有内容时返回什么。

考虑一个调用四个工具的研究 Agent:网络搜索、文档检索系统、结构化数据库查询和引用格式化器。如果引用格式化器失败,Agent 拥有它所需信息的四分之三。正确的响应不是丢弃部分结果并报告完全失败——而是返回可用的内容并明确说明缺少什么。

这需要将工具设计为考虑部分成功。一个可以检索当前状况但无法检索预报数据的天气工具应返回 { "current": {...}, "forecast": null, "forecast_error": "upstream timeout" },而不是抛出异常。一个找到五个请求文档中三个的文档检索工具应返回找到的内容并注明缺少的两个。

然后 Agent 可以明确地推理部分结果:"我找到了三个相关文档,但由于权限错误无法检索两个。这是我从可用来源了解到的,以及哪些内容是不确定的。"这比无声失败要好得多——更糟糕的是 Agent 捏造缺失的部分。

这里的设计原则是,工具应该像精心设计成功路径响应一样精心设计降级响应。

用户需要看到什么

这正是大多数 Agent UI 完全失败的地方。架构可能很复杂——仔细的错误分类、指数退避、熔断器、部分结果——用户仍然看到一个旋转指示器,直到 Agent 成功或给出一条没有上下文的简短失败消息。

用户可见的重试状态很重要。当 Agent 正在重试工具调用时,用户应该知道。"正在重试搜索(第 2 次,共 3 次)"比沉默更可信。它设定期望,减少焦虑,并防止用户放弃一个下次尝试本会成功的会话。

视觉处理应反映严重程度。正在进行的瞬时重试值得一个中性的琥珀色指示器,而不是令人担忧的红色。永久性权限失败值得一条清晰的消息和建议的操作("此工具需要日历访问权限——点击此处重新连接你的账户")。完全服务不可用值得诚实承认结果将是不完整的。

几个有效的具体模式:

  • 进行中的工具状态:显示正在调用哪个工具以及是否在重试。能看到 Agent 在工作的用户比看着静态加载转圈的用户更有耐心。
  • 部分结果横幅:当结果基于不完整的工具数据时,显示说明缺少什么及原因的提示。"结果可能不完整——文档数据库不可用。"
  • 可操作的错误消息:当发生永久性失败时,给用户一些可以做的事情。"搜索 API 不可用。你可以几分钟后重试,继续使用已检索到的信息,或重新表述你的请求以避免网络搜索。"
  • 上下文保留:如果工具失败中断了多步骤工作流,不要丢失用户的输入或早期结果。保存状态以便用户可以从失败点恢复,而不是从头开始。

目标是工具调用失败感觉像减速带,而不是墙。

回退链

对于 Agent 仍需产生某些输出的不可恢复工具失败,回退链提供了结构。这个想法是定义一个优雅降级而不是完全失败的响应层级:

  1. 完整工具结果:主路径,当工具成功时。
  2. 替代工具:可以部分满足相同需求的不同工具(不同的搜索提供商、缓存结果、更简单的实现)。
  3. 缓存响应:来自上次成功调用的过时但仍然有效的结果。
  4. 承认差距:诚实的响应,说明无法检索到什么及原因,而不是幻觉一个填充。

第四层——承认差距——比听起来更重要。工具失败最坏的结果不是失败本身,而是 Agent 无声地捏造工具本应提供的数据。一个用听起来合理的捏造填补缺失搜索结果的模型,比一个说"我无法搜索这个——这是我从其他来源了解到的"的模型要糟糕得多。

设计明确的回退链迫使人们思考:当每个工具不可用时,这个 Agent 做什么?在设计时而不是事故响应时回答这个问题的团队会发布更有弹性的系统。

在系统提示中编码错误契约

一个被忽视的杠杆是系统提示本身。大多数系统提示描述 Agent 在工具成功时应该做什么。很少描述工具失败时应该做什么。

给模型关于失败行为的明确指令会显著改变其输出。像"如果网络搜索返回无结果或错误,在询问用户是否尝试不同方法之前明确说明"这样的指令,可以防止模型无声地幻觉从未发生过的搜索结果。像"如果工具调用超时,向用户报告超时并询问是否等待或继续使用可用信息"这样的指令,给模型一个决策框架,而不是让它即兴发挥。

这些指令不需要面面俱到。涵盖每个工具最可能的三四种失败模式,模型就会推广到相邻情况。任何失败指导的缺失正是导致大多数团队观察到的不稳定行为的原因——模型即兴发明恢复策略,而这些即兴发挥是不一致且经常错误的。

可观测性是前提条件

这一切——错误分类、熔断器、部分结果、用户可见状态——在没有可观测性的情况下都无法实现。每次工具调用都应发出结构化日志,捕获:工具名称、输入参数(经过脱敏)、结果(成功或错误类型)、持续时间、重试次数以及是否触发了回退。

这种遥测服务于两个目的。首先,它使事故可调试:当用户报告 Agent 给出了错误答案时,你可以重建哪些工具运行了、哪些失败了以及模型如何响应。其次,它揭示系统性模式:在正常负载下失败率 15% 的工具是等待变成事故的可靠性问题。

在没有这种插桩的情况下发布 Agent 功能的团队是在盲目飞行。失败模式最终会浮现——唯一的问题是当它们发生时你是否能理解它们。

生产就绪的工具失败是什么样的

2026 年 3 月的一项调查发现,78% 的企业有 AI Agent 试点,但只有 14% 成功将其扩展到生产使用。集成复杂性和不一致的输出质量占了大部分差距。工具调用失败处理处于两者的核心:无法优雅管理工具失败的 Agent 会产生不一致的输出,而缺乏回退模式使集成复杂性变得灾难性而非可管理的。

演示思维——构建成功路径、发布、随后修复失败——对 Agent 不起作用,就像它对分布式系统不起作用一样。失败模式是可预见的。错误契约是可设计的。UX 模式是成熟的。

实践检查清单:

  • 在决定如何处理之前,将工具错误分类为瞬时或永久性
  • 在工具包装层实现熔断器以防止级联超时
  • 为每个接触外部服务的工具设计部分成功响应结构
  • 为每个工具的不可用性定义明确的回退链
  • 为每个工具的可能失败模式在系统提示中添加失败处理指令
  • 在 UI 中呈现工具状态、重试进度和部分结果说明
  • 用结构化日志对每次工具调用进行插桩

能经受住生产环境考验的 Agent 是那些围绕工具会失败这一假设构建的——定期地、不可预测地,并且在最糟糕的时刻。错误契约不是锦上添花。它是用户信任的 Agent 与用户放弃的 Agent 之间的区别。

References:Let's stay in touch and Follow me for more thoughts and updates