给 Agent 加上防爆闸:Tool Calling 异常循环的防护设计
2026/8/2 1:44:53 网站建设 项目流程

给 Agent 加上防爆闸:Tool Calling 异常循环的防护设计

把大模型接入业务系统做 Tool Calling(工具调用)时,很多团队都会踩到一个坑——Agent 突然卡死在某一个工具调用上,陷入无限死循环。

其实模型并不是“故意不停”。常见的情况有三种:一是工具返回的 JSON 格式不符合模型的预期,模型误以为是自己参数填错了,下一轮修改一下参数继续调;二是系统本身已经执行成功了,但响应在网络传输中丢了,模型不知道已经成功,盲目发起重试;三是 Prompt 里根本没有写明白什么情况下应该结束任务。

只靠设置“最大调用轮数(Max Iterations)”只能在最后强行关闭任务,根本没法阻止中间已经发生的重复扣费、重复写数据库或者误发邮件这些严重后果。要解决这个问题,必须用确定性的后端代码去约束不确定的模型输出。


大模型 Tool Calling 死循环的工程根因

要设计防爆机制,先看看大模型在 Tool Calling 时发生死循环的三种常见情况:

flowchart TD A[LLM 发起 Tool Call 请求] --> B[后端执行器调用微服务 API] B --> C{API 返回结果形态} C -->|类型 1: 格式不相符/未加结构化界定| D[模型误判为参数未提供 ➔ 再次产生相同调用] C -->|类型 2: 网络超时但后端已成功| E[模型未感知幂等 ➔ 重复发起写操作调用] C -->|类型 3: 缺少收敛判定条件| F[模型在相似工具间反复交替切换] D --> G[陷入无限循环死锁] E --> G F --> G
  1. 响应格式不对导致模型一直在“猜参数”
    如果 API 返回了一大堆带有堆栈信息的错误文本,或者不是标准的 JSON,LLM 分析上下文时很容易误判,觉得是自己上一次传入的参数不对。于是它会在下一轮稍微改改参数再试一次,陷入“失败 ➔ 猜参数 ➔ 再次失败”的怪圈。
  2. 网络超时引发的重复写入
    在微服务环境下,网络超时经常发生在 API 已经处理完、正在回传响应的阶段。由于没有全局 Task ID 和幂等 Key 的限制,当编排层把超时错误扔给模型时,模型会直接重试,导致原本不能重复执行的操作(比如扣款、下单)被执行了多次。
  3. 目标不明确导致任务收敛不了
    如果 Prompt 给的目标太模糊(比如“帮我分析并优化这份数据”),又没有给明确的停止触发条件,LLM 就会在好几个功能差不多的查询工具之间来回调用,自己不知道什么时候该输出最终答案。

把循环拆成可审计的状态机体系

为了打破死循环,服务端绝对不能直接拿大模型的输出去调工具。必须在编排层引入一个有限状态机(FSM)

在整个任务的生命周期里,任务只能处于以下几种明确的状态,而且每次状态变更都要记日志:

  • Planning:大模型正在分析上下文,生成下一步的行动计划。
  • Validating:防爆闸正在检查模型想调用的工具名称、参数格式、权限、预算和幂等 Key。
  • Executing:后端正在安全地执行这个工具。
  • NeedsReview:碰到了敏感操作或者规则校验异常,暂停自动执行,转给人工确认。
  • Completed/Failed:任务正常完成或者因为不可恢复的错误退出。
stateDiagram-v2 [*] --> Planning Planning --> Validating: 模型返回 Tool Call 请求 state Validating { [*] --> CheckBudget CheckBudget --> CheckPermission: 预算正常 CheckPermission --> CheckIdempotency: 鉴权通过 CheckIdempotency --> ValidateSchema: 无幂等冲突 } Validating --> Executing: 防爆闸全量校验通过 Validating --> NeedsReview: 触发高风险操作或规则校验异常 Validating --> Failed: 超出硬预算 limit Executing --> Planning: 工具执行成功,结果写入 Context NeedsReview --> Executing: 人工批准执行 NeedsReview --> Failed: 人工拒绝或超时 Planning --> Completed: 模型产出最终 Answer Completed --> [*] Failed --> [*]

有了状态机之后,每次工具调用就不再是随意的 Prompt 拼接,而是变成可以监控、审计和人工接管的确定性流程。


确定性防护防线:硬限制、语义幂等 Key 与业务审批隔离

防爆闸的设计应该采用三层拦截机制:

第一层:全局硬配额限制

这是保底的熔断机制。对单个 Task 必须在服务端强制配置以下阈值:

  • 最大工具调用轮数(比如最多 15 轮);
  • 单 Task 最大 Token 消耗上限
  • 任务总超时时间(比如最多 180 秒);
  • 单个工具连续重试次数(同一个工具不能连续尝试超过 3 次)。

只要触发了任何一条硬限制,状态机立刻终止任务,切到Failed或者NeedsReview状态。

第二层:基于参数 Hash 的语义幂等 Key

为了防止模型连续发起完全相同的无效调用,服务端必须对模型给出的工具参数做标准化处理:

  1. 把 JSON 参数的 Key 按字母顺序升序排列;
  2. 过滤掉无意义的空格、换行符和默认空值;
  3. TaskID + ToolName + NormalizedArgsHash拼成全局唯一的幂等 Key。

如果服务端检查发现这个幂等 Key 在当前任务里已经成功执行过了,拦截器就会直接拦截这次调用,把上一次执行的结果直接填进 Context,并提示模型:“该操作已经完成,请直接读取已有结果进行下一步。”

