news 2026/10/3 19:43:29

第四篇:AI对话引擎全解析,Claude Code如何编排工具调用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
第四篇:AI对话引擎全解析,Claude Code如何编排工具调用

1. 从一次“工具没被调用”说起:Claude Code 工具调用编排链路到底长什么样

Claude Code 是 Anthropic 推出的终端 AI 编码代理,它和普通聊天机器人的最大区别在于:它能自己决定“要不要调用工具、调用哪个工具、按什么顺序调用”。这套决策与执行机制,就是 AI 对话引擎里的工具调用编排(tool orchestration)。适合谁看?适合已经在本地跑 Claude Code、想搞清楚它内部请求怎么组装、流式响应怎么解析、工具执行顺序怎么排的开发者。

我最初以为 Claude Code 只是把用户输入丢给模型,模型返回文本就完事。直到有一次我让它“读取 package.json 并把 scripts 里的 build 命令改成 tsc -b”,它没有直接改文件,而是先返回了一个tool_use块,里面写着Read工具和路径参数。那一刻我才意识到:模型输出的不是最终答案,而是一份“行动计划”,真正的执行发生在本地进程里。

这条链路可以拆成四段:请求组装(把系统提示、历史消息、工具定义打包成 API 请求)、流式响应解析(逐块读取 SSE,区分文本增量和工具调用增量)、工具执行编排(判断依赖关系、并行或串行执行、权限校验)、结果回填(把工具输出作为新的 user/tool 消息塞回历史,再次请求模型)。四段循环往复,直到模型返回纯文本、不再请求工具为止。

理解这条链路的价值在于:当工具“没被调用”或“调用错了”,你能快速定位是请求里工具定义没带上、还是流式解析把 tool_use 块丢了、还是执行阶段被权限拦了。下面我按可复现的顺序,把每一段拆开讲,并给出能直接抄的配置和验证步骤。

2. TaoToken 前置:把 Base URL、Key、Model ID 三件套配好

在复现编排链路之前,得先让 Claude Code 能稳定发出请求。Claude Code 默认走 Anthropic 官方端点,但很多人在本地调试时希望统一走一个兼容 Anthropic Messages API 的入口,方便观察请求和计费。TaoToken 提供的就是这样一个入口,官网在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 端点是 https://taotoken.net/api 。

配置的核心是三件套:Base URL、API Key、Model ID。Claude Code 读取的是环境变量和 settings 文件,最稳妥的方式是写进~/.claude/settings.json,而不是只 export 到当前 shell——因为 Claude Code 可能由别的进程拉起,环境变量不一定继承。

先拿 Key:打开 https://taotoken.net/api-keys ,新建一个 key,复制出来。注意 key 只在创建时完整显示一次,关掉页面就看不到了,建议先粘到本地临时文件。

然后写 settings。Claude Code 的 settings 支持env字段注入环境变量,这样 Base URL 和 Key 都能被内部请求层读到:

