首页 / 文章 / Python OpenAI SDK 改 base_url

Python OpenAI SDK 改 base_url

OpenAI Python SDK 只改 base_url 和 api_key 就能指向二五云API。包含普通调用、流式读取、httpx 超时设置、重试与异常捕获,末尾按 claude-sonnet-5 算一次调用多少钱。

改两行就能跑

OpenAI Python SDK 的地址由 base_url 决定,默认指向 api.openai.com。换成站点地址后,其余调用代码不用动,网关按请求体里的 model 字段选上游。一个 Key 能调多个厂商的模型,换模型只改一个字符串,账单在站点统一结算,不用给每家分别绑卡。

python
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["YUN25_API_KEY"],
    base_url="https://ai.idcidiot.com/v1",
    timeout=60.0,
    max_retries=2,
)

base_url 写到 /v1 为止,SDK 会在后面拼 /chat/completions。写成完整路径会得到 /v1/chat/completions/chat/completions,直接 404。Key 从环境变量读,别写死在代码里;客户端对象可以全局复用一个,它自带连接池,每次请求都新建会拖慢速度。

项目里把 openai 的版本钉住。SDK 大版本之间改过默认重试和超时行为,升级后先跑一遍冒烟用例。虚拟环境用 venv 或 uv 都行,重点是别让系统 Python 里的旧版本混进来。地址和模型名放进配置或环境变量,测试环境和生产环境跑同一份代码。

一次普通调用

python
resp = client.chat.completions.create(
    model="claude-sonnet-5",
    messages=[
        {"role": "system", "content": "回答保持简短,不要复述问题。"},
        {"role": "user", "content": "解释一下 SSE 里的 retry 字段。"},
    ],
    temperature=0.3,
    max_tokens=600,
)
print(resp.choices[0].message.content)
print(resp.usage.prompt_tokens, resp.usage.completion_tokens)

返回对象和官方 SDK 一致,usage 里是输入输出 token 数,扣费按这个算。max_tokens 限制的是输出长度,不是上下文总量,别把它当上下文上限用。claude-sonnet-5 的上下文是 1,000,000 token,最大输出 128,000 token。

messages 里的 system 同样计费,而且每次请求都要重发一遍,系统提示写得越长,成本越随调用次数放大。temperature 只影响采样,不改计费;想省钱先看输入长度和输出上限,这两项才是账单的主要来源。

流式

python
stream = client.chat.completions.create(
    model="claude-sonnet-5",
    messages=[{"role": "user", "content": "写一个 20 行的 Python 日志装饰器"}],
    stream=True,
    stream_options={"include_usage": True},
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)

开了 stream_options 后,末尾一块的 choices 是空数组,usage 才带统计,所以循环里要先判断 chunk.choices,不判断会抛 IndexError。流式降低的是首字延迟,总耗时不一定更短,适合需要边生成边显示的场景。

要把全文存下来,就在循环里把 delta.content 累加到一个列表,结束后 "".join(...)。只打印不保存,程序退出时拿不到完整结果。finish_reason 在末尾一块里,值为 length 说明输出被 max_tokens 截断,不是模型写完了。

超时与重试

SDK 默认对连接错误、408、409、429 和 5xx 自动重试两次,max_retries 可以改。重试对幂等请求安全,对长输出意义不大,超时该单独调:

python
import httpx

client = OpenAI(
    api_key=os.environ["YUN25_API_KEY"],
    base_url="https://ai.idcidiot.com/v1",
    timeout=httpx.Timeout(10.0, read=300.0),
    max_retries=3,
)

第一个参数是连接超时,read 是两次数据之间的等待上限。生成长文时把 read 放大到 300 秒,不要把整体超时直接调成 10 分钟,连接阶段卡住会白等。重试次数也不是越多越好,持续 429 时加次数只会放大压力。

超时还会牵扯计费。客户端断开时上游可能已经生成完并正常扣费,重试等于再买一次同样的输出。长输出优先放大超时,别指望重试救回来。

错误处理

python
from openai import APIConnectionError, APIStatusError, APITimeoutError, RateLimitError

try:
    resp = client.chat.completions.create(
        model="claude-sonnet-5",
        messages=[{"role": "user", "content": "ping"}],
    )
except RateLimitError:
    print("限流,退避后重试")
except APITimeoutError:
    print("读超时,缩小 max_tokens 或放大 read 超时")
except APIConnectionError:
    print("连接失败,检查网络和 base_url")
except APIStatusError as e:
    print(e.status_code, e.response.text)

RateLimitError 是 APIStatusError 的子类,APITimeoutError 是 APIConnectionError 的子类,捕获顺序要从具体到宽泛,反过来写后面的分支永远进不去。e.response.text 里是网关返回的原文,诊断 400 比 str(e) 有用。

把 status_code 和返回原文记进日志,再按状态码分流:401 查 Key,404 查地址,400 查请求体字段,429 退避,5xx 重试。这套判断比在代码里到处 except Exception 稳。

成本

claude-sonnet-5 的 model_ratio 是 1.5,Claude Max 分组 0.85 倍,输入价 = 1.5 × 2 × 0.85 = $2.55/百万 token,输出按 completion_ratio 5 折算为 $12.75/百万 token。一次 5,000 输入、1,200 输出的调用约 $0.0128 + $0.0153 = $0.028,一天 1,000 次就是 $28 上下,按 22 个工作日算一个月 $600 出头。

把脚本里的模型换成 glm-5.3-flash(官渠国模 0.2 倍,$0.16/$0.56)能把这个数字压到十分之一,适合分类、抽取这类短输出任务。倍率表在 价格页,模型清单在 /model/。先跑通上面这段示例,再把 client 抽成模块级单例。

用 二五云API 跑通这些模型

OpenAI 兼容接口,一个 Key 调用 GPT、Claude、Gemini、DeepSeek、Kimi 等模型,按量计费。

免费注册查看模型价格

相关文章