Python 是调用大模型 API 最常用的语言:官方 SDK 维护最积极,示例最多,写脚本、做原型、跑数据处理都很顺手。这篇教程面向刚开始接触大模型 API 的开发者,从环境准备讲起,用两种 SDK 写法分别调用 Claude、GPT 和 Gemini,再依次补上多轮对话、流式输出、超时和错误重试,最后给出一份可以直接复用的项目骨架。
文中示例都使用 Zivv 作为接口地址,一把 Key 就能调用三家模型。如果你还没有 Key,可以先在 注册页 创建账号并小额充值;想先了解中转网关的原理,可以读 AI API 中转站是什么。
动手前先搞懂四个概念
写第一行代码之前,花两分钟理解这几个概念,后面看报错、算费用都会轻松很多:
- Token:模型处理文本的基本单位。英文大约一个单词对应一到两个 Token,中文一个汉字通常对应一个或多个 Token。计费、上下文长度限制都以 Token 为单位
- 输入与输出:每次请求的费用由输入 Token(你发过去的系统提示、历史消息和问题)和输出 Token(模型生成的回答)两部分组成,输出单价一般高于输入
- 上下文窗口:单次请求里输入加输出能容纳的最大 Token 数。超过窗口会直接报错,所以长文档和长对话要注意裁剪
- max_tokens:限制模型本次最多输出多少 Token。设得太小回答会被截断,设得太大不会多扣费,但会让异常情况下的输出失控
Zivv 的扣费方式是「分组倍率 × 模型官方价格」,按实际 Token 用量计算,充值汇率 ¥1 = $1。每个模型的输入、输出单价可以在模型广场查到。理解了这几点,你就能看懂下文代码里打印出来的 Token 统计,并且能大致估算一个脚本跑下来要花多少钱。
环境准备
建议使用 Python 3.9 及以上版本,并为项目单独建一个虚拟环境,避免和系统里的其他包冲突:
python3 -m venv .venv
source .venv/bin/activate # Windows 用 .venv\Scripts\activate
pip install openai anthropic python-dotenv三个包的作用分别是:
- openai:OpenAI 官方 SDK,配合 OpenAI 兼容接口可以调用所有模型
- anthropic:Anthropic 官方 SDK,走 Claude 原生协议
- python-dotenv:从 .env 文件读取环境变量,避免把 Key 写进代码
用环境变量管理 Key
把 Key 硬编码在代码里,是新手最常犯、代价也最大的错误:代码一旦推到公开仓库,Key 几分钟内就可能被扫描盗用。正确做法是在项目根目录建一个 .env 文件:
# .env 文件,记得加入 .gitignore
ZIVV_API_KEY=sk-your-key-here
OPENAI_BASE_URL=https://zivv.pro/v1
ANTHROPIC_BASE_URL=https://zivv.pro然后把 .env 加进 .gitignore:
echo ".env" >> .gitignore代码里用 load_dotenv() 加载即可。顺带一提,openai SDK 会自动读取 OPENAI_API_KEY 和 OPENAI_BASE_URL,anthropic SDK 会自动读取 ANTHROPIC_API_KEY 和 ANTHROPIC_BASE_URL。本文为了清楚起见,统一显式传参。
写法一:openai SDK 调用所有模型
Zivv 的 OpenAI 兼容接口地址是 https://zivv.pro/v1。用 openai SDK 时,换模型只需要换 model 参数:
import os
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
client = OpenAI(
api_key=os.environ["ZIVV_API_KEY"],
base_url="https://zivv.pro/v1",
)
for model in ["claude-sonnet-5", "gpt-5.5", "gemini-3.5-flash"]:
resp = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": "你是一个简洁的助手,回答不超过两句话"},
{"role": "user", "content": "什么是闭包?"},
],
)
print(model, "=>", resp.choices[0].message.content)
print("tokens:", resp.usage.prompt_tokens, resp.usage.completion_tokens)这段代码跑完,你就用同一个客户端拿到了三家模型的回答,以及每次请求的输入、输出 Token 数。模型 id 会随版本更新,以 模型广场 或接口 GET /v1/models 的返回为准。
这种写法最大的优势是统一:一套代码、一种响应结构,切换模型零成本,特别适合需要做模型对比或按任务分流的项目,分流思路可以参考 多模型路由策略。已有项目从官方 OpenAI 迁过来的改动清单见 OpenAI 兼容迁移指南。
写法二:anthropic SDK 调用 Claude
如果项目以 Claude 为主,并且要用扩展思考、工具调用等原生能力,推荐直接用 anthropic SDK:
import os
from dotenv import load_dotenv
import anthropic
load_dotenv()
client = anthropic.Anthropic(
api_key=os.environ["ZIVV_API_KEY"],
base_url="https://zivv.pro", # 只写域名,SDK 会自动拼 /v1/messages
)
message = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
system="你是一个简洁的助手,回答不超过两句话",
messages=[{"role": "user", "content": "什么是闭包?"}],
)
print(message.content[0].text)
print("tokens:", message.usage.input_tokens, message.usage.output_tokens)和 openai 写法相比,有三个关键差异需要记住:
- base_url 不带 /v1:写成 https://zivv.pro/v1 会导致请求路径重复,返回 404
- max_tokens 必填:原生协议要求显式指定最大输出长度
- system 是顶层参数:不放进 messages 列表里
两种写法怎么选
| 对比项 | openai SDK | anthropic SDK |
|---|---|---|
| Base URL | https://zivv.pro/v1 | https://zivv.pro |
| 能调用的模型 | Claude、GPT、Gemini 全部 | Claude 系列 |
| 系统提示 | messages 里 role 为 system | 顶层 system 参数 |
| max_tokens | 可选 | 必填 |
| 返回文本 | resp.choices[0].message.content | message.content[0].text |
| Token 统计 | usage.prompt_tokens / completion_tokens | usage.input_tokens / output_tokens |
| 适合场景 | 多模型混用、已有 OpenAI 代码 | 深度使用 Claude 原生功能 |
两者并不冲突:同一个项目里,可以用 openai SDK 处理通用任务,用 anthropic SDK 处理需要原生能力的 Claude 任务,共用同一把 Zivv Key 和同一份余额。
Gemini 也有原生协议入口 https://zivv.pro/v1beta,适合已经在用 Google 官方 SDK 的项目,具体配置方式以 SDK 文档和 Zivv 端点说明 为准。入门阶段直接用 openai SDK 调 Gemini 最省事。
多轮对话
大模型 API 本身是无状态的,所谓「记住上下文」,是你每次请求都把历史消息一起发过去:
history = [{"role": "system", "content": "你是 Python 老师"}]
def chat(user_input):
history.append({"role": "user", "content": user_input})
resp = client.chat.completions.create(model="claude-sonnet-5", messages=history)
answer = resp.choices[0].message.content
history.append({"role": "assistant", "content": answer})
return answer
print(chat("列表和元组有什么区别?"))
print(chat("那我该什么时候用元组?"))要注意,历史越长,每次请求的输入 Token 越多,费用也随之增长。对话很长时,可以只保留最近若干轮,或者让模型把早期对话总结成一段摘要再继续。
流式输出
聊天类应用几乎都需要流式输出:模型边生成边返回,用户不用干等完整结果。openai SDK 加上 stream=True 即可:
stream = client.chat.completions.create(
model="gpt-5.5",
messages=[{"role": "user", "content": "写一首关于秋天的短诗"}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
print()if chunk.choices 这个判断不能省:部分片段(比如开启用量统计时的最后一个片段)可能不带 choices,直接取下标会报错。
anthropic SDK 提供了更方便的上下文管理器写法:
with client.messages.stream(
model="claude-sonnet-5",
max_tokens=1024,
messages=[{"role": "user", "content": "写一首关于秋天的短诗"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
final = stream.get_final_message()
print()
print("output tokens:", final.usage.output_tokens)超时与错误重试
线上代码一定会遇到网络抖动、限流和上游过载。两个 SDK 都内置了重试:默认对连接错误、429 和 5xx 自动重试 2 次,并带退避等待。你可以按需调整:
client = OpenAI(
api_key=os.environ["ZIVV_API_KEY"],
base_url="https://zivv.pro/v1",
timeout=120, # 单次请求超时秒数
max_retries=3, # 自动重试次数
)业务上还需要区分哪些错误值得重试、哪些应该立刻报警:
import openai
try:
resp = client.chat.completions.create(
model="claude-sonnet-5",
messages=[{"role": "user", "content": "你好"}],
)
except openai.AuthenticationError:
print("401:Key 无效,检查环境变量是否加载")
except openai.NotFoundError:
print("404:模型 id 或 base_url 写错")
except openai.RateLimitError:
print("429:请求过快,降低并发或稍后重试")
except openai.APIConnectionError:
print("网络连接失败,检查代理和网络")
except openai.APIStatusError as e:
print("其他状态码:", e.status_code, e.message)anthropic SDK 的异常类名基本一致(anthropic.RateLimitError、anthropic.APIStatusError 等),把 openai 换成 anthropic 即可。常见状态码的含义和处理:
| 状态码 | 含义 | 是否重试 | 处理方式 |
|---|---|---|---|
| 401 | Key 无效 | 否 | 检查 Key 和请求头,见 401 排查 |
| 402 | 余额不足 | 否 | 充值,见 402 排查 |
| 404 | 模型或路径不存在 | 否 | 核对模型 id 和 base_url,见 404 排查 |
| 429 | 请求过多 | 是 | 指数退避,见 429 排查 |
| 500/502/503 | 服务端临时错误 | 是 | 短暂等待后重试 |
| 529 | 上游过载 | 是 | 拉长退避间隔,见 529 排查 |
原则是:客户端错误(4xx 中除了 429)重试没有意义,只会浪费时间;服务端和限流错误才需要重试,且重试次数控制在 2 到 3 次,避免放大故障。
并发调用
批量处理数据时,串行调用太慢,可以用异步客户端 AsyncOpenAI 配合 asyncio 并发:
import asyncio
from openai import AsyncOpenAI
aclient = AsyncOpenAI(api_key=os.environ["ZIVV_API_KEY"], base_url="https://zivv.pro/v1")
sem = asyncio.Semaphore(5) # 最多 5 个并发
async def summarize(text):
async with sem:
resp = await aclient.chat.completions.create(
model="gemini-3.5-flash",
messages=[{"role": "user", "content": "一句话总结:" + text}],
)
return resp.choices[0].message.content
async def main(texts):
return await asyncio.gather(*(summarize(t) for t in texts))
print(asyncio.run(main(["第一段文本", "第二段文本", "第三段文本"])))用信号量限制并发数,是避免触发 429 的简单有效办法。批量任务优先选速度快、单价低的模型,成本估算方法见 Token 成本计算指南。
新手最常踩的三个坑
除了上面表格里的状态码,入门阶段还有几类问题出现频率很高:
- 环境变量没加载:在 IDE 里运行和在终端里运行,工作目录可能不同,导致找不到 .env。可以给
load_dotenv()传入 .env 的绝对路径,或者打印一下 Key 的前几位确认已读取(不要打印完整 Key) - 代理干扰:如果系统设置了 HTTPS_PROXY 等全局代理变量,SDK 会自动走代理,代理不稳定时表现为超时或连接被重置。Zivv 在国内可直接访问,排查时先去掉代理测试
- 把 SDK 版本当成问题根源:大部分报错与 SDK 版本无关,而是参数或地址写错。升级 SDK 前,先用 curl 直接请求一次接口,确认 Key 和模型 id 正常,再回头查代码
推荐的项目骨架
把上面的内容组织起来,一个小项目可以这样分层:
- config.py:加载 .env,集中管理 Key、Base URL 和默认模型名
- llm.py:封装客户端初始化、超时、重试和统一的调用函数
- 业务代码:只调用 llm.py 暴露的函数,不直接接触 SDK
这样做的好处是:换模型、换供应商、调整重试策略,都只改一个文件。团队开发时,再给每位成员和每个环境分配独立 Key,并在 团队模式 里设置预算,用量可以按成员和 Key 分别统计。
常见问题 FAQ
Q:Python 调用 Claude API 用 openai 还是 anthropic 库? A:只调用 Claude 并需要扩展思考、工具调用等原生能力,用 anthropic;需要同时调用 GPT、Gemini 或已有 OpenAI 代码,用 openai 配合兼容接口。两者可以共用一把 Zivv Key。
Q:报错 404 Not Found 是什么原因? A:最常见的是 base_url 写错:openai SDK 要带 /v1,anthropic SDK 不带 /v1。其次是模型 id 拼错,以模型广场显示为准。
Q:openai 库可以调用 Gemini 吗? A:可以。把 base_url 设为 https://zivv.pro/v1,model 填 gemini-3.5-flash 等 Gemini 模型 id 即可,返回结构与调用 GPT 完全一致。
Q:流式输出时报 list index out of range 怎么办? A:部分流式片段不含 choices,取值前加 if chunk.choices 判断即可。
Q:可以用这些 SDK 生成 Embedding 向量吗? A:Zivv 目前不提供 Embedding 接口,向量化请使用其他服务,生成回答的部分再交给 Claude、GPT 或 Gemini。
Q:API Key 放在哪里最安全? A:本地开发用 .env 文件并加入 .gitignore,服务器上用环境变量或密钥管理服务。Zivv 只记录 Token 用量用于计费,不存储请求和响应正文。
下一步
- 跑通本文写法一的三模型示例,确认 Key 和网络正常
- 选定项目的主力写法,按骨架封装 llm.py
- 加上流式输出和错误处理,再接入真实业务
- 在控制台用量页核对 Token 消耗,调整模型选择
更多 SDK 参数和版本说明见 OpenAI SDK 接入文档。如果你还想在编辑器或终端里直接用这些模型,可以继续看 Cursor 自定义 API 配置 和 国内使用 Claude API 指南。