☰
39.Agent为什么会循环调用工具从规划执行到终止条件
2026/9/25 20:25:06 网站建设 项目流程

Agent 为什么会循环调用工具?从规划、执行到终止条件

码海寻道 · 大模型、智能体与 RAG 工程组件系列第 39 篇

Agent 一直调用搜索、反复重试同一个 API,或者在两个工具之间来回跳转,通常不是模型“突然失控”,而是系统没有为循环定义状态、进展判断和明确的停止条件。

一、Agent 为什么天然会循环?

Agent 的基本模式就是:

思考 → 调用工具 → 观察结果 → 再思考

只要模型认为信息还不足,或者工具结果没有明确表示成功,它就可能继续调用。以下情况尤其容易循环:

  • 工具返回空结果但没有明确状态;
  • 模型无法判断任务是否完成;
  • 错误被包装成普通文本;
  • 每一轮都把同样的上下文重新传入;
  • 没有最大步数、时间和成本上限。

二、先给工具结果定义状态

不要只返回一段字符串:

{"result":"没有找到"}

更好的结果结构是:

{"status":"no_result","items":[],"retryable":false,"next_action":"ask_user_for_more_context"}

模型和控制器才能区分“没有数据”“暂时失败”和“参数错误”。

三、必须设置硬性上限

MAX_STEPS=8MAX_TOOL_CALLS=12MAX_RUNTIME_SECONDS=60MAX_COST_CENTS=30

达到任意上限时,Agent 应停止并返回可解释的状态,例如“未能在规定时间内完成,请缩小查询范围”。硬上限是最后一道保险,不应只依赖 Prompt 里的自然语言提醒。

这些上限必须由服务端运行时强制执行,并按租户、用户、任务类型设置预算。除了步数和耗时,还应限制模型 Token、工具调用总数、单个工具调用次数、并发分支数和费用;预算耗尽时进入明确的budget_exhausted状态,而不是继续把错误交给模型。

四、检测重复调用

保存最近的工具调用指纹:

fingerprint=hash_json({"name":tool_name,"arguments":normalized_arguments,"state_version":state_version,})iffingerprintinrecent_calls:return{"status":"duplicate_call","retryable":False,"message":"相同工具和参数已经调用过",}

重复调用不一定永远错误,例如查询实时库存可能需要刷新。但这类工具应明确允许刷新次数和时间间隔。

对于有副作用的工具,重复检测还不够,必须把idempotency_key传给下游服务,并在数据库或业务服务中保证同一任务不会重复扣款、发货或删除。重试前要区分“请求未到达”“执行结果未知”和“已明确失败”三种状态。

五、重试要区分错误类型

可以重试

  • 网络超时;
  • 临时 429;
  • 上游短暂 5xx;
  • 连接池暂时耗尽。

不应自动重试

  • 参数校验失败;
  • 权限拒绝;
  • 资源不存在;
  • SQL 语义错误;
  • 用户没有提供必要信息。

重试应使用指数退避和最大次数,不能让 Agent 自己决定无限重试。

六、规划和执行要分开

一个更容易控制的结构:

Planner:生成有限步骤计划 ↓ Executor:执行当前步骤 ↓ Validator:检查结果是否满足条件 ↓ 结束 / 修正计划 / 转人工

Planner 不应直接拥有所有写操作权限,Executor 也不应擅自改变任务目标。

在调用有副作用的工具前,建议先持久化一次 Checkpoint,记录目标、参数、状态版本和审批状态;工具返回后再写入结果。这样进程崩溃或人工暂停时,可以判断是否需要恢复、查询下游执行状态,避免把一次未知结果当成失败而重复执行。

七、如何判断“有进展”?

每轮执行后更新明确的状态:

{"goal":"找到合同付款日期","completed":["确认合同 ID","读取合同正文"],"remaining":["定位付款条款"],"evidence":["doc-001:v3:chunk-08"],"last_action":"search_contract_clause"}

如果连续两轮没有新增证据、没有改变状态或没有缩小问题范围,应停止循环并请求用户补充信息。

八、终止条件应分层设计

成功终止

目标字段齐全、证据满足阈值、用户确认完成。

安全终止

需要写入、付款、删除或越权操作时暂停等待审批。

失败终止

达到重试、步数、时间或成本上限,或者遇到不可恢复错误。

追问终止

缺少订单号、日期、租户或其他关键参数,向用户提问而不是继续猜。

九、LangGraph 中的循环控制思路

LangGraph 用节点和条件边表达循环,可以在状态中记录steps、tool_calls和status,通过条件边决定继续或结束:

call_model ├── 有 tool_call → run_tool → call_model ├── 已完成 → END ├── 需要审批 → interrupt └── 超过限制 → fail

持久化 Checkpoint 让暂停和恢复成为可管理的状态,而不是依赖进程内变量。

十、监控循环异常

重点监控:

  • 平均和最大工具调用次数;
  • 相同工具重复调用率;
  • 单次运行耗时和 Token;
  • 达到最大步数的比例;
  • 因权限和参数错误重试的比例;
  • 人工中断和用户放弃率。

结语

Agent 循环本身不是问题,失去边界的循环才是问题。通过结构化工具结果、错误分类、重复检测、最大步数、进展状态和分层终止条件,可以把 Agent 从“不断尝试”变成可控的执行系统。

下一篇将把这些原则应用到数据库 Agent,讨论如何防止误删、SQL 注入和越权查询。

参考资料

  1. LangGraph 官方文档:Graph API
  2. LangGraph 官方文档:Recursion Limit
  3. LangGraph 官方文档:Interrupts

本文为“码海寻道”原创技术文章。循环限制、重试和成本上限应在服务端强制执行,不能只写在 Prompt 中。

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

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

立即咨询