第三层:高风险写操作的人工审批

对于扣款、删除数据、修改权限这些不可逆的操作,防爆闸会把对应的工具标记为RequiresApproval。不管模型的推理看起来多么合理,都必须把任务挂起到NeedsReview状态,发送通知给人工审批界面,拿到明确的批准信号后才由后端继续执行。


生产级 Go 语言 Agent Guardrail 防爆中间件实现

下面是一段集成了预算检查、JSON 参数 Hash 计算、幂等拦截和审批控制的 Go 代码实现:

package guardrail import ( "crypto/sha256" "encoding/hex" "encoding/json" "errors" "fmt" "sort" "sync" "time" ) var ( ErrBudgetExceeded = errors.New("task budget or max iterations exceeded") ErrDuplicateCall = errors.New("duplicate tool call with identical arguments") ErrApprovalRequired = errors.New("action requires manual human approval") ) type ToolCall struct { ID string `json:"id"` Name string `json:"name"` Arguments map[string]interface{} `json:"arguments"` } type TaskContext struct { TaskID string ToolCalls int MaxToolCalls int Deadline time.Time Approved map[string]bool mu sync.RWMutex } type Guardrail struct { sensitiveTools map[string]bool completedKeys map[string]string mu sync.Mutex } func NewGuardrail(sensitiveTools []string) *Guardrail { st := make(map[string]bool) for _, tool := range sensitiveTools { st[tool] = true } return &Guardrail{ sensitiveTools: st, completedKeys: make(map[string]string), } } func (g *Guardrail) Allow(ctx *TaskContext, call ToolCall) (string, error) { ctx.mu.Lock() defer ctx.mu.Unlock() // 1. 硬限制配额检查 ctx.ToolCalls++ if ctx.ToolCalls > ctx.MaxToolCalls || time.Now().After(ctx.Deadline) { return "", ErrBudgetExceeded } // 2. 参数标准化并生成 Hash argHash, err := g.computeNormalizedHash(call.Name, call.Arguments) if err != nil { return "", fmt.Errorf("failed to normalize arguments: %w", err) } idempotencyKey := fmt.Sprintf("%s:%s:%s", ctx.TaskID, call.Name, argHash) // 3. 幂等性检查 g.mu.Lock() cachedResult, exists := g.completedKeys[idempotencyKey] g.mu.Unlock() if exists { // 返回上一次的结果,阻止重复调用 return cachedResult, ErrDuplicateCall } // 4. 敏感写操作人工审批检查 if g.sensitiveTools[call.Name] { if !ctx.Approved[call.ID] { return "", ErrApprovalRequired } } return idempotencyKey, nil } func (g *Guardrail) RecordSuccess(idempotencyKey string, resultStr string) { g.mu.Lock() defer g.mu.Unlock() g.completedKeys[idempotencyKey] = resultStr } func (g *Guardrail) computeNormalizedHash(toolName string, args map[string]interface{}) (string, error) { keys := make([]string, 0, len(args)) for k := range args { keys = append(keys, k) } sort.Strings(keys) sortedMap := make([]string, 0, len(args)) for _, k := range keys { valBytes, err := json.Marshal(args[k]) if err != nil { return "", err } sortedMap = append(sortedMap, fmt.Sprintf("%q:%s", k, string(valBytes))) } rawPayload := fmt.Sprintf("%s|{%s}", toolName, joinStrings(sortedMap, ",")) hash := sha256.Sum256([]byte(rawPayload)) return hex.EncodeToString(hash[:]), nil } func joinStrings(elems []string, sep string) string { if len(elems) == 0 { return "" } res := elems[0] for _, s := range elems[1:] { res += sep + s } return res }

故障注入测试与实际权衡

故障注入自动化测试

防爆系统不能只用正常流程做测试,必须做针对编排层的故障注入测试

  1. 模拟 API 返回损坏的 JSON:让测试 API 返回格式错误的文本,验证防爆闸能不能在达到次数限制前正常拦截,而不是直接崩溃。
  2. 模拟丢包与延迟:在测试 API 里注入超时,模拟“后端写成功了但响应丢了”的场景,看看幂等 Key 机制能不能挡住二次重复写入。
  3. 模拟高自信度的违规调用:构造带有误导性的 Prompt 诱导模型调用敏感工具,验证审批拦截是否有效。

权衡分析

设计 Agent 防控时,要找准安全和灵活之间的平衡点:

评估维度完全放任 (无 Guardrail)适度工程防线 (推荐)过度保守
安全性与控费很差,极易死循环拉爆账单或者误删数据。好,硬限制配幂等 Key 能挡住绝大多数异常。很好,但复杂任务很容易被打断。
复杂任务成功率表面看起来高,实际上很多靠运气。,能提供明确反馈引导模型收敛。低,正常的长链任务容易被误杀。
人工介入成本事后救火成本极高。低,只有敏感写操作需要点一下确认。极高,每一步都要确认,失去了自动化的意义。

总结

大模型的优势在于灵活的推理能力,但后端系统要求的是确定性的安全与稳定。

靠修改 Prompt 很难彻底避免死循环。正确的做法是把推理和执行分开:大模型只负责给出下一步想做什么,至于这一步能不能做,由后端的防爆闸中间件说了算。


参考资料

  • OWASP Top 10 for LLM Applications
  • NIST AI Risk Management Framework
  • Building Effective Agents - Anthropic

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询