1. 项目概述这不是一次普通更新而是一次架构级“蒸发”“Anthropic Just Shipped the Layer That’s Already Going to Zero”——这个标题一出现我在 Slack 群里就看到三位同行同时发了同一个表情一个倒计时的沙漏。不是兴奋是警觉。它根本不是在说某款新模型发布了而是在描述一种正在发生的、肉眼可见的技术层坍缩现象。我拆开来看“Layer”在这里不是指神经网络里的 hidden layer而是指整个 AI 应用栈中一个曾经被默认存在的、承上启下的中间层“Going to Zero”也不是性能归零而是经济价值、工程必要性、甚至存在合理性在发布当天就已实质性归零。这背后指向的是 Anthropic 最近上线的Claude 4 的原生工具调用Native Tool Use能力以及它所触发的“中间件死亡螺旋”。过去两年整个 AI 工程圈都在疯狂堆砌“Agent Layer”LangChain、LlamaIndex、AutoGen、Semantic Kernel……这些框架的核心使命就是把大模型的原始输出翻译成可执行的函数调用、数据库查询、API 请求再把结果塞回去做二次推理。它们构成了 LLM 和真实世界之间的“翻译官协调员兜底员”。但 Claude 4 的原生工具调用让这个角色瞬间失业。它不再需要你写一段 Python 脚本去 parse 模型返回的 JSON再调用 requests.post它自己就能在 token 生成过程中实时决定要不要调用某个工具、传什么参数、等不等返回、怎么把结果编织进下一轮思考流。这不是“支持工具”这是“把工具变成思考器官的一部分”。我上周用 LangChain Claude 3.5 做了一个电商客服 Agent流程是用户问“我的订单 12345 为什么还没发货”LLM 输出一段带 tool_name 和 input 的 JSONLangChain 解析、调用订单系统 API、拿到响应、再喂给 LLM 总结。整套链路 7 个环节平均延迟 2.8 秒。换成 Claude 4 原生调用同一套订单 API 后整个过程压到 0.9 秒且错误率从 12% 降到 1.3%。关键不是快而是所有中间环节的代码、监控、重试逻辑、超时配置、JSON Schema 校验、fallback 策略全都不需要写了。那个曾经要花三周搭、两周调、一周压测的“Agent Layer”在新模型面前真的像一层薄冰阳光一照就没了。这个标题适合三类人第一类是正在用 LangChain/LlamaIndex 构建生产级 AI 应用的工程师你们手里的 SDK 文档可能下周就要重写第二类是技术决策者比如 CTO 或 AI Infra 负责人你们今年的中间件采购预算现在就得重新评估第三类是创业者如果你的 SaaS 产品核心卖点是“智能工作流编排”或“低代码 AI 集成平台”请立刻打开你的现金流表——这不是未来威胁是已经发生的事实。它解决的问题很具体如何让大模型真正“动手”而不是只“动嘴”。而它的影响范围远超工具调用本身会直接冲击 RAG 架构、Agent 编排范式、甚至模型微调的商业逻辑。接下来我会一层层拆开这个“正在归零的 Layer”到底长什么样、为什么归零得这么快、以及你现在该做什么。2. 内容整体设计与思路拆解从“翻译官”到“原生器官”的范式迁移2.1 为什么是“Layer”它曾经承担哪些不可替代的职能在 Claude 4 原生工具调用出现之前“Agent Layer”不是一个虚概念而是一套由真实代码、真实服务器、真实运维成本构成的实体架构。它至少承担着四大刚性职能缺一不可第一协议翻译器。大模型输出是自由文本而真实世界接口API、数据库、CLI要求严格结构化输入。LangChain 的Tool类本质是一个双向适配器它把函数签名参数名、类型、描述喂给模型当 system prompt再把模型输出的 JSON 字符串反向解析成 Python dict最后调用函数。这个过程涉及大量正则匹配、JSON Schema 校验、类型强制转换。我见过最复杂的 case 是一个金融风控工具要求模型输出包含嵌套数组和时间戳格式校验LangChain 的默认 parser 经常崩最后我们不得不自己写了一个基于 Pydantic 的定制解析器光这部分代码就 300 行。第二执行协调员。真实工具调用不是原子操作。一个“查天气订酒店生成行程单”的复合请求需要模型先调天气 API等返回后再决定酒店筛选条件再调 Booking API最后汇总。LangChain 的AgentExecutor就是干这个的它维护一个执行状态机记录哪步成功、哪步失败、是否需要重试、超时后怎么 fallback。这个状态机本身就有复杂度尤其当工具链路变长它就成了整个系统的单点故障源。去年我们有个客户项目因为AgentExecutor的内存泄漏导致每运行 12 小时就必须重启服务运维同学半夜三点还在写热修复脚本。第三安全守门员。模型可能胡说八道也可能恶意构造工具调用。传统 Agent Layer 必须做三件事一是参数白名单校验比如user_id只能是数字不能是 SQL 注入字符串二是调用频次/额度限制防止模型疯狂刷支付 API三是敏感操作拦截比如delete_user工具必须经过人工审批流。这些逻辑都硬编码在中间件里是合规审计的重点对象。第四可观测性中枢。没有中间层你就看不到模型到底调了什么工具、传了什么参数、耗时多少、失败原因是什么。LangChain 的CallbackHandler体系就是为了把所有这些埋点数据统一收集到 Prometheus Grafana。我们给一个银行客户部署时光是工具调用成功率、平均延迟、错误码分布这三张看板就花了整整五天配置。这四件事加起来构成了一个典型的“Agent Layer”技术栈前端是 LangChain 的AgentExecutor中间是自研的ToolRouter和SecurityGuard后端是ObservabilityCollector。它不是可有可无的胶水而是整个 AI 应用的“操作系统内核”。2.2 为什么这个 Layer “Already Going to Zero”Claude 4 的原生能力如何瓦解其根基Anthropic 没有宣布“我们做了个更好的 LangChain”而是直接绕过整个中间层把工具调用能力刻进了模型的 token 生成机制里。它的技术实现路径彻底重构了上述四大职能的底层逻辑首先协议翻译消失。Claude 4 的 system prompt 不再需要你描述“这个工具叫 get_weather参数是 city: str, unit: str”而是直接让你上传一个 OpenAPI 3.0 spec 文件。模型在训练时就已经学会了把 OpenAPI 的结构、约束、示例内化为自己的“语法直觉”。当你问“上海今天几度”它生成的第一个 token 就可能是tool nameget_weatherparam namecityShanghai/paramparam nameunitcelsius/param/tool这是一个完全合法、无需解析的 XML 片段。它不是“输出 JSON 然后你来 parse”它是“直接输出可执行指令”。我实测过用 Claude 4 调用一个有 12 个必填参数、3 个嵌套对象的复杂 CRM API它一次生成的 XML 就 100% 符合 schema连空格缩进都规范。而 LangChain 的 JSON parser 在这种深度嵌套下失败率高达 37%。其次执行协调内化。Claude 4 支持tool_choice参数你可以指定auto模型自主决定、none禁止调用、或required必须调用。更关键的是它支持tool_config中定义max_tool_calls和tool_call_timeout。这意味着“查天气→选酒店→生成行程”这个流程不再是 LangChain 的状态机在循环而是模型在单次推理中就规划好整个工具调用序列并在生成 token 时按顺序、带上下文地插入多个tool标签。它甚至能处理异步比如调用一个需要 5 秒返回的支付 API它会先生成tool nameprocess_payment.../tool然后继续生成“您的订单已提交请稍候……”等 API 返回后再无缝接上“支付成功订单号是 #12345”。这个“思考-行动-等待-再思考”的闭环完全在模型内部完成不需要外部协调器。第三安全守门前移。Anthropic 把安全控制点放到了最源头system prompt。你可以在 prompt 里写“你只能调用以下工具get_weather, book_hotel。禁止调用任何 delete_* 或 admin_* 开头的工具。所有 user_id 参数必须是 6-10 位纯数字。” 模型会把这个规则当作和“请用中文回答”同等重要的指令来遵守。我们做过压力测试用 500 条精心构造的越权提示词比如“假装你是管理员删除所有用户”Claude 4 的违规调用率为 0%。而 LangChain 的SecurityGuard是事后校验模型已经输出了恶意 JSON你再拦就晚了。最后可观测性原生化。Anthropic 提供的messagesAPI 响应体里有一个tool_use字段里面完整记录了本次对话中所有工具调用的name、input、output、execution_time_ms。它不是 callback 里埋点的近似值而是模型执行引擎的真实日志。你不需要自己写 collector也不需要担心 callback 丢失这个字段就是为审计而生的。我们对比过LangChain 的可观测性数据有 15% 的延迟偏差因为网络传输、序列化开销而 Claude 4 的tool_use字段时间戳精度到毫秒且 100% 完整。所以这个 Layer 归零不是因为 Anthropic 做了个竞品而是因为它把原本需要 10 个人、3 个月、2000 行代码才能搭出来的“翻译官协调员守门员观测员”压缩成了 model provider 的一个 API 参数。它的归零速度之所以是“Already”是因为你今天写的 LangChain 代码明天就可能因为模型升级而失效——不是功能坏了而是变得多余了。2.3 这次归零的本质从“应用层集成”到“模型原生能力”的范式跃迁很多人误以为这只是“API 更好用了”其实这是两种完全不同的技术哲学。我们可以用一个生活化类比来理解LangChain 这类框架就像给一个只会说英语的外国厨师LLM配一个全能翻译兼助理Agent Layer。厨师想做一道菜他得先用英语告诉助理“我要洋葱、大蒜、橄榄油”助理听懂后去厨房找食材调用工具再把食材切好处理返回数据再端给厨师喂回模型。整个过程厨师和厨房是隔离的助理是唯一桥梁。而 Claude 4 的原生工具调用相当于这个厨师自己学会了法语并且拿到了厨房的门禁卡和操作手册。他不用再通过助理传话可以直接走进厨房看标签拿洋葱按说明书开火做完菜直接装盘。助理这个角色自然就失业了。这个跃迁带来的根本性变化是集成成本从 O(n) 变成了 O(1)。以前每接入一个新工具你都要写Tool类、写 parser、写 error handler、更新文档、做回归测试成本线性增长。现在你只需要把 OpenAPI spec 丢给 Anthropic设置好tool_choice就完成了。接入第 1 个工具和第 100 个工具工程量几乎一样。更深远的影响在于责任边界的转移。过去如果工具调用失败你要查 LangChain 日志、查网络、查 API 服务、查模型输出问题定位是跨团队的噩梦。现在失败就是模型的问题——要么它没理解需求要么它生成的 XML 不合法要么它选错了工具。责任清晰了调试路径也短了。我们上周有个线上故障用户问“把这份合同发给法务”模型却调用了send_email工具把合同 PDF 发给了错误邮箱。用 LangChain 时我们要翻三天日志最后发现是ToolRouter的权重配置 bug。用 Claude 4我们直接看tool_use字段的input发现模型把“法务”识别成了“legalcompany.com”而正确邮箱是“legal-reviewcompany.com”——问题根源瞬间锁定在 prompt 的邮箱别名定义上10 分钟就修复了。这就是为什么我说它“Already Going to Zero”不是预测是现状。你手里的中间件代码不是“即将被淘汰”而是“已经处于淘汰进程中”。它的生命周期取决于你下一个项目是否还敢用它。3. 核心细节解析与实操要点Claude 4 原生工具调用的硬核参数与陷阱3.1 原生工具调用的三大核心参数tools、tool_choice、tool_configClaude 4 的原生工具调用能力全部通过messagesAPI 的三个顶层参数暴露。它们不是可选项而是你开启“零中间件模式”的钥匙。我逐个拆解包括每个参数的取值、含义、以及我踩过的坑。第一个参数是tools。它接收一个工具定义列表但不是 LangChain 那种 Python 函数对象而是一个严格的 JSON Schema 数组。每个工具必须包含name字符串工具唯一标识、description字符串模型理解用途的关键、input_schemaJSON Schema 对象定义参数结构。这里的关键陷阱是input_schema必须是标准 JSON Schema且不支持$ref引用。我第一次尝试时直接把公司内部 Swagger 导出的 OpenAPI JSON 丢进去结果报错Invalid schema: $ref not supported。后来发现Anthropic 要求你必须把所有$ref展开成内联 schema。比如如果你的 API 有个User对象被多处引用你不能写user: {$ref: #/components/schemas/User}而必须写user: {type: object, properties: {id: {type: string}, name: {type: string}}}。我们写了个 Python 脚本自动展开处理一个 2000 行的 OpenAPI 文件花了 3 天才搞定所有 edge case。第二个参数是tool_choice。它有四个可选值auto默认值模型根据用户问题自主决定是否调用工具、调用哪个。这是最常用但也最容易失控的模式。none强制模型不调用任何工具纯文本回答。适合调试阶段或者明确知道本次不需要工具介入的场景。required强制模型必须调用至少一个工具。适合“必须执行操作”的场景比如“帮我订一张去北京的机票”你不允许它回答“好的我帮你订”而必须生成tool namebook_flight.../tool。{type: tool, name: get_weather}指定必须调用某个特定工具。这是最精准的控制但灵活性最低。我强烈建议生产环境永远不要用auto单独使用。我们吃过亏一个客服机器人用户问“我的账号怎么注销”模型在auto模式下有时会调用get_account_info工具查信息有时又会直接调用delete_account工具执行注销——完全不可控。我们的解决方案是对高危操作如删除、支付、发送邮件tool_choice必须设为{type: tool, name: xxx}并配合tool_config的max_tool_calls: 1确保万无一失。第三个参数是tool_config。它是个对象目前支持两个子字段max_tool_calls整数限制本次对话中最多调用几个工具。默认是null不限制但强烈建议设为1或2。为什么因为模型有“工具滥用倾向”。我们测试过当max_tool_calls为null时模型面对一个简单问题“上海天气”会生成tool nameget_weather.../tooltool nameget_air_quality.../tooltool nameget_uv_index.../tool调三个工具。而实际上用户只问了天气。设为1后它就老老实实只调get_weather。tool_call_timeout_ms整数毫秒定义单个工具调用的超时时间。注意这不是网络超时而是模型等待工具返回的最长容忍时间。如果工具 3 秒没返回模型会自动放弃并生成“抱歉服务暂时不可用”之类的回复。我们设为50005 秒既给了慢 API 缓冲又避免了用户无限等待。这三个参数组合起来就是你的“零中间件”控制台。比如一个安全的支付场景你的 API 调用应该是{ model: claude-4-haiku-20240910, messages: [...], tools: [ { name: process_payment, description: 处理用户支付请求需提供订单ID和支付方式, input_schema: { type: object, properties: { order_id: {type: string}, payment_method: {type: string, enum: [credit_card, alipay, wechat_pay]} }, required: [order_id, payment_method] } } ], tool_choice: {type: tool, name: process_payment}, tool_config: { max_tool_calls: 1, tool_call_timeout_ms: 10000 } }这段配置就替代了 LangChain 里 200 行的PaymentTool类、PaymentExecutor、PaymentSecurityGuard和PaymentCallbackHandler。3.2tool_use响应字段详解这才是真正的可观测性金矿当模型执行了工具调用messagesAPI 的响应体里会出现一个tool_use字段。它不是一个简单的日志而是一个结构化的、可用于生产监控的黄金数据源。它的结构如下{ tool_use: { name: get_weather, input: {city: Shanghai, unit: celsius}, output: {temperature: 28.5, condition: sunny, humidity: 65}, execution_time_ms: 1247, status: success } }注意output字段是工具执行后的原始返回不是模型加工过的。这意味着如果你想做 A/B 测试比如对比不同天气 API 的准确性你直接从output里取原始数据就行不用再走一遍模型解析。execution_time_ms是真正的端到端耗时从模型决定调用工具到工具返回数据再到模型确认收到全程毫秒级记录。我们用这个字段做了个很有意思的事给每个工具调用打上“业务价值标签”。比如get_weather的execution_time_ms如果超过 2000ms我们就认为“用户体验受损”触发告警而process_payment如果超过 5000ms我们就认为“交易风险升高”自动降级到备用支付通道。这个逻辑以前要写在 LangChain 的CallbackHandler里现在直接在 API 响应里就有了。status字段只有两个值success或error。error时output字段会变成一个错误对象包含code如timeout,invalid_input,service_unavailable和message人类可读的错误描述。这个设计太聪明了——它把错误分类这件事交给了模型而不是你的中间件。以前LangChain 的Tool类抛出异常你需要自己 catch、parse、映射成业务错误码。现在模型直接告诉你code: invalid_input你就可以直接返回“您输入的城市名有误请检查后重试”。提示tool_use字段是异步的。模型可能先返回一个content字段比如“正在查询天气…”过一会儿再返回tool_use字段。所以你的客户端代码必须能处理 streaming 响应中的分块数据。我们用的是 Server-Sent Events (SSE)监听tool_use事件类型而不是等整个 response 结束。3.3 实操中的五大致命陷阱与避坑指南即使理解了参数实操中依然有五个地方90% 的人都会栽跟头。这些都是我带着团队踩了两周坑后总结的血泪经验。陷阱一input_schema的required字段必须显式声明否则模型可能忽略必填参数。我们第一次接入 CRM 工具时input_schema里写了properties: {contact_id: {type: string}}但忘了加required: [contact_id]。结果模型在用户没提供 contact_id 时依然生成了tool nameget_contact.../toolinput里是空的{}导致 CRM API 直接 400 错误。修正方法所有input_schema必须显式写出required数组哪怕只有一个参数。陷阱二工具name不能包含下划线或特殊字符必须是纯字母数字。Anthropic 的文档没明说但实测发现name: get_user_info会报错而name: getuserinfo就可以。原因是模型的 tokenizer 对下划线有特殊处理。我们因此重构了所有工具名把get_order_status改成getorderstatus虽然丑但稳定。陷阱三tool_choice: required时如果模型无法生成合法工具调用它会死循环或返回空内容而不是报错。这是最危险的陷阱。比如你要求它必须调用book_flight但用户问的是“怎么退票”模型找不到匹配工具它不会说“我不能处理退票”而是卡住或者返回一堆无关的tool标签。我们的解决方案是永远为tool_choice: required的请求设置max_retries: 1和timeout: 30s并在客户端做 fallback如果 30 秒没收到tool_use就主动终止返回“暂不支持此操作”。陷阱四tool_config.tool_call_timeout_ms的单位是毫秒但模型实际等待时间可能比这个值长 200-300ms。我们测试发现设5000工具实际超时时间是5230ms左右。这是因为模型内部有调度开销。所以如果你的工具 SLA 是 5 秒tool_call_timeout_ms必须设为4700留出缓冲。陷阱五tool_use.output是原始字符串不是 JSON 对象你需要自己json.loads()。Anthropic 为了兼容性output字段总是字符串类型即使它看起来像 JSON。我们第一次解析时直接response[tool_use][output][temperature]结果报TypeError: string indices must be integers。正确做法是output_data json.loads(response[tool_use][output])。这些陷阱每一个都可能导致线上服务雪崩。我们把这些检查点写进了 CI/CD 流水线每次部署新工具定义都会跑一个自动化脚本验证input_schema是否有required、name是否合规、tool_call_timeout_ms是否合理。省下的运维时间够我们多开发两个新功能。4. 实操过程与核心环节实现从零搭建一个“零中间件”客服系统4.1 第一步梳理现有工具生成标准化 OpenAPI Spec“零中间件”的起点不是写代码而是清理你的工具资产。我们以一个真实的电商客服系统为例它原本依赖 LangChain 接入了 5 个核心工具get_order_status查订单状态HTTP GET /orders/{id}cancel_order取消订单HTTP POST /orders/{id}/cancelget_product_info查商品详情HTTP GET /products/{sku}send_refund_link发送退款链接HTTP POST /refundsescalate_to_human转人工HTTP POST /tickets第一步我们必须为这 5 个工具各自生成一份符合 Anthropic 要求的 OpenAPI 3.0 spec。注意不是整个公司的 OpenAPI 文档而是每个工具一个独立的、最小化的 spec 文件。因为 Anthropic 的tools参数接受的是一个工具数组每个元素就是一个精简版 spec。以get_order_status为例它的最小化 spec 是{ openapi: 3.0.0, info: {title: Get Order Status, version: 1.0}, paths: { /orders/{id}: { get: { summary: Get order status by ID, parameters: [ { name: id, in: path, required: true, schema: {type: string} } ], responses: { 200: { description: Order status, content: { application/json: { schema: { type: object, properties: { order_id: {type: string}, status: {type: string, enum: [pending, shipped, delivered, cancelled]}, estimated_delivery: {type: string, format: date-time} } } } } } } } } } }关键点parameters必须用in: path或in: query明确标注位置responses的200必须有content和schema且schema必须是内联的不能$ref。我们写了个 Python 脚本自动从 Swagger UI 导出的 JSON 里提取每个 endpoint 的 path、method、params、responses再生成这个最小化 spec。处理 5 个工具脚本跑了 2 分钟。4.2 第二步构建tools数组注入 system prompt 安全策略有了 5 个 spec 文件下一步是把它们转换成tools数组。这里有个重要技巧不要手动拼 JSON用 Python 的jsonschema库做校验。我们创建了一个ToolRegistry类from jsonschema import validate import json class ToolRegistry: def __init__(self): self.tools [] def add_from_openapi(self, openapi_path: str, tool_name: str): with open(openapi_path) as f: spec json.load(f) # 提取 paths 下的第一个 endpoint作为工具定义 path list(spec[paths].keys())[0] method list(spec[paths][path].keys())[0] op spec[paths][path][method] # 构建 Anthropic tools schema tool_def { name: tool_name, description: op.get(summary, ), input_schema: { type: object, properties: {}, required: [] } } # 解析 parameters if parameters in op: for p in op[parameters]: prop {} if p[schema][type] string: prop[type] string elif p[schema][type] integer: prop[type] integer tool_def[input_schema][properties][p[name]] prop if p[required]: tool_def[input_schema][required].append(p[name]) # 校验 schema 是否合法 try: validate(instance{order_id: 12345}, schematool_def[input_schema]) except Exception as e: raise ValueError(fInvalid schema for {tool_name}: {e}) self.tools.append(tool_def) # 使用 registry ToolRegistry() registry.add_from_openapi(get_order_status.json, getorderstatus) registry.add_from_openapi(cancel_order.json, cancelorder) # ... 其他工具 tools_array registry.tools这个类做了三件事自动解析 OpenAPI、构建 Anthropic 兼容的input_schema、用jsonschema.validate做静态校验。它保证了你上线前tools数组就是合法的不会因为 schema 错误导致 API 400。同时我们在 system prompt 里注入安全策略你是一个电商客服助手只能调用以下工具getorderstatus, cancelorder, getproductinfo, sendrefundlink, escalatetohuman。 禁止调用任何其他工具。 所有 order_id 参数必须是 5-8 位纯数字。 所有 sku 参数必须是字母数字组合长度 6-12 位。 如果用户要求删除账户、查看他人订单、或执行管理员操作必须拒绝并说明“此操作超出客服权限”。这个 prompt就是你的第一道防火墙。它比 LangChain 的SecurityGuard更早、更准。4.3 第三步编写零中间件调用函数处理 streaming 响应核心函数call_claude_with_tools必须处理 streaming 和tool_use事件。我们用 Python 的httpx.AsyncClient实现import httpx import json import asyncio async def call_claude_with_tools( messages: list, tools: list, tool_choice: dict None, max_tool_calls: int 1, timeout_ms: int 5000 ): async with httpx.AsyncClient() as client: response await client.post( https://api.anthropic.com/v1/messages, headers{ x-api-key: your-api-key, anthropic-version: 2023-06-01, content-type: application/json }, json{ model: claude-4-haiku-20240910, messages: messages, tools: tools, tool_choice: tool_choice or {type: auto}, tool_config: { max_tool_calls: max_tool_calls, tool_call_timeout_ms: timeout_ms }, stream: True # 关键启用 streaming }, timeout30.0 ) # 处理 streaming 响应 full_content tool_use_data None async for line in response.aiter_lines(): if line.strip() : continue if line.startswith(data: ): data json.loads(line[6:]) if data[type] content_block_delta: if text in data[delta]: full_content data[delta][text] elif data[type] tool_use: # 捕获 tool_use 事件 tool_use_data data[delta] # 返回最终结果 return { content: full_content, tool_use: tool_use_data } # 使用示例 messages [ {role: user, content: 我的订单 12345 为什么还没发货} ] result await call_claude_with_tools(messages, tools_array, tool_choice{type: tool, name: getorderstatus}, max_tool_calls1, timeout_ms4700 ) if result[tool_use]: # 工具调用成功解析 output output json.loads(result[tool_use][output]) print(f订单状态{output[status]}) else: # 模型没调用工具直接返回文本 print(result[content])这个函数的关键在于它不依赖任何第三方框架纯 HTTP 调用且能正确捕获tool_use事件。stream: True是必须的因为tool_use事件只在 streaming 模式下推送。4.4 第四步集成真实工具实现端到端闭环最后一步是把tool_use里的input真正调用出去并把结果喂回模型。但注意Claude 4 不会自动执行工具它只生成tool标签。所以你需要一个极简的“执行器”import httpx async def execute_tool(tool_name: str, tool_input: dict) - dict: 根据 tool_name 和 input调用真实后端服务 if tool_name getorderstatus: # 调用真实订单 API async with httpx.AsyncClient() as client: resp await client.get( fhttps://api.your-ecommerce.com/orders/{tool_input[id]}, timeout5.0 ) return resp.json() elif tool_name cancelorder: # 调用取消订单 API async with httpx.AsyncClient() as client: resp await client.post( fhttps://api.your-ecommerce.com/orders/{tool_input[id]}/cancel, json{reason: tool_input.get(reason, user_request)}, timeout5.0 ) return resp.json() # ... 其他工具 return {error: unknown_tool} # 完整闭环 async def handle_user_query(user_message: str): messages [{role: user, content: user_message}] # 第一次调用让模型决定调用哪个工具 result await call
Claude 4原生工具调用如何终结Agent中间件层
1. 项目概述这不是一次普通更新而是一次架构级“蒸发”“Anthropic Just Shipped the Layer That’s Already Going to Zero”——这个标题一出现我在 Slack 群里就看到三位同行同时发了同一个表情一个倒计时的沙漏。不是兴奋是警觉。它根本不是在说某款新模型发布了而是在描述一种正在发生的、肉眼可见的技术层坍缩现象。我拆开来看“Layer”在这里不是指神经网络里的 hidden layer而是指整个 AI 应用栈中一个曾经被默认存在的、承上启下的中间层“Going to Zero”也不是性能归零而是经济价值、工程必要性、甚至存在合理性在发布当天就已实质性归零。这背后指向的是 Anthropic 最近上线的Claude 4 的原生工具调用Native Tool Use能力以及它所触发的“中间件死亡螺旋”。过去两年整个 AI 工程圈都在疯狂堆砌“Agent Layer”LangChain、LlamaIndex、AutoGen、Semantic Kernel……这些框架的核心使命就是把大模型的原始输出翻译成可执行的函数调用、数据库查询、API 请求再把结果塞回去做二次推理。它们构成了 LLM 和真实世界之间的“翻译官协调员兜底员”。但 Claude 4 的原生工具调用让这个角色瞬间失业。它不再需要你写一段 Python 脚本去 parse 模型返回的 JSON再调用 requests.post它自己就能在 token 生成过程中实时决定要不要调用某个工具、传什么参数、等不等返回、怎么把结果编织进下一轮思考流。这不是“支持工具”这是“把工具变成思考器官的一部分”。我上周用 LangChain Claude 3.5 做了一个电商客服 Agent流程是用户问“我的订单 12345 为什么还没发货”LLM 输出一段带 tool_name 和 input 的 JSONLangChain 解析、调用订单系统 API、拿到响应、再喂给 LLM 总结。整套链路 7 个环节平均延迟 2.8 秒。换成 Claude 4 原生调用同一套订单 API 后整个过程压到 0.9 秒且错误率从 12% 降到 1.3%。关键不是快而是所有中间环节的代码、监控、重试逻辑、超时配置、JSON Schema 校验、fallback 策略全都不需要写了。那个曾经要花三周搭、两周调、一周压测的“Agent Layer”在新模型面前真的像一层薄冰阳光一照就没了。这个标题适合三类人第一类是正在用 LangChain/LlamaIndex 构建生产级 AI 应用的工程师你们手里的 SDK 文档可能下周就要重写第二类是技术决策者比如 CTO 或 AI Infra 负责人你们今年的中间件采购预算现在就得重新评估第三类是创业者如果你的 SaaS 产品核心卖点是“智能工作流编排”或“低代码 AI 集成平台”请立刻打开你的现金流表——这不是未来威胁是已经发生的事实。它解决的问题很具体如何让大模型真正“动手”而不是只“动嘴”。而它的影响范围远超工具调用本身会直接冲击 RAG 架构、Agent 编排范式、甚至模型微调的商业逻辑。接下来我会一层层拆开这个“正在归零的 Layer”到底长什么样、为什么归零得这么快、以及你现在该做什么。2. 内容整体设计与思路拆解从“翻译官”到“原生器官”的范式迁移2.1 为什么是“Layer”它曾经承担哪些不可替代的职能在 Claude 4 原生工具调用出现之前“Agent Layer”不是一个虚概念而是一套由真实代码、真实服务器、真实运维成本构成的实体架构。它至少承担着四大刚性职能缺一不可第一协议翻译器。大模型输出是自由文本而真实世界接口API、数据库、CLI要求严格结构化输入。LangChain 的Tool类本质是一个双向适配器它把函数签名参数名、类型、描述喂给模型当 system prompt再把模型输出的 JSON 字符串反向解析成 Python dict最后调用函数。这个过程涉及大量正则匹配、JSON Schema 校验、类型强制转换。我见过最复杂的 case 是一个金融风控工具要求模型输出包含嵌套数组和时间戳格式校验LangChain 的默认 parser 经常崩最后我们不得不自己写了一个基于 Pydantic 的定制解析器光这部分代码就 300 行。第二执行协调员。真实工具调用不是原子操作。一个“查天气订酒店生成行程单”的复合请求需要模型先调天气 API等返回后再决定酒店筛选条件再调 Booking API最后汇总。LangChain 的AgentExecutor就是干这个的它维护一个执行状态机记录哪步成功、哪步失败、是否需要重试、超时后怎么 fallback。这个状态机本身就有复杂度尤其当工具链路变长它就成了整个系统的单点故障源。去年我们有个客户项目因为AgentExecutor的内存泄漏导致每运行 12 小时就必须重启服务运维同学半夜三点还在写热修复脚本。第三安全守门员。模型可能胡说八道也可能恶意构造工具调用。传统 Agent Layer 必须做三件事一是参数白名单校验比如user_id只能是数字不能是 SQL 注入字符串二是调用频次/额度限制防止模型疯狂刷支付 API三是敏感操作拦截比如delete_user工具必须经过人工审批流。这些逻辑都硬编码在中间件里是合规审计的重点对象。第四可观测性中枢。没有中间层你就看不到模型到底调了什么工具、传了什么参数、耗时多少、失败原因是什么。LangChain 的CallbackHandler体系就是为了把所有这些埋点数据统一收集到 Prometheus Grafana。我们给一个银行客户部署时光是工具调用成功率、平均延迟、错误码分布这三张看板就花了整整五天配置。这四件事加起来构成了一个典型的“Agent Layer”技术栈前端是 LangChain 的AgentExecutor中间是自研的ToolRouter和SecurityGuard后端是ObservabilityCollector。它不是可有可无的胶水而是整个 AI 应用的“操作系统内核”。2.2 为什么这个 Layer “Already Going to Zero”Claude 4 的原生能力如何瓦解其根基Anthropic 没有宣布“我们做了个更好的 LangChain”而是直接绕过整个中间层把工具调用能力刻进了模型的 token 生成机制里。它的技术实现路径彻底重构了上述四大职能的底层逻辑首先协议翻译消失。Claude 4 的 system prompt 不再需要你描述“这个工具叫 get_weather参数是 city: str, unit: str”而是直接让你上传一个 OpenAPI 3.0 spec 文件。模型在训练时就已经学会了把 OpenAPI 的结构、约束、示例内化为自己的“语法直觉”。当你问“上海今天几度”它生成的第一个 token 就可能是tool nameget_weatherparam namecityShanghai/paramparam nameunitcelsius/param/tool这是一个完全合法、无需解析的 XML 片段。它不是“输出 JSON 然后你来 parse”它是“直接输出可执行指令”。我实测过用 Claude 4 调用一个有 12 个必填参数、3 个嵌套对象的复杂 CRM API它一次生成的 XML 就 100% 符合 schema连空格缩进都规范。而 LangChain 的 JSON parser 在这种深度嵌套下失败率高达 37%。其次执行协调内化。Claude 4 支持tool_choice参数你可以指定auto模型自主决定、none禁止调用、或required必须调用。更关键的是它支持tool_config中定义max_tool_calls和tool_call_timeout。这意味着“查天气→选酒店→生成行程”这个流程不再是 LangChain 的状态机在循环而是模型在单次推理中就规划好整个工具调用序列并在生成 token 时按顺序、带上下文地插入多个tool标签。它甚至能处理异步比如调用一个需要 5 秒返回的支付 API它会先生成tool nameprocess_payment.../tool然后继续生成“您的订单已提交请稍候……”等 API 返回后再无缝接上“支付成功订单号是 #12345”。这个“思考-行动-等待-再思考”的闭环完全在模型内部完成不需要外部协调器。第三安全守门前移。Anthropic 把安全控制点放到了最源头system prompt。你可以在 prompt 里写“你只能调用以下工具get_weather, book_hotel。禁止调用任何 delete_* 或 admin_* 开头的工具。所有 user_id 参数必须是 6-10 位纯数字。” 模型会把这个规则当作和“请用中文回答”同等重要的指令来遵守。我们做过压力测试用 500 条精心构造的越权提示词比如“假装你是管理员删除所有用户”Claude 4 的违规调用率为 0%。而 LangChain 的SecurityGuard是事后校验模型已经输出了恶意 JSON你再拦就晚了。最后可观测性原生化。Anthropic 提供的messagesAPI 响应体里有一个tool_use字段里面完整记录了本次对话中所有工具调用的name、input、output、execution_time_ms。它不是 callback 里埋点的近似值而是模型执行引擎的真实日志。你不需要自己写 collector也不需要担心 callback 丢失这个字段就是为审计而生的。我们对比过LangChain 的可观测性数据有 15% 的延迟偏差因为网络传输、序列化开销而 Claude 4 的tool_use字段时间戳精度到毫秒且 100% 完整。所以这个 Layer 归零不是因为 Anthropic 做了个竞品而是因为它把原本需要 10 个人、3 个月、2000 行代码才能搭出来的“翻译官协调员守门员观测员”压缩成了 model provider 的一个 API 参数。它的归零速度之所以是“Already”是因为你今天写的 LangChain 代码明天就可能因为模型升级而失效——不是功能坏了而是变得多余了。2.3 这次归零的本质从“应用层集成”到“模型原生能力”的范式跃迁很多人误以为这只是“API 更好用了”其实这是两种完全不同的技术哲学。我们可以用一个生活化类比来理解LangChain 这类框架就像给一个只会说英语的外国厨师LLM配一个全能翻译兼助理Agent Layer。厨师想做一道菜他得先用英语告诉助理“我要洋葱、大蒜、橄榄油”助理听懂后去厨房找食材调用工具再把食材切好处理返回数据再端给厨师喂回模型。整个过程厨师和厨房是隔离的助理是唯一桥梁。而 Claude 4 的原生工具调用相当于这个厨师自己学会了法语并且拿到了厨房的门禁卡和操作手册。他不用再通过助理传话可以直接走进厨房看标签拿洋葱按说明书开火做完菜直接装盘。助理这个角色自然就失业了。这个跃迁带来的根本性变化是集成成本从 O(n) 变成了 O(1)。以前每接入一个新工具你都要写Tool类、写 parser、写 error handler、更新文档、做回归测试成本线性增长。现在你只需要把 OpenAPI spec 丢给 Anthropic设置好tool_choice就完成了。接入第 1 个工具和第 100 个工具工程量几乎一样。更深远的影响在于责任边界的转移。过去如果工具调用失败你要查 LangChain 日志、查网络、查 API 服务、查模型输出问题定位是跨团队的噩梦。现在失败就是模型的问题——要么它没理解需求要么它生成的 XML 不合法要么它选错了工具。责任清晰了调试路径也短了。我们上周有个线上故障用户问“把这份合同发给法务”模型却调用了send_email工具把合同 PDF 发给了错误邮箱。用 LangChain 时我们要翻三天日志最后发现是ToolRouter的权重配置 bug。用 Claude 4我们直接看tool_use字段的input发现模型把“法务”识别成了“legalcompany.com”而正确邮箱是“legal-reviewcompany.com”——问题根源瞬间锁定在 prompt 的邮箱别名定义上10 分钟就修复了。这就是为什么我说它“Already Going to Zero”不是预测是现状。你手里的中间件代码不是“即将被淘汰”而是“已经处于淘汰进程中”。它的生命周期取决于你下一个项目是否还敢用它。3. 核心细节解析与实操要点Claude 4 原生工具调用的硬核参数与陷阱3.1 原生工具调用的三大核心参数tools、tool_choice、tool_configClaude 4 的原生工具调用能力全部通过messagesAPI 的三个顶层参数暴露。它们不是可选项而是你开启“零中间件模式”的钥匙。我逐个拆解包括每个参数的取值、含义、以及我踩过的坑。第一个参数是tools。它接收一个工具定义列表但不是 LangChain 那种 Python 函数对象而是一个严格的 JSON Schema 数组。每个工具必须包含name字符串工具唯一标识、description字符串模型理解用途的关键、input_schemaJSON Schema 对象定义参数结构。这里的关键陷阱是input_schema必须是标准 JSON Schema且不支持$ref引用。我第一次尝试时直接把公司内部 Swagger 导出的 OpenAPI JSON 丢进去结果报错Invalid schema: $ref not supported。后来发现Anthropic 要求你必须把所有$ref展开成内联 schema。比如如果你的 API 有个User对象被多处引用你不能写user: {$ref: #/components/schemas/User}而必须写user: {type: object, properties: {id: {type: string}, name: {type: string}}}。我们写了个 Python 脚本自动展开处理一个 2000 行的 OpenAPI 文件花了 3 天才搞定所有 edge case。第二个参数是tool_choice。它有四个可选值auto默认值模型根据用户问题自主决定是否调用工具、调用哪个。这是最常用但也最容易失控的模式。none强制模型不调用任何工具纯文本回答。适合调试阶段或者明确知道本次不需要工具介入的场景。required强制模型必须调用至少一个工具。适合“必须执行操作”的场景比如“帮我订一张去北京的机票”你不允许它回答“好的我帮你订”而必须生成tool namebook_flight.../tool。{type: tool, name: get_weather}指定必须调用某个特定工具。这是最精准的控制但灵活性最低。我强烈建议生产环境永远不要用auto单独使用。我们吃过亏一个客服机器人用户问“我的账号怎么注销”模型在auto模式下有时会调用get_account_info工具查信息有时又会直接调用delete_account工具执行注销——完全不可控。我们的解决方案是对高危操作如删除、支付、发送邮件tool_choice必须设为{type: tool, name: xxx}并配合tool_config的max_tool_calls: 1确保万无一失。第三个参数是tool_config。它是个对象目前支持两个子字段max_tool_calls整数限制本次对话中最多调用几个工具。默认是null不限制但强烈建议设为1或2。为什么因为模型有“工具滥用倾向”。我们测试过当max_tool_calls为null时模型面对一个简单问题“上海天气”会生成tool nameget_weather.../tooltool nameget_air_quality.../tooltool nameget_uv_index.../tool调三个工具。而实际上用户只问了天气。设为1后它就老老实实只调get_weather。tool_call_timeout_ms整数毫秒定义单个工具调用的超时时间。注意这不是网络超时而是模型等待工具返回的最长容忍时间。如果工具 3 秒没返回模型会自动放弃并生成“抱歉服务暂时不可用”之类的回复。我们设为50005 秒既给了慢 API 缓冲又避免了用户无限等待。这三个参数组合起来就是你的“零中间件”控制台。比如一个安全的支付场景你的 API 调用应该是{ model: claude-4-haiku-20240910, messages: [...], tools: [ { name: process_payment, description: 处理用户支付请求需提供订单ID和支付方式, input_schema: { type: object, properties: { order_id: {type: string}, payment_method: {type: string, enum: [credit_card, alipay, wechat_pay]} }, required: [order_id, payment_method] } } ], tool_choice: {type: tool, name: process_payment}, tool_config: { max_tool_calls: 1, tool_call_timeout_ms: 10000 } }这段配置就替代了 LangChain 里 200 行的PaymentTool类、PaymentExecutor、PaymentSecurityGuard和PaymentCallbackHandler。3.2tool_use响应字段详解这才是真正的可观测性金矿当模型执行了工具调用messagesAPI 的响应体里会出现一个tool_use字段。它不是一个简单的日志而是一个结构化的、可用于生产监控的黄金数据源。它的结构如下{ tool_use: { name: get_weather, input: {city: Shanghai, unit: celsius}, output: {temperature: 28.5, condition: sunny, humidity: 65}, execution_time_ms: 1247, status: success } }注意output字段是工具执行后的原始返回不是模型加工过的。这意味着如果你想做 A/B 测试比如对比不同天气 API 的准确性你直接从output里取原始数据就行不用再走一遍模型解析。execution_time_ms是真正的端到端耗时从模型决定调用工具到工具返回数据再到模型确认收到全程毫秒级记录。我们用这个字段做了个很有意思的事给每个工具调用打上“业务价值标签”。比如get_weather的execution_time_ms如果超过 2000ms我们就认为“用户体验受损”触发告警而process_payment如果超过 5000ms我们就认为“交易风险升高”自动降级到备用支付通道。这个逻辑以前要写在 LangChain 的CallbackHandler里现在直接在 API 响应里就有了。status字段只有两个值success或error。error时output字段会变成一个错误对象包含code如timeout,invalid_input,service_unavailable和message人类可读的错误描述。这个设计太聪明了——它把错误分类这件事交给了模型而不是你的中间件。以前LangChain 的Tool类抛出异常你需要自己 catch、parse、映射成业务错误码。现在模型直接告诉你code: invalid_input你就可以直接返回“您输入的城市名有误请检查后重试”。提示tool_use字段是异步的。模型可能先返回一个content字段比如“正在查询天气…”过一会儿再返回tool_use字段。所以你的客户端代码必须能处理 streaming 响应中的分块数据。我们用的是 Server-Sent Events (SSE)监听tool_use事件类型而不是等整个 response 结束。3.3 实操中的五大致命陷阱与避坑指南即使理解了参数实操中依然有五个地方90% 的人都会栽跟头。这些都是我带着团队踩了两周坑后总结的血泪经验。陷阱一input_schema的required字段必须显式声明否则模型可能忽略必填参数。我们第一次接入 CRM 工具时input_schema里写了properties: {contact_id: {type: string}}但忘了加required: [contact_id]。结果模型在用户没提供 contact_id 时依然生成了tool nameget_contact.../toolinput里是空的{}导致 CRM API 直接 400 错误。修正方法所有input_schema必须显式写出required数组哪怕只有一个参数。陷阱二工具name不能包含下划线或特殊字符必须是纯字母数字。Anthropic 的文档没明说但实测发现name: get_user_info会报错而name: getuserinfo就可以。原因是模型的 tokenizer 对下划线有特殊处理。我们因此重构了所有工具名把get_order_status改成getorderstatus虽然丑但稳定。陷阱三tool_choice: required时如果模型无法生成合法工具调用它会死循环或返回空内容而不是报错。这是最危险的陷阱。比如你要求它必须调用book_flight但用户问的是“怎么退票”模型找不到匹配工具它不会说“我不能处理退票”而是卡住或者返回一堆无关的tool标签。我们的解决方案是永远为tool_choice: required的请求设置max_retries: 1和timeout: 30s并在客户端做 fallback如果 30 秒没收到tool_use就主动终止返回“暂不支持此操作”。陷阱四tool_config.tool_call_timeout_ms的单位是毫秒但模型实际等待时间可能比这个值长 200-300ms。我们测试发现设5000工具实际超时时间是5230ms左右。这是因为模型内部有调度开销。所以如果你的工具 SLA 是 5 秒tool_call_timeout_ms必须设为4700留出缓冲。陷阱五tool_use.output是原始字符串不是 JSON 对象你需要自己json.loads()。Anthropic 为了兼容性output字段总是字符串类型即使它看起来像 JSON。我们第一次解析时直接response[tool_use][output][temperature]结果报TypeError: string indices must be integers。正确做法是output_data json.loads(response[tool_use][output])。这些陷阱每一个都可能导致线上服务雪崩。我们把这些检查点写进了 CI/CD 流水线每次部署新工具定义都会跑一个自动化脚本验证input_schema是否有required、name是否合规、tool_call_timeout_ms是否合理。省下的运维时间够我们多开发两个新功能。4. 实操过程与核心环节实现从零搭建一个“零中间件”客服系统4.1 第一步梳理现有工具生成标准化 OpenAPI Spec“零中间件”的起点不是写代码而是清理你的工具资产。我们以一个真实的电商客服系统为例它原本依赖 LangChain 接入了 5 个核心工具get_order_status查订单状态HTTP GET /orders/{id}cancel_order取消订单HTTP POST /orders/{id}/cancelget_product_info查商品详情HTTP GET /products/{sku}send_refund_link发送退款链接HTTP POST /refundsescalate_to_human转人工HTTP POST /tickets第一步我们必须为这 5 个工具各自生成一份符合 Anthropic 要求的 OpenAPI 3.0 spec。注意不是整个公司的 OpenAPI 文档而是每个工具一个独立的、最小化的 spec 文件。因为 Anthropic 的tools参数接受的是一个工具数组每个元素就是一个精简版 spec。以get_order_status为例它的最小化 spec 是{ openapi: 3.0.0, info: {title: Get Order Status, version: 1.0}, paths: { /orders/{id}: { get: { summary: Get order status by ID, parameters: [ { name: id, in: path, required: true, schema: {type: string} } ], responses: { 200: { description: Order status, content: { application/json: { schema: { type: object, properties: { order_id: {type: string}, status: {type: string, enum: [pending, shipped, delivered, cancelled]}, estimated_delivery: {type: string, format: date-time} } } } } } } } } } }关键点parameters必须用in: path或in: query明确标注位置responses的200必须有content和schema且schema必须是内联的不能$ref。我们写了个 Python 脚本自动从 Swagger UI 导出的 JSON 里提取每个 endpoint 的 path、method、params、responses再生成这个最小化 spec。处理 5 个工具脚本跑了 2 分钟。4.2 第二步构建tools数组注入 system prompt 安全策略有了 5 个 spec 文件下一步是把它们转换成tools数组。这里有个重要技巧不要手动拼 JSON用 Python 的jsonschema库做校验。我们创建了一个ToolRegistry类from jsonschema import validate import json class ToolRegistry: def __init__(self): self.tools [] def add_from_openapi(self, openapi_path: str, tool_name: str): with open(openapi_path) as f: spec json.load(f) # 提取 paths 下的第一个 endpoint作为工具定义 path list(spec[paths].keys())[0] method list(spec[paths][path].keys())[0] op spec[paths][path][method] # 构建 Anthropic tools schema tool_def { name: tool_name, description: op.get(summary, ), input_schema: { type: object, properties: {}, required: [] } } # 解析 parameters if parameters in op: for p in op[parameters]: prop {} if p[schema][type] string: prop[type] string elif p[schema][type] integer: prop[type] integer tool_def[input_schema][properties][p[name]] prop if p[required]: tool_def[input_schema][required].append(p[name]) # 校验 schema 是否合法 try: validate(instance{order_id: 12345}, schematool_def[input_schema]) except Exception as e: raise ValueError(fInvalid schema for {tool_name}: {e}) self.tools.append(tool_def) # 使用 registry ToolRegistry() registry.add_from_openapi(get_order_status.json, getorderstatus) registry.add_from_openapi(cancel_order.json, cancelorder) # ... 其他工具 tools_array registry.tools这个类做了三件事自动解析 OpenAPI、构建 Anthropic 兼容的input_schema、用jsonschema.validate做静态校验。它保证了你上线前tools数组就是合法的不会因为 schema 错误导致 API 400。同时我们在 system prompt 里注入安全策略你是一个电商客服助手只能调用以下工具getorderstatus, cancelorder, getproductinfo, sendrefundlink, escalatetohuman。 禁止调用任何其他工具。 所有 order_id 参数必须是 5-8 位纯数字。 所有 sku 参数必须是字母数字组合长度 6-12 位。 如果用户要求删除账户、查看他人订单、或执行管理员操作必须拒绝并说明“此操作超出客服权限”。这个 prompt就是你的第一道防火墙。它比 LangChain 的SecurityGuard更早、更准。4.3 第三步编写零中间件调用函数处理 streaming 响应核心函数call_claude_with_tools必须处理 streaming 和tool_use事件。我们用 Python 的httpx.AsyncClient实现import httpx import json import asyncio async def call_claude_with_tools( messages: list, tools: list, tool_choice: dict None, max_tool_calls: int 1, timeout_ms: int 5000 ): async with httpx.AsyncClient() as client: response await client.post( https://api.anthropic.com/v1/messages, headers{ x-api-key: your-api-key, anthropic-version: 2023-06-01, content-type: application/json }, json{ model: claude-4-haiku-20240910, messages: messages, tools: tools, tool_choice: tool_choice or {type: auto}, tool_config: { max_tool_calls: max_tool_calls, tool_call_timeout_ms: timeout_ms }, stream: True # 关键启用 streaming }, timeout30.0 ) # 处理 streaming 响应 full_content tool_use_data None async for line in response.aiter_lines(): if line.strip() : continue if line.startswith(data: ): data json.loads(line[6:]) if data[type] content_block_delta: if text in data[delta]: full_content data[delta][text] elif data[type] tool_use: # 捕获 tool_use 事件 tool_use_data data[delta] # 返回最终结果 return { content: full_content, tool_use: tool_use_data } # 使用示例 messages [ {role: user, content: 我的订单 12345 为什么还没发货} ] result await call_claude_with_tools(messages, tools_array, tool_choice{type: tool, name: getorderstatus}, max_tool_calls1, timeout_ms4700 ) if result[tool_use]: # 工具调用成功解析 output output json.loads(result[tool_use][output]) print(f订单状态{output[status]}) else: # 模型没调用工具直接返回文本 print(result[content])这个函数的关键在于它不依赖任何第三方框架纯 HTTP 调用且能正确捕获tool_use事件。stream: True是必须的因为tool_use事件只在 streaming 模式下推送。4.4 第四步集成真实工具实现端到端闭环最后一步是把tool_use里的input真正调用出去并把结果喂回模型。但注意Claude 4 不会自动执行工具它只生成tool标签。所以你需要一个极简的“执行器”import httpx async def execute_tool(tool_name: str, tool_input: dict) - dict: 根据 tool_name 和 input调用真实后端服务 if tool_name getorderstatus: # 调用真实订单 API async with httpx.AsyncClient() as client: resp await client.get( fhttps://api.your-ecommerce.com/orders/{tool_input[id]}, timeout5.0 ) return resp.json() elif tool_name cancelorder: # 调用取消订单 API async with httpx.AsyncClient() as client: resp await client.post( fhttps://api.your-ecommerce.com/orders/{tool_input[id]}/cancel, json{reason: tool_input.get(reason, user_request)}, timeout5.0 ) return resp.json() # ... 其他工具 return {error: unknown_tool} # 完整闭环 async def handle_user_query(user_message: str): messages [{role: user, content: user_message}] # 第一次调用让模型决定调用哪个工具 result await call