【Python 与 OpenAI API 深度探索:从基础到未来】
第一章:OpenAI API 概览与核心概念
1.1 OpenAI API 是什么?能做什么?
OpenAI API (Application Programming Interface,应用程序编程接口) 是一套允许开发者通过编程方式访问和使用 OpenAI 开发的各种先进人工智能模型的服务。这些模型经过海量数据的训练,能够在多种任务上达到甚至超越人类水平。通过 API,开发者可以将这些强大的 AI 能力集成到自己的应用程序、网站或工作流程中,而无需自行承担训练和部署这些复杂模型的巨大成本和技术挑战。
OpenAI API 的核心价值在于其提供的多样化模型,每种模型都有其擅长的领域:
1.1.1 GPT (Generative Pre-trained Transformer) 模型家族
gpt-4o
(Omni): OpenAI 当前最先进的多模态模型,能够处理和生成文本、音频和图像。它在理解和生成方面达到了新的高度,速度更快,成本更低(相对于 gpt-4-turbo
)。它是处理跨文本、视觉和音频输入的复杂任务的首选。gpt-4-turbo
: gpt-4
的增强版,拥有更大的上下文窗口 (128k tokens),更新的知识库 (截至2023年4月),并且在某些任务上性能更优,成本也相对较低。支持 JSON mode 和并行函数调用。gpt-4
: 比 gpt-3.5
更强大,具有更强的推理能力、更广泛的知识和更长的上下文窗口 (通常有 8k 和 32k 版本)。适用于需要深度理解和复杂推理的任务。gpt-3.5-turbo
: gpt-3
系列的优化版本,广泛应用于聊天机器人和各种文本生成任务。性价比高,响应速度快,是许多应用的理想选择。支持多种上下文窗口大小 (如 4k, 16k)。它是 InstructGPT 系列的继承者。text-davinci-003
, text-curie-001
, text-babbage-001
, text-ada-001
: 这些是旧版的 Completions API 模型,虽然仍然可用,但 OpenAI 强烈建议新应用使用更新的 Chat Completions API 模型 (如 gpt-3.5-turbo
, gpt-4
),因为它们更强大、更灵活且通常更具成本效益。1.1.2 DALL·E 模型 (图像生成)
dall-e-3
: 最新一代图像生成模型,对自然语言提示的理解能力更强,生成的图像质量更高、更符合细节要求,尤其擅长处理复杂的场景和细致的描述。它通常与 ChatGPT 集成,可以帮助用户优化提示词。dall-e-2
: 上一代图像模型,仍然非常强大。除了从文本生成图像,它还支持图像编辑 (in-painting, out-painting) 和生成图像变体。1.1.3 Whisper 模型 (语音转文本)
whisper-1
: 当前 API 提供的 Whisper 模型。1.1.4 Embeddings 模型 (文本向量化)
text-embedding-3-large
: 最新、性能最好的嵌入模型,支持高达 3072 维。text-embedding-3-small
: 最新一代中更小、更高效的模型,支持高达 1536 维。这两个新模型支持通过 dimensions
参数缩短输出向量的维度,而不会显著损失概念表示能力。text-embedding-ada-002
: 上一代广泛使用的嵌入模型,输出 1536 维向量。性价比高。1.1.5 Moderation 模型 (内容审核)
text-moderation-latest
: 指向当前最新的审核模型。text-moderation-stable
: 指向一个相对稳定,不经常更新的审核模型版本。1.1.6 Fine-tuning (模型微调)
gpt-3.5-turbo
, babbage-002
, davinci-002
等 (具体列表请查阅最新 OpenAI 文档)。1.1.7 Assistants API (构建AI助手)
通过组合使用这些模型和功能,开发者可以构建出功能强大且多样化的 AI 应用。接下来的章节将详细介绍如何使用 Python 与这些 API 进行交互。
1.2 API 密钥管理与安全性
在使用 OpenAI API 之前,您首先需要获取一个 API 密钥。这个密钥是您访问 API 服务的凭证,因此必须妥善保管。
1.2.1 获取 API 密钥
# 这是一个注释,提醒您API密钥的重要性
# API密钥示例格式 (请勿在代码中硬编码您的真实密钥):
# sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# (上面的 x 代表随机字母和数字)
# 您的真实API密钥应该像这样:
# "sk-proj-YourUniqueCharactersAndNumbers" (对于项目密钥)
# 或者传统的 "sk-YourUniqueCharactersAndNumbers"
print("请访问 https://platform.openai.com/api-keys 获取您的API密钥。") # 打印提示信息
1.2.2 安全存储和使用密钥
将 API 密钥直接硬编码到您的 Python 脚本中是一种非常不安全的做法,尤其当代码需要共享或提交到版本控制系统 (如 Git) 时。以下是更安全的密钥管理方法:
环境变量 (推荐):
.bashrc
, .zshrc
) 中添加:export OPENAI_API_KEY='你的真实API密钥'
然后执行 source ~/.bashrc
(或对应的配置文件) 使其生效。$Env:OPENAI_API_KEY = "你的真实API密钥"
import os # 导入os模块,用于访问操作系统功能
# 从环境变量中获取OpenAI API密钥
api_key = os.getenv("OPENAI_API_KEY")
if api_key:
# print(f"成功获取到API密钥: {api_key[:5]}...{api_key[-4:]}") # 打印部分密钥用于验证,注意不要完整打印
pass # 如果获取到密钥,则执行后续操作
else:
print("错误:未在环境变量中找到 OPENAI_API_KEY。") # 如果未获取到,打印错误信息
print("请确保您已正确设置了 OPENAI_API_KEY 环境变量。")
# OpenAI Python 客户端库默认会自动查找名为 OPENAI_API_KEY 的环境变量
# 所以如果设置了环境变量,初始化客户端时通常不需要显式传递密钥
中文解释:import os
: 导入 Python 内置的 os
模块,它提供了与操作系统交互的功能。api_key = os.getenv("OPENAI_API_KEY")
: 调用 os.getenv()
函数,尝试从环境变量中读取名为 “OPENAI_API_KEY” 的变量值。如果该环境变量存在,则返回其值;否则返回 None
。if api_key:
: 判断 api_key
是否成功获取到值 (不是 None
或空字符串)。else:
: 如果未获取到密钥,则打印提示信息。配置文件 (例如 .env
文件):
.env
文件),然后使用库 (如 python-dotenv
) 来加载这些配置。python-dotenv
:pip install python-dotenv
.env
文件 (与您的 Python 脚本在同一目录或项目根目录):OPENAI_API_KEY="你的真实API密钥"
ANOTHER_CONFIG_VAR="some_value"
.env
文件添加到您的 .gitignore
文件中,以防止其被提交到 Git 仓库。.gitignore
文件中添加一行:.env
import os # 导入os模块
from dotenv import load_dotenv # 从dotenv库导入load_dotenv函数
# 加载 .env 文件中的环境变量
# 这会查找当前目录或父目录中的 .env 文件,并将其中的键值对加载到环境变量中
load_dotenv()
api_key = os.getenv("OPENAI_API_KEY") # 从已加载的环境变量中获取API密钥
if api_key:
# print(f"成功从.env文件加载API密钥: {api_key[:5]}...{api_key[-4:]}") # 验证密钥
pass
else:
print("错误:未能从 .env 文件加载 OPENAI_API_KEY。") # 错误提示
print("请确保 .env 文件存在且包含 OPENAI_API_KEY。")
中文解释:from dotenv import load_dotenv
: 从 python-dotenv
库中导入 load_dotenv
函数。load_dotenv()
: 执行此函数会查找项目中的 .env
文件,并将其中的每一行 KEY=VALUE
解析为环境变量,加载到当前的运行环境中。这样 os.getenv()
就能读取到它们了。密钥管理服务 (适用于生产环境和团队协作):
# import boto3 # 导入AWS SDK for Python
# from botocore.exceptions import ClientError # 导入boto3客户端错误
# def get_secret(secret_name, region_name="your-aws-region"):
# """从AWS Secrets Manager获取密钥"""
# session = boto3.session.Session() # 创建一个boto3会话
# client = session.client(
# service_name='secretsmanager',
# region_name=region_name
# ) # 创建Secrets Manager客户端
# try:
# get_secret_value_response = client.get_secret_value(
# SecretId=secret_name
# ) # 调用API获取密钥值
# except ClientError as e:
# print(f"获取密钥 {secret_name} 失败: {e}") # 打印错误
# raise e # 重新抛出异常
# else:
# # Secrets Manager 可以存储字符串或二进制,这里假设是字符串
# # 如果密钥是JSON字符串,可能需要进一步解析
# if 'SecretString' in get_secret_value_response:
# secret = get_secret_value_response['SecretString'] # 获取密钥字符串
# # 通常密钥会以JSON格式存储,例如 {"OPENAI_API_KEY": "your_key"}
# # import json
# # return json.loads(secret).get("OPENAI_API_KEY")
# return secret # 返回获取到的密钥(或解析后的密钥)
# else:
# # decoded_binary_secret = base64.b64decode(get_secret_value_response['SecretBinary'])
# # return decoded_binary_secret
# print(f"密钥 {secret_name} 的格式非SecretString")
# return None
# if __name__ == "__main__":
# # 假设你在AWS Secrets Manager中存储的密钥名为 "openai/api_key"
# # 并且该密钥的值就是你的OpenAI API密钥字符串或一个包含它的JSON
# try:
# # openai_api_key = get_secret("your_secret_name_in_aws", "your_aws_region")
# # if openai_api_key:
# # print(f"成功从AWS Secrets Manager获取API密钥: {openai_api_key[:5]}...")
# # else:
# # print("未能从AWS Secrets Manager获取到API密钥。")
# print("AWS Secrets Manager 示例代码已注释掉,请根据实际情况取消注释并配置。")
# except Exception as e:
# # print(f"调用AWS Secrets Manager出错: {e}")
# pass
中文解释 (AWS Secrets Manager 示例):boto3
(AWS SDK for Python) 从 AWS Secrets Manager 服务中检索预先存储的密钥。实际使用时需要正确配置 AWS凭证、区域以及密钥名称。这种方法将密钥管理中心化,提高了安全性,尤其适用于云环境中的生产应用。1.2.3 API 密钥的最佳实践
.gitignore
忽略包含密钥的文件。遵循这些安全实践,可以最大限度地降低 API 密钥泄露的风险及其潜在的不良后果。
1.3 理解 Token 与计费
OpenAI API 的使用并非免费 (通常新用户会有少量免费额度),其计费方式与一个核心概念紧密相关:Token。
1.3.1 Token 是什么?如何计算?
gpt-3.5-turbo
或 gpt-4
)分解后的 Token 数量和具体的 Token 形式。tiktoken
库 (Python):OpenAI 开源了 tiktoken
库,允许开发者在代码中精确计算文本会被特定模型计为多少 Token。这对于成本预估和防止超出模型上下文长度限制非常有用。import tiktoken # 导入tiktoken库
def count_tokens(text: str, model_name: str = "gpt-3.5-turbo") -> int:
"""
使用tiktoken计算给定文本在特定模型下的token数量。
参数:
text (str): 需要计算token的文本。
model_name (str): OpenAI模型的名称,例如 "gpt-3.5-turbo", "gpt-4", "text-embedding-ada-002"。
不同模型可能使用不同的编码方式。
返回:
int: 文本对应的token数量。
"""
try:
# 获取模型对应的编码器
# tiktoken.encoding_for_model() 会根据模型名称返回正确的编码器实例
encoding = tiktoken.encoding_for_model(model_name)
except KeyError:
# 如果模型名称未知,则尝试使用一个通用的编码器 (例如cl100k_base,gpt-4和gpt-3.5-turbo使用)
print(f"警告: 模型 '{
model_name}' 未找到特定编码,将使用 'cl100k_base'。")
encoding = tiktoken.get_encoding("cl100k_base")
# 使用编码器的encode方法将文本转换为token ID列表
token_ids = encoding.encode(text)
# token ID列表的长度即为token数量
return len(token_ids)
# 示例用法
sample_text_en = "Hello, world! This is a test sentence."
sample_text_zh = "你好,世界!这是一个测试句子。"
# 针对 gpt-3.5-turbo 模型计算token
tokens_en_gpt35 = count_tokens(sample_text_en, "gpt-3.5-turbo")
tokens_zh_gpt35 = count_tokens(sample_text_zh, "gpt-3.5-turbo")
print(f"英文文本: '{
sample_text_en}'") # 打印英文示例文本
print(f"使用 gpt-3.5-turbo 计算的Token数量: {
tokens_en_gpt35}") # 打印英文Token数
print(f"中文文本: '{
sample_text_zh}'") # 打印中文示例文本
print(f"使用 gpt-3.5-turbo 计算的Token数量: {
tokens_zh_gpt35}") # 打印中文Token数
# 针对 gpt-4 模型计算token
tokens_en_gpt4 = count_tokens(sample_text_en, "gpt-4")
tokens_zh_gpt4 = count_tokens(sample_text_zh, "gpt-4")
print(f"使用 gpt-4 计算的英文Token数量: {
tokens_en_gpt4}") # 打印英文Token数 (GPT-4)
print(f"使用 gpt-4 计算的中文Token数量: {
tokens_zh_gpt4}") # 打印中文Token数 (GPT-4)
# 演示编码和解码
encoding_gpt35 = tiktoken.encoding_for_model("gpt-3.5-turbo")
encoded_tokens_zh = encoding_gpt35.encode(sample_text_zh)
print(f"中文文本 '{
sample_text_zh}' 被编码为 Token IDs: {
encoded_tokens_zh}") # 打印编码后的Token ID
decoded_text_zh = encoding_gpt35.decode(encoded_tokens_zh)
print(f"Token IDs 解码回文本: '{
decoded_text_zh}'") # 打印解码后的文本
中文解释:
import tiktoken
: 导入 OpenAI 官方提供的 tiktoken
库。
tiktoken.encoding_for_model(model_name)
: 这个函数会根据你指定的 model_name
(例如 “gpt-3.5-turbo”) 返回一个该模型使用的特定编码器对象。不同的模型族可能使用不同的编码方案 (BPE - Byte Pair Encoding)。
encoding = tiktoken.get_encoding("cl100k_base")
: 如果 encoding_for_model
找不到特定模型,我们回退到获取一个已知的编码器,例如 “cl100k_base”,它是 gpt-4
, gpt-3.5-turbo
和 text-embedding-ada-002
等模型使用的编码。
token_ids = encoding.encode(text)
: 编码器的 encode
方法接收一个字符串,并返回一个由整数组成的列表,这些整数是文本被分解成的各个 Token 的 ID。
len(token_ids)
: 这个列表的长度就是文本所包含的 Token 数量。
decoded_text_zh = encoding_gpt35.decode(encoded_tokens_zh)
: 编码器的 decode
方法可以将 Token ID 列表转换回原始文本字符串。
1.3.2 不同模型的 Token 限制与成本
Token 限制 (Context Window):
gpt-4o
: 128,000 tokensgpt-4-turbo
(如 gpt-4-1106-preview
, gpt-4-0125-preview
): 128,000 tokensgpt-4
: 8,192 tokens (gpt-4
) 或 32,768 tokens (gpt-4-32k
)gpt-3.5-turbo
(如 gpt-3.5-turbo-0125
): 16,385 tokens (输入), 4,096 tokens (输出)gpt-3.5-turbo
(旧版如 gpt-3.5-turbo-0613
): 4,096 tokens 或 16,385 tokens (gpt-3.5-turbo-16k
)text-embedding-ada-002
: 8,191 tokensgpt-4-turbo
, gpt-4o
) 更为合适。成本:
gpt-4
系列) 比能力稍弱的模型 (如 gpt-3.5-turbo
) 价格更高。hd
模式) 计费。# 假设的费率 (美元/1k tokens) - 这些数字仅为示例,请务必查阅官方文档!
MOCK_RATES = {
"gpt-3.5-turbo": {
"input": 0.0005, "output": 0.0015}, # 假设每1k输入token $0.0005, 每1k输出token $0.0015
"gpt-4": {
"input": 0.03, "output": 0.06}, # 假设每1k输入token $0.03, 每1k输出token $0.06
"gpt-4o": {
"input": 0.005, "output": 0.015}, # 假设每1k输入token $0.005, 每1k输出token $0.015
}
def estimate_cost(input_tokens: int, output_tokens: int, model_name: str) -> float:
"""
估算API调用的成本。
参数:
input_tokens (int): 输入的token数量。
output_tokens (int): 输出的token数量。
model_name (str): 使用的模型名称。
返回:
float: 估算的成本 (美元)。
"""
if model_name not in MOCK_RATES:
print(f"警告: 模型 '{
model_name}' 的费率未知,无法估算成本。")
return 0.0
rate = MOCK_RATES[model_name] # 获取对应模型的费率
cost = (input_tokens / 1000) * rate["input"] + \
(output_tokens / 1000) * rate["output"] # 计算总成本
return cost
# 示例:估算一次调用的成本
prompt = "请帮我写一首关于春天的诗,大约100字。"
# 假设模型生成了包含150个token的响应
# (这些token数是随意假设的,实际中需要用tiktoken精确计算)
# 使用 gpt-3.5-turbo
input_tokens_gpt35 = count_tokens(prompt, "gpt-3.5-turbo") # 计算输入token
# 假设输出 token 数,实际应从 API 响应的 usage 字段获取
# 或在请求中通过 max_tokens 限制,然后根据实际输出来计算
output_tokens_gpt35 = count_tokens("春天的风,轻拂杨柳岸。桃花笑靥,燕儿呢喃。", "gpt-3.5-turbo") # 假设这是模型的输出
cost_gpt35 = estimate_cost(input_tokens_gpt35, output_tokens_gpt35, "gpt-3.5-turbo")
print(f"使用 gpt-3.5-turbo (输入: {
input_tokens_gpt35} tokens, 输出: {
output_tokens_gpt35} tokens) 的估算成本: ${
cost_gpt35:.6f}")
# 使用 gpt-4o
input_tokens_gpt4o = count_tokens(prompt, "gpt-4o")
output_tokens_gpt4o = count_tokens("春风拂绿柳,桃花逐水流。莺歌燕舞时,万象更新柔。", "gpt-4o") # 假设这是模型的输出
cost_gpt4o = estimate_cost(input_tokens_gpt4o, output_tokens_gpt4o, "gpt-4o")
print(f"使用 gpt-4o (输入: {
input_tokens_gpt4o} tokens, 输出: {
output_tokens_gpt4o} tokens) 的估算成本: ${
cost_gpt4o:.6f}")
# 注意:上述成本估算使用的是假设的费率MOCK_RATES。
# 实际成本请务必参考OpenAI官方最新的定价页面。
# API响应中通常会包含 `usage` 字段,明确告知了该次调用的确切token消耗。
中文解释:
MOCK_RATES
: 这是一个字典,模拟存储了不同模型的输入和输出 Token 的单价 (每千 Token)。再次强调,这里的费率是假设的,实际费率会变化,请务必查阅 OpenAI 官方文档。
estimate_cost(...)
: 这个函数接收输入 Token 数、输出 Token 数和模型名称,然后根据 MOCK_RATES
中的费率计算总成本。计算方法是:(输入Token数 / 1000) * 输入单价 + (输出Token数 / 1000) * 输出单价
。
这个示例清晰地展示了不同模型以及输入/输出 Token 数量对成本的直接影响。
1.3.3 成本控制与优化策略
管理和优化 OpenAI API 的使用成本对于任何规模的应用都至关重要。
gpt-3.5-turbo
可能已经足够,并且成本远低于 gpt-4
或 gpt-4o
。max_tokens
):
max_tokens
参数,可以限制模型生成响应的最大 Token 数量。这不仅可以防止意外生成过长的文本,也能直接控制输出 Token 的成本。max_tokens
设置得太小,可能会导致输出不完整 (finish_reason
会是 length
)。tiktoken
预估成本:
tiktoken
计算 Prompt 的 Token 数量,可以提前预估输入成本。usage
字段:
usage
对象,其中详细说明了该次请求消耗的 prompt_tokens
(输入Token)、completion_tokens
(输出Token) 和 total_tokens
(总Token)。// API 响应中 usage 字段示例
{
// ... 其他响应内容 ...
"usage": {
"prompt_tokens": 56,
"completion_tokens": 150,
"total_tokens": 206
}
}
stream=True
):
text-embedding-3-small
比 text-embedding-3-large
便宜。text-embedding-3
系列模型,可以使用 dimensions
参数来获取更短的嵌入向量,这不仅可以减少存储和计算成本(例如在向量数据库中),而且 OpenAI 对这些缩短维度的嵌入有更低的定价。通过综合运用这些策略,开发者可以有效地控制和优化 OpenAI API 的使用成本,确保项目的可持续性。
1.4 API 请求与响应结构
与 OpenAI API 交互本质上是进行 HTTP 网络请求。理解其通用的请求和响应结构有助于更好地使用和调试 API。
1.4.1 通用请求头 (HTTP Headers)
当您通过 HTTP 直接调用 API (例如使用 curl
或 requests
库,而不是 OpenAI 官方 Python 客户端库) 时,通常需要设置以下请求头:
Authorization
: 用于身份验证。
Bearer YOUR_OPENAI_API_KEY
Authorization: Bearer sk-xxxxxxxxxxxxxxxxxxxx
Content-Type
: 指定请求体的格式。
application/json
。Content-Type: application/json
OpenAI-Organization
(可选): 如果您的账户属于多个组织,您可以使用此头部指定请求应归属于哪个组织。值为您的组织 ID (Organization ID)。
OpenAI-Organization: org-xxxxxxxxxxxxxxxx
OpenAI-Project
(可选): 如果您使用了项目功能并希望将请求与特定项目关联,可以指定项目 ID。
OpenAI-Project: proj_xxxxxxxxxxxxxxxx
当使用 OpenAI 官方 Python 客户端库 (openai
) 时,库会自动处理这些请求头的设置,您通常只需要在初始化客户端时提供 API 密钥。
1.4.2 基本 API 端点 (Endpoints)
OpenAI API 的所有端点都以一个基础 URL 开始,通常是 https://api.openai.com/v1/
。
不同的功能对应不同的路径:
POST /v1/chat/completions
POST /v1/embeddings
POST /v1/images/generations
POST /v1/audio/transcriptions
(这是一个 multipart/form-data
请求,因为需要上传文件)POST /v1/fine_tuning/jobs
POST /v1/files
(用于上传文件)POST /v1/assistants
POST /v1/threads
POST /v1/moderations
完整的 API 端点列表和各端点的具体参数可以在 OpenAI API 参考文档 中找到。
1.4.3 理解 JSON 响应格式
OpenAI API 的绝大多数成功响应都会返回 JSON 格式的数据。JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式,易于人阅读和编写,也易于机器解析和生成。
一个典型的 JSON 响应结构可能包含以下部分:
id
: 唯一标识该次 API 响应的 ID (例如,Chat Completion ID)。object
: 响应对象的类型 (例如,chat.completion
, embedding
, list
)。created
: API 响应创建的 Unix 时间戳。model
: 本次请求使用的模型名称 (例如,gpt-3.5-turbo-0125
)。choices
(常用于 Chat Completions, Completions): 一个数组,包含了模型生成的候选项。
choice
对象通常包含:
index
: 候选项的索引。message
(Chat Completions): 包含 role
(assistant
) 和 content
(模型生成的文本) 的对象。text
(旧版 Completions): 模型生成的文本。finish_reason
: 模型停止生成的原因 (如 stop
- 自然停止, length
- 达到 max_tokens
, tool_calls
- 需要调用工具, content_filter
- 内容被过滤)。logprobs
(如果请求了): Token 的对数概率信息。data
(常用于 Embeddings, Files list, Models list): 一个数组,包含请求的数据列表 (例如,嵌入向量列表,文件对象列表)。usage
(常用于 Chat Completions, Completions, Embeddings): 包含 Token 使用信息的对象,如 prompt_tokens
, completion_tokens
, total_tokens
。示例 JSON 响应 (Chat Completion):
{
"id": "chatcmpl-xxxxxxxxxxxxxxxxxxxxxxxxx",
"object": "chat.completion",
"created": 1700000000,
"model": "gpt-3.5-turbo-0125",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "你好!我能为你做些什么?"
},
"logprobs": null,
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 12,
"total_tokens": 22
},
"system_fingerprint": "fp_xxxxxxxxxx" // 用于追踪模型变动的系统指纹
}
在 Python 中,可以使用 json
模块或 requests
库内置的 .json()
方法来轻松解析这些 JSON 响应。
1.4.4 错误处理与状态码
当 API 请求出现问题时,OpenAI API 会返回一个非 200 OK
的 HTTP 状态码,并且响应体通常也是 JSON 格式,包含一个 error
对象,其中描述了错误信息。
常见的 HTTP 状态码及其含义:
200 OK
: 请求成功。400 Bad Request
: 请求无效。通常是由于请求参数错误、格式不正确等。错误响应中会包含具体原因。
max_tokens
超出模型限制等。401 Unauthorized
: 身份验证失败。通常是 API 密钥不正确、缺失或已过期/被撤销。
error.type
: invalid_request_error
error.code
: invalid_api_key
403 Forbidden
: 请求被拒绝,通常与权限或内容策略有关(例如,触发了内容安全过滤器)。404 Not Found
: 请求的资源不存在 (例如,错误的端点,或尝试检索不存在的文件/模型)。429 Too Many Requests
: 超出速率限制 (Rate Limit)。OpenAI 对 API 的调用频率和 Token 处理速率都有限制,以保证服务的稳定性。
error.type
: tokens
或 requests
Retry-After
字段,建议等待一段时间后再重试。500 Internal Server Error
: OpenAI 服务器端发生内部错误。这种情况通常是暂时的,可以稍后重试。502 Bad Gateway
: OpenAI 服务器作为网关或代理,从上游服务器收到了无效的响应。503 Service Unavailable
: OpenAI 服务器当前不可用 (例如,过载或正在进行维护)。通常也是暂时的,可以稍后重试。示例错误 JSON 响应:
{
"error": {
"message": "You exceeded your current quota, please check your plan and billing details. For more information on usage limits, please visit: https://platform.openai.com/account/billing/limits.",
"type": "insufficient_quota", // 错误类型
"param": null, // 导致错误的参数 (如果适用)
"code": "insufficient_quota" // 错误码
}
}
或者对于速率限制:
{
"error": {
"message": "Rate limit reached for requests to gpt-4 in organization org-xxxx on tokens per min (TPM): Limit 10000, Used 9500, Requested 1000. Please try again in 3s. Visit https://platform.openai.com/account/rate-limits to learn more.",
"type": "tokens",
"param": null,
"code": "rate_limit_exceeded"
}
}
健壮的应用程序应该能够正确处理这些错误情况,例如通过记录错误、通知用户、实现重试机制 (特别是对于 429
, 500
, 503
错误) 等。OpenAI Python 客户端库会将这些错误封装为特定的异常类。
1.5 OpenAI Python 客户端库 (openai
)
虽然可以直接使用 requests
等 HTTP 库与 OpenAI API 交互,但 OpenAI 官方提供了 Python 客户端库 (openai
),它极大地简化了 API 调用过程,封装了请求构建、身份验证、错误处理等细节。强烈建议使用官方库进行开发。
1.5.1 安装与基本配置
安装:
使用 pip 安装最新版本的 openai
库:
pip install --upgrade openai
或者,如果您需要异步功能,可以安装带有 httpx
依赖的版本 (尽管最新版 openai
默认包含了 httpx
):
# pip install openai[httpx] # 旧版可能需要,新版通常已包含
基本配置 (API 密钥):
如 1.2.2 节所述,推荐将 API 密钥设置为环境变量 OPENAI_API_KEY
。openai
库会自动检测并使用这个环境变量。
如果由于某种原因不能使用环境变量,您可以在初始化客户端时显式传递密钥:
from openai import OpenAI # 导入OpenAI类
# 假设你没有设置环境变量,或者想覆盖环境变量
# explicit_api_key = "sk-your_actual_api_key_here" # 非常不推荐直接硬编码
# client = OpenAI(api_key=explicit_api_key) # 显式传递API密钥
# 如果设置了环境变量 OPENAI_API_KEY,则可以不传 api_key 参数
try:
client = OpenAI() # 客户端会自动查找 OPENAI_API_KEY 环境变量
# 您可以进行一次简单的调用来测试配置是否成功,例如列出模型
# models = client.models.list()
# print("成功初始化OpenAI客户端并连接。可用模型数量:", len(models.data))
print("OpenAI客户端初始化成功(依赖环境变量OPENAI_API_KEY)。")
except Exception as e:
print(f"初始化OpenAI客户端失败: {
e}") # 打印初始化错误
print("请检查您的OPENAI_API_KEY环境变量是否已正确设置,或者网络连接是否正常。")
# 也可以通过 openai.api_key = "YOUR_KEY" 来全局设置 (已不推荐用于 >=1.0.0 版本)
# import openai
# openai.api_key = os.getenv("OPENAI_API_KEY") # 旧版用法示例,新版请使用客户端实例
中文解释:
from openai import OpenAI
: 从 openai
库中导入核心的 OpenAI
类 (用于同步操作) 或 AsyncOpenAI
(用于异步操作,稍后介绍)。
client = OpenAI()
: 创建 OpenAI
类的一个实例。如果 OPENAI_API_KEY
环境变量已设置,它会自动被客户端使用。
client = OpenAI(api_key="sk-...")
: 如果需要显式提供 API 密钥,可以在构造函数中通过 api_key
参数传入。
1.5.2 同步与异步客户端
openai
Python 库 (版本 >= 1.0.0) 提供了同步和异步两种方式来调用 API。
同步客户端 (OpenAI
):
from openai import OpenAI # 导入同步客户端
# 初始化同步客户端
# 它会自动查找 OPENAI_API_KEY 环境变量
client = OpenAI()
# 后续章节将展示如何使用 client 对象调用各种 API
# 例如: response = client.chat.completions.create(...)
print("同步OpenAI客户端已准备就绪。")
异步客户端 (AsyncOpenAI
):
asyncio
和 httpx
实现,允许进行非阻塞的 API 调用。async def
函数中使用 await
关键字。from openai import AsyncOpenAI # 导入异步客户端
import asyncio # 导入asyncio库
# 初始化异步客户端
# 它也会自动查找 OPENAI_API_KEY 环境变量
async_client = AsyncOpenAI()
async def main_async_example(): # 定义一个异步函数
try:
# 异步调用示例:列出模型 (这是一个轻量级调用,适合测试)
models_list = await async_client.models.list() # 使用await进行异步调用
# print(f"异步获取到 {len(models_list.data)} 个模型。")
print("异步OpenAI客户端已准备就绪,并成功连接测试。")
except Exception as e:
print(f"异步客户端测试失败: {
e}")
finally:
await async_client.close() # 异步客户端使用完毕后,建议显式关闭以释放资源
# if __name__ == "__main__":
# # 要运行异步代码,需要使用 asyncio.run()
# # asyncio.run(main_async_example())
# print("异步客户端示例代码已注释,如需运行请取消注释并确保在异步上下文中执行。")
# 提示:如果在Jupyter Notebook等已经有事件循环的环境中运行,
# 可以直接 await async_client.models.list() (在async cell中)
# 或者使用 get_running_loop().create_task(main_async_example())
中文解释 (异步):
from openai import AsyncOpenAI
: 导入异步版本的客户端 AsyncOpenAI
。
async_client = AsyncOpenAI()
: 初始化异步客户端。
async def main_async_example():
: 定义一个异步函数。异步操作必须在异步函数内执行。
models_list = await async_client.models.list()
: await
关键字用于等待异步操作 async_client.models.list()
完成。在等待期间,事件循环可以去执行其他任务,从而实现非阻塞。
await async_client.close()
: 在异步操作完成后,调用 close()
方法来妥善关闭底层的 HTTP 连接和资源。这在使用 httpx.AsyncClient
时是一个好习惯。对于脚本结束时会自动清理的简单情况可能不是严格必须,但对于长时间运行的服务是推荐的。
1.5.3 初始化客户端 (OpenAI()
)
OpenAI
和 AsyncOpenAI
客户端在初始化时还可以接受其他一些有用的参数,用于自定义其行为:
from openai import OpenAI, AsyncOpenAI # 导入客户端
import httpx # 导入httpx库,用于自定义传输配置
# --- 同步客户端的更多初始化选项 ---
sync_client_custom = OpenAI(
api_key="sk-your_explicit_key_if_needed", # 显式设置API密钥 (如果不用环境变量)
organization="org-your_organization_id", # 指定组织ID (如果账户属于多个组织)
project="proj_your_project_id", # 指定项目ID (如果使用了项目功能)
timeout=httpx.Timeout(30.0, connect=5.0), # 设置超时: 总超时30秒,连接超时5秒
# 默认总超时是 10 分钟,连接超时是未指定 (通常依赖系统)
# 对于 stream=True 的请求,默认是无限等待直到流结束
max_retries=2, # 自动重试次数 (针对可重试的错误,如429, 5xx)
# 默认是 2 次
# http_client=httpx.Client(proxies="http://localhost:8080"), # 传递自定义的httpx客户端实例,例如用于设置代理
)
print("自定义配置的同步OpenAI客户端已创建 (但未使用实际密钥或ID)。")
# --- 异步客户端的更多初始化选项 (类似) ---
async_client_custom = AsyncOpenAI(
api_key="sk-your_explicit_key_if_needed",
organization="org-your_organization_id",
project="proj_your_project_id",
timeout=httpx.Timeout(60.0, read=20.0, write=10.0, connect=5.0), # 更细致的超时: 总60s, 读20s, 写10s, 连接5s
max_retries=3,
# http_client=httpx.AsyncClient(transport=httpx.ASGITransport(app=my_asgi_app)), # 高级自定义
)
print("自定义配置的异步OpenAI客户端已创建 (但未使用实际密钥或ID)。")
# 你可以通过 client.api_key, client.organization 等属性访问这些配置
# 例如:
# print(f"同步客户端的超时设置: {sync_client_custom.timeout}")
# print(f"异步客户端的重试次数: {async_client_custom.max_retries}")
# 使用完异步客户端后,记得关闭
async def close_custom_async_client()