模型名能拼对、Key 也有,但真接进项目就卡壳:流式和非流式返回结构不一样,长思考字段不知道从哪取,一上量就超时或被限流,报错了也不知道是鉴权还是参数问题。这篇不讲协议兼容层,只给工程侧的做法:怎么发请求、怎么解返回、超时与并发怎么设、失败怎么降级、日志该记什么,以及一套从外到内的排查顺序。
模型名能拼对、Key 也有,但真接进项目就开始卡:流式和非流式返回结构不一样,长思考字段不知道从哪取,一上量就超时或被限流,报错了也不知道是鉴权问题还是参数问题。这篇只讲工程侧的做法:请求怎么发、返回怎么解、超时并发怎么设、失败怎么降级、日志记什么,最后给一套从外到内的排查顺序。
有一点先说清楚:不同接入方式(官方直连、自家网关、第三方中转)在字段命名和错误包装上会有差异,所以下面给的字段名和路径都要以你当前那条链路为准。凡是涉及模型规格、参数取值、计费口径这类会变的东西,别抄博客,直接到站内核对当时的数字。
发请求前先把这几个字段定死
先确认你走的是哪套协议。多数兼容层按 OpenAI 风格收发,但确实有人把它转成 Anthropic 风格,两者 messages 的结构、系统提示放的位置都不一样。抄错了不是报错就是静默丢字段,你会以为是模型不行。判断方法很简单:拿一个最小请求打过去,看返回体里是 choices 还是 content 数组,一眼就能分清。
- 模型名和参数写进配置,不要散在代码里:模型标识、温度、最大输出长度、是否流式各放一个常量或环境变量,换版本时只改一处
- 长思考类模型一般有控制思考量的开关或字段,具体字段名和取值去官方价格与规格页确认,不传也能跑,但输出长度和耗时差别很大
- 请求里带一个业务侧自建的追踪标识,比如订单号或任务号,后面查日志、对账、复现问题能一条线串起来
- 把 base_url 也做成配置项,别硬编码在文件顶部;网关换域名时你会感谢自己做了这一步
返回结果怎么解:流式和非流式两套逻辑
非流式的处理顺序是:先判 HTTP 状态码,再解 body,最后才取正文。别拿到 200 就直接抓内容——有些网关会把错误信息包在 200 里。正常结构里正文、结束原因、用量是三块,用了多少 token 从用量字段拿,不要自己按字符数估算,估出来的数对不上账。
流式要按行读、按空行切事件、跳过心跳或注释行。每个数据块里可能只带一小段增量文本,正文得自己拼;结束标记出现后再去取用量,而且用量有时只在最后一块里给。长思考模型的返回里可能同时有思考段和正文段,两者字段名不同,业务代码只取正文,思考段单独留存或直接丢掉,别混在一起展示给用户,那样体验很怪。
| 场景 | 先判什么 | 常见坑 |
|---|---|---|
| 非流式 | HTTP 状态码 | 200 里其实包了错误对象 |
| 流式 | 结束标记 | 用量只在最后一块,漏取就统计不到 |
| 工具调用 | 参数字符串能否解析 | 参数是 JSON 字符串,不解析直接用会炸 |
| 长思考 | 思考段与正文段的字段名 | 两段混拼,用户看到一堆推理过程 |
超时、并发、降级:上量前必须定的三件事
超时要分三档设:建连、读取、整体各一个值。长思考模型的读取超时要比普通对话留得宽,而整体超时必须大于读取超时,否则等于没设。想先量出自己这条链路的延迟基线,可以去 /tools/speed-test 跑一遍,拿到的是你当前环境的真实数字,比拍脑袋定值靠谱。
- 压并发别按机器能开多少线程来设,按服务端限流和你的成本预算来。用队列加固定并发数,跑一轮看 429 出现频率再调,出现频繁就往下压。
- 加重试失败请求走指数退避重试,重试次数控制在个位数,仍失败就进失败队列,不要无限循环。重试要只对可重试的错误做,参数错误重试一百次也不会成功。
- 做降级降级分两层:换更轻的档位或备用路由继续跑;或直接转异步任务,先把请求收下再慢慢处理。关键是降级策略要可配置、可观测,别写死在 if 里。
- 预发压测本地跑通不等于线上跑得通。在预发用接近真实的请求量压一遍,看首字延迟和端到端延迟的分布,而不是只看平均值。
日志记什么,出问题按什么顺序查
每个请求至少记这几项:时间、业务标识、模型标识、是否流式、输入输出 token 数、首字耗时、总耗时、HTTP 状态码、错误码与错误摘要。输出内容按需脱敏,别把整段对话落盘。日志字段固定下来,后面做统计和告警才不用改代码。
- 一看鉴权:401 或 403 走鉴权排查路径,先查 Key 是否过期、是否带对了前缀、网关有没有做白名单
- 二看参数与模型名:400 多为字段类型不对或模型标识不合法,拿最小请求复现最快
- 三看限流与超时:429 和超时要分开处理,前者要退避,后者要调档
- 四看返回解析:状态码正常但取不到正文,基本是字段路径写错了
遇到「偶尔失败、重试就好」的情况,别只加 retry 就完事。把失败请求的完整响应体存下来,看是不是返回了另一种结构,比如错误信息被包装在正常结构里。另外把每次调用和返回结构的变化记进变更记录,上游接口调整时你能第一时间对上,而不是等用户报障才回头翻日志。
接完之后,用一个小评测兜住回归
准备一组固定用例就够了,覆盖:正常短问答、长思考、需要工具调用、超长输入截断、故意传错参数。不必追求数量,追求的是每次改动都能跑一遍、结果能对比。用例和期望结果都放版本库里,别放在某个人本地。
| 检查项 | 怎么算过 | 不适用的情况 |
|---|---|---|
| 能稳定拿到正文 | 连续多次调用都取到非空正文 | 上游大规模故障时不算数 |
| 结构化字段能解出 | 用量、结束原因能正常解析 | 上游改字段名需要先对齐文档 |
| 耗时与用量无突变 | 与基线比没有数量级漂移 | 首次接入没有基线,先跑一轮建基线 |
要认清这套小评测的边界:它只能兜住「接口层没坏」,也就是字段还在、正文能取、耗时没爆。回答质量是变好还是变差,得靠人工抽检或更完整的人评流程,别拿它当质量结论。每次换模型版本、改提示词、改超时并发参数,都跑一遍这组用例,三项都过就算这次改动在接口层是安全的。
下一步拿你自己那条链路的最小请求打一遍,把返回的字段名和站内页面上的当前规格对一遍,再顺手跑一次延迟基线,后面调超时和并发就有据可依了。→常见问题
返回里既有思考内容又有正文,我该取哪个字段?
先看响应结构里两段是不是分开的字段,业务展示只取正文那一段,思考段可以单独记日志或直接忽略。不同接入口径字段名可能不一样,别硬编码猜名字,先把一次完整响应打印出来看清楚再写解析逻辑。
请求老是超时,是不是把超时时间调大就行?
不一定。先分清是首字慢还是整体慢:首字慢多半是排队或模型本身耗时,调大读取超时有用;整体慢可能是输出太长,要压最大输出长度或改成分批。超时时间调到很大只会把连接一直挂住,拖垮并发池。
同一个 Key 偶尔报限流,重试就能过,算正常吗?
算常见但别放着不管。先看是不是瞬时并发超过了自己设的上限,把并发数调低或改成排队;再看是不是没做退避,失败就立刻重试反而加剧限流。如果加退避后还频繁出现,就该考虑拆 Key 或换路由了。