- 人工智能
- AI Agent
- 代码智能体
- AI 应用
- CLI
- 开发工具
【免费下载链接】forgecode
AI enabled pair programmer for Claude, GPT, O Series, Grok, Deepseek, Gemini and 300+ models
在 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)); } }整个调用链可以拆解为四个步骤:
- 统计错误:
adjust_record(&tool_call_records)把本轮所有失败/成功的工具调用记录同步给error_tracker; - 计算余量:
remaining_attempts(&result.name)针对具体失败的工具名,算出该工具剩余的可失败次数; - 渲染模板:通过 TemplateEngine 以
forge-tool-retry-message.md为模板、{attempts_left, allowed_max_attempts}为数据渲染出提示文本; - 追加到输出:
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() } }关键设计点有三:
- 按工具名独立计数:
errors: HashMap<ToolName, usize>意味着每个工具(如read、shell、search)各自维护失败计数,一个工具耗尽重试次数不会连累其他工具; - 成功后清零:
adjust方法在统计失败(*count += 1)的同时,会对"本轮有明确成功证据且未同时失败"的工具执行errors.remove(tool),实现失败计数重置——这是防止 Agent 因历史失败而永久性失去重试资格的关键; - 饱和减法防溢出:
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 的重试提示机制是一条完整且克制的自愈链路:
- 工具失败 →
ToolErrorTracker.adjust_record按工具计数; - 编排器计算
remaining_attempts并通过TemplateEngine渲染forge-tool-retry-message.md; <retry>提示随工具输出写回上下文,引导 Agent "分析错误 → 定位根因 → 调整方案 → 重试";- 达到
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
相关推荐
大麦抢票自动化指南:3 分钟跑通配置与实战拆解
大麦抢票自动化指南:3 分钟跑通配置与实战拆解 热门演出的票源从释放到售罄往往不到 10 秒,而人眼发现按钮变化、读完三组选项再连点,最快也要 3 秒以上。大麦
GUI 自动化RPA3步解决my-tv播放失败:从错误提示到自动重试全攻略
3步解决my tv播放失败:从错误提示到自动重试全攻略 你还在为视频播放失败烦恼吗?直播卡顿、加载超时、黑屏闪退这些问题不仅影响观看体验,更可能导致用户流失。本
音视频直播MemOS Local Plugin 决策修复(Decision Repair)管道:让 Agent 从失败循环中自愈的反馈闭环实现解析
MemOS Local Plugin 决策修复(Decision Repair)管道:让 Agent 从失败循环中自愈的反馈闭环实现解析 导读 MemOS Lo
人工智能大模型Agent 记忆AI AgentRAG知识图谱dsh-plugin
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考