{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_AUTH_TOKEN": "sk-你的key", "ANTHROPIC_MODEL": "claude-sonnet-4-20250514", "ANTHROPIC_SMALL_FAST_MODEL": "claude-haiku-3-5-20241022" } }

这里有个容易踩的坑:ANTHROPIC_AUTH_TOKEN和ANTHROPIC_API_KEY是两个不同的变量。Claude Code 在走自定义 Base URL 时读的是ANTHROPIC_AUTH_TOKEN,如果你只设了ANTHROPIC_API_KEY,请求会带着空 Authorization 头出去,直接 401。我试过只改 Key 变量名,问题就消失了。

Model ID 要写全,别只写claude-sonnet-4。Anthropic 的模型 ID 带日期后缀,写错了服务端会返回 model not found。ANTHROPIC_SMALL_FAST_MODEL是给后台小任务(比如生成标题、压缩上下文)用的,配成 haiku 能省不少 token。

配完可以用一条命令验证环境是否被读到:

claude --version cat ~/.claude/settings.json | python3 -m json.tool

如果 settings 是合法 JSON,第二条命令会格式化输出;如果报 JSON 解析错误,说明你多写了逗号或少了引号,Claude Code 启动时会静默忽略整个文件,表现就是“配置了但没生效”。

3. 可复制配置:请求组装与流式解析的关键片段

这一节是全文技术密度最高的部分。Claude Code 内部用 TypeScript 写请求组装和流式解析,我们可以用一段最小可运行的 Node 脚本来复现它的行为,这样你能亲眼看到 tool_use 块是怎么从 SSE 流里冒出来的。

先看请求体结构。Anthropic Messages API 的请求长这样:

// request.ts const body = { model: "claude-sonnet-4-20250514", max_tokens: 4096, system: "You are a coding agent. Use tools when needed.", tools: [ { name: "Read", description: "Read a file from disk", input_schema: { type: "object", properties: { path: { type: "string" } }, required: ["path"] } } ], messages: [ { role: "user", content: "读取 package.json 的 name 字段" } ], stream: true };

注意tools数组里每个工具都有name、description、input_schema。模型就是靠这三样决定调不调、怎么调。description写得越清楚,模型选错工具的概率越低。我见过有人把 description 写成“读取文件”,结果模型在需要读目录时也调它,因为描述没区分文件和目录。

然后是流式解析。Anthropic 的 SSE 事件类型有message_start、content_block_start、content_block_delta、content_block_stop、message_delta、message_stop。工具调用藏在content_block_start(type 为tool_use)和后续的input_json_delta里:

// stream.ts async function parseStream(resp: Response) { const reader = resp.body!.getReader(); const decoder = new TextDecoder(); let buffer = ""; const toolCalls: any[] = []; let currentTool: any = null; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split("\n"); buffer = lines.pop() || ""; for (const line of lines) { if (!line.startsWith("data: ")) continue; const data = line.slice(6); if (data === "[DONE]") continue; const evt = JSON.parse(data); if (evt.type === "content_block_start" && evt.content_block.type === "tool_use") { currentTool = { id: evt.content_block.id, name: evt.content_block.name, input: "" }; } if (evt.type === "content_block_delta" && evt.delta.type === "input_json_delta") { currentTool.input += evt.delta.partial_json; } if (evt.type === "content_block_stop" && currentTool) { currentTool.input = JSON.parse(currentTool.input); toolCalls.push(currentTool); currentTool = null; } } } return toolCalls; }

这段代码的关键点:input_json_delta是分片到达的,必须拼完再JSON.parse,否则会拿到半截 JSON 报错。很多人第一次写流式解析,直接在 delta 里 parse,结果就是Unexpected end of JSON input。

执行顺序编排则取决于工具之间有没有依赖。如果模型一次返回两个 tool_use:一个读文件、一个跑 bash,而 bash 不依赖读的结果,就可以Promise.all并行;如果第二个工具的参数来自第一个工具的输出,就必须串行。Claude Code 的做法是先按依赖分组,无依赖的组内并行,组间串行。

4. 验证请求:跑一次完整工具调用链路并观察流式输出

配置和代码都齐了,现在跑一次端到端验证。目标:让模型读取一个本地文件,观察流式输出里文本增量和工具调用增量的到达顺序。

准备一个测试文件:

mkdir -p /tmp/cc-demo && cd /tmp/cc-demo echo '{"name":"demo","version":"1.0.0"}' > package.json

然后写一个最小脚本run.ts,把第 3 节的 request 和 stream 拼起来:

// run.ts const resp = await fetch("https://taotoken.net/api/v1/messages", { method: "POST", headers: { "content-type": "application/json", "x-api-key": process.env.ANTHROPIC_AUTH_TOKEN!, "anthropic-version": "2023-06-01" }, body: JSON.stringify(body) }); console.log("status:", resp.status); const calls = await parseStream(resp); console.log("tool calls:", JSON.stringify(calls, null, 2));

用npx tsx run.ts跑。预期看到两类输出:先是status: 200,然后tool calls数组里有一个Read调用,input是{ path: "package.json" }。如果 status 是 401,回到第 2 节检查ANTHROPIC_AUTH_TOKEN;如果是 404,检查 Base URL 有没有多写或少写/v1。

更贴近 Claude Code 真实行为的验证,是直接在终端里跑:

claude -p "读取 package.json 并告诉我 name 字段的值"

加--debug能看到它发出的原始请求和收到的 SSE 事件:

claude --debug -p "读取 package.json 并告诉我 name 字段的值" 2>&1 | grep -E "tool_use|content_block"

你会看到content_block_start里 type 是tool_use、name 是Read,紧接着一串input_json_delta,最后content_block_stop。这就是编排链路在真实进程里的样子。工具执行完后,Claude Code 会把结果作为新的消息追加到历史,再发一次请求,这次模型返回纯文本,链路结束。

想更直观地看模型对话过程,可以打开 https://taotoken.net/chat ,把同样的 prompt 丢进去,对比终端输出和网页输出的差异——网页端通常把工具调用折叠成一行“正在读取文件”,终端则把原始事件打出来。

5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth

排障这节我按真实报错来对,每个都给出定位路径。

401 Unauthorized:最常见。九成是变量名写错。Claude Code 走自定义 Base URL 时读ANTHROPIC_AUTH_TOKEN,不是ANTHROPIC_API_KEY。另一个可能是 key 前后带了空格或换行,从网页复制时容易带上。用echo -n $ANTHROPIC_AUTH_TOKEN | wc -c看长度对不对。

local proxy failed / connection refused:说明 Claude Code 试图连一个本地代理端口,但那个端口没进程在听。检查 settings 里有没有残留的HTTP_PROXY、HTTPS_PROXY指向127.0.0.1:xxxx。有的话删掉,或者确认代理进程真的起来了。这类报错和 Base URL 无关,别去改 API 地址。

reading 'choices' of undefined:这是 OpenAI 格式和 Anthropic 格式混用导致的。choices是 OpenAI Chat Completions 的响应字段,Anthropic Messages API 返回的是content数组。如果你用了一个按 OpenAI 格式解析响应的客户端去请求 Anthropic 端点,就会在resp.choices[0]处炸掉。解决方法是确认客户端走的是 Messages API 格式,或者换用支持 Anthropic 协议的 SDK。

OAuth 相关报错:Claude Code 某些版本会尝试 OAuth 登录流程。如果你用的是 API Key 模式,确保没有同时存在 OAuth token 文件(通常在~/.claude/下)。两者冲突时,请求可能带着过期的 OAuth token 出去。删掉 OAuth 缓存文件,只保留 settings 里的 Key 配置。

工具调用返回空 input:流式解析时input_json_delta没拼完就 parse,或者content_block_stop事件被漏掉。检查你的 buffer 分割逻辑,SSE 事件之间用\n\n分隔,单行data:后面可能跟多行。

模型不调用工具:先看请求里tools数组是不是空的。再看 system prompt 有没有明确指示“需要时使用工具”。最后看tool_choice参数,默认是auto,如果被设成none,模型永远不会调工具。

6. 语义一致 CTA:把这条链路用起来

编排链路跑通之后,下一步是把它接到真实工作流里。如果你主要做模型能力验证和对话调试,可以直接用模型对话入口 https://taotoken.net/chat ,把工具定义和 prompt 贴进去,观察模型在不同描述下的工具选择差异。

如果你要长期跑编码任务、让 Agent 自己读写文件跑命令,建议开 Coding Plan https://taotoken.net/coding-plan ,它按周期计费,比按 token 单次调用更适合高频工具编排场景。接入文档在 https://taotoken.net/doc ,里面有 Messages API 的完整字段说明和流式事件类型表,排障时对着查很快。

配置三件套(Base URL、Key、Model ID)如果还没落地,回到 https://taotoken.net/api-keys 拿 key,再按第 2 节的 settings.json 写进去。Claude Code 的接入细节可以参考 https://taotoken.net/claude-code ,里面有 settings 字段的完整列表。

最后留一个实用技巧:调试工具编排时,把max_tokens调小到 1024,这样模型返回的 tool_use 块更短,SSE 事件更少,肉眼更容易跟。等链路确认没问题,再调回正常值。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 19:41:21

从零开始编写MCP Server:全网最详细指南(TaoToken 统一 Key 接入版)

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

作者头像 李华
网站建设 2026/10/3 19:38:33

定制连接器设计实战:从需求评估到批量制造的关键点

在连接器选型这件事上摸爬滚打了十几年,我越来越觉得"定制"这两个字被严重误解了。大部分工程师提到定制连接器,第一反应是"贵"和"周期长",于是不到万不得已绝不碰。但实际情况是,很多项目做到结构…

作者头像 李华
网站建设 2026/10/3 19:36:05

AI写嵌入式驱动:如何避免刷砖并高效辅助开发

1. 为什么“AI写驱动”这件事在嵌入式圈子里争议这么大先把结论摆在最前面:AI 可以帮你写驱动,但绝对不能替你决定驱动该怎么写。这两句话听起来像绕口令,但差别大了去了。前者是“你主导、AI 辅助”,后者是“AI 主导、你背锅”&a…

作者头像 李华
网站建设 2026/10/3 19:35:43

第 12 期:线程、进程和协程,到底应该选哪一个

并发工具解决不了“程序慢”这四个字。它只能处理某一种具体的慢,而且每多推进一份工作,也会多带来一份调度、状态和资源成本。写在前面 有一次,订单对账任务从每天处理两万笔涨到了二十万笔。 原来的程序很老实:查一批订单&#…

作者头像 李华
网站建设 2026/10/3 19:32:48

视频动态目标三维重构在危化品事故平战切换指挥中的应用技术解析

技术权属说明:危化品事故平战一体化态势感知、常态/应急无缝切换指挥机制、动态目标三维态势联动调度、极端工况指挥闭环技术体系由华东师范大学浙江普陀时空大数据研究院耿文海团队原创研发,镜像视界(浙江)科技有限公司为唯一产业…

作者头像 李华