news 2026/9/28 2:46:19

Forge 工具调用失败重试提示模板解析:从错误追踪到 Agent 自愈闭环

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Forge 工具调用失败重试提示模板解析:从错误追踪到 Agent 自愈闭环
  • 人工智能
  • AI Agent
  • 代码智能体
  • AI 应用
  • CLI
  • 开发工具

【免费下载链接】forgecode

AI enabled pair programmer for Claude, GPT, O Series, Grok, Deepseek, Gemini and 300+ models

项目地址:https://gitcode.com/gh_mirrors/forge39/forgecode
点击查看免费下载

在 Forge(AI 驱动的结对编程工具,面向 Claude、GPT、O 系列、Grok、Deepseek、Gemini 及 300+ 模型)的每轮对话中,工具调用失败是 Agent 必须高频面对的现实:文件读取权限不足、Shell 命令退出码非零、网络请求超时……如何让 Agent 在失败后冷静复盘、调整策略并继续尝试,而不是陷入死循环或被一次错误直接打断?本文以仓库中的核心提示模板 templates/forge-tool-retry-message.md 为线索,结合 orch.rs 与 tool_call.rs 的底层实现,完整讲解 Forge 的"错误追踪 → 剩余尝试次数注入 → 重试提示生成 → 上限中断"自愈闭环,并给出max_tool_failure_per_turn配置项的实战调优建议。读完本文,你将理解 Forge 如何约束 Agent 的试错行为,以及这套机制在任意 LLM 驱动的工具调用场景中如何复用。

一、重试提示模板是什么

templates/forge-tool-retry-message.md是 Forge 在每次工具调用失败后,注入到 Agent 上下文中的一段"反思引导"提示模板。模板全文如下:

Tool call failed - **Attempts remaining:** {{attempts_left}} - **Next steps:** Analyze the error, identify the root cause, and adjust your approach before retrying.

这是一份典型的 Handlebars 就记录了真实渲染产物:

<retry>Tool call failed - **Attempts remaining:** 2 - **Next steps:** Analyze the error, identify the root cause, and adjust your approach before retrying.</retry>

这段提示的语义非常清晰:

  • Attempts remaining(剩余尝试次数):告诉 Agent 当前工具在本轮对话中还剩几次失败余地,用数字量化试错空间;
  • Next steps(下一步):给出固定的行为准则——分析错误、定位根因、调整方案后再重试,把 Agent 的后续行为从"盲目重放失败调用"引导到"针对性复盘"。

二、模板在哪里被渲染:主循环中的错误注入点

模板的调用入口位于 crates/forge_app/src/orch.rs。这是 Forge 编排器(Orchestrator)主循环中"处理工具调用结果"的关键段落:

