Agent 循环控制:如何优雅地判断“继续调用”还是“结束流程”?
Agent 循环控制:如何优雅地判断“继续调用”还是“结束流程”?
大家好,我是你们的老朋友,一名深耕后端与 AI 工程化的技术博主。
在构建 LLM Agent(智能体)应用时,我们最常遇到的一个核心痛点就是:Agent 什么时候该停?
很多开发者在初学 ReAct(Reasoning + Acting)模式时,都会陷入一个误区:认为只要把工具交给 LLM,它就能完美地自主决定何时停止。但在生产环境中,你可能会遇到这样的尴尬场景:
- Agent 陷入了死循环,反复调用同一个搜索工具。
- Agent 明明已经拿到了答案,却还在执着地查询无关数据,导致 Token 费用飙升。
- Agent 因为网络超时返回空结果,直接崩溃或胡言乱语。
这背后的本质问题,就是 Agent Loop Control(代理循环控制)。今天,我们就来深入浅出地拆解这个问题,看看在企业级开发中,我们是如何让 Agent “知进退”的。
一、核心本质:思考 vs 完成
简单来说,Agent 的每一轮交互都在做一个二选一的决策:
- 继续思考(Continue):当前信息不足,需要调用工具获取更多数据。
- 任务完成(Finish):信息已足够,可以生成最终回答给用户。
经典的 ReAct 范式 流程如下:
Thought (思考) → Action (行动/调用工具) → Observation (观察结果)
↓
Thought (基于新结果再次思考) → ...
↓
Final Answer (最终回答)
举个栗子 🌰
假设用户问:“查询患者最近血糖趋势”
- 第一轮:
Thought: 我需要先查检验数据。Action:query_lis(patient_id=1001)Observation:{"glucose": [6.1, 7.8, 8.5]}
- 第二轮:
Thought: 数据已获取,数值呈上升趋势,我可以总结回答了。Final Answer: “患者最近三次血糖分别为 6.1, 7.8, 8.5 mmol/L,呈升高趋势。”
在这个理想过程中,LLM 通过输出特定的结构(如 Final Answer 标记)来告诉框架:“我结束了”。
二、为什么不能只信 LLM?
在 Demo 阶段,LLM 的自主判断通常工作良好。但在企业级生产环境中,完全依赖 LLM 的“自觉”是危险的。
LLM 本质是一个概率模型,它可能会出现:
- 幻觉循环:一直认为自己没找到答案,无限调用工具。
- 格式错误:输出的 JSON 解析失败,导致流程卡死。
- 资源浪费:为了追求“完美”,进行无意义的冗余查询。
因此,生产系统必须引入“外部控制层”,不能完全把方向盘交给 LLM。
三、企业级常见的四种控制策略
为了让 Agent 既聪明又稳定,我们通常采用以下四种手段组合拳:
1. 最大循环次数(硬止损)
这是最简单也最有效的防死循环机制。
MAX_ITERATIONS = 5
for i in range(MAX_ITERATIONS):
response = agent.step()
if response.is_final:
break
else:
# 超过最大次数,强制终止
raise Exception("Agent exceeded maximum iterations")
建议:根据任务复杂度设置阈值,简单问答设为 3-5 次,复杂分析设为 10-15 次。
2. 置信度与信息充分性判断
有时候,LLM 可能不知道自已“知道了”。我们可以引入一个评估环节,判断当前收集的信息是否足以回答问题。
- 逻辑:如果
Context中已经包含了检验结果+患者历史,则无需再调用知识库。 - 实现:可以在 Prompt 中明确指示:“如果你认为当前信息已足够回答用户问题,请直接输出 Final Answer,不要额外调用工具。”
3. 工具结果质量判断(Fallback 机制)
工具调用不是每次都能成功的。如果工具返回了异常,继续循环没有意义。
// 情况 A:空结果
{ "result": [] }
// 情况 B:超时
{ "timeout": true }
处理策略:
检测到上述情况时,立即中断循环,进入 Fallback 流程,例如回复用户:“暂时无法获取实时数据,请稍后重试”,而不是让 Agent 去瞎猜。
4. 状态机控制(LangGraph 的核心价值)
这是目前最推荐的企业级架构方式。使用如 LangGraph 这样的框架,将流程显式地建模为状态机。
流程图示
在 LangGraph 中,我们可以通过一个简单的函数来控制路由:
def should_continue(state: State) -> str:
"""
判断是继续调用工具,还是结束流程
"""
messages = state["messages"]
last_message = messages[-1]
# 1. 检查是否有工具调用请求
if hasattr(last_message, "tool_calls") and last_message.tool_calls:
return "tools"
# 2. 检查是否达到最大步数(外部保护)
if state["step_count"] > MAX_STEPS:
return "end"
# 3. 默认结束
return "end"
这种方式的优点是:流程可视、可控、可调试。你可以清楚地看到 Agent 在哪个节点做出了什么决定。
四、进阶方案:Planner + Executor 模式
对于非常复杂的任务(比如:“分析过去半年某科室的医保违规风险并生成报告”),简单的 ReAct 循环容易迷失方向。
这时,我们推荐采用 Planner(规划者) + Executor(执行者) 架构:
- Planner:先生成完整的执行计划(Plan)。
- Step 1: 查询 LIS 获取血糖数据。
- Step 2: 查询 HIS 获取用药记录。
- Step 3: 结合指南进行风险分析。
- Step 4: 生成报告。
- Executor:严格按照计划一步步执行。
优势:
- 确定性高:不会无限发散。
- 可解释性强:用户可以随时看到当前执行到了计划的哪一步。
- 易于并行:如果 Step 1 和 Step 2 无依赖,可以并行调用工具。
五、总结与最佳实践
在多轮工具调用的场景中,我的建议是构建三层防御机制:
- 第一层:LLM 自主判断
- 利用 ReAct 或 Function Calling 的原生能力,让 LLM 根据 Observation 决定下一步。
- 第二层:状态机编排(Orchestration)
- 使用 LangGraph 等框架,显式定义
Tool Node和Judge Node,掌握流程控制权。
- 使用 LangGraph 等框架,显式定义
- 第三层:系统级兜底(Guardrails)
- 设置
Max Iterations。 - 设置
Timeout。 - 对空结果、错误结果做明确的 Fallback 处理。
- 设置
最后的一句话建议:
对于简单任务,ReAct + 最大循环次数足矣;对于复杂、高可靠要求的生产任务,请毫不犹豫地选择 Planner-Executor 或 State Graph(状态图) 架构。稳定,才是 AI 应用落地的基石。
参考资料
- LangGraph Documentation: Control Flow
- ReAct Paper: Synergizing Reasoning and Acting in Language Models
- LangChain Agents Module
希望这篇文章能帮你理清 Agent 循环控制的思路。如果你在实战中遇到过奇怪的循环 Bug,欢迎在评论区分享你的踩坑经历!👇
更多推荐


所有评论(0)