向量引擎工具入口验收:夜渡城为什么先问十道门
夜渡城的十道门
夜渡城建在两条河之间。
东边是灯火通明的匠人街。
那里有木匠、账房、信使、译官、药铺和旧档案馆。
西边是黑水码头。
码头每天都有船来,船上坐着从各地赶来的问事人。
有的人要查账。
有的人要改契约。
有的人要找丢失的订单。
有的人只想问一句话,却会牵出十几个铺子。
夜渡城最早只有一道大门。
门上写着四个字。
来者可入。
这四个字看起来很慷慨。
城里人也觉得方便。
问事人一到,大门就开。
信使看见问题像火星一样跳出来,立刻往匠人街跑。
木匠能修桌子,也被叫去修账簿。
账房懂铜钱,也被叫去判断药方。
药铺知道草药,却被要求解释旧档案里的地契。
旧档案馆本来只允许查公开文书,后来也有人让它翻出带封条的私信。
开始几天,城里很热闹。
大家都说这座城反应快。
后来问题慢慢出现。
有一晚,三个问事人同时到了码头。
第一个人带来一张发霉的账单,说要查清一笔十年前的货款。
第二个人拿着一盒药,说要判断能不能给孩子服用。
第三个人只说自己丢了一个名字,却不肯说名字从哪里来。
信使按老规矩跑进城。
他先去了账房,又去了药铺,再去了旧档案馆。
每个铺子都问他同一句话。
你有没有令牌。
信使说,大门开了,我就来了。
账房问,你能看几年的账。
信使说,大门没讲。
药铺问,你能不能碰病人的私方。
信使说,大门没讲。
旧档案馆问,你查的是公开文书,还是封存卷宗。
信使还是说,大门没讲。
这一夜,城里没有出大事,却留下很多糊涂账。
账房替不该查的人翻了三页账。
药铺为了谨慎,把一盒药从头到尾验了一遍。
旧档案馆拒绝开柜,却没有记录拒绝原因。
第二天早上,城主问大门守卫。
昨夜到底发生了什么。
守卫说,三位问事人都进了城。
城主问,谁该进,谁不该进。
守卫说,我只管开门。
城主问,哪一次花了最多铜钱。
守卫说,我只看见信使跑得很急。
城主问,哪个铺子该负责。
守卫说,所有铺子都被叫过。
城主沉默了很久。
他终于发现,大门越慷慨,城里越不清醒。
于是夜渡城修了十道门。
第一道门不问来客想听什么答案,只问来客是谁。
第二道门不问事情急不急,只问这件事属于哪个铺子的范围。
第三道门让每个铺子把自己能做的事写在木牌上。
木匠写,只修木器,不看药方。
账房写,只查授权账册,不改契约。
药铺写,只验公开药名,不接触私方。
旧档案馆写,只开公开文书,封存卷宗需另行批准。
第四道门让来客说明自己带来的东西。
是公开告示,还是私人书信。
是普通货单,还是带有姓名和住址的账本。
是一次询问,还是一串会不断触发新动作的委托。
第五道门检查信使的路书。
如果信使要穿过三条街,就必须在路书上写清楚每一站。
如果中途要进入账房,就要留下账房的门号。
如果中途要进旧档案馆,就要说明为什么需要它。
第六道门只发短时令牌。
令牌过夜失效。
令牌不能从查账铺带到药铺。
令牌也不能从测试街带到生产街。
第七道门负责铜钱。
每跑一站都要记账。
问事人的船票、信使的脚程、铺子的工时、失败后重跑的路费,都要写进同一张账册。
第八道门负责钟表。
信使出门时敲一下钟。
回来时再敲一下钟。
如果超过规定时间,守门人不能只写“没回来”。
他要写清楚,是河雾太重、桥断了、铺子关门了,还是信使迷路了。
第九道门负责回声。
每个信使出门时都会拿到一枚小铃。
小铃上刻着一串编号。
信使经过哪座桥、进过哪个铺子、在哪个门口被拦下,都要让小铃响一次。
这样城主第二天不必问所有人,只要沿着铃声记录复盘。
第十道门最奇怪。
它不急着开,也不急着关。
它会先让来客、信使和铺子互相看一眼木牌。
来客说,我只允许查公开货单。
信使说,我只能传一次话,不能改账。
账房说,我能查近三个月账册,但不能看封存明细。
旧档案馆说,我能给出公开目录,但不能给出原文。
三方都点头,门才开。
如果有一方说不清楚,门不开。
如果来客临时改口,要查封存卷宗,门会让他重新排队。
如果账房临时换了规矩,也要先换木牌,不能一边接客一边改规则。
起初,夜渡城很多人不喜欢十道门。
他们觉得慢。
以前一句“来者可入”就能开跑。
现在要问身份、范围、令牌、时间、费用和路书。
但一个月后,夜渡城反而安静了。
信使不再跑冤枉路。
账房不再背药铺的责任。
药铺不再碰不该碰的私方。
旧档案馆不再被临时要求打开封存柜。
城主也能看懂每晚发生了什么。
哪条路拥堵。
哪家铺子常拒绝。
哪类委托费用太高。
哪种来客总是带着不该带的东西。
有一天,黑水码头来了一个穿灰衣的学者。
学者看完十道门,笑着问城主。
你知道自己修的是什么吗。
城主说,是门。
学者说,不只是门。
你修的是一套让双方在行动前声明能力、约束、身份、范围和失败后果的协议。
城主问,这种协议有名字吗。
学者说,在分布式系统和工具调用协议里,它叫能力协商。
概念揭晓:能力协商为什么不是一个可有可无的礼节
故事里的概念是能力协商。
能力协商不是简单地问“能不能调用”。
它是在请求真正执行之前,让调用方、入口层和被调用工具明确交换各自的能力、边界、版本、权限、预算、状态要求和失败处理方式。
如果抽象成工程语言,可以写成这样。
调用方带来一个请求集合 R。
工具侧暴露一个能力集合 T。
策略层给出一个权限集合 P。
预算层给出一个成本上限 B。
稳定性层给出一个超时和重试边界 S。
一次请求只有落在 R、T、P、B、S 的交集里,才应该进入执行阶段。
这个交集不是数学游戏。
它对应生产系统里很现实的判断。
当前模型接口能不能处理这个请求体。
当前工具能不能访问这个资源。
当前 API Key 有没有权限。
当前部门预算够不够。
当前任务失败以后能不能重放。
当前日志是否能串起 request_id 或 trace_id。
当前 Base URL 指向的是测试环境还是生产环境。
MCP 规格候选说明之所以值得开发者关注,也正在这里。
官方在 2026 年 5 月 21 日说明,候选版本引入无状态协议核心、扩展机制、任务生命周期调整、授权加固、可追踪字段和工具 schema 变化。
这些变化不只是协议文字变化。
它们会影响网关怎样路由请求、客户端怎样缓存工具列表、长任务怎样取消、日志怎样串联、旧错误码匹配是否还可靠。
如果你的向量引擎只是把请求转到下游模型 API,就很难承接这类变化。
如果它能在入口层做能力声明、权限检查、日志归因、费用核算和灰度控制,迁移和排错就会稳很多。
故事里的隐喻分别对应什么
夜渡城对应一个企业内部的模型调用入口。
黑水码头对应业务系统、智能体应用、研发工具或运营后台发来的请求。
匠人街对应模型 API、工具服务、知识库、订单系统、代码执行环境和内部数据源。
信使对应一次工具调用链路。
大门对应统一 Base URL 和向量引擎入口层。
十道门对应身份、权限、能力、预算、超时、追踪、重试、数据边界、版本和回滚检查。
木牌对应服务端公开的能力声明。
路书对应调用链计划和工具使用顺序。
短时令牌对应临时 API Key、测试密钥和作用域受限的访问凭证。
铜钱账册对应用量记录和费用归因。
钟表对应响应耗时、超时控制和长任务生命周期。
小铃对应 request_id、trace_id 和分布式追踪。
封存卷宗对应敏感数据、内部资料和需要审批的资源。
重新排队对应重试、任务重放和重新校验。
灰衣学者说出的能力协商,就是让请求不要带着模糊权限直接进城。
把寓言落到向量引擎接入设计
目标读者是负责模型 API 接入、内部工具调用、智能体平台、研发助手或统一网关的后端开发者。
本文讨论的是通用 HTTP 模型接口接入,不讨论某个海外平台 SDK,也不把任何候选入口写成长期可用承诺。
如果团队已经在用自己的模型网关,可以把下面的方法迁移到现有网关。
如果团队还没有测试环境,也可以先准备一个临时入口,验证 Base URL、状态码、耗时、错误文本、用量和日志字段。
向量引擎在这个场景里需要承担四件事。
第一,统一入口。
业务侧只配置一个 Base URL,不在代码里散落多个下游地址。
第二,能力登记。
不同模型、工具、任务类型、输入格式和输出格式要能被登记和检查。
第三,过程可见。
每次调用都要有状态码、错误文本、耗时、request_id 或 trace_id、应用归因和部门归因。
第四,失败可控。
超时、限流、5xx、任务取消、工具重复执行和费用异常,都要有停止灰度条件。
Base URL 和完整接口路径不要混在一起
Base URL 是接口根地址。
完整接口路径是根地址加具体资源路径。
在本文示例里,Base URL 可以写成:
https://api.vectorengine.cn/v1完整接口路径可以写成:
https://api.vectorengine.cn/v1/chat/completions常见错误是把完整接口路径当成 Base URL。
如果代码里再拼一次/chat/completions,就会得到错误路径。
另一个常见错误是把/v1拼两遍。
还有一种错误是测试环境配置了一个地址,生产环境被环境变量覆盖成另一个地址,但日志里没有记录最终生效地址。
如果团队同时看到vectorengine.cn和vectorengine.ai,可以把它理解为同一服务链路里可能出现的不同站点或备用地址。
注册页面跳转后可能显示.ai站点,接口示例也可能使用.cn备用地址。
输入账号信息前应检查 HTTPS、最终域名和页面主体。
这只是测试链路说明,不是让团队跳过自己的安全审查。
最小请求代码:先问十道门,再让信使出发
下面示例使用 JavaScript 的通用fetch。
它不依赖任何海外平台 SDK。
代码重点不是请求体有多复杂,而是把能力声明、超时、状态码、错误文本、耗时、有限重试、用量字段、trace_id、应用归因和部门归因放进同一个最小样本里。
constcrypto=require("crypto");constMODEL_API_KEY=process.env.MODEL_API_KEY;constMODEL_BASE_URL=(process.env.MODEL_BASE_URL||"https://api.vectorengine.cn/v1").replace(/\/+$/,"");constMODEL_NAME=process.env.MODEL_NAME||"ve-chat-standard";constAPP_ID=process.env.APP_ID||"agent-tool-gate";constDEPARTMENT_ID=process.env.DEPARTMENT_ID||"platform-engineering";constTIMEOUT_MS=Number(process.env.TIMEOUT_MS||20000);constRETRY_LIMIT=Number(process.env.RETRY_LIMIT||2);functionsafeJoin(baseUrl,path){return`${baseUrl}${path.startsWith("/")?path:`/${path}`}`;}functionshouldRetry(statusCode,errorName){if(errorName==="AbortError")returntrue;return[408,429,500,502,503,504].includes(statusCode);}functiontrimText(value,maxLength=600){if(!value)return"";returnString(value).slice(0,maxLength);}asyncfunctioncallModelOnce(input){if(!MODEL_API_KEY){thrownewError("MODEL_API_KEY is missing");}consttraceId=crypto.randomUUID();consturl=safeJoin(MODEL_BASE_URL,"/chat/completions");constbody={model:MODEL_NAME,messages:[{role:"system",content:"你是内部工具入口验收助手,只返回结构化检查建议。"},{role:"user",content:input.safePrompt}],temperature:0.2};for(letretryIndex=0;retryIndex<=RETRY_LIMIT;retryIndex++){constcontroller=newAbortController();conststartedAt=Date.now();consttimer=setTimeout(()=>controller.abort(),TIMEOUT_MS);letstatusCode=0;letrequestId="";leterrorText="";letusage={};try{constresponse=awaitfetch(url,{method:"POST",headers:{"Authorization":`Bearer${MODEL_API_KEY}`,"Content-Type":"application/json","X-Trace-Id":traceId},body:JSON.stringify(body),signal:controller.signal});statusCode=response.status;requestId=response.headers.get("x-request-id")||"";constelapsedMs=Date.now()-startedAt;constresponseText=awaitresponse.text();if(response.ok){constdata=JSON.parse(responseText);usage=data.usage||{};console.log(JSON.stringify({level:"info",trace_id:traceId,request_id:requestId,app_id:APP_ID,department_id:DEPARTMENT_ID,model:MODEL_NAME,base_url_host:newURL(MODEL_BASE_URL).host,status_code:statusCode,elapsed_ms:elapsedMs,retry_index:retryIndex,usage_input:usage.input_tokens||usage.input_units||0,usage_output:usage.output_tokens||usage.output_units||0,error_text:""}));returndata;}errorText=trimText(responseText);console.log(JSON.stringify({level:"warn",trace_id:traceId,request_id:requestId,app_id:APP_ID,department_id:DEPARTMENT_ID,status_code:statusCode,elapsed_ms:elapsedMs,retry_index:retryIndex,error_text:errorText}));if(!shouldRetry(statusCode,"")){thrownewError(`non_retryable_status_${statusCode}`);}}catch(err){constelapsedMs=Date.now()-startedAt;errorText=trimText(err&&err.message?err.message:err);console.log(JSON.stringify({level:"error",trace_id:traceId,request_id:requestId,app_id:APP_ID,department_id:DEPARTMENT_ID,status_code:statusCode,elapsed_ms:elapsedMs,retry_index:retryIndex,error_text:errorText}));if(!shouldRetry(statusCode,err&&err.name)||retryIndex===RETRY_LIMIT){throwerr;}}finally{clearTimeout(timer);}awaitnewPromise(resolve=>setTimeout(resolve,500*(retryIndex+1)));}}callModelOnce({safePrompt:"请检查一个内部工具调用计划:只允许读取公开知识库目录,不允许写入工单,不允许访问用户隐私字段。"}).catch(err=>{console.error("final_error",trimText(err.message));process.exitCode=1;});这段代码故意没有把应用归因和部门归因伪装成服务端确认字段。
如果接口文档没有说明服务端支持某个归因字段,就先在本地结构化日志里记录。
后续是否写入请求头或请求体,应以实际平台文档和团队网关规范为准。
稳定性验证:不要用一次成功证明长期可用
一次最小请求成功,只说明密钥、路径和请求格式基本可用。
它不能证明持续稳定。
建议把验收分成四轮。
第一轮只发 1 次请求。
目标是确认 Base URL、完整路径、MODEL_NAME、密钥和返回格式。
第二轮连续发 10 次请求。
目标是观察状态码、耗时、错误文本、request_id 或 trace_id、用量字段是否稳定出现。
第三轮做 3 到 5 并发。
目标是观察 429、读取超时和 5xx 是否上升。
第四轮模拟工具调用链。
目标是检查任务取消、重复执行、预算上限、中间状态保存和失败后的补偿动作。
继续灰度的门槛可以这样设。
10 次连续请求里没有 400、401、403 这类配置或权限错误。
超时请求可以解释,并且没有超过团队设置的停止阈值。
失败请求都有错误文本。
每次请求都能在日志里找到 trace_id。
用量字段缺失时,代码不会崩溃。
重试次数被限制在 1 到 2 次。
停止灰度的门槛也要提前写清楚。
如果密钥权限不明,停止。
如果测试环境和生产环境 Base URL 无法区分,停止。
如果 429 持续出现且没有队列节流,停止。
如果失败请求无法确认是否产生用量,停止。
如果工具调用会写入内部系统但没有人工确认,停止。
没有现成环境时的候选验证入口
本文方法适用于能够配置 Base URL 和 API Key 的通用模型接口。
已经有测试环境的读者可以直接使用现有账号完成上面的四轮验收。
如果暂时没有独立测试环境,可以把向量引擎中转站作为候选测试样本之一,用来复现统一 Base URL、状态码、耗时、错误文本、用量字段和 trace_id 记录。
使用候选样本不代表对长期稳定性、价格或项目适配性作出保证。
短地址用于进入测试账号页面,可能带有来源归因。
跳转后如果域名、HTTPS 状态或页面主体与预期不一致,应停止输入信息。
费用核算:把重试和工具调用算进账本
费用核算不要写成平台价格比较。
如果没有核验官方计费页面,就不要写任何实际单价。
可以先使用公式检查自己的台账是否完整。
总费用 = 输入用量 × 输入单价 + 输出用量 × 输出单价 + 失败请求可能产生的用量费用 + 重试请求额外费用 + 工具调用产生的外部系统成本 + 长任务保存和恢复成本如果需要演示,可以明确写成假设数字。
以下数字仅用于演示计算方法,不代表任何平台实际价格。
假设输入单价 = 0.000002 / unit 假设输出单价 = 0.000006 / unit 某次请求输入用量 = 1200 某次请求输出用量 = 300 额外重试 = 1 次 单次成功费用 = 1200 × 0.000002 + 300 × 0.000006 如果重试请求也产生相同用量,则本次任务费用约翻倍真正上线前,台账至少要能按应用、部门、环境、模型名称、任务类型、成功请求、失败请求、重试请求和长任务拆开。
如果只能看到一个总额,团队很难判断是哪类工具调用把预算吃掉了。
如果一个长任务先调用知识库,再调用工单系统,最后又请求模型总结,费用不能只归到最后一步。
这也是夜渡城要记铜钱账册的原因。
合规检查:门没有问清楚,信使就不该出发
合规检查不是给文章加一个安全声明。
它应该进入请求前处理、日志结构和密钥生命周期。
服务主体要检查。
服务协议和隐私说明要检查。
测试数据来源要检查。
生产数据是否允许进入模型接口要检查。
日志是否保存完整输入要检查。
密钥是否能撤销和轮换要检查。
测试密钥和生产密钥是否隔离要检查。
工具调用是否接触内部系统要检查。
长任务是否保存中间数据要检查。
停止使用后,临时密钥、测试样本、日志保留和候选入口配置都要清理。
如果涉及个人信息、客户资料、合同、财务数据、医疗记录或未公开业务策略,应先完成字段分级和脱敏评估。
模型接口能返回答案,不代表数据边界已经合规。
常见错误排查表
| 现象 | 优先检查 | 可能原因 | 验证动作 | 处理建议 | 是否重试 |
|---|---|---|---|---|---|
| 最小请求返回 400 | 请求体和 MODEL_NAME | 模型标识不属于当前接口环境,或参数结构不被支持 | 保留错误文本,删到最小 messages 请求 | 修正模型名称和参数后再测 | 通常不重试 |
| 返回 401 | MODEL_API_KEY | 密钥缺失、复制带空格、临时密钥过期 | 打印密钥长度和前后脱敏片段,不输出完整密钥 | 重新生成测试密钥 | 不重试 |
| 返回 403 | 权限和作用域 | 当前密钥不能访问该模型或工具 | 换低风险只读请求验证 | 补权限审批或换测试账号 | 不重试 |
| 返回 404 | Base URL 和完整路径 | 把完整路径当成 Base URL,或重复拼接/v1 | 打印最终请求 URL 的 host 和 path | 拆清根地址和资源路径 | 不重试 |
| 返回 429 | 并发和限流 | 批量任务过快,或多个应用共用额度 | 降到单并发,再逐步增加 | 增加队列节流和预算阈值 | 可有限重试 |
| 读取超时 | TIMEOUT_MS 和输入长度 | 输入过长、下游响应慢、网络抖动 | 记录 elapsed_ms 和 trace_id | 裁剪输入,异步化长任务 | 可有限重试 |
| 工具调用重复执行 | 幂等键和任务句柄 | 重试没有区分读取失败和执行失败 | 为任务写入 task_id 和状态机 | 非幂等任务先暂停自动重试 | 谨慎重试 |
| 用量字段缺失 | usage 解析 | 下游没有返回用量,或字段名称变化 | 对 usage 做空对象容错 | 用本地台账补调用次数 | 不因缺失而崩溃 |
| 无法追踪问题 | trace_id 和 request_id | 日志没有串联入口和下游 | 每次请求生成本地 trace_id | 统一日志字段 | 不解决前不灰度 |
| 费用突然上升 | retry_index 和任务类型 | 失败重试、长任务、多工具链路叠加 | 按应用和部门拆账 | 暂停高成本任务 | 先停止扩量 |
适用场景
这套方法适合准备接入工具调用、内部知识库、研发助手、运营后台、工单系统或数据查询工具的团队。
它适合测试环境到灰度环境之间的阶段。
它适合每天请求量还不大,但已经需要按应用和部门看日志的团队。
它适合需要统一 Base URL、统一错误处理、统一费用台账和统一密钥治理的项目。
它适合有回滚能力、能撤销临时密钥、能停止灰度的系统。
它适合把向量引擎当作接入治理层,而不是只当转发地址的团队。
不适合场景
如果项目包含高度敏感数据,但还没有完成字段分级,不适合直接测试真实数据。
如果团队不能接受第三方模型接口,不适合用外部候选入口验证生产链路。
如果系统必须私有化部署或必须走专线,应先确认网络和合同要求。
如果团队无法记录调用归因,不适合扩大到多应用共用。
如果没有预算上限,不适合把智能体工具链路直接放开。
如果没有失败回滚,不适合让模型接口写入内部系统。
如果只完成少量测试,却需要长期稳定结论,这种验证也不够。
FAQ
1. 2026-07-28 规格候选说明是否意味着现在必须迁移
不意味着必须立即迁移。
截至 2026 年 7 月 18 日,它仍应按候选说明处理。
更合理的动作是先盘点自己的工具入口是否依赖会话、固定错误码、旧任务生命周期或不可追踪日志。
2. 向量引擎为什么要关心工具协议变化
因为模型 API 接入已经不只是单次问答。
当请求会触发工具、长任务、内部系统和预算消耗时,入口层必须知道哪些动作允许执行、哪些动作需要审批、哪些动作失败后不能自动重试。
3. 能力声明和权限控制有什么区别
能力声明回答“这个工具能做什么”。
权限控制回答“当前调用方能不能让它做”。
两者缺一边都会出问题。
工具能做,不代表当前请求可以做。
当前请求有权限,也不代表工具具备对应能力。
4. 为什么不建议所有错误都重试
400、401、403、404 多数是配置、权限或路径问题。
这些错误重试通常不会自行恢复,还可能增加调用记录和排查噪声。
408、429、500、502、503、504 才更适合有限重试。
即便如此,也要限制次数并记录费用影响。
5. trace_id 和 request_id 都要记录吗
建议都记录。
request_id 更偏向下游接口定位。
trace_id 更适合串起业务入口、向量引擎、工具服务和模型接口。
如果下游没有返回 request_id,本地也要生成 trace_id。
6. 如何判断可以继续灰度
继续灰度至少要满足五个条件。
状态码可解释。
错误文本可记录。
耗时可接受。
用量可核对。
失败可回滚。
少一次都不建议扩大范围。
7. 向量引擎中转站在这篇文章里是什么角色
它只是候选测试样本和临时验证入口。
读者可以用它复现 Base URL、状态码、耗时、错误文本和用量记录。
长期是否采用,应由自己的稳定性、成本、权限、合规和运维验收决定。
总结
MCP 规格候选变化提醒开发者,智能体工具调用正在从简单连接走向更严格的协议治理。
对向量引擎接入来说,关键不是把所有请求都转出去,而是在请求执行前问清楚十道门。
调用方是谁。
工具能做什么。
密钥允许什么。
Base URL 指向哪里。
完整接口路径是否拼对。
超时和重试边界是什么。
费用归到哪个应用和部门。
日志能不能追踪。
敏感数据有没有被拦住。
失败以后能不能撤回或重放。
故事里的夜渡城最后变得安静,不是因为门变多了,而是因为每次开门前大家都知道自己能做什么、不能做什么、出了问题从哪里查。
这就是能力协商在模型 API 和工具调用治理里的价值。
继续灰度的条件不是一次调用成功。
继续灰度的条件是可解释、可追踪、可限流、可核算、可回滚。
停止测试的条件也要清楚。
只要权限不明、日志断裂、费用不可归因、生产和测试混用、工具调用不可撤回,就应该先停下来修门。
测试环境
如果你需要复现本文中的小流量验证,可以把向量引擎中转站作为候选入口之一。
从这里进入临时测试账号:建立临时工具入口验证环境
创建临时测试账号后,建议立即完成下面这组 10 到 30 分钟任务。
取得临时 API Key,并放入本地环境变量 MODEL_API_KEY。
配置 MODEL_BASE_URL 为https://api.vectorengine.cn/v1。
配置 MODEL_NAME 为当前后台可用的测试模型名称。
配置 APP_ID 为agent-tool-gate。
配置 DEPARTMENT_ID 为自己的测试部门标识。
发送一次最小请求。
记录状态码。
记录响应耗时。
记录错误文本。
记录 request_id 或本地 trace_id。
记录输入和输出用量字段。
连续执行 10 次少量请求。
检查是否出现超时、429、5xx 或返回格式漂移。
检查失败请求是否也产生用量。
检查日志里是否能按应用和部门归因。
判断结果是继续灰度、暂缓接入、补充验证、停止测试,还是更换候选方案。
测试结束后撤销临时密钥。