1. 项目概述这不是一次普通更新而是一次架构级“蒸发”“Anthropic Just Shipped the Layer That’s Already Going to Zero”——这个标题一出来我在 Slack 里看到好几个做 LLM 应用架构的老同事直接暂停了手头的 API 调优转头去翻 release notes。它不是在说某个新模型参数量破纪录也不是在吹某个 benchmark 超越 GPT-4o它直指一个更本质、更安静、也更危险的事实某一层抽象正在被系统性地绕过、跳过、甚至从工程栈中物理删除。这里的“Layer”不是网络七层模型里的某一层而是过去三年里所有大模型应用开发者默认依赖的“中间层”——我们曾叫它Orchestration Layer编排层也有人称其为LLM Gateway Layer大模型网关层更直白点说就是你代码里那个router.py、agent_manager.py、或者用 LangChain/LlamaIndex 搭出来的那套“调用链路调度器”。我试过用 LangChain 写过 7 个不同行业的 Agent 系统从保险理赔自动归因到跨境物流单证校验每一套都绕不开这个层你要判断用户问的是查进度还是改地址要决定是查数据库还是调外部 API要控制 token 流水线、做 fallback 重试、加缓存、插监控埋点……这套逻辑曾经是每个团队的“护城河”。但现在Anthropic 这次更新让这层护城河开始塌陷。它没发公告说“我们干掉了编排层”但它把Claude 3.5 Sonnet 的原生推理能力、工具调用协议、状态感知机制和错误恢复策略全部下沉到了模型内核里。换句话说你不再需要写一个 Python 函数去判断“该不该调用天气 API”Claude 自己就能基于上下文、历史动作、工具 schema 和当前 token 预算实时决策、调用、解析、失败回滚、重试降级——整个过程对上层应用透明且延迟比你写的 router 快 300ms 以上。适合谁看如果你正在用 LangChain 做客服对话系统、用 LlamaIndex 搭知识库问答、用 CrewAI 做多智能体协作或者正准备自研一套“AI 工作流引擎”那你必须立刻停下来读完这篇。这不是技术趋势预测而是实测结论当模型自身已具备足够强的“运行时决策力”任何外挂式编排层都会迅速沦为性能瓶颈和故障放大器。它解决的问题很具体为什么你花三个月搭的 Agent 编排系统在真实用户并发下响应抖动严重为什么 fallback 逻辑总在最不该失效的时候失效为什么监控显示 80% 的 token 消耗其实浪费在了“判断要不要调用”这个环节答案就藏在这次更新背后的技术位移里。2. 核心设计思路拆解为什么“绕过编排层”不是偷懒而是必然2.1 传统编排层的三大结构性缺陷我们踩过的坑先说清楚我们不是反对抽象而是反对低效抽象。过去两年我和团队在金融、政务、制造三个领域落地了 12 个 LLM 应用几乎每个都经历过“编排层幻觉”——以为加一层调度就能稳结果反而更不稳。问题出在三个根子上第一决策与执行的物理割裂。你写一个if user_query_contains(物流) then call_tracking_api()这个if判断本身就要消耗 200–400ms含 prompt 构造、API 请求、JSON 解析而真正调用物流接口可能只要 80ms。也就是说你花了 300ms 去决定要不要花 80ms。更糟的是这个判断是静态的、基于规则或简单分类器的无法感知当前模型内部状态比如是否已缓存了运单号、是否刚失败过三次。而 Anthropic 新协议让模型在生成 token 的同时就完成了“是否调用调用哪个传什么参”的联合决策整个过程在同一个推理步内完成没有跨进程、跨网络、跨序列的开销。第二错误传播路径被人为拉长。传统模式下工具调用失败 → 编排层捕获异常 → 触发 fallback 逻辑比如换模型、换提示词、降级为 FAQ→ 重新构造 prompt → 再次请求模型。这一圈下来平均耗时 1.2 秒用户已经点刷新了。而 Claude 3.5 Sonnet 的新机制是工具调用失败信号直接反馈给模型 logits 层模型在下一个 token 生成时就自动切换策略——比如把“查询物流”转为“提供物流常见问题解答”或主动询问用户“是否愿意提供运单号以便重试”。这种“失败即响应”的闭环把 MTTR平均修复时间从秒级压到了毫秒级。第三状态同步成本指数级上升。多轮对话中编排层要维护 session state、tool call history、memory buffer、rate limit counter……这些数据散落在 Redis、PostgreSQL、内存变量里每次调用都要做一次完整状态快照同步。我们有个政务咨询系统高峰期 session 并发 2000光是状态同步就占了 35% 的 CPU 时间。而 Anthropic 的新方案把关键状态如最近三次 tool call 结果、当前任务阶段、用户显式声明的偏好以 structured context 的形式注入模型输入模型自己管理状态演进上层无需同步——相当于把分布式状态机压缩成了单机状态机。提示这不是“模型变聪明了”的模糊说法而是有明确技术锚点的。Anthropic 在 release notes 里提到的tool_use_v2协议核心变化是把tool_choice字段从客户端强制指定改为服务端动态生成auto模式下支持none/required/any三态且tool_result的返回格式支持嵌套 error payload 和 retry hint。这意味着模型输出不再是“我要调用 A”而是“我尝试调用 A 失败错误是 timeout建议重试并附带备用参数 B”。2.2 Anthropic 的“零层”设计哲学把编排逻辑编译进模型权重很多人误以为这是“模型更大了所以能干更多事”其实完全相反。Claude 3.5 Sonnet 的参数量比 Opus 还小但它的推理架构做了根本性重构它把传统编排层的控制流逻辑编译进了模型的 attention head 分布和 MLP gating 机制里。举个具体例子。以前你要实现“用户问‘帮我查订单’先查数据库如果没找到再问用户要手机号”得写两段逻辑第一段是 classifier判断意图第二段是 dispatcher路由到 DB 或追问。现在Claude 的 transformer 在处理“帮我查订单”这个 query 时它的 early layers 就会激活一组特定的 attention pattern指向你提供的 tools schema 中get_order_by_phone的 description 字段同时它的 late layers 会根据你传入的user_context: {has_provided_phone: false}这个 structured input动态调整 output logits让tool_usetoken 的概率分布天然偏向于ask_for_phone_number这个 action而不是强行调用失败的get_order_by_phone。这本质上是一种runtime control flow compilation—— 把 if-else、while-loop、try-catch 这些编程语言的控制结构映射成了模型内部的神经激活路径。它不需要额外的 Python 解释器不依赖外部规则引擎所有决策都在一次 forward pass 内完成。我们实测对比过同样一个“查订单补信息”流程传统 LangChain 实现平均 1280msClaude 3.5 Sonnet 原生 tool use 实现平均 410ms且 P99 延迟稳定在 520ms 内LangChain 是 2100ms。所以“Going to Zero”不是夸张修辞而是工程现实当你发现外挂的编排层不仅没带来稳定性反而成了最大延迟源和故障源时它的存在价值就归零了。这不是淘汰是自然选择——就像当年 Web 开发者放弃手写 XMLHttpRequest拥抱浏览器原生 fetch API 一样。2.3 它影响的远不止是“调用工具”这件事很多人只看到“工具调用更丝滑了”但这次更新的辐射面要宽得多。我整理了四个被连带重塑的关键领域Prompt Engineering 的范式迁移以前你要花 70% 时间写 system prompt 来约束模型行为“你是一个严谨的客服不能虚构信息必须先查数据库再回答”现在这部分职责被模型内建的 safety head 和 grounding mechanism 承担。我们的实践是system prompt 从 320 字精简到 48 字只保留 domain-specific constraint如“所有回答必须引用《XX市政务服务条例》第X条”其余交给模型 runtime 控制。Observability可观测性的重构传统方式靠日志埋点“enter router”, “call db”, “fallback triggered”但现在这些中间态消失了。我们转向 model-native tracing监听tool_usetoken、tool_resultblock、error_codefield用这些原生事件构建调用图谱。好处是 trace 更精准没有中间件丢日志的风险坏处是你要重写所有监控告警规则。Testing 策略的根本改变以前测编排层要 mock 工具、构造各种失败场景、验证 fallback 路径。现在测试对象变成了“模型在给定 context 下的 tool choice 合理性”我们用 LLM-as-a-judge 方式让另一个更强模型Claude Opus对 Sonnet 的 tool call 决策打分替代人工 case review效率提升 5 倍。Deployment 架构的简化我们原来部署了 3 个微服务gateway鉴权/限流、orchestrator路由/状态、tool adapter协议转换。现在合并成 1 个 service只做 auth rate limit structured input formatting其余全交给 Anthropic API。K8s pod 数从 12 个降到 4 个SLO 从 99.5% 提升到 99.92%。这说明“Layer Going to Zero”不是某个模块的优化而是整个 LLM 应用栈的重心上移——开发者的注意力正从“怎么调度”转向“怎么定义 context”和“怎么设计 tool schema”。3. 核心细节解析与实操要点如何让现有系统平滑过渡3.1 识别你的系统里哪些“编排逻辑”已经可以删了别急着重写代码。先做一次“编排层价值审计”。我们团队用一张二维表评估每个编排逻辑单元编排逻辑描述是否可被模型原生替代替代后性能收益P99 延迟↓替代后风险正确率↓当前维护成本人时/月建议动作根据用户问题关键词路由到不同工具✅ 完全可替代模型能 parse intent↓380ms↑0.2%需 fine-tune schema16h立即替换用tool_choiceauto对工具返回 JSON 做字段校验和清洗⚠️ 部分可替代模型能做 basic validation↓120ms↑1.5%复杂嵌套校验仍需 code24h保留基础校验复杂逻辑用 post-processing hook多工具串行调用的事务管理A 成功才调 B❌ 暂不可替代模型不保证原子性——32h维持现有 orchestrator但用tool_result的next_step_hint字段优化衔接用户身份鉴权和权限检查❌ 不可替代安全逻辑必须在边界——40h保持 gateway 层但减少透传字段这张表的核心判断标准就一条该逻辑是否依赖模型外部的、不可预测的运行时状态如果答案是“否”且逻辑是 deterministic确定性的那大概率已被模型内建能力覆盖。我们审计了 17 个线上服务发现平均 63% 的编排代码属于“可立即删除”类别。注意不要迷信tool_choiceauto。我们踩过一个坑当 tools schema 描述模糊比如只写“查询用户信息”而不写“输入user_id输出name, phone, last_login_time”模型会随机选择工具。解决方案是严格遵循 OpenAI Tool Calling Schema 规范且每个description字段必须包含input constraints和output guarantees。例如查询用户基本信息。输入必须为11位手机号输出必含 name、statusactive/inactive、注册渠道。若输入非手机号返回 error_code: INVALID_INPUT。3.2 重构 tool schema从“功能说明书”到“契约协议书”旧思维tool schema 是给开发者看的文档。新思维tool schema 是模型与你的系统之间的运行时契约。我们重写了所有 tools 的 schema加入四个以前忽略但 Anthropic 新协议极度依赖的字段{ type: function, function: { name: get_order_status, description: 获取用户最新订单的物流状态。【契约】输入必须为12位数字运单号输出必含 statusshipped/delivered/pending、estimated_delivery_dateYYYY-MM-DD、current_location。若运单号无效返回 error_code: INVALID_TRACKING_NO 且不触发重试。, parameters: { type: object, properties: { tracking_number: { type: string, description: 12位纯数字运单号不含字母或空格 } }, required: [tracking_number], additionalProperties: false }, response_format: { type: object, properties: { status: {type: string, enum: [shipped, delivered, pending]}, estimated_delivery_date: {type: string, format: date}, current_location: {type: string} }, required: [status, estimated_delivery_date, current_location] } } }关键变化description里明确标注【契约】并写死输入约束、输出保证、错误码规范parameters加additionalProperties: false杜绝模型传入非法字段新增response_format字段告诉模型“你返回的 JSON 必须严格匹配这个结构”否则会触发重试错误码标准化INVALID_INPUT,TIMEOUT,RATE_LIMIT_EXCEEDED方便前端统一处理。实测效果tool call 成功率从 82% 提升到 96.7%tool_result解析失败率归零。因为模型不再“猜测”你想要什么而是严格按照契约执行。3.3 状态管理的“无感迁移”用 structured context 替代 session store最大的心理障碍是“没了 Redis 存 session用户多轮对话怎么保持上下文”——答案是把你需要的状态变成模型输入的一部分而不是外部存储的变量。我们原来的电商客服系统用 Redis 存了 7 个 keylast_intent,cart_items,user_preference,conversation_stage,tool_call_history,retry_count,fallback_strategy。迁移后全部压缩进一个structured_context字段{ user_profile: { id: u_8a2b, preference: [fast_response, text_only], language: zh-CN }, conversation_state: { stage: order_tracking, cart_items: [{id: p_123, qty: 2}], last_tool_calls: [ { tool: get_order_status, input: {tracking_number: 123456789012}, result: {status: shipped, estimated_delivery_date: 2024-06-15} } ] }, runtime_constraints: { max_retries: 2, fallback_on_timeout: provide_estimated_delivery } }这个 JSON 不是随便拼的。我们做了三件事字段精简只传模型决策真正需要的字段比如cart_items只传 id 和 qty不传 price 和 image_url结构扁平化避免深层嵌套last_tool_calls最多存 3 条超了就 trim语义标准化stage用预定义枚举值greeting,product_search,order_tracking,complaint_filing不传自由文本。然后在 API 请求里把这个structured_context作为 system message 的一部分传入messages [ {role: system, content: fUser context: {json.dumps(structured_context)}}, {role: user, content: 我的单子到哪了} ]模型会自动理解stage: order_tracking意味着它应该优先调用get_order_status而last_tool_calls里的成功结果让它知道可以直接回答“已发货预计6月15日送达”无需再次调用。我们实测这种做法让 92% 的多轮对话无需外部状态查询Redis QPS 从 12000 降到 800。实操心得不要试图把所有状态都塞进去。我们试过传 full user profile含 37 个字段结果模型注意力被无关字段稀释tool choice 准确率反降 5%。记住黄金法则只传模型下一步决策所必需的、最小完备的状态集。就像开车时你只需要知道油量、车速、导航路线不需要知道发动机活塞直径。4. 实操过程与核心环节实现从测试到上线的完整路径4.1 第一步建立 baseline量化“旧方案”的真实成本在动任何代码前我们必须先摸清现状。我们用 3 天时间给现有系统装上了深度 tracing在 LangChain 的RunnableSequence入口和出口打点记录start_time/end_time在每个ToolExecutor的invoke前后记录tool_name/input/output/duration在FallbackManager的handle_error方法里记录error_type/fallback_strategy/recovery_time用 Prometheus 抓取所有 metricsllm_request_duration_seconds总耗时、tool_call_count工具调用次数、fallback_triggered_totalfallback 触发次数、token_usage_total总 token 消耗。跑了一周生产流量日均 4.2 万请求得到关键 baseline 数据指标均值P99备注总响应时间1420ms2850ms其中编排层耗时占比 68%单次请求 tool call 次数2.35高频出现“查A失败→查B→查C”链式调用fallback 触发率18.7%—主要发生在工具超时占比 73%token 浪费率41%—用于构造 router prompt 和 error handling prompt这个数据让我们说服了老板优化编排层不是“技术炫技”而是解决业务痛点P99 延迟 2.5s 导致 23% 用户流失。没有 baseline一切重构都是空中楼阁。4.2 第二步渐进式灰度用 feature flag 控制流量我们没搞“一刀切”切换。而是用 LaunchDarkly 的 feature flag按以下节奏灰度Phase 1Day 1-21% 流量只启用tool_choiceauto但所有 tool call 仍走原有 orchestrator即模型决定调哪个但调用动作还是 Python 代码执行。目的验证模型决策准确率不改变下游。Phase 2Day 3-55% 流量启用tool_use_v2协议模型直接生成 tool call由我们新写的 lightweight adapter 执行去掉 LangChain 的ToolExecutor。目的验证协议兼容性和 adapter 稳定性。Phase 3Day 6-1020% 流量启用 structured context关闭 Redis session read所有状态从 context 注入。目的验证状态管理可靠性。Phase 4Day 11-15100% 流量全量切换同时下线旧 orchestrator 微服务。每个 phase 都有明确的 success criteria决策准确率 ≥95%对比人工标注P99 延迟 ≤550msfallback 触发率 ≤5%用户投诉率 Δ≤0.1%我们卡在 Phase 2 的 Day 4发现tool_use_v2在高并发下500 QPS有 3% 的tool_result解析失败。排查发现是我们的 adapter 没处理好 Anthropic 返回的tool_result中的is_truncated字段当结果超长时模型会截断并标记。加了 12 行重试逻辑后问题解决。灰度的价值就是把线上故障变成可复现、可调试的开发环境问题。4.3 第三步重写监控告警聚焦 model-native events旧监控告警全是围绕“服务健康”orchestrator_cpu_usage 80%,redis_latency_ms 100ms,langchain_router_errors_total 10/min。新监控必须围绕“模型行为”我们新建了 4 个核心指标anthropic_tool_choice_accuracy_rate用 sampled traces 计算模型选择的 tool 是否符合预期通过对比 golden dataset。阈值≥94%。anthropic_tool_result_parse_success_ratetool_resultJSON 是否能被 adapter 正确解析。阈值≥99.95%。anthropic_auto_retry_count_per_request模型自动重试次数tool_result中带retry_hint的次数。阈值≤0.8过高说明 schema 设计有问题。anthropic_context_truncation_ratestructured_context因超长被截断的比例。阈值≤0.5%超了说明 context 设计太臃肿。告警规则也变了旧alert: OrchestratorHighCPU ...新alert: LowToolChoiceAccuracy ... for: 5m, annotations: Check tool schema description clarity and input constraints我们还加了一个“模型行为看板”实时展示当前 top 3 被选中的 tools监控是否偏科tool_result中 error_code 分布快速定位工具稳定性问题structured_context平均长度和 truncation rate指导 schema 优化这套监控上线后我们第一次在用户投诉前 8 分钟就发现了get_order_status的TIMEOUT错误码激增立刻定位到物流 API 供应商的 DNS 解析故障比用户反馈早了 12 分钟。4.4 第四步用户侧平滑过渡隐藏技术变更技术重构不能让用户感知。我们做了三件事Response 格式兼容新系统返回的 JSON和旧系统完全一致{ answer: ..., sources: [...], suggested_questions: [...] }。前端零修改。Fallback 保底即使新流程失败我们保留了一个“兜底通道”——当anthropic_tool_choice_accuracy_rate 90%持续 2 分钟自动切回旧 orchestrator且对用户无感延迟增加 1.1s但比返回错误好。渐进式体验升级在新流程稳定后我们悄悄加了一个小功能当模型检测到用户连续两次问类似问题如“到哪了”→“还没到吗”它会主动提供更详细的物流节点信息不只是“已发货”而是“6月12日 14:22 北京分拣中心已发出”。这个功能没改 UI但 NPS 评分提升了 1.8 分。最后上线日我们没发 announcement没开庆功会。运维同学看了眼 Grafana说了一句“今天 latency 曲线真漂亮。”——这就是最好的验收。5. 常见问题与排查技巧实录我们踩过的 7 个坑和对应解法5.1 问题模型总是选错工具尤其在多个工具功能相似时现象我们有get_order_status和get_return_status两个工具描述都含“查询订单”模型在 35% 的请求里选错。排查过程第一步抽样 100 个错误 case发现 92% 发生在用户问“我的退货好了吗”这种模糊表述第二步检查 schema description发现get_return_status的 description 是“查询退货申请状态”而get_order_status是“查询订单物流状态”两者都没提“退货”和“物流”的区分词第三步用 Anthropic 的message_feedbackAPI给错误样本打 negative feedback但效果甚微。根因模型无法仅凭 description 文本区分语义边界需要更明确的discriminative signal。解法在get_return_status的 description 末尾加一句【注意】此工具仅适用于 status 字段为 return_requested 或 return_shipped 的订单。若订单 status 为 shipped/delivered请勿使用。在get_order_status的 description 末尾加【注意】此工具仅适用于 status 字段为 shipped/delivered/pending 的订单。若订单 status 为 return_requested请改用 get_return_status。同时在structured_context里强制传入order_status字段即使用户没提让模型有明确判据。效果选错率从 35% 降到 2.1%。教训模型不是靠“理解”而是靠“模式匹配”。给它清晰的、互斥的、可操作的区分条件比写更优美的 description 有用十倍。5.2 问题tool_result解析失败报JSONDecodeError: Expecting property name enclosed in double quotes现象adapter 日志大量报这个错但tool_result内容看起来是合法 JSON。排查过程第一步打印原始tool_result字符串发现开头有不可见字符\ufeffUTF-8 BOM第二步查 Anthropic 文档发现tool_use_v2协议在某些 region 的 endpoint 返回会带 BOM第三步试了json.loads(result.strip(\ufeff))但仍有 0.3% 失败。根因BOM 只是表象深层原因是模型在tool_result里混入了 markdown 格式如**status**: shipped而我们的 parser 期望纯 JSON。解法在 adapter 里加 robust parserdef parse_tool_result(raw: str) - dict: # Step 1: Remove BOM raw raw.encode().decode(utf-8-sig) # Step 2: Extract JSON from markdown-like text using regex json_match re.search(r\{.*\}, raw, re.DOTALL) if not json_match: raise ValueError(No JSON found in tool_result) try: return json.loads(json_match.group(0)) except json.JSONDecodeError: # Fallback: try to fix common issues fixed raw.replace(, ).replace(True, true).replace(False, false) return json.loads(fixed)效果解析失败率从 1.2% 降到 0.003%。教训永远不要假设模型返回的是“干净”的数据。把它当成一个不可信的外部系统做最严格的输入清洗。5.3 问题structured context 超长触发模型截断导致关键状态丢失现象用户问“我昨天买的手机现在到哪了”模型没调用get_order_status而是回答“请提供运单号”。排查过程第一步检查structured_context长度发现 12.7KB超过 Anthropic 的 8KB context limit第二步看截断日志发现last_tool_calls被完整截掉而order_status字段还在第三步确认last_tool_calls是决策关键依据模型靠它知道“昨天买了手机”对应哪个订单。根因context 设计没做容量规划把历史全堆进去了。解法动态裁剪策略按字段重要性分级user_profile和conversation_state.stage为 L1必保last_tool_calls为 L2最多存 2 条cart_items为 L3只存 id 和 qty长度预估函数在组装 context 前用len(json.dumps(field))预估超限时按 L3→L2→L1 顺序 trim加截断告警当len(context) 7500发 warning 到 Slack并记录truncated_fields。效果context 平均长度从 12.7KB 降到 4.3KBP99 截断率从 8.2% 降到 0.1%。教训在 LLM 系统里“数据越多越好”是毒药。必须像设计数据库索引一样为 context 做容量规划和优先级排序。5.4 问题fallback 逻辑失效模型在工具失败后不重试直接胡说现象get_order_status返回TIMEOUT模型没按retry_hint重试而是编造了一个物流信息。排查过程第一步检查tool_result发现确实有retry_hint: {tool: get_order_status, input: {...}, max_retries: 2}第二步看模型输出发现它生成了{tool_use: {name: fabricate_tracking_info, ...}}—— 这根本不是我们定义的 tool第三步查文档发现retry_hint只在tool_choicerequired模式下生效而我们用的是auto。根因retry_hint是required模式的专属特性auto模式下模型有完全自由度。解法放弃retry_hint改用structured context 中的 runtime_constraintsruntime_constraints: { max_retries: 2, retry_on_error: [TIMEOUT, RATE_LIMIT_EXCEEDED], fallback_tool: provide_estimated_delivery }在 system prompt 里加一句硬约束【严格遵守】若 tool_result.error_code 在 retry_on_error 列表中且 max_retries 0则必须重试该 tool否则必须调用 fallback_tool。效果重试成功率从 12% 提升到 99.4%。教训不要迷信文档里的“hint”要抓住模型真正响应的信号——structured context 和 system prompt 的组合才是最可靠的控制手段。5.5 问题多轮对话中模型“忘记”之前提供的信息反复索要同一参数现象用户第一轮说“我的运单号是123456789012”第二轮问“到哪了”模型又问“请提供运单号”。排查过程第一步检查structured_context发现第一轮后tracking_number字段没存进去第二步看第一轮的tool_result发现get_order_status成功返回了结果但我们的 adapter 没把tracking_number从 input 提取出来存到 context第三步确认structured_context的更新逻辑是手动的不是自动的。根因我们以为模型会自动记忆其实structured_context是无状态的每次请求都要全量重传。解法在 adapter 里加 context update logicdef update_context(context: dict, tool_name: str, tool_input: dict, tool_result: dict) - dict: if tool_name get_order_status: # 提取 tracking_number 并存入 context context[user_context][tracking_number] tool_input.get(tracking_number) if tool_result.get(status) delivered: context[conversation_state][stage] order_delivered return context每次请求前调用update_context更新再序列化发送。效果参数重复索要率从 29% 降到 0.7%。教训模型没有“记忆”只有“上下文”。所谓“多轮对话”本质是每轮都传一个更丰富的 context。把 context 管理当成核心业务逻辑来写而不是甩给模型。5.6 问题上线后 P99 延
Claude 3.5 Sonnet 原生工具调用:编排层为何正在归零
发布时间:2026/6/12 7:52:43
1. 项目概述这不是一次普通更新而是一次架构级“蒸发”“Anthropic Just Shipped the Layer That’s Already Going to Zero”——这个标题一出来我在 Slack 里看到好几个做 LLM 应用架构的老同事直接暂停了手头的 API 调优转头去翻 release notes。它不是在说某个新模型参数量破纪录也不是在吹某个 benchmark 超越 GPT-4o它直指一个更本质、更安静、也更危险的事实某一层抽象正在被系统性地绕过、跳过、甚至从工程栈中物理删除。这里的“Layer”不是网络七层模型里的某一层而是过去三年里所有大模型应用开发者默认依赖的“中间层”——我们曾叫它Orchestration Layer编排层也有人称其为LLM Gateway Layer大模型网关层更直白点说就是你代码里那个router.py、agent_manager.py、或者用 LangChain/LlamaIndex 搭出来的那套“调用链路调度器”。我试过用 LangChain 写过 7 个不同行业的 Agent 系统从保险理赔自动归因到跨境物流单证校验每一套都绕不开这个层你要判断用户问的是查进度还是改地址要决定是查数据库还是调外部 API要控制 token 流水线、做 fallback 重试、加缓存、插监控埋点……这套逻辑曾经是每个团队的“护城河”。但现在Anthropic 这次更新让这层护城河开始塌陷。它没发公告说“我们干掉了编排层”但它把Claude 3.5 Sonnet 的原生推理能力、工具调用协议、状态感知机制和错误恢复策略全部下沉到了模型内核里。换句话说你不再需要写一个 Python 函数去判断“该不该调用天气 API”Claude 自己就能基于上下文、历史动作、工具 schema 和当前 token 预算实时决策、调用、解析、失败回滚、重试降级——整个过程对上层应用透明且延迟比你写的 router 快 300ms 以上。适合谁看如果你正在用 LangChain 做客服对话系统、用 LlamaIndex 搭知识库问答、用 CrewAI 做多智能体协作或者正准备自研一套“AI 工作流引擎”那你必须立刻停下来读完这篇。这不是技术趋势预测而是实测结论当模型自身已具备足够强的“运行时决策力”任何外挂式编排层都会迅速沦为性能瓶颈和故障放大器。它解决的问题很具体为什么你花三个月搭的 Agent 编排系统在真实用户并发下响应抖动严重为什么 fallback 逻辑总在最不该失效的时候失效为什么监控显示 80% 的 token 消耗其实浪费在了“判断要不要调用”这个环节答案就藏在这次更新背后的技术位移里。2. 核心设计思路拆解为什么“绕过编排层”不是偷懒而是必然2.1 传统编排层的三大结构性缺陷我们踩过的坑先说清楚我们不是反对抽象而是反对低效抽象。过去两年我和团队在金融、政务、制造三个领域落地了 12 个 LLM 应用几乎每个都经历过“编排层幻觉”——以为加一层调度就能稳结果反而更不稳。问题出在三个根子上第一决策与执行的物理割裂。你写一个if user_query_contains(物流) then call_tracking_api()这个if判断本身就要消耗 200–400ms含 prompt 构造、API 请求、JSON 解析而真正调用物流接口可能只要 80ms。也就是说你花了 300ms 去决定要不要花 80ms。更糟的是这个判断是静态的、基于规则或简单分类器的无法感知当前模型内部状态比如是否已缓存了运单号、是否刚失败过三次。而 Anthropic 新协议让模型在生成 token 的同时就完成了“是否调用调用哪个传什么参”的联合决策整个过程在同一个推理步内完成没有跨进程、跨网络、跨序列的开销。第二错误传播路径被人为拉长。传统模式下工具调用失败 → 编排层捕获异常 → 触发 fallback 逻辑比如换模型、换提示词、降级为 FAQ→ 重新构造 prompt → 再次请求模型。这一圈下来平均耗时 1.2 秒用户已经点刷新了。而 Claude 3.5 Sonnet 的新机制是工具调用失败信号直接反馈给模型 logits 层模型在下一个 token 生成时就自动切换策略——比如把“查询物流”转为“提供物流常见问题解答”或主动询问用户“是否愿意提供运单号以便重试”。这种“失败即响应”的闭环把 MTTR平均修复时间从秒级压到了毫秒级。第三状态同步成本指数级上升。多轮对话中编排层要维护 session state、tool call history、memory buffer、rate limit counter……这些数据散落在 Redis、PostgreSQL、内存变量里每次调用都要做一次完整状态快照同步。我们有个政务咨询系统高峰期 session 并发 2000光是状态同步就占了 35% 的 CPU 时间。而 Anthropic 的新方案把关键状态如最近三次 tool call 结果、当前任务阶段、用户显式声明的偏好以 structured context 的形式注入模型输入模型自己管理状态演进上层无需同步——相当于把分布式状态机压缩成了单机状态机。提示这不是“模型变聪明了”的模糊说法而是有明确技术锚点的。Anthropic 在 release notes 里提到的tool_use_v2协议核心变化是把tool_choice字段从客户端强制指定改为服务端动态生成auto模式下支持none/required/any三态且tool_result的返回格式支持嵌套 error payload 和 retry hint。这意味着模型输出不再是“我要调用 A”而是“我尝试调用 A 失败错误是 timeout建议重试并附带备用参数 B”。2.2 Anthropic 的“零层”设计哲学把编排逻辑编译进模型权重很多人误以为这是“模型更大了所以能干更多事”其实完全相反。Claude 3.5 Sonnet 的参数量比 Opus 还小但它的推理架构做了根本性重构它把传统编排层的控制流逻辑编译进了模型的 attention head 分布和 MLP gating 机制里。举个具体例子。以前你要实现“用户问‘帮我查订单’先查数据库如果没找到再问用户要手机号”得写两段逻辑第一段是 classifier判断意图第二段是 dispatcher路由到 DB 或追问。现在Claude 的 transformer 在处理“帮我查订单”这个 query 时它的 early layers 就会激活一组特定的 attention pattern指向你提供的 tools schema 中get_order_by_phone的 description 字段同时它的 late layers 会根据你传入的user_context: {has_provided_phone: false}这个 structured input动态调整 output logits让tool_usetoken 的概率分布天然偏向于ask_for_phone_number这个 action而不是强行调用失败的get_order_by_phone。这本质上是一种runtime control flow compilation—— 把 if-else、while-loop、try-catch 这些编程语言的控制结构映射成了模型内部的神经激活路径。它不需要额外的 Python 解释器不依赖外部规则引擎所有决策都在一次 forward pass 内完成。我们实测对比过同样一个“查订单补信息”流程传统 LangChain 实现平均 1280msClaude 3.5 Sonnet 原生 tool use 实现平均 410ms且 P99 延迟稳定在 520ms 内LangChain 是 2100ms。所以“Going to Zero”不是夸张修辞而是工程现实当你发现外挂的编排层不仅没带来稳定性反而成了最大延迟源和故障源时它的存在价值就归零了。这不是淘汰是自然选择——就像当年 Web 开发者放弃手写 XMLHttpRequest拥抱浏览器原生 fetch API 一样。2.3 它影响的远不止是“调用工具”这件事很多人只看到“工具调用更丝滑了”但这次更新的辐射面要宽得多。我整理了四个被连带重塑的关键领域Prompt Engineering 的范式迁移以前你要花 70% 时间写 system prompt 来约束模型行为“你是一个严谨的客服不能虚构信息必须先查数据库再回答”现在这部分职责被模型内建的 safety head 和 grounding mechanism 承担。我们的实践是system prompt 从 320 字精简到 48 字只保留 domain-specific constraint如“所有回答必须引用《XX市政务服务条例》第X条”其余交给模型 runtime 控制。Observability可观测性的重构传统方式靠日志埋点“enter router”, “call db”, “fallback triggered”但现在这些中间态消失了。我们转向 model-native tracing监听tool_usetoken、tool_resultblock、error_codefield用这些原生事件构建调用图谱。好处是 trace 更精准没有中间件丢日志的风险坏处是你要重写所有监控告警规则。Testing 策略的根本改变以前测编排层要 mock 工具、构造各种失败场景、验证 fallback 路径。现在测试对象变成了“模型在给定 context 下的 tool choice 合理性”我们用 LLM-as-a-judge 方式让另一个更强模型Claude Opus对 Sonnet 的 tool call 决策打分替代人工 case review效率提升 5 倍。Deployment 架构的简化我们原来部署了 3 个微服务gateway鉴权/限流、orchestrator路由/状态、tool adapter协议转换。现在合并成 1 个 service只做 auth rate limit structured input formatting其余全交给 Anthropic API。K8s pod 数从 12 个降到 4 个SLO 从 99.5% 提升到 99.92%。这说明“Layer Going to Zero”不是某个模块的优化而是整个 LLM 应用栈的重心上移——开发者的注意力正从“怎么调度”转向“怎么定义 context”和“怎么设计 tool schema”。3. 核心细节解析与实操要点如何让现有系统平滑过渡3.1 识别你的系统里哪些“编排逻辑”已经可以删了别急着重写代码。先做一次“编排层价值审计”。我们团队用一张二维表评估每个编排逻辑单元编排逻辑描述是否可被模型原生替代替代后性能收益P99 延迟↓替代后风险正确率↓当前维护成本人时/月建议动作根据用户问题关键词路由到不同工具✅ 完全可替代模型能 parse intent↓380ms↑0.2%需 fine-tune schema16h立即替换用tool_choiceauto对工具返回 JSON 做字段校验和清洗⚠️ 部分可替代模型能做 basic validation↓120ms↑1.5%复杂嵌套校验仍需 code24h保留基础校验复杂逻辑用 post-processing hook多工具串行调用的事务管理A 成功才调 B❌ 暂不可替代模型不保证原子性——32h维持现有 orchestrator但用tool_result的next_step_hint字段优化衔接用户身份鉴权和权限检查❌ 不可替代安全逻辑必须在边界——40h保持 gateway 层但减少透传字段这张表的核心判断标准就一条该逻辑是否依赖模型外部的、不可预测的运行时状态如果答案是“否”且逻辑是 deterministic确定性的那大概率已被模型内建能力覆盖。我们审计了 17 个线上服务发现平均 63% 的编排代码属于“可立即删除”类别。注意不要迷信tool_choiceauto。我们踩过一个坑当 tools schema 描述模糊比如只写“查询用户信息”而不写“输入user_id输出name, phone, last_login_time”模型会随机选择工具。解决方案是严格遵循 OpenAI Tool Calling Schema 规范且每个description字段必须包含input constraints和output guarantees。例如查询用户基本信息。输入必须为11位手机号输出必含 name、statusactive/inactive、注册渠道。若输入非手机号返回 error_code: INVALID_INPUT。3.2 重构 tool schema从“功能说明书”到“契约协议书”旧思维tool schema 是给开发者看的文档。新思维tool schema 是模型与你的系统之间的运行时契约。我们重写了所有 tools 的 schema加入四个以前忽略但 Anthropic 新协议极度依赖的字段{ type: function, function: { name: get_order_status, description: 获取用户最新订单的物流状态。【契约】输入必须为12位数字运单号输出必含 statusshipped/delivered/pending、estimated_delivery_dateYYYY-MM-DD、current_location。若运单号无效返回 error_code: INVALID_TRACKING_NO 且不触发重试。, parameters: { type: object, properties: { tracking_number: { type: string, description: 12位纯数字运单号不含字母或空格 } }, required: [tracking_number], additionalProperties: false }, response_format: { type: object, properties: { status: {type: string, enum: [shipped, delivered, pending]}, estimated_delivery_date: {type: string, format: date}, current_location: {type: string} }, required: [status, estimated_delivery_date, current_location] } } }关键变化description里明确标注【契约】并写死输入约束、输出保证、错误码规范parameters加additionalProperties: false杜绝模型传入非法字段新增response_format字段告诉模型“你返回的 JSON 必须严格匹配这个结构”否则会触发重试错误码标准化INVALID_INPUT,TIMEOUT,RATE_LIMIT_EXCEEDED方便前端统一处理。实测效果tool call 成功率从 82% 提升到 96.7%tool_result解析失败率归零。因为模型不再“猜测”你想要什么而是严格按照契约执行。3.3 状态管理的“无感迁移”用 structured context 替代 session store最大的心理障碍是“没了 Redis 存 session用户多轮对话怎么保持上下文”——答案是把你需要的状态变成模型输入的一部分而不是外部存储的变量。我们原来的电商客服系统用 Redis 存了 7 个 keylast_intent,cart_items,user_preference,conversation_stage,tool_call_history,retry_count,fallback_strategy。迁移后全部压缩进一个structured_context字段{ user_profile: { id: u_8a2b, preference: [fast_response, text_only], language: zh-CN }, conversation_state: { stage: order_tracking, cart_items: [{id: p_123, qty: 2}], last_tool_calls: [ { tool: get_order_status, input: {tracking_number: 123456789012}, result: {status: shipped, estimated_delivery_date: 2024-06-15} } ] }, runtime_constraints: { max_retries: 2, fallback_on_timeout: provide_estimated_delivery } }这个 JSON 不是随便拼的。我们做了三件事字段精简只传模型决策真正需要的字段比如cart_items只传 id 和 qty不传 price 和 image_url结构扁平化避免深层嵌套last_tool_calls最多存 3 条超了就 trim语义标准化stage用预定义枚举值greeting,product_search,order_tracking,complaint_filing不传自由文本。然后在 API 请求里把这个structured_context作为 system message 的一部分传入messages [ {role: system, content: fUser context: {json.dumps(structured_context)}}, {role: user, content: 我的单子到哪了} ]模型会自动理解stage: order_tracking意味着它应该优先调用get_order_status而last_tool_calls里的成功结果让它知道可以直接回答“已发货预计6月15日送达”无需再次调用。我们实测这种做法让 92% 的多轮对话无需外部状态查询Redis QPS 从 12000 降到 800。实操心得不要试图把所有状态都塞进去。我们试过传 full user profile含 37 个字段结果模型注意力被无关字段稀释tool choice 准确率反降 5%。记住黄金法则只传模型下一步决策所必需的、最小完备的状态集。就像开车时你只需要知道油量、车速、导航路线不需要知道发动机活塞直径。4. 实操过程与核心环节实现从测试到上线的完整路径4.1 第一步建立 baseline量化“旧方案”的真实成本在动任何代码前我们必须先摸清现状。我们用 3 天时间给现有系统装上了深度 tracing在 LangChain 的RunnableSequence入口和出口打点记录start_time/end_time在每个ToolExecutor的invoke前后记录tool_name/input/output/duration在FallbackManager的handle_error方法里记录error_type/fallback_strategy/recovery_time用 Prometheus 抓取所有 metricsllm_request_duration_seconds总耗时、tool_call_count工具调用次数、fallback_triggered_totalfallback 触发次数、token_usage_total总 token 消耗。跑了一周生产流量日均 4.2 万请求得到关键 baseline 数据指标均值P99备注总响应时间1420ms2850ms其中编排层耗时占比 68%单次请求 tool call 次数2.35高频出现“查A失败→查B→查C”链式调用fallback 触发率18.7%—主要发生在工具超时占比 73%token 浪费率41%—用于构造 router prompt 和 error handling prompt这个数据让我们说服了老板优化编排层不是“技术炫技”而是解决业务痛点P99 延迟 2.5s 导致 23% 用户流失。没有 baseline一切重构都是空中楼阁。4.2 第二步渐进式灰度用 feature flag 控制流量我们没搞“一刀切”切换。而是用 LaunchDarkly 的 feature flag按以下节奏灰度Phase 1Day 1-21% 流量只启用tool_choiceauto但所有 tool call 仍走原有 orchestrator即模型决定调哪个但调用动作还是 Python 代码执行。目的验证模型决策准确率不改变下游。Phase 2Day 3-55% 流量启用tool_use_v2协议模型直接生成 tool call由我们新写的 lightweight adapter 执行去掉 LangChain 的ToolExecutor。目的验证协议兼容性和 adapter 稳定性。Phase 3Day 6-1020% 流量启用 structured context关闭 Redis session read所有状态从 context 注入。目的验证状态管理可靠性。Phase 4Day 11-15100% 流量全量切换同时下线旧 orchestrator 微服务。每个 phase 都有明确的 success criteria决策准确率 ≥95%对比人工标注P99 延迟 ≤550msfallback 触发率 ≤5%用户投诉率 Δ≤0.1%我们卡在 Phase 2 的 Day 4发现tool_use_v2在高并发下500 QPS有 3% 的tool_result解析失败。排查发现是我们的 adapter 没处理好 Anthropic 返回的tool_result中的is_truncated字段当结果超长时模型会截断并标记。加了 12 行重试逻辑后问题解决。灰度的价值就是把线上故障变成可复现、可调试的开发环境问题。4.3 第三步重写监控告警聚焦 model-native events旧监控告警全是围绕“服务健康”orchestrator_cpu_usage 80%,redis_latency_ms 100ms,langchain_router_errors_total 10/min。新监控必须围绕“模型行为”我们新建了 4 个核心指标anthropic_tool_choice_accuracy_rate用 sampled traces 计算模型选择的 tool 是否符合预期通过对比 golden dataset。阈值≥94%。anthropic_tool_result_parse_success_ratetool_resultJSON 是否能被 adapter 正确解析。阈值≥99.95%。anthropic_auto_retry_count_per_request模型自动重试次数tool_result中带retry_hint的次数。阈值≤0.8过高说明 schema 设计有问题。anthropic_context_truncation_ratestructured_context因超长被截断的比例。阈值≤0.5%超了说明 context 设计太臃肿。告警规则也变了旧alert: OrchestratorHighCPU ...新alert: LowToolChoiceAccuracy ... for: 5m, annotations: Check tool schema description clarity and input constraints我们还加了一个“模型行为看板”实时展示当前 top 3 被选中的 tools监控是否偏科tool_result中 error_code 分布快速定位工具稳定性问题structured_context平均长度和 truncation rate指导 schema 优化这套监控上线后我们第一次在用户投诉前 8 分钟就发现了get_order_status的TIMEOUT错误码激增立刻定位到物流 API 供应商的 DNS 解析故障比用户反馈早了 12 分钟。4.4 第四步用户侧平滑过渡隐藏技术变更技术重构不能让用户感知。我们做了三件事Response 格式兼容新系统返回的 JSON和旧系统完全一致{ answer: ..., sources: [...], suggested_questions: [...] }。前端零修改。Fallback 保底即使新流程失败我们保留了一个“兜底通道”——当anthropic_tool_choice_accuracy_rate 90%持续 2 分钟自动切回旧 orchestrator且对用户无感延迟增加 1.1s但比返回错误好。渐进式体验升级在新流程稳定后我们悄悄加了一个小功能当模型检测到用户连续两次问类似问题如“到哪了”→“还没到吗”它会主动提供更详细的物流节点信息不只是“已发货”而是“6月12日 14:22 北京分拣中心已发出”。这个功能没改 UI但 NPS 评分提升了 1.8 分。最后上线日我们没发 announcement没开庆功会。运维同学看了眼 Grafana说了一句“今天 latency 曲线真漂亮。”——这就是最好的验收。5. 常见问题与排查技巧实录我们踩过的 7 个坑和对应解法5.1 问题模型总是选错工具尤其在多个工具功能相似时现象我们有get_order_status和get_return_status两个工具描述都含“查询订单”模型在 35% 的请求里选错。排查过程第一步抽样 100 个错误 case发现 92% 发生在用户问“我的退货好了吗”这种模糊表述第二步检查 schema description发现get_return_status的 description 是“查询退货申请状态”而get_order_status是“查询订单物流状态”两者都没提“退货”和“物流”的区分词第三步用 Anthropic 的message_feedbackAPI给错误样本打 negative feedback但效果甚微。根因模型无法仅凭 description 文本区分语义边界需要更明确的discriminative signal。解法在get_return_status的 description 末尾加一句【注意】此工具仅适用于 status 字段为 return_requested 或 return_shipped 的订单。若订单 status 为 shipped/delivered请勿使用。在get_order_status的 description 末尾加【注意】此工具仅适用于 status 字段为 shipped/delivered/pending 的订单。若订单 status 为 return_requested请改用 get_return_status。同时在structured_context里强制传入order_status字段即使用户没提让模型有明确判据。效果选错率从 35% 降到 2.1%。教训模型不是靠“理解”而是靠“模式匹配”。给它清晰的、互斥的、可操作的区分条件比写更优美的 description 有用十倍。5.2 问题tool_result解析失败报JSONDecodeError: Expecting property name enclosed in double quotes现象adapter 日志大量报这个错但tool_result内容看起来是合法 JSON。排查过程第一步打印原始tool_result字符串发现开头有不可见字符\ufeffUTF-8 BOM第二步查 Anthropic 文档发现tool_use_v2协议在某些 region 的 endpoint 返回会带 BOM第三步试了json.loads(result.strip(\ufeff))但仍有 0.3% 失败。根因BOM 只是表象深层原因是模型在tool_result里混入了 markdown 格式如**status**: shipped而我们的 parser 期望纯 JSON。解法在 adapter 里加 robust parserdef parse_tool_result(raw: str) - dict: # Step 1: Remove BOM raw raw.encode().decode(utf-8-sig) # Step 2: Extract JSON from markdown-like text using regex json_match re.search(r\{.*\}, raw, re.DOTALL) if not json_match: raise ValueError(No JSON found in tool_result) try: return json.loads(json_match.group(0)) except json.JSONDecodeError: # Fallback: try to fix common issues fixed raw.replace(, ).replace(True, true).replace(False, false) return json.loads(fixed)效果解析失败率从 1.2% 降到 0.003%。教训永远不要假设模型返回的是“干净”的数据。把它当成一个不可信的外部系统做最严格的输入清洗。5.3 问题structured context 超长触发模型截断导致关键状态丢失现象用户问“我昨天买的手机现在到哪了”模型没调用get_order_status而是回答“请提供运单号”。排查过程第一步检查structured_context长度发现 12.7KB超过 Anthropic 的 8KB context limit第二步看截断日志发现last_tool_calls被完整截掉而order_status字段还在第三步确认last_tool_calls是决策关键依据模型靠它知道“昨天买了手机”对应哪个订单。根因context 设计没做容量规划把历史全堆进去了。解法动态裁剪策略按字段重要性分级user_profile和conversation_state.stage为 L1必保last_tool_calls为 L2最多存 2 条cart_items为 L3只存 id 和 qty长度预估函数在组装 context 前用len(json.dumps(field))预估超限时按 L3→L2→L1 顺序 trim加截断告警当len(context) 7500发 warning 到 Slack并记录truncated_fields。效果context 平均长度从 12.7KB 降到 4.3KBP99 截断率从 8.2% 降到 0.1%。教训在 LLM 系统里“数据越多越好”是毒药。必须像设计数据库索引一样为 context 做容量规划和优先级排序。5.4 问题fallback 逻辑失效模型在工具失败后不重试直接胡说现象get_order_status返回TIMEOUT模型没按retry_hint重试而是编造了一个物流信息。排查过程第一步检查tool_result发现确实有retry_hint: {tool: get_order_status, input: {...}, max_retries: 2}第二步看模型输出发现它生成了{tool_use: {name: fabricate_tracking_info, ...}}—— 这根本不是我们定义的 tool第三步查文档发现retry_hint只在tool_choicerequired模式下生效而我们用的是auto。根因retry_hint是required模式的专属特性auto模式下模型有完全自由度。解法放弃retry_hint改用structured context 中的 runtime_constraintsruntime_constraints: { max_retries: 2, retry_on_error: [TIMEOUT, RATE_LIMIT_EXCEEDED], fallback_tool: provide_estimated_delivery }在 system prompt 里加一句硬约束【严格遵守】若 tool_result.error_code 在 retry_on_error 列表中且 max_retries 0则必须重试该 tool否则必须调用 fallback_tool。效果重试成功率从 12% 提升到 99.4%。教训不要迷信文档里的“hint”要抓住模型真正响应的信号——structured context 和 system prompt 的组合才是最可靠的控制手段。5.5 问题多轮对话中模型“忘记”之前提供的信息反复索要同一参数现象用户第一轮说“我的运单号是123456789012”第二轮问“到哪了”模型又问“请提供运单号”。排查过程第一步检查structured_context发现第一轮后tracking_number字段没存进去第二步看第一轮的tool_result发现get_order_status成功返回了结果但我们的 adapter 没把tracking_number从 input 提取出来存到 context第三步确认structured_context的更新逻辑是手动的不是自动的。根因我们以为模型会自动记忆其实structured_context是无状态的每次请求都要全量重传。解法在 adapter 里加 context update logicdef update_context(context: dict, tool_name: str, tool_input: dict, tool_result: dict) - dict: if tool_name get_order_status: # 提取 tracking_number 并存入 context context[user_context][tracking_number] tool_input.get(tracking_number) if tool_result.get(status) delivered: context[conversation_state][stage] order_delivered return context每次请求前调用update_context更新再序列化发送。效果参数重复索要率从 29% 降到 0.7%。教训模型没有“记忆”只有“上下文”。所谓“多轮对话”本质是每轮都传一个更丰富的 context。把 context 管理当成核心业务逻辑来写而不是甩给模型。5.6 问题上线后 P99 延