先说结论背后的东西:现在很多大模型服务,都刻意长得跟 OpenAI 的接口一模一样。同样的字段名、同样的返回结构、同样的调用方式。于是各家只要把网址换掉,你原来的代码不用重写。
这在代码里就是一行:
client = OpenAI(
api_key=settings.AI_CHAT_API_KEY,
base_url="https://api.deepseek.com", # 换别家就改这里
timeout=8,
)
model="deepseek-chat" 再跟着改成对方的名字,理论上就完了。
为什么有人愿意长得跟别人一样
因为这样最省事。写客户端的人不用为每家学一套新写法,做服务的人也不用求着别人适配自己。你装一个 openai 库,能连的服务可能十来家。这个格式慢慢就成了事实上的通用插座。
对用的人来说,好处是切换成本低。今天这家贵了、慢了、或者哪天不干了,改个网址和模型名就能搬走。
坏处是,它只是"长得一样",不是"里头一样"。
卡住的三处
第一处是模型名。每家起名的规矩不一样,有的带日期,有的带尺寸,有的还分 chat 和 reasoning。填错了不会提示你"名字写错了",一般回一句 404 或者 model not found。我们第一次换的时候就卡了十分钟,最后是在对方控制台的下拉框里把名字复制过来的。
第二处是参数。temperature、max_tokens 这类大部分都认,但总有一两家对某个参数不感冒,或者范围不一样。你传 max_tokens=400,有的服务叫 max_completion_tokens,不认的那个要么静默忽略,要么直接报错。静默忽略最烦,因为你以为限制生效了。
第三处是返回内容。字段结构一致,不代表内容一致。有的会在回答前带一段思考过程,有的返回里多几个空字段,有的 content 是 None 但是把字放在别的字段里。我们那段取文本的代码是这么写的:
text = (resp.choices[0].message.content or '').strip()
return text[:1000] or None
那个 or '' 不是随手写的,是被空值坑过之后加的。
一句提醒
所以"改一行网址"这句话,对演示成立,对上线不太成立。真要换,至少把模型名、参数返回值、错误码这三样逐个对一遍,别只看请求能不能发出去。
这篇也发在 VidDown 技术博客,我们客服那块用的就是上面这套兼容写法。
还有个容易忘的:超时是跟着服务走的。原来那家平均 2 秒回,换一家偶尔 12 秒,原来设的 8 秒就会开始频繁降级。我们当时查了挺久,一直往字段名上想,后来把请求日志拉出来逐条对时间,才看见是等超时了。