尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Python Agent 踩坑实录:第一次写 Agent,我忘了加一行代码,循环跑了 200 次
Python Agent 踩坑实录第一次写 Agent我忘了加一行代码循环跑了 200 次摘要本文从一次初学者写 Agent 忘了加最大步数限制、循环跑了 200 次才停的经历出发分析 Agent 循环失控的三个常见原因没有max_steps、工具描述模糊导致模型反复试错、不检查模型是否已经给出答案。文章给出一段 30 行的完整可运行代码和三个必须加的保护措施适合刚接触 Agent 的 Python 学习者。文章目录Python Agent 踩坑实录第一次写 Agent我忘了加一行代码循环跑了 200 次一、问题背景我的 Agent 跑了 200 次才停二、最小复现代码五步循环失控三、原因分析三个初学者常犯的错误错误一用 while True 而不是固定次数循环错误二工具描述写得太模糊错误三没有把模型的最终回答和工具调用区分清楚四、修复方案三个必须加的保护保护一固定循环次数保护二把工具描述写清楚保护三打印每一步看清楚在发生什么完整代码五、初学者容易忽略的其他细节细节一tool_call_id 必须对应细节二messages 的顺序不能乱细节三参数 JSON 解析可能失败六、排查技巧怎么知道 Agent 哪里出了问题方法一打印每一步的消息列表方法二设置一个很小的 max_steps方法三统计工具调用次数七、总结一、问题背景我的 Agent 跑了 200 次才停刚学 Agent 的时候我跟着教程写了一个能查天气的小助手。代码很简单调用大模型如果大模型要调工具就执行工具把工具结果返回给大模型。跑起来后我输入“北京天气怎么样”然后就去倒水了。回来一看终端还在刷屏调用工具: get_weather({city: 北京}) 工具结果: 晴25°C 调用工具: get_weather({city: 北京}) 工具结果: 晴25°C 调用工具: get_weather({city: 北京}) 工具结果: 晴25°C ...重复 200 次Agent 一直在调用同一个工具传同一个参数拿同一个结果永远不结束。我以为是模型的问题换了几个模型都一样。后来才发现问题出在我自己的代码里——我忘了一行最重要的代码。这就是本文要讲的坑——Agent 的循环必须有人为设的上限不能指望模型自己停下来。二、最小复现代码五步循环失控先看错误版本importjsonfromopenaiimportOpenAI clientOpenAI()defget_weather(city:str)-str:data{北京:晴25°C,上海:多云28°C}returndata.get(city,f查不到{city})tools[{type:function,function:{name:get_weather,description:查天气,# 描述太模糊parameters:{type:object,properties:{city:{type:string}},required:[city],},},}]defrun_agent(user_input:str):messages[{role:user,content:user_input}]whileTrue:# 问题在这里没有循环上限responseclient.chat.completions.create(modelgpt-4o-mini,messagesmessages,toolstools,)messageresponse.choices[0].messageifnotmessage.tool_calls:returnmessage.content messages.append(message)fortcinmessage.tool_calls:argsjson.loads(tc.function.arguments)resultget_weather(args[city])messages.append({role:tool,tool_call_id:tc.id,content:result,})print(run_agent(北京天气怎么样))输出调用工具: get_weather({city: 北京}) 工具结果: 晴25°C 调用工具: get_weather({city: 北京}) 工具结果: 晴25°C ...无限循环直到你按 CtrlC问题就在这一行whileTrue:# 没有退出条件三、原因分析三个初学者常犯的错误错误一用 while True 而不是固定次数循环whileTrue:# 危险依赖模型自己停下来初学者容易觉得“模型应该知道自己什么时候完成”。实际上模型可能觉得“信息还不够”继续调用工具模型可能重复调用同一个工具模型可能陷入某种推理循环。永远不要用while True写 Agent 循环。错误二工具描述写得太模糊description:查天气,# 太模糊模型不知道需要传什么参数参数是什么格式参数是必填还是选填模糊的描述会让模型反复试错比如先试{city: 北京}再试{location: 北京}再试{address: 北京}——每次试错都是一次循环。错误三没有把模型的最终回答和工具调用区分清楚ifnotmessage.tool_calls:returnmessage.content看起来没问题但如果模型返回的content是空字符串比如它只是想调用工具这个判断会误判。四、修复方案三个必须加的保护保护一固定循环次数defrun_agent(user_input:str,max_steps:int5):# 加默认参数messages[{role:user,content:user_input}]forstepinrange(max_steps):# 固定次数循环responseclient.chat.completions.create(modelgpt-4o-mini,messagesmessages,toolstools,)messageresponse.choices[0].messageifnotmessage.tool_calls:returnmessage.content messages.append(message)fortcinmessage.tool_calls:argsjson.loads(tc.function.arguments)resultget_weather(args[city])messages.append({role:tool,tool_call_id:tc.id,content:result,})returnf超过最大步数{max_steps}任务未完成# 超限返回提示关键点for step in range(max_steps)保证最多循环 5 次到点自动退出。保护二把工具描述写清楚tools[{type:function,function:{name:get_weather,description:查询指定城市的天气。输入城市名称返回该城市的天气信息。,parameters:{type:object,properties:{city:{type:string,description:城市名称例如北京、上海、深圳,}},required:[city],},},}]关键点描述要包含三要素——做什么、传什么、格式是什么。保护三打印每一步看清楚在发生什么forstepinrange(max_steps):print(f\n[第{step1}步])# ... 调用大模型 ...ifnotmessage.tool_calls:print(f[完成]{message.content})returnmessage.contentfortcinmessage.tool_calls:print(f[工具]{tc.function.name}({tc.function.arguments}))# ... 执行工具 ...print(f[结果]{result})关键点初学者一定要能看到每一步发生了什么才能发现问题。完整代码importjsonfromopenaiimportOpenAI clientOpenAI()defget_weather(city:str)-str:data{北京:晴25°C,上海:多云28°C}returndata.get(city,f查不到{city})tools[{type:function,function:{name:get_weather,description:查询指定城市的天气。输入城市名称返回该城市的天气信息。,parameters:{type:object,properties:{city:{type:string,description:城市名称例如北京、上海、深圳,}},required:[city],},},}]defrun_agent(user_input:str,max_steps:int5):messages[{role:user,content:user_input}]forstepinrange(max_steps):print(f\n[第{step1}步])responseclient.chat.completions.create(modelgpt-4o-mini,messagesmessages,toolstools,)messageresponse.choices[0].messageifnotmessage.tool_calls:print(f[完成]{message.content})returnmessage.content messages.append(message)fortcinmessage.tool_calls:argsjson.loads(tc.function.arguments)print(f[工具]{tc.function.name}({args}))resultget_weather(args[city])print(f[结果]{result})messages.append({role:tool,tool_call_id:tc.id,content:result,})print(f[超限] 达到最大步数{max_steps})returnf超过最大步数{max_steps}print(run_agent(北京天气怎么样))输出[第 1 步] [工具] get_weather({city: 北京}) [结果] 晴25°C [第 2 步] [完成] 北京今天晴天气温 25°C。五、初学者容易忽略的其他细节细节一tool_call_id必须对应messages.append({role:tool,tool_call_id:tc.id,# 必须和上面工具的 id 一致content:result,})如果tool_call_id写错了大模型会报错或者不知道这是哪个工具的结果。细节二messages的顺序不能乱正确的顺序是用户消息大模型的响应包含工具调用工具结果。如果顺序乱了大模型会困惑。细节三参数 JSON 解析可能失败argsjson.loads(tc.function.arguments)模型偶尔会返回不合法的 JSON。初学者可以先打印看print(tc.function.arguments)# 看看实际返回什么argsjson.loads(tc.function.arguments)六、排查技巧怎么知道 Agent 哪里出了问题方法一打印每一步的消息列表fori,msginenumerate(messages):print(f[{i}]{msg})看清楚大模型每一轮收到了什么、返回了什么。方法二设置一个很小的 max_steps调试时把max_steps设为 2 或 3快速看到问题不用等 200 次循环。方法三统计工具调用次数fromcollectionsimportCounter tool_counterCounter()# 在工具调用处tool_counter[tc.function.name]1# 循环结束时打印print(f工具调用统计:{dict(tool_counter)})如果某个工具被调用了很多次说明模型在反复试错。七、总结这次踩坑让我记住了一句话Agent 的循环必须有人为设的上限不能指望模型自己停下来。核心要点回顾不要用while True写 Agent 循环用for step in range(max_steps)max_steps设 5-10 就够了超限要返回明确的提示工具描述要写清楚做什么、传什么、格式是什么每一步都打印出来看清楚在发生什么tool_call_id必须和工具调用的 id 对应。给初学者的三句话先把这 30 行代码跑通比看十篇高级文章都有用出问题先打印每一步的消息列表90% 的问题都能看出来不要急着用 LangChain先用最基础的openai库理解原理。参考资料OpenAI Function Calling 官方指南OpenAI Python SDK 快速开始
RELATED

相关推荐

工业级电源路径保护:TPS259483AYWPR与STM32F405ZG协同设计

工业级电源路径保护:TPS259483AYWPR与STM32F405ZG协同设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/8 7:25:19
微内核工具系统设计与AI辅助故障排查实践

微内核工具系统设计与AI辅助故障排查实践

1. 工具系统的定位与设计取舍1.1 为什么内核需要一套自己的工具系统写内核最痛苦的一件事,不是功能实现不出来,而是功能写好了之后不知道它到底跑得对不对。前几篇我们把DSH的调度器、内存管理、设备框架都搭了起来,内核能开机、能执行任务了…

📅 2026/10/8 7:25:19
17、触摸事件分发流程:MotionEvent的封装,从InputDispatcher到ViewRootImpl

17、触摸事件分发流程:MotionEvent的封装,从InputDispatcher到ViewRootImpl

17.1 事件封装的起点:InputDispatcher好,我们直接进入正题。上一章我们聊到InputDispatcher从队列里取出事件,准备发给目标窗口。但这里有个关键动作——事件封装。说白了,就是把原始的输入数据,包装成一个Java层能识别…

📅 2026/10/8 7:25:19
MORE NEWS

更多资讯

📰

Helm Charts 中 MongoDB 自定义初始化脚本指南:基于 stable/mongodb Chart 的 docker-entrypoint-initdb.d 机制

【免费下载链接】charts ⚠️(OBSOLETE) Curated applications for Kubernetes 项目地址: https://gitcode.com/gh_mirrors/chart/charts 点击查看 免费下载 导读 本文围绕 Kubernetes 官方 Charts 仓库中 stable/mongodb Chart 的初始化脚本机制展开,…

📰

MMDetection3D ScanNet 数据集使用指南:数据导出、训练管线与评测全流程

人工智能计算机视觉深度学习自动驾驶 【免费下载链接】mmdetection3d OpenMMLabs next-generation platform for general 3D object detection. 项目地址: https://gitcode.com/gh_mirrors/mm/mmdetection3d 点击查看 免费下载 ScanNet 是 MMDetection3D 中面向室内…

📰

react-day-picker 的 PreviousMonthButtonProps 类型别名解析:上一月导航按钮的 Props 契约与自定义实践

UI组件前端 【免费下载链接】react-day-picker DayPicker is a customizable date picker component for React. Add date pickers, calendars, and date inputs to your web applications. 项目地址: https://gitcode.com/gh_mirrors/re/react-day-picker 点击查看…

📰

OpenShell外壳定制指南:从开始菜单接管到工作流优化

1. 从"OpenShell"这个名字说起:它到底指什么第一次看到"OpenShell"这个词,很多人会下意识地把它和"开源终端""命令行外壳"联系起来。这个直觉不算错,但只对了一半。在真实的工程语境里,O…

📰

TSN超帧技术解析:从时间切片到工业以太网确定性传输

做运动控制的同行应该都有过这种体验:一条产线上二三十个伺服轴,控制周期压到1ms,PLC跟驱动器之间靠普通以太网交换机通信。平时跑着没事,但只要旁边的视频监控、MES报表、远程桌面一跑大流量,伺服就开始偶发抖动&…

📰

996引擎-WPS公式:自定义公式 format 模板字符串(支持颜色 四则混合运算)

996引擎-WPS公式:自定义公式 format 模板字符串【支持颜色 & 四则混合运算】 自定义公式 format 基础版 1. 支持语法对照表 2. 补充规则说明 3. 最终验证用例(全覆盖) 自定义公式 format 对应的显示预览颜色版本 自定义公式 format 支持颜色,支持自定义占位符 v202609x…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