先说结论:如果新旧两边都走 OpenAI 兼容协议(用 OpenAI SDK,或者直接请求 /v1/chat/completions),必改的只有三处:客户端初始化时的 base_url 和 api_key,以及请求里的 model。messages 列表、role、content 这套主体结构一般不用动。
不过,改完这三处能不能直接上线,要看代码里有没有用到下面四类东西:
- 输出长度参数:
max_tokens还是max_completion_tokens - 采样参数:
temperature、top_p、presence_penalty等 - 流式输出里的
usage统计和推理过程字段 - 工具调用(
tools/tool_choice)和结构化输出(response_format)
这四类都没用到,三处配置改完基本就能跑。用到了,就按下面的顺序逐项检查。

先确认:迁移后还在兼容协议里吗
这一点决定了改动量有多大。
- 新旧都是 OpenAI 兼容的 Chat Completions:属于本文讨论的情况,改动集中在配置和参数上。
- 代码用的是 Responses API:先确认目标端点支持这条接口路径。如果不支持,要么把调用改回 Chat Completions,要么继续用原来的服务。两套接口的请求和响应结构不同,不是改一个 URL 就能切过去的。
- 从兼容协议换成某家厂商的原生 SDK:消息格式和返回结构都可能变,相当于重写调用层。建议在业务代码和 SDK 之间加一层自己的封装,以后再换模型就只改这一层。
第一步:改三处配置,并把它们从代码里拿出来
很多项目迁移时最费事的地方,是模型名和地址散落在几十个文件里。建议借这次迁移,把三个值统一改成从环境变量读取:
import os
from openai import OpenAI
client = OpenAI(
base_url=os.environ["LLM_BASE_URL"], # 新端点地址,通常带 /v1
api_key=os.environ["LLM_API_KEY"],
)
MODEL = os.environ["LLM_MODEL"]
resp = client.chat.completions.create(
model=MODEL,
messages=[{"role": "user", "content": "你好"}],
)这样做以后,切换模型和回滚都只需要改配置,不用重新发版。Node.js、LangChain 的写法大同小异,入口参数名略有区别,可以参考 Python 调用 AI API 和 LangChain 怎么改自定义 base_url。地址要不要带 /v1、改完报 404 怎么查,在 OpenAI 兼容接口怎么用 里有说明。
如果目标是 NexAIX,端点是 https://api.nexaix.net/v1,提供的是 OpenAI 兼容接口,把 base_url 换成这个地址、填上新的 Key 即可。model 要填目标模型在平台上的准确名称,不能沿用旧平台的写法。可用模型和名称以 模型清单 为准。每个模型页都标明了供给方式:开源权重模型部署在自有算力上,闭源模型走厂商官方授权渠道。下面几步要核对的参数支持情况,也到对应模型页去看。
第二步:检查输出长度参数
这是换模型后最常见的 400 报错来源。
传统的 Chat Completions 用 max_tokens 限制输出长度。OpenAI 的 o 系列推理模型和一些较新的模型改成了要求 max_completion_tokens,传旧参数会直接报错。改名的原因是推理模型会生成一段用户看不到的思考内容(reasoning tokens),这部分同样消耗额度。
所以这里有两件事要处理:
- 按模型类型决定传哪个参数名;
- 给推理模型设上限时,要给思考过程留出余量。上限压得太紧,可能出现思考还没结束就被截断、可见输出很短甚至为空的情况。
第三步:采样参数不要写死
不同模型对采样参数的支持范围不一样。有的模型 temperature 只接受 0 到 1,有的接受 0 到 2。深度推理模型通常把 temperature、top_p、presence_penalty 固定住,或者干脆不接受这些参数。
如果旧代码在每个请求里都写死了 temperature=1.5 这类值,换到推理模型上就可能报错。比较稳妥的做法是集中构造参数,按模型决定传什么:
# 按目标模型的文档填写,不要凭模型名猜
REASONING_MODELS = {"<推理模型名>"}
def build_params(model: str, max_out: int, temperature=None) -> dict:
params = {}
if model in REASONING_MODELS:
params["max_completion_tokens"] = max_out
# 推理模型通常不接受或固定采样参数,这里直接不传
else:
params["max_tokens"] = max_out
if temperature is not None:
params["temperature"] = temperature
return params
resp = client.chat.completions.create(
model=MODEL,
messages=messages,
**build_params(MODEL, 2048, temperature=0.7),
)第四步:流式输出里的 usage 和推理字段
开启 stream=True 后,usage(Token 消耗)不一定会自动返回。如果监控、计费或限额代码依赖它,迁移后很容易出现「请求正常,统计全是空」的情况。按 OpenAI 的约定,需要显式请求:
stream = client.chat.completions.create(
model=MODEL,
messages=messages,
stream=True,
stream_options={"include_usage": True},
)
usage = None
for chunk in stream:
if chunk.choices: # 携带 usage 的最后一个 chunk,choices 可能为空
delta = chunk.choices[0].delta
if delta.content:
print(delta.content, end="")
if getattr(chunk, "usage", None):
usage = chunk.usage有两点需要注意:
- 兼容层不一定支持
stream_options。有的会忽略它,有的可能报错,要在目标端点上实际跑一次。 - 推理模型的思考过程字段、reasoning tokens 统计,不同实现的返回方式不一样。解析代码要容忍字段缺失,不要因为少了一个字段就让整条流中断。
第五步:工具调用和结构化输出
如果代码依赖 tools、tool_choice 或 response_format(JSON Mode / Structured Outputs),要先确认目标模型原生支持这些参数,不能默认「兼容接口就一定支持」。
目标模型只支持纯文本对话时,需要在业务层做降级:在 Prompt 里写明输出格式,再用正则或 JSON 解析取出结果,解析失败就重试或报错。这种方式比原生支持脆弱,在高频或格式要求严格的场景里,最好还是换一个支持工具调用的模型。
上线前怎么验证
能跑通不等于能上线。建议准备一组有代表性的真实输入,在新旧两边各跑一遍,对比以下几项:
- 报错率:重点看 400,大多是参数名或参数取值不被接受。错误码的含义可以对照 AI API 错误码怎么看。
- finish_reason:如果大量出现
length,说明长度上限设小了,推理模型尤其要注意。 - JSON 解析成功率、工具调用成功率:业务依赖结构化结果时,这两项比回答内容本身更关键。
- usage 是否正常回传:流式和非流式都要检查。
- 回答质量:为旧模型调好的 Prompt,换到新模型上效果可能不一样,关键链路要人工抽查一部分结果。
验证通过后,用前面抽出来的配置项做灰度:先让一部分流量走新模型,观察一段时间再全量切换。出了问题,把配置改回去就能回滚。如果要在新端点上跑这组回归测试,可以先注册领取测试额度,接入步骤见官网导航中的「文档」。
NexAIX-官方博客
评论(0)