1. 从一次“手动粘贴”说起Agent Loop 到底解决什么问题如果你用过大模型写代码大概率经历过这个场景让模型帮你看看当前目录有什么文件它说“我无法访问你的文件系统”于是你手动把ls的结果复制粘贴回去它再基于这段文本继续推理。整个过程里你本人就是那个循环——模型负责想你负责跑命令、贴结果、再喂回去。Agent Loop智能体循环要干的事就是把这个“人肉循环”交给程序。语言模型本身只能推理文本碰不到真实世界读不了文件、跑不了测试、看不到报错。但只要给它一个工具定义再套一个while循环它就能自己决定“我要调用 shell 跑 dir”程序执行完把结果塞回消息列表模型看到结果再决定下一步。循环持续到模型不再请求工具为止。一句话概括一个工具 一个循环 一个最小智能体。这也是 Claude Code 这类编码智能体的骨架后面所有花哨能力——多工具、子智能体、权限控制——都是在这个循环上叠加的机制循环本身始终不变。这篇就带你从零跑通这个循环用 TaoToken 的统一 Key 接入模型写一份可复制的配置骨架然后完成一次真实的工具调用往返亲眼看到stop_reason从tool_use变成end_turn。2. 前置准备用 TaoToken 统一 Key 接入模型在写循环之前先把“模型从哪来”这件事解决掉。Claude Code 的 Agent Loop 依赖 Anthropic 风格的 Messages API你需要一个兼容该协议的接入点和一个可用的 Key。TaoToken 在这里的作用是提供统一的 API 入口让你不用为每个模型单独折腾一套鉴权和 base_url。你需要准备三样东西第一一个 API Key。登录 TaoToken 控制台在 API Keys 页面创建一个复制出来形如sk-xxxx的字符串。这个 Key 就是后面所有请求的凭证。第二确认接入地址。Anthropic SDK 的base_url填https://taotoken.net/api注意这里不带任何查询参数SDK 会自己在后面拼接/v1/messages。第三选一个支持工具调用的模型 ID。工具调用tool use不是所有模型都支持选之前可以在模型对话页面先手动试一句“帮我列一下目录”看它会不会返回工具调用块。实测下来带 function calling 能力的模型都能正常走通这个循环。注意不要把 Key 硬编码进代码提交到仓库。用.env文件管理并把它加进.gitignore。这是踩过的坑里最常见的一个。3. 可复制配置settings.json 与 .env 骨架Claude Code 本身支持通过settings.json配置模型接入但这一课我们聚焦 Agent Loop 的代码实现所以配置分两层一层是给 Claude Code CLI 用的settings.json一层是给我们的 Python 脚本用的.env。先看settings.json的骨架。放在项目根目录的.claude/settings.json或者用户级的~/.claude/settings.json{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: sk-你的Key, ANTHROPIC_MODEL: 你的模型ID } }这个配置的作用是让 Claude Code CLI 启动时读取环境变量把请求打到 TaoToken 的接入点。ANTHROPIC_AUTH_TOKEN就是你的统一 KeyANTHROPIC_MODEL填你在控制台选好的模型 ID。再看 Python 脚本用的.envANTHROPIC_BASE_URLhttps://taotoken.net/api ANTHROPIC_AUTH_TOKENsk-你的Key MODEL_ID你的模型ID然后在代码里用python-dotenv加载。这里有个细节值得单独说如果你的环境里同时存在ANTHROPIC_AUTH_TOKEN和ANTHROPIC_BASE_URLAnthropic SDK 在某些版本下会优先用 token 走官方端点导致请求打错地方。稳妥做法是加载完.env后如果检测到ANTHROPIC_BASE_URL存在就主动清掉ANTHROPIC_AUTH_TOKEN只保留 base_url 让 SDK 用 Key 鉴权import os from dotenv import load_dotenv load_dotenv(overrideTrue) if os.getenv(ANTHROPIC_BASE_URL): os.environ.pop(ANTHROPIC_AUTH_TOKEN, None)依赖安装很简单pip install anthropic python-dotenv到这里模型接入就通了。接下来写循环本体。4. 拆解 Agent Loop从一条消息到一次工具往返4.1 工具定义与系统提示工具定义是一个 JSON Schema告诉模型“你有 shell 这个工具它接受一个 command 字符串参数”。系统提示则交代运行环境让模型知道自己在什么系统上、工作目录在哪import platform SYSTEM fYou are a coding agent running on {platform.system()} ({platform.platform()}). Working directory: {os.getcwd()}. Use tools to solve tasks. Act, dont explain. TOOLS [ { name: shell, description: Run a shell command., input_schema: { type: object, properties: {command: {type: string}}, required: [command], }, }, ]这里有个 Windows 环境的坑工具名用shell而不是bash。CMD 属于 Shell 的一种是 Windows 的命令行 Shell和 Unix 的 Bash 有显著差异。如果你把工具描述写成 bash模型可能生成ls -la这类命令在 CMD 里直接报错。用中性的shell命名配合系统提示里的平台信息模型会自己选dir这类正确命令。4.2 循环骨架五步走整个 Agent Loop 就是一个while True核心逻辑五步第一步把用户 prompt 作为第一条 user 消息放进messages。第二步把messages和tools一起发给模型。第三步把模型的响应追加为 assistant 消息。第四步检查stop_reason——如果不是tool_use说明模型不再需要工具循环结束。第五步遍历响应里的每个tool_use块执行工具把结果以tool_result的形式作为新的 user 消息追加回去然后回到第二步。def agent_loop(messages: list): while True: response client.messages.create( modelMODEL, systemSYSTEM, messagesmessages, toolsTOOLS, max_tokens8000, ) messages.append({role: assistant, content: response.content}) if response.stop_reason ! tool_use: return results [] for block in response.content: if block.type tool_use: print(f$ {block.input[command]}) output run_shell(block.input[command]) results.append({ type: tool_result, tool_use_id: block.id, content: output, }) messages.append({role: user, content: results})注意tool_use_id这个字段它把工具结果和模型发出的那次调用一一对应起来。如果对不上模型会认为工具没执行可能重复调用。这是协议层面的硬要求不能省。4.3 工具执行函数与安全边界run_shell负责真正执行命令。它需要处理三件事危险命令拦截、超时控制、输出截断。import subprocess def run_shell(command: str) - str: dangerous [rm -rf /, sudo, shutdown, reboot, /dev/] if any(d in command for d in dangerous): return Error: Dangerous command blocked try: r subprocess.run( command, shellTrue, cwdos.getcwd(), capture_outputTrue, textTrue, timeout120, ) out (r.stdout r.stderr).strip() return out[:50000] if out else (no output) except subprocess.TimeoutExpired: return Error: Timeout (120s)危险命令拦截是必须的因为模型可能生成破坏性命令。超时设 120 秒避免某个卡住的命令把整个循环挂死。输出截断到 50000 字符防止一次dir /s把上下文撑爆。4.4 主入口交互式 REPL最后加一个简单的交互入口让你能连续输入指令if __name__ __main__: history [] while True: try: query input(s01 ) except (EOFError, KeyboardInterrupt): break if query.strip().lower() in (q, exit, ): break history.append({role: user, content: query}) agent_loop(history) response_content history[-1][content] if isinstance(response_content, list): for block in response_content: if hasattr(block, text): print(block.text) print()把history放在循环外面是为了让多轮对话共享上下文。每次agent_loop结束后history里已经包含了完整的 user、assistant、tool_result 消息链下一轮直接接着用。5. 验证一次工具调用往返配置和代码都齐了跑起来验证。启动脚本后输入s01 列出当前目录的所有文件名预期看到的过程是这样的脚本先打印一行黄色的$ dir这是模型决定调用的命令然后run_shell执行它把结果作为tool_result追加循环回到顶部再次请求模型这次模型拿到目录列表stop_reason变成end_turn循环退出最后打印出模型基于目录内容生成的回答。如果你在调试时打印完整的response会看到第一次响应的结构大致是Message( idmsg_..., content[ToolUseBlock(idcall_..._0, input{command: dir}, nameshell, typetool_use)], model你的模型ID, roleassistant, stop_reasontool_use, ... )关键看两个字段content里是ToolUseBlock而不是TextBlockstop_reason是tool_use。如果模型返回的是TextBlock且stop_reason是end_turn说明它没走工具调用直接回答了——这通常意味着工具定义没传对或者模型不支持工具调用。第二次请求的响应里content会变成TextBlockstop_reason是end_turn循环正常退出。这一进一出就是一次完整的工具调用往返。6. 常见报错排查报错一stop_reason一直是end_turn模型不调工具。先检查tools参数有没有传进messages.create。再检查模型是否支持工具调用可以在模型对话页面手动发一句带工具定义的请求测试。如果模型支持但就是不调把系统提示里的 “Act, dont explain” 保留这句话对触发工具调用有明显作用。报错二tool_result报 400提示tool_use_id不匹配。检查你追加tool_result时用的tool_use_id是不是来自同一个响应里的block.id。跨轮次复用 ID 会直接报错。报错三Windows 下命令执行失败提示“不是内部或外部命令”。模型可能生成了 Unix 风格命令。确认工具名是shell而非bash系统提示里带上platform.system()的信息。如果还是不行在工具描述里补一句“On Windows, use CMD commands like dir, type, findstr”。报错四请求打到官方端点而非 TaoToken。检查.env里ANTHROPIC_BASE_URL是否生效以及代码里有没有清掉ANTHROPIC_AUTH_TOKEN。SDK 的 base_url 优先级在部分版本里会被环境变量覆盖显式传base_url参数更稳client Anthropic(base_urlos.getenv(ANTHROPIC_BASE_URL))报错五循环停不下来一直调工具。给循环加一个最大轮次保护比如for _ in range(20)替代while True超过就强制退出并打印当前消息链方便定位是哪个工具结果让模型误判了。7. 下一步把循环跑稳再叠加能力Agent Loop 本身就这么点东西难的是让它稳定跑起来。建议你先用这个最小版本跑通三五个真实任务——列目录、读文件、跑测试——确认工具调用往返没问题再去叠加多工具、权限控制、子智能体这些机制。如果你想把 Claude Code 的完整能力接进来长期用可以在 TaoToken 控制台创建一个 Coding Plan把模型、Key、额度统一管理省得每次换项目都重新配一遍。接入文档里有 Anthropic SDK 和 Claude Code CLI 两种方式的详细说明遇到鉴权或端点问题可以直接对照排查。想先验证模型对工具调用的支持情况用模型对话页面手动发一条带工具定义的请求最快。