self.error_tracker.adjust_record(&tool_call_records); let allowed_max_attempts = self.error_tracker.limit(); for (_, result) in tool_call_records.iter_mut() { if result.is_error() { let attempts_left = self.error_tracker.remaining_attempts(&result.name); // Add attempt information to the error message so the agent // can reflect on it. let context = serde_json::json!({ "attempts_left": attempts_left, "allowed_max_attempts": allowed_max_attempts, }); let text = TemplateEngine::default() .render("forge-tool-retry-message.md", &context)?; let message = Element::new("retry").text(text); result.output.combine_mut(ToolOutput::text(message)); } }

整个调用链可以拆解为四个步骤:

  1. 统计错误:adjust_record(&tool_call_records)把本轮所有失败/成功的工具调用记录同步给error_tracker;
  2. 计算余量:remaining_attempts(&result.name)针对具体失败的工具名,算出该工具剩余的可失败次数;
  3. 渲染模板:通过 TemplateEngine 以forge-tool-retry-message.md为模板、{attempts_left, allowed_max_attempts}为数据渲染出提示文本;
  4. 追加到输出:Element::new("retry")将渲染结果包装为<retry>元素,combine_mut合并到该工具调用的输出中,最终作为上下文消息写回对话。

也就是说,每次工具失败,Agent 都会在下一轮推理前看到这条重试提示,从而在模型层面获得"失败原因 + 剩余预算 + 行动指引"三合一的信息。

三、剩余次数从哪里来:ToolErrorTracker 的实现原理

模板中{{attempts_left}}的数值并非凭空产生,它来自 crates/forge_domain/src/tools/call/tool_call.rs 中定义的ToolErrorTracker:

#[derive(Default, Clone, Debug, Getters)] pub struct ToolErrorTracker { errors: HashMap<ToolName, usize>, // 每个工具当前的连续失败计数 limit: usize, // 单轮内每个工具允许的最大失败次数 } impl ToolErrorTracker { pub fn new(limit: usize) -> Self { ... } pub fn adjust_record(&mut self, records: &[(ToolCallFull, ToolResult)]) -> &mut Self { // 失败的工具计数 +1,成功的工具清空计数 } pub fn remaining_attempts(&self, tool_name: &ToolName) -> usize { let current_attempts = self.error_count(tool_name); self.limit.saturating_sub(current_attempts) } pub fn limit_reached(&self) -> bool { !self.maxed_out_tools().is_empty() } }

关键设计点有三:

  1. 按工具名独立计数:errors: HashMap<ToolName, usize>意味着每个工具(如read、shell、search)各自维护失败计数,一个工具耗尽重试次数不会连累其他工具;
  2. 成功后清零:adjust方法在统计失败(*count += 1)的同时,会对"本轮有明确成功证据且未同时失败"的工具执行errors.remove(tool),实现失败计数重置——这是防止 Agent 因历史失败而永久性失去重试资格的关键;
  3. 饱和减法防溢出:remaining_attempts使用saturating_sub计算limit - current_attempts,即使计数异常也不会下溢为负数。

追踪器在应用启动时初始化,app.rs 中通过ToolErrorTracker::new(max_tool_failure_per_turn)注入限值;编排器创建时也会带默认值(orch.rs 的error_tracker: Default::default())。测试基建 orch_runner.rs 中则使用ToolErrorTracker::new(3)作为典型的 3 次上限样例。

四、到达上限会发生什么:强制中断保护

当某工具失败次数达到limit后,limit_reached()返回true,编排器会在 orch.rs 触发中断保护:

if self.error_tracker.limit_reached() { self.send(ChatResponse::Interrupt { reason: InterruptionReason::MaxToolFailurePerTurnLimitReached { limit: *self.error_tracker.limit() as u64, errors: self.error_tracker.errors().clone(), }, }) .await?; // Should yield if too many errors are produced ... }

也就是说,Forge 并不会让 Agent 无限重试同一把工具:一旦某个工具在本轮对话中失败次数触顶,编排器立即发送ChatResponse::Interrupt,携带MaxToolFailurePerTurnLimitReached中断原因以及失败工具的完整错误清单,强制结束当前轮次,把控制权交还给用户。这一设计避免了以下两类常见问题:

  • 死循环风险:Agent 反复用同一参数调用同一工具导致无限失败循环;
  • 资源浪费:单轮对话被失败的工具调用无限占用,模型请求数与 token 预算失控。

五、上限如何配置:max_tool_failure_per_turn

limit的取值来自配置项max_tool_failure_per_turn,定义于 crates/forge_config/src/config.rs:

/// Maximum tool failures per turn before the orchestrator forces /// completion. #[serde(default, skip_serializing_if = "Option::is_none")] pub max_tool_failure_per_turn: Option<usize>,
  • 字段含义:每轮对话中,单个工具在强制结束前允许出现的最大失败次数;
  • 类型与序列化:Option<usize>配合skip_serializing_if = "Option::is_none",未配置时该字段不会写入配置文件,由编排器使用默认值兜底;
  • 配置入口:对应 Forge 主配置文件(forge.schema.json中同样登记了retry相关字段),可在 agent 级或全局配置中设置。

实战调优建议:

场景建议值理由
常规编码对话3(默认语义)给 Agent 足够的反思-调整-重试空间,又不至于无限纠缠
高风险命令(部署、删除、写操作)1~2尽早中断,避免破坏性操作反复执行放大风险
网络/远端 API 类工具3~5容忍瞬时抖动,给"退避重试"留出余地

需要注意的是,该上限是按工具维度、按轮次生效的:同一工具在本轮内计数,成功后清零;新的一轮对话重新开始计数。因此一个长期运行的会话不会因为历史失败而永久锁定某个工具。

六、模板机制的可复用设计:TemplateEngine 与动态上下文

forge-tool-retry-message.md只是 Forge 模板体系中的一员(同级还有forge-commit-message-prompt.md、forge-pending-todos-reminder.md、forge-partial-summary-frame.md等,均位于 templates 目录)。它们统一由 TemplateEngine 渲染,其核心实现只有两行:

pub fn render<V: serde::Serialize>( &self, template: impl Into<Template<V>>, data: &V, ) -> anyhow::Result<String> { let template = template.into(); Ok(self.handlebar.render(&template.template, data)?) }
  • 模板以字符串形式注册到全局HANDLEBARS实例(TemplateEngine::default()即克隆该实例);
  • render接收任意serde::Serialize的数据结构(本例中即{attempts_left, allowed_max_attempts}),完成占位符替换;
  • 同一引擎同时承担 compact.rs、title_generator.rs 等模块的模板渲染,形成统一的提示词管理入口。

这种"模板文件 + 结构化上下文"的解耦设计值得借鉴:把易变的提示措辞从代码中剥离为独立 Markdown 文件,便于产品同学直接调整文案;把每次注入的数值(如剩余次数、上限)作为运行时数据传入,实现同一模板在不同场景下的动态复用。如果你在自己项目中实现 LLM 工具调用循环,完全可以仿照这套模式:一个ErrorTracker负责统计、一个模板负责引导、一个中断信号负责兜底。

七、小结

Forge 的重试提示机制是一条完整且克制的自愈链路:

  1. 工具失败 →ToolErrorTracker.adjust_record按工具计数;
  2. 编排器计算remaining_attempts并通过TemplateEngine渲染forge-tool-retry-message.md;
  3. <retry>提示随工具输出写回上下文,引导 Agent "分析错误 → 定位根因 → 调整方案 → 重试";
  4. 达到max_tool_failure_per_turn上限 →ChatResponse::Interrupt强制收尾,保护轮次预算。

对 Agent 而言,它获得了明确的失败反馈与剩余预算;对用户而言,它避免了无限循环与资源浪费;对开发者而言,它展示了"提示模板 + 运行时数据 + 硬性上限"三件套如何构成健壮的工具调用失败处理方案。无论是直接使用 Forge,还是在自己的 Agent 工程中复刻这套机制,templates/forge-tool-retry-message.md都是一个极佳的起点样例。

  • 人工智能
  • AI Agent
  • 代码智能体
  • AI 应用
  • CLI
  • 开发工具

【免费下载链接】forgecode

AI enabled pair programmer for Claude, GPT, O Series, Grok, Deepseek, Gemini and 300+ models

项目地址:https://gitcode.com/gh_mirrors/forge39/forgecode
点击查看免费下载

相关推荐

上一篇:10分钟打造高效笔记系统:Obsidian模板库的终极指南
下一篇:vCheck-vSphere与PowerCLI集成:7个高级自动化技巧和实用脚本示例

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 2:44:50

Arm Development Studio安装激活全攻略:从下载到调试一站式实操指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/28 2:43:19

RGB-D目标跟踪实战:数据对齐、梯度回传与深度敏感区域优化

简介&#xff1a;这是一份面向计算机视觉初学者与进阶学习者的多模态目标跟踪实践项目&#xff0c;聚焦RGB与Depth双模态融合技术&#xff0c;适用于课程设计、毕业设计及工程实训等场景。项目基于Python实现&#xff0c;采用边缘引导的单目深度估计网络EG-BTS构建COCO2017 RGB…

作者头像 李华
网站建设 2026/9/28 2:41:49

STM32F103移植CherryUSB实现MSC U盘功能详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华