1. 从一次“卡住”的工具调用说起Agent Loop 与 Tools 执行链路到底怎么跑如果你正在看 Learn-Claude-Code 这个项目大概率已经写过那个几十行的s01_agent_loop.py也照着s02_tool_use.py加过read_file、write_file。但很多人跑通之后心里还是虚的模型说“我要读文件”程序到底在哪一步接住了这个请求tool_use_id是谁生成的为什么工具结果要以user角色塞回去这篇文章就围绕 Learn-Claude-Code 里 Agent Loop 与 Tools 的协作机制把一次工具调用的完整执行链路拆开并给出可复制的 Agent Loop 伪代码、Tools 注册配置最后把 endpoint 改到 TaoToken 做一次真实的工具调用验证。先说清楚这套东西是什么、能做什么、适合谁。Learn-Claude-Code 是一个从 0 到 1 构建 AI 编程 Agent 的开源教学项目核心不是教你“用”智能体而是教你“造”智能体。它用 12 个递进章节从最基础的 Agent Loop 出发逐步叠加工具调用、任务规划、上下文压缩、子智能体协作。适合两类人一是想搞懂 Claude Code 这类工具内部原理的开发者二是准备自己写一个 coding agent、但被各种框架绕晕的人。它的核心观点很硬核——Agent 的核心不在模型本身而在模型外面的运行环境Harness。模型负责决策Harness 负责提供工具、上下文和执行能力。我试过把 s01 和 s02 的代码逐行跑一遍最大的感受是真正让 Agent “活”起来的不是模型多聪明而是那条while stop_reason tool_use的循环。没有它模型再强也只能“说”不能“做”。下面按执行链路一步步拆。2. TaoToken 前置准备把 endpoint 换成兼容 Anthropic 协议的入口在动手改代码之前得先有一个能跑通 Anthropic Messages API 的入口。Learn-Claude-Code 的参考实现用的是Anthropic(base_url...)客户端所以只要你的服务兼容 Anthropic 协议就能直接接进来。TaoToken 提供的就是这样一个入口官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 。你需要准备三样东西这三件套在任何 Anthropic 兼容接入里都跑不掉第一是 Base URL。在 Learn-Claude-Code 里对应ANTHROPIC_BASE_URL填https://taotoken.net/api。注意这里不要带任何路径后缀客户端会自己拼/v1/messages。第二是 API Key。去控制台创建地址是 https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。创建后复制那串sk-开头的字符串填到ANTHROPIC_API_KEY。这个 Key 只显示一次建议先存到密码管理器。第三是 Model ID。填到MODEL_ID。不同模型 ID 不一样具体以文档为准文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。如果你不确定用哪个可以先在模型对话页面试一下地址是 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。把这三样写进项目根目录的.env文件。Learn-Claude-Code 的s01_agent_loop.py顶部有load_dotenv(overrideTrue)会自动读取。这里有个坑要提前说代码里有一句if os.getenv(ANTHROPIC_BASE_URL): os.environ.pop(ANTHROPIC_AUTH_TOKEN, None)它的作用是当设置了自定义 Base URL 时清掉可能存在的ANTHROPIC_AUTH_TOKEN避免和ANTHROPIC_API_KEY冲突。如果你本地环境变量里残留了旧的 token不清掉会出现鉴权混乱。.env内容大致长这样ANTHROPIC_API_KEYsk-你的key ANTHROPIC_BASE_URLhttps://taotoken.net/api MODEL_ID你的模型ID保存后不要急着跑先确认 Python 环境里装了anthropic和python-dotenv。项目根目录执行pip install -r requirements.txt即可。这一步做完前置就齐了接下来进入代码层。3. 可复制配置Agent Loop 伪代码与 Tools 注册片段这一节是全文的技术核心。我把 s01 和 s02 的关键结构抽出来给你一份可以直接抄进自己项目的配置。先看 Agent Loop 的伪代码骨架它几乎不会随工具数量变化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: handler TOOL_HANDLERS.get(block.name) output handler(**block.input) if handler else fUnknown tool: {block.name} results.append({ type: tool_result, tool_use_id: block.id, content: output, }) messages.append({role: user, content: results})这段代码里有四个关键点逐个说。第一while True的退出条件不是循环次数而是response.stop_reason ! tool_use这是模型自己给的信号。第二每轮都把整个messages历史发过去而不是只发最新一条这样模型才能看到之前所有工具结果。第三block.id是模型生成的工具调用唯一标识必须原样回填到tool_result的tool_use_id否则模型对不上号。第四工具结果以user角色追加这是 Anthropic 消息协议的规定不是 bug。再看 Tools 注册。s02 里工具从 1 个变成 4 个但循环没改靠的就是 dispatch map。配置片段如下TOOL_HANDLERS { bash: lambda **kw: run_bash(kw[command]), read_file: lambda **kw: run_read(kw[path], kw.get(limit)), write_file: lambda **kw: run_write(kw[path], kw[content]), edit_file: lambda **kw: run_edit(kw[path], kw[old_text], kw[new_text]), }对应的 schema 定义给模型看让模型知道能调什么、参数是什么TOOLS [ { name: read_file, description: Read file contents., input_schema: { type: object, properties: { path: {type: string}, limit: {type: integer}, }, required: [path], }, }, { name: write_file, description: Write content to file., input_schema: { type: object, properties: { path: {type: string}, content: {type: string}, }, required: [path, content], }, }, ]这里体现了一个重要分层TOOLS面向模型是 schemaTOOL_HANDLERS面向运行时是执行函数。两者一一对应职责分离。加一个新工具只需要往这两处各加一项循环一行都不用动。这就是教程那句“Adding a tool means adding one handler”的真正含义。如果你用的是 Claude Code 本体而不是这个教学项目配置方式类似核心还是 Base URL、Key、Model ID 三件套。Claude Code 的 settings 里对应字段名可能不同但语义一致。想省事的话Coding Plan 页面有现成的接入说明地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。4. 验证请求跑一次真实工具调用看链路是否闭合配置写完得验证。验证的目标不是“能出字”而是“工具调用链路完整闭合”。我建议用一个最小任务让 Agent 读一个文件然后基于内容回答。这样能同时验证 read_file 工具、tool_result 回填、以及模型基于结果继续推理这三步。先准备一个测试文件比如demo.txt内容随便写一句hello from tool chain。然后启动 s02 脚本输入Read the file demo.txt and tell me what it says.预期链路是这样的第一轮 API 调用模型返回stop_reasontool_usecontent里有一个tool_useblocknameread_fileinput{path: demo.txt}。程序通过TOOL_HANDLERS找到run_read执行后拿到文件内容。程序把结果包装成tool_result带上tool_use_id以user角色追加进messages。第二轮 API 调用模型看到工具结果返回stop_reasonend_turn输出文件内容。循环退出。如果你在终端看到类似这样的输出说明链路通了 read_file: hello from tool chain The file demo.txt contains: hello from tool chain这里有个细节值得注意run_read里做了safe_path校验和输出截断。safe_path会把路径解析成绝对路径检查是否还在工作目录内防止../../逃逸。输出截断则限制在 50000 字符避免超长内容顶爆上下文。这两个工程意识在 s01 的run_bash里也有对应版本——黑名单拦截危险命令、timeout120防卡死、输出裁剪。工具执行层最重要的几个要点其实在最小实现里就已经埋好了。验证成功后你可以再试一个多轮任务比如“创建一个 greet.py然后读回来确认”。这会触发 write_file 和 read_file 两次工具调用能更完整地观察messages列表如何一轮轮变长。每轮追加的 assistant 输出和 tool_result 都会沉淀在历史里模型下一轮能看到全部上下文。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth跑不通的时候报错信息往往很含糊。我把几个高频错误和对应原因列出来对照排查。第一个是401 Unauthorized。最常见的原因是 Key 没生效或 Base URL 不匹配。检查.env里ANTHROPIC_API_KEY是否填了完整字符串有没有多余空格。再检查ANTHROPIC_BASE_URL是否写成了https://taotoken.net/api不要多写/v1。如果本地 shell 里还残留ANTHROPIC_AUTH_TOKEN按前面说的代码会 pop 掉它但如果你手动 export 过最好 unset 一下。第二个是local proxy failed或连接被拒。这类错误通常和网络环境有关不是代码问题。确认你的 Base URL 拼写正确没有把https写成http。如果你在公司网络里检查是否有出站限制。这里不展开网络配置只提醒一点Base URL 必须是完整的https://taotoken.net/api。第三个是reading choices或类似字段读取失败。这个报错说明客户端在按 OpenAI 的响应格式解析但实际拿到的是 Anthropic 格式或者反过来。Learn-Claude-Code 用的是Anthropic客户端走的是/v1/messages响应里是content数组和stop_reason没有choices。如果你混用了 OpenAI SDK就会报这个。确认你 import 的是from anthropic import Anthropic。第四个是 OAuth 相关报错。如果你之前登录过 Claude Code 本体本地可能缓存了 OAuth 凭证和 API Key 模式冲突。解决办法是清掉相关缓存或者显式设置ANTHROPIC_API_KEY并确保ANTHROPIC_BASE_URL指向你的入口。Claude Code 的接入文档里有更细的说明地址是 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。还有一个隐蔽的坑stop_reason判断写错。有人写成if response.stop_reason end_turn: return逻辑上等价但如果模型返回其他值比如max_tokens循环会继续可能死循环。建议严格用! tool_use作为退出条件和教程保持一致。排查顺序建议先看 HTTP 状态码再看响应体结构最后看messages列表里 tool_result 有没有正确回填。大部分问题出在第一步和第三步。6. 语义一致 CTA把这条链路接到你自己的项目里拆完这条链路你会发现 Agent 的骨架其实很朴素一个循环、一张工具表、一套消息协议。真正决定能力边界的是挂在这个循环上的工具系统和运行环境。Learn-Claude-Code 后面 10 个章节——任务规划、上下文压缩、子智能体、技能加载——本质上都是往这条最小循环上叠加能力而不是重写框架。如果你想继续往下走几个入口按需取用。想验证模型对话效果去 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。想长期跑编码任务或搭 Agent看 Coding Plan地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。接入过程中卡在鉴权或协议先翻文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 再去控制台确认 Key 状态 https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite 。最后留一个实操建议把agent_loop里的工具执行那段单独抽成一个函数打印每次block.name、block.input、block.id和返回的output前 200 字符。跑几个任务下来你会对“模型决策 → 程序执行 → 结果回填”这条链路形成肌肉记忆。这比看十遍流程图都管用。