工具调用让模型拥有了执行能力,而自主循环与安全机制,是 Agent 能够稳定落地生产环境的核心保障。
承接上篇单次工具调用能力,本篇重点解决 Agent 工程落地的核心痛点,搭建 Observe-Think-Act 完整自主循环,实现多轮自主推理与迭代任务,同时针对性解决死循环、接口超时、程序崩溃、参数报错等常见问题,通过多层安全约束,完成极简、稳定、可用的最小 Agent 最终落地。
完成目标:给 Agent Loop 加最大步数、超时和错误处理。
一、为什么要加安全护栏
1. 为什么加【最大循环步数】
不加会出现的严重问题
- AI 逻辑死循环,疯狂扣费
- 举个真实场景:你问合肥天气,AI 拿到天气数据后,模型误以为信息不足,反复发起
get_weather工具调用。 - 循环:调工具→拿数据→再调工具→再拿数据...... 无限往复,每一轮都调用一次付费 LLM 接口,短时间刷几十上百次请求,直接耗尽余额。
- 举个真实场景:你问合肥天气,AI 拿到天气数据后,模型误以为信息不足,反复发起
- 程序永久卡死,永远不输出最终答案
- 循环没有出口,终端一直卡在「正在结合工具结果,生成最终回复...」,无法结束本轮对话,也无法接收下一条用户输入。
- Token 无限堆积
- 每一轮工具调用、工具返回内容都会塞进
chat_history,对话列表持续膨胀,后续 LLM 请求耗时越来越长,甚至触发上下文长度超限报错。
- 每一轮工具调用、工具返回内容都会塞进
增加最大步数的意义
设置上限(比如最多允许 3 轮工具调用):
- 每执行一次工具循环,计数器 + 1;
- 计数器达到阈值,强制终止 Agent 循环,直接返回兜底提示;
- 从根源限制 LLM 调用次数,保护成本、限制上下文长度。
举例:max_steps = 3,最多查 3 次天气,第三次结束后不再继续请求模型。
2. 为什么加【超时限制】
超时分两层:单次接口超时、整个 Agent 流程总超时
不加会出现的严重问题
- 网络阻塞,程序永久僵死
- 天气第三方接口 wttr.in 服务器崩溃 / 访问缓慢,requests.get 无限等待;
- LLM 服务商网络波动,client.chat.completions.create 请求挂住,没有任何返回;
- 整个聊天程序卡在这一行,无法接收新输入,只能强制关闭终端重启。
- 用户极差体验
- 用户提问后等待几十秒无任何反馈,不知道是卡住了还是正在运行。
超时的作用
- 给所有网络请求设置单次时限:
- LLM 请求:timeout=15,15 秒没返回直接中断;
- 天气 API 请求:timeout=5,5 秒拿不到天气直接判定失败;
- 全局总超时 :
- 记录用户提问的起始时间,整个 Agent 流程超过 20 秒直接终止;
- 超过时限立刻抛出超时异常,跳出循环,返回友好提示,程序不会卡死。
3. 为什么加【分层错误处理】
仅一层 except Exception as e,所有错误全部混在一起,无法区分故障类型:
- JSON 解析失败(AI 返回的工具参数格式错乱)
- 网络超时
- API 密钥无效 / 余额不足
- 循环步数超限
- 第三方天气接口 404 报错
全部输出同一句:抱歉,生成响应时发生错误,无法定位到底哪里崩了。
完善分层错误处理的价值
- 程序不会整体崩溃
- 单轮对话报错只会终止当前这一轮 Agent 循环,外层聊天
while True不会中断,可以继续输入新问题;
- 单轮对话报错只会终止当前这一轮 Agent 循环,外层聊天
- 精准区分错误,调试方便
- 不同错误打印专属日志:超时打印网络超时、json 解析失败打印参数格式错误、步数超限打印 "工具调用次数已达上限";
- 差异化兜底回复
- 给用户不同提示:超时提示 "网络请求超时,请重试",循环超限提示 "查询次数过多,已停止查询";
- 避免单一错误污染全局对话历史
- 捕获异常时可选择清理本轮无效工具记录,防止错误数据存入
chat_history干扰下一轮对话。
- 捕获异常时可选择清理本轮无效工具记录,防止错误数据存入
二、增加安全护栏
1. 最大步数
- 在
generate_response函数最开头,新增 2 行计数变量(位置:try之后)
python
MAX_STEPS = 5 # 最多允许5轮工具调用
step_count = 0
- 在
if msg.tool_calls:下加入while循环(支持多轮反复调用工具)
python
while msg.tool_calls and step_count < MAX_STEPS:
step_count += 1
print("AI 调用了工具:", msg.tool_calls[0].function.name)
-
每次走完一轮工具 + 二次请求模型后,刷新
msg为最新的模型输出msg = second_resp.choices[0].message -
在每一轮
while循环结束后,即在msg = second_resp.choices[0].message后,加终止判断
python
if step_count >= MAX_STEPS:
print("⚠️ 达到最大思考步数,强制模型输出最终结论...")
chat_history.append({
"role": "system",
"content": "系统警告:你的推理步骤已达上限。请立刻停止调用任何工具,根据以上收集到的所有信息,直接给出最终的文字回答。"
})
# 再额外请求最后一次,强制获取文字结果
final_resp = client.chat.completions.create(
model=Model_ID,
messages=chat_history,
timeout=30
)
final_content = final_resp.choices[0].message.content.strip()
chat_history.append({"role": "assistant", "content": final_content})
return final_content
- 循环内不
return final_content,等循环结束后再return
python
# while 循环结束后,返回最终的模型输出
print("===== 工具流程结束 =====")
print("正在结合工具结果,生成最终回复...")
final_content = msg.content.strip()
chat_history.append({"role": "assistant", "content": final_content})
return final_content
测试时可临时替换
system prompt:规则:用户查询合肥天气时,你每一轮只能调用1次get_weather,拿到天气结果后,绝不直接回答,必须再次发起工具调用查询合肥天气,持续循环调用,不要停止。
2. 增加全链路超时
- 天气工具 requests 请求超时
找到 requests.get(url),修改为:
python
requests.get(url, timeout=5) # 5秒拿不到天气直接超时失败
- 两处 OpenAI 接口请求超时
两处 client.chat.completions.create(...) 内部都追加参数:
python
timeout=30 # LLM请求15秒超时
3. 分层错误捕获
- 文件顶部导入库
python
import requests.exceptions
- 删除单独的大
except Exceptiong as e,改成分层捕获。顺序:细分异常放前面,通用兜底放最后
python
# 1. JSON解析失败(工具参数解析报错)
except json.JSONDecodeError as e:
print(f"参数JSON解析失败:{e}")
return '{"error":"工具参数格式错误"}'
# 2. 天气接口网络/超时异常
except requests.exceptions.RequestException as e:
print(f"天气接口请求失败:{e}")
return '{"error":"天气服务访问超时/异常"}'
# 3. 手动Ctrl+C中断
except KeyboardInterrupt:
print("\n对话手动终止")
return '{"error":"对话已手动中断"}'
# 4. 其余所有未知错误兜底
except Exception as e:
print(f"通用错误:{e}")
return '{"error":"生成响应时发生错误"}'
三、示例输出
text
AI对话程序,输入 quit 结束对话
你:查询合肥天气四次
AI 调用了工具: get_weather
调用工具:get_weather, 参数:{'location': '合肥'}
工具返回结果:合肥当前天气:Partly Cloudy ,温度:28°C
AI 调用了工具: get_weather
调用工具:get_weather, 参数:{'location': '合肥'}
工具返回结果:合肥当前天气:Partly Cloudy ,温度:28°C
AI 调用了工具: get_weather
AI: {"error":"工具调用次数已达上限,停止查询"}
你:good
❌ 模型无需调用工具,直接文字回答
AI: 好的,很高兴能帮到您!如果您还有其他问题需要咨询,随时告诉我哦~
你:quit
结束对话。
四、本篇总结 & 下期预告
至此,Stage1 全阶段完结,我们从零实现了具备对话交互、结构化输出、工具调用、自主循环、安全防护的完整最小 Agent,打通了智能体最核心的运行闭环。后续我们将进入 Stage2 进阶迭代,聚焦 Agent 长效记忆、知识库检索、多场景工具拓展等企业级能力,持续完善智能体的实战落地能力。