1. 从一次“卡住”的 Agent 说起为什么你的循环跑不起来很多人第一次写 Agent代码看起来没问题调 LLM、解析 tool_use、执行工具、把结果塞回消息历史然后while True继续。但真跑起来要么第一轮就退出要么无限循环烧 token要么工具结果回灌后模型像失忆一样重复调同一个工具。问题几乎都不在“循环”本身而在循环的状态管理和退出条件。Agent Loop 的本质用一句话说清楚LLM 在一个循环里根据环境反馈决定下一步调什么工具直到它自己判断任务完成。它和普通问答应用最大的区别是——控制权在模型手里而不是在你预先写死的流程里。普通应用是“问一句答一句”Agent 是“给个目标它自己决定读哪个文件、跑哪条命令、什么时候停”。这篇文章面向三类人一是刚接触 Agent、想搞懂循环到底怎么转的开发者二是用 Claude Code 类工具但不知道背后发生了什么的用户三是想自己手搓一个最小 Agent、却被消息历史和 stop_reason 绕晕的人。我会用一个“找 TODO 并整理清单”的真实任务把每一轮循环拆开给你看再给一份可复制的最小配置和伪代码最后教你怎么验证“自主迭代”到底有没有生效。先说结论Agent Loop 的核心伪代码不超过 10 行但让它稳定跑起来你需要盯住三件事——消息历史是否完整追加、stop_reason 是否被正确判断、最大轮次是否设了上限。这三件事任何一件出问题Agent 就会从“自主执行”退化成“复读机”或者“死循环”。下面逐层拆。2. TaoToken 前置准备把模型调用这层先打通在写循环之前得先有一个能稳定调用的模型入口。Agent Loop 每一轮都要调一次 LLM调用层不稳定循环就没法验证。我用的是 TaoToken 的 API 入口它兼容 Anthropic 风格的消息格式tool_use 和 tool_result 的结构能直接对上省去自己拼协议的成本。你需要准备三样东西我把它叫做“三件套”缺一不可Base URLhttps://taotoken.net/apiAPI Key在控制台的 API Keys 页面生成形如sk-...Model ID比如claude-sonnet-4-5这类具体模型标识不要写模糊的别名获取路径很直接先访问官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册登录然后进控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 创建 API Key具体在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 这个页面。文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 遇到字段不确定时对着看。这里有个容易踩的坑很多人把 Base URL 写成带/v1或者带具体路径的形式结果请求 404。记住Base URL 就是https://taotoken.net/api具体路径由 SDK 或你的请求代码拼接。另外 API Key 不要硬编码进提交到 Git 的文件里用环境变量。如果你用的是 Claude Code 这类工具配置方式通常是改settings.json或者环境变量。下面这份是最小可用的环境变量配置你可以直接复制export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_API_KEYsk-你的key export ANTHROPIC_MODELclaude-sonnet-4-5如果你更习惯用配置文件Claude Code 的settings.json大致长这样路径一般在~/.claude/settings.json{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的key, ANTHROPIC_MODEL: claude-sonnet-4-5 } }注意ANTHROPIC_BASE_URL后面不要加斜杠也不要加/v1。我试过加斜杠结果请求路径变成双斜杠服务端返回 404排查了半小时才发现是这个小问题。配置完之后先用一个最简单的请求验证连通性别急着写循环。3. 可复制的最小 Agent Loop 配置与伪代码现在进入核心部分。我把 Agent Loop 拆成“配置层”和“循环层”两块。配置层负责告诉程序去哪调模型、用什么工具循环层就是那个 while。先看工具定义。Agent 能调什么工具取决于你怎么声明。以“搜索 TODO”为例工具 schema 用 JSON 描述{ name: search, description: 在指定目录下按正则搜索文件内容返回匹配的行, input_schema: { type: object, properties: { pattern: { type: string, description: 搜索的正则表达式 }, path: { type: string, description: 搜索的目录路径 } }, required: [pattern, path] } }这个 schema 会被塞进每一轮请求的tools字段。模型看到它才知道自己有个叫search的工具可以用。然后是循环本体。下面这段伪代码是整个 Agent 的心脏你可以直接照着翻译成 Pythonmessages [{role: user, content: user_task}] max_turns 20 for turn in range(max_turns): response call_llm( modelMODEL_ID, messagesmessages, toolsTOOLS ) # 把模型这一轮的回复追加进历史 messages.append({role: assistant, content: response.content}) if response.stop_reason tool_use: tool_results [] for block in response.content: if block.type tool_use: result execute_tool(block.name, block.input) tool_results.append({ type: tool_result, tool_use_id: block.id, content: result }) # 工具结果作为 user 消息回灌 messages.append({role: user, content: tool_results}) continue else: # stop_reason 是 end_turn任务结束 print(response.content) break else: print(达到最大轮次强制停止)这段代码里有三个关键点每一个都对应一类常见 bug。第一messages.append必须把 assistant 的完整 content 追加进去包括 tool_use 块。很多人只追加文本结果下一轮模型不知道自己调过工具就会重复调。第二tool_result 要作为user 角色的消息回灌并且带上tool_use_id。这个 id 是模型生成的用来把结果和请求对应起来。id 对不上模型会报错或者忽略结果。第三stop_reason是唯一的退出信号。tool_use表示继续end_turn表示结束。不要用“content 里有没有文本”来判断因为模型可能同时返回文本和 tool_use。如果你用 Claude Code 的 coding-plan 模式配置会更省事但底层还是这套循环。想长期跑编码类 Agent可以看 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 这个入口它把循环和工具链都封装好了。4. 验证自主迭代是否生效三个可观测信号写完循环不代表它真的在“自主迭代”。你需要能观测到它每一轮在干什么。我总结了三个信号只要这三个都正常说明循环是活的。信号一轮次递增且工具调用不重复。在循环里打印每一轮的turn和block.name。如果连续两轮调同一个工具、同样的参数说明消息历史没回灌成功模型失忆了。正常的 Agent 应该像人一样搜完 src 再搜 tests而不是反复搜 src。信号二stop_reason 的变化轨迹。一个健康的多轮任务stop_reason 序列应该是tool_use → tool_use → ... → end_turn。如果第一轮就是end_turn说明模型没打算用工具可能是工具描述写得太模糊或者任务本身不需要工具。如果一直是tool_use直到撞上 max_turns说明模型陷入了循环需要检查工具返回的结果是不是让它“看不懂”。信号三最终输出引用了中间结果。任务结束时模型的回答里应该包含它搜到的具体内容比如“src/auth.js 第 42 行”。如果它只是泛泛而谈“我找到了几个 TODO”说明工具结果没被真正利用。你可以用一个真实任务来验证让 Agent 在项目里找所有 TODO 并整理成清单。观察它是否先搜./src再判断要不要搜./tests最后汇总。这个任务我实测下来健康的循环会跑 2 到 3 轮第一轮搜 src第二轮搜 tests第三轮输出。如果只跑一轮就结束或者跑了十几轮还在搜就是循环有问题。想直接看模型对话效果、不写代码验证可以用 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite 这个入口手动发任务观察它调工具的节奏。5. 常见报错排查401、local proxy failed、reading choices循环跑不起来报错信息往往很迷惑。我把几个高频错误和对应原因列出来你对着查。401 Unauthorized。九成是 API Key 问题。检查三件事Key 有没有复制完整前后有没有空格、环境变量有没有生效echo $ANTHROPIC_API_KEY、Base URL 和 Key 是不是配套的。如果 Key 是从别的平台拿的配到 TaoToken 的 Base URL 上必然 401。local proxy failed / connection refused。这个报错通常出现在你本地起了代理但没起来或者 Base URL 指向了本地地址。检查ANTHROPIC_BASE_URL是不是被其他配置覆盖了。Claude Code 会读多个层级的配置项目级的settings.json可能覆盖全局的。用env | grep ANTHROPIC确认实际生效的值。reading choices of undefined。这个报错说明你的代码在按 OpenAI 的响应格式解析但实际返回的是 Anthropic 风格。Anthropic 的响应里没有choices字段内容在content数组里停止信号在stop_reason。如果你混用了两套 SDK就会出这个错。统一用一套别一边用 OpenAI SDK 一边发 Anthropic 格式的请求。OAuth 相关报错。如果你用的是 Claude Code 的 OAuth 登录模式又同时配了 API Key两者会冲突。选一种要么纯 API Key要么纯 OAuth。混用会导致鉴权头重复或者格式错误。tool_use_id 不匹配。报错信息类似“tool_result 找不到对应的 tool_use”。原因是你在回灌结果时tool_use_id写错了或者把多个工具的结果合并成一条消息但只带了一个 id。每个 tool_result 必须单独对应一个 tool_use_id。排查顺序建议先确认 401鉴权再确认 Base URL连通性最后确认响应格式解析。大部分问题在前两步就能定位。6. 把循环用起来从单次问答到自主执行的最后一公里理解了循环机制接下来就是把它用到实际场景。Agent Loop 的价值不在于“能循环”而在于“循环里模型能根据反馈调整策略”。前面那个修 login bug 的例子9 轮循环里经历了“读代码 → 改代码 → 跑测试 → 测试失败 → 读测试 → 改测试 → 再跑测试 → 通过”这种自我纠正是固定 workflow 做不到的。如果你想让 Agent 长期跑编码任务建议从 coding-plan 入手它把循环、工具链、上下文管理都封装好了你只需要给任务。入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。如果你要自己集成到现有系统就照第 3 节的伪代码手搓重点盯住消息历史和 stop_reason。最后给一个实用技巧在循环里加一个“轮次日志”把每一轮的turn、stop_reason、调用的工具名和参数、工具返回的前 100 个字符打出来。这个日志在调试时比任何断点都好用你能一眼看出模型在哪一轮“想歪了”。等 Agent 稳定跑起来再把这个日志降级成 debug 级别。Agent Loop 说到底就是一个 while 循环但让它真正“自己跑起来”的是循环里那些不起眼的状态管理细节。把消息历史喂全、把 stop_reason 判对、把最大轮次设好你的 Agent 就能从聊天机器人变成能干活的执行体。
阅读完成 · 觉得有帮助?