【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 系列模型是 OpenAI 最著名的成果之一,它们是基于 Transformer 架构的大型语言模型 (LLM)。这些模型擅长理解和生成自然语言文本,能够执行广泛的文本相关任务。
- 主要模型及其特点:
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 模型能够根据文本描述生成全新的、富有创意的图像和艺术作品。它们将自然语言处理与计算机视觉相结合。
- 主要模型及其特点:
dall-e-3
: 最新一代图像生成模型,对自然语言提示的理解能力更强,生成的图像质量更高、更符合细节要求,尤其擅长处理复杂的场景和细致的描述。它通常与 ChatGPT 集成,可以帮助用户优化提示词。dall-e-2
: 上一代图像模型,仍然非常强大。除了从文本生成图像,它还支持图像编辑 (in-painting, out-painting) 和生成图像变体。
- 应用场景:
- 艺术创作与设计
- 广告与营销素材生成
- 产品概念可视化
- 游戏与虚拟世界素材创建
- 教育与娱乐内容配图
-
1.1.3 Whisper 模型 (语音转文本)
- 概述:Whisper 是一种通用的语音识别模型,可以将音频内容转换为文本。它在多种语言、口音和嘈杂环境下都表现出卓越的准确性。
- 主要模型及其特点:
whisper-1
: 当前 API 提供的 Whisper 模型。
- 应用场景:
- 会议记录与访谈转录
- 语音助手与语音控制
- 视频字幕生成
- 语音邮件转文本
- 数据分析 (从音频中提取信息)
- 多语言语音内容的翻译 (可以转录并翻译成英文)
-
1.1.4 Embeddings 模型 (文本向量化)
- 概述:Embeddings 模型可以将文本转换为高维度的数字向量 (numerical vectors)。这些向量捕捉了文本的语义信息,使得机器可以更容易地理解文本之间的关系和相似性。
- 主要模型及其特点:
text-embedding-3-large
: 最新、性能最好的嵌入模型,支持高达 3072 维。text-embedding-3-small
: 最新一代中更小、更高效的模型,支持高达 1536 维。这两个新模型支持通过dimensions
参数缩短输出向量的维度,而不会显著损失概念表示能力。text-embedding-ada-002
: 上一代广泛使用的嵌入模型,输出 1536 维向量。性价比高。
- 应用场景:
- 语义搜索 (根据意义而非关键词查找文本)
- 文本聚类与分类
- 推荐系统
- 异常检测
- 问答系统 (查找与问题最相关的文档片段)
- 衡量文本相似度
-
1.1.5 Moderation 模型 (内容审核)
- 概述:Moderation 模型用于检测文本内容是否违反 OpenAI 的使用政策,例如是否包含仇恨言论、自残内容、色情内容、暴力内容等。
- 主要模型及其特点:
text-moderation-latest
: 指向当前最新的审核模型。text-moderation-stable
: 指向一个相对稳定,不经常更新的审核模型版本。
- 应用场景:
- 保护在线社区免受有害内容的侵害
- 确保用户生成的内容符合规范
- 过滤 AI 生成内容的潜在不当输出
-
1.1.6 Fine-tuning (模型微调)
- 概述:Fine-tuning 允许开发者使用自己的数据集来进一步训练 OpenAI 的基础模型,使其在特定任务上表现更好或学习特定的知识、风格。
- 支持微调的模型:包括
gpt-3.5-turbo
,babbage-002
,davinci-002
等 (具体列表请查阅最新 OpenAI 文档)。 - 应用场景:
- 提升特定行业或领域术语的理解能力
- 定制化模型输出的风格和语气
- 在模型原有知识库之外教授新知识 (有限度)
- 优化特定任务的性能,如特定类型的分类或摘要
-
1.1.7 Assistants API (构建AI助手)
- 概述:Assistants API 是一个更高级别的抽象,旨在帮助开发者构建复杂的 AI 助手。它简化了许多常见任务,如管理持久对话状态 (Threads)、调用模型定义的工具 (如 Code Interpreter, Retrieval, Function calling) 以及处理文件。
- 核心组件:Assistant (助手配置), Thread (对话线程), Message (消息), Run (执行任务), Tool (工具)。
- 应用场景:
- 构建具有记忆能力和上下文感知的高级聊天机器人
- 创建能够执行代码、分析数据、从文档中检索信息的 AI 代理
- 自动化复杂的工作流程,如客户支持、数据分析报告生成等
通过组合使用这些模型和功能,开发者可以构建出功能强大且多样化的 AI 应用。接下来的章节将详细介绍如何使用 Python 与这些 API 进行交互。
1.2 API 密钥管理与安全性
在使用 OpenAI API 之前,您首先需要获取一个 API 密钥。这个密钥是您访问 API 服务的凭证,因此必须妥善保管。
-
1.2.1 获取 API 密钥
- 注册 OpenAI 账户:如果您还没有 OpenAI 账户,请访问 OpenAI 官网 并注册。
- 访问 API Keys 页面:登录后,导航到您的账户设置中的 “API keys” 部分。通常可以在个人头像下拉菜单中找到 “View API keys” 或类似的选项。
- 创建新的密钥:点击 “Create new secret key” 按钮。您可以选择为密钥命名,以便区分其用途 (例如 “my-python-app-key”)。
- 复制并保存密钥:创建成功后,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) 时。以下是更安全的密钥管理方法:-
环境变量 (推荐):
- 原理:将 API 密钥存储在操作系统的环境变量中。您的应用程序在运行时从环境中读取该密钥。
- 设置方法:
- Linux/macOS: 在您的 shell 配置文件 (如
.bashrc
,.zshrc
) 中添加:
然后执行export OPENAI_API_KEY='你的真实API密钥'
source ~/.bashrc
(或对应的配置文件) 使其生效。 - Windows:
- 通过系统属性 (搜索 “环境变量”) -> “高级” -> “环境变量…” 设置。
- 或者在 PowerShell 中临时设置 (仅当前会话有效):
$Env:OPENAI_API_KEY = "你的真实API密钥"
- Linux/macOS: 在您的 shell 配置文件 (如
- Python 中读取:
中文解释: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
- Python 中读取:
中文解释: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()
就能读取到它们了。
- 安装
- 原理:将密钥存储在一个单独的、不受版本控制的配置文件中 (例如
-
密钥管理服务 (适用于生产环境和团队协作):
- 原理:使用专门的密钥管理服务,如 HashiCorp Vault, AWS Secrets Manager, Google Cloud Secret Manager, Azure Key Vault 等。这些服务提供了更高级的安全特性,如访问控制、审计日志、密钥轮换等。
- 集成方式:通常通过这些服务提供的 SDK 或客户端库在应用程序中安全地获取密钥。
- 示例概念 (AWS Secrets Manager):
中文解释 (AWS Secrets Manager 示例):# 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
此示例代码展示了如何使用boto3
(AWS SDK for Python) 从 AWS Secrets Manager 服务中检索预先存储的密钥。实际使用时需要正确配置 AWS凭证、区域以及密钥名称。这种方法将密钥管理中心化,提高了安全性,尤其适用于云环境中的生产应用。
-
-
1.2.3 API 密钥的最佳实践
- 不要在客户端代码中嵌入密钥:例如,不要在公开的 JavaScript 或移动应用代码中包含 API 密钥。应该通过后端服务代理 API 请求。
- 不要将密钥提交到版本控制系统:如前所述,使用
.gitignore
忽略包含密钥的文件。 - 为不同应用使用不同的密钥:这样可以方便地撤销某个应用的访问权限,而不会影响其他应用。也可以更好地追踪不同应用的用量。
- 定期轮换密钥:虽然 OpenAI 目前不强制密钥轮换,但在安全策略中考虑定期更换密钥是一个好习惯,尤其是在密钥可能已泄露的情况下。
- 限制密钥权限 (如果平台支持):有些 API 平台允许创建具有特定权限范围或访问特定资源的密钥。OpenAI 目前的 API 密钥通常具有对账户下所有服务的访问权限,但组织管理员可以进行一些成员和权限管理。对于项目密钥 (Project API Keys),可以实现更细致的权限控制。
- 监控 API 使用情况:定期检查 OpenAI 账户中的用量仪表盘,注意是否有异常的 API 调用,这可能表明密钥已泄露。
- 使用组织和项目功能:如果在一个团队或组织内工作,利用 OpenAI 的组织 (Organization) 和项目 (Project) 功能来管理访问和计费。项目 API 密钥可以被限定在特定项目内,提供更好的隔离和控制。
遵循这些安全实践,可以最大限度地降低 API 密钥泄露的风险及其潜在的不良后果。
1.3 理解 Token 与计费
OpenAI API 的使用并非免费 (通常新用户会有少量免费额度),其计费方式与一个核心概念紧密相关:Token。
-
1.3.1 Token 是什么?如何计算?
- 定义:在自然语言处理中,Token 是模型处理文本的基本单位。对于英文文本,一个 Token 通常可以是一个单词、一个单词的一部分 (如 “eating” 中的 “eat” 和 “ing”),或者一个标点符号。对于中文等其他语言,一个 Token 可能对应一个汉字,也可能对应一个词的一部分。
- 粗略估计:
- 对于英文:1 个 Token 大约是 4 个字符或 0.75 个单词。
- 对于中文:1 个 Token 大约是 0.5 - 0.7 个汉字 (具体取决于模型和编码)。一个汉字通常算作1到2个token。
- Token 包含的内容:输入给模型的文本 (Prompt) 和模型生成的文本 (Completion/Response) 都会被计算 Token 数量。
- 计算工具:
- OpenAI 的 Tokenizer 工具:OpenAI 提供了一个在线的 Tokenizer 工具,您可以在上面粘贴文本,查看其被特定模型(如
gpt-3.5-turbo
或gpt-4
)分解后的 Token 数量和具体的 Token 形式。 tiktoken
库 (Python):OpenAI 开源了tiktoken
库,允许开发者在代码中精确计算文本会被特定模型计为多少 Token。这对于成本预估和防止超出模型上下文长度限制非常有用。
- OpenAI 的 Tokenizer 工具:OpenAI 提供了一个在线的 Tokenizer 工具,您可以在上面粘贴文本,查看其被特定模型(如
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):
- 每个 OpenAI 模型都有一个最大的 Token 数量限制,称为“上下文窗口” (Context Window)。这个限制包括了输入 Prompt 的 Token 和模型生成响应的 Token 的总和 (对于 Chat Models,是整个对话历史 + 生成的新消息)。
- 如果您的输入+预期输出超过了这个限制,API 请求可能会失败,或者输出会被截断。
- 示例模型及其典型上下文窗口大小 (请务必查阅 OpenAI 官方文档获取最新和最准确的信息):
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 tokens
- 重要: 选择模型时,需要考虑其上下文窗口是否能容纳您的任务所需的输入和预期输出。对于需要处理长文档或长对话历史的应用,具有更大上下文窗口的模型 (如
gpt-4-turbo
,gpt-4o
) 更为合适。
-
成本:
- OpenAI API 的价格根据所使用的模型、输入 Token 的数量和输出 Token 的数量来计算。
- 通常,更强大的模型 (如
gpt-4
系列) 比能力稍弱的模型 (如gpt-3.5-turbo
) 价格更高。 - 输入 Token 和输出 Token 的价格可能不同。例如,对于某些模型,输入 Token 的价格可能比输出 Token 的价格便宜。
- 价格单位:通常以每 1,000 Tokens (1k Tokens) 或每 1,000,000 Tokens (1M Tokens) 美元计价。
- 查看最新价格: 请务必访问 OpenAI 官方定价页面 获取最新的、针对不同模型的详细价格信息。价格会随时间和模型更新而调整。
- Fine-tuning 的成本: Fine-tuning 模型会涉及训练成本 (按训练数据中的 Token 总数和训练时长/轮数计算) 和之后使用微调模型的推理成本 (通常与基础模型类似或略高)。
- DALL·E 成本: 按生成的图像数量、尺寸和质量 (如 DALL·E 3 的
hd
模式) 计费。 - Whisper 成本: 按音频时长 (例如,每分钟) 计费。
- Assistants API 成本: 涉及多个方面,包括底层模型调用 (如 GPT-4)、Code Interpreter 的使用 (按会话时长)、Retrieval (按存储和查询量)。
# 假设的费率 (美元/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
。 - 在满足性能要求的前提下,优先选择成本效益更高的模型。
- 并非所有任务都需要最强大的模型。对于简单任务 (如简单分类、格式转换),
- 优化 Prompt 长度:
- Prompt 越长,消耗的输入 Token 就越多,成本也越高。
- 尽量使 Prompt 简洁明了,只包含必要的信息。
- 移除不相关的上下文或示例。
- 使用更高效的 Prompt Engineering 技巧 (例如,few-shot learning 时提供简短但信息量大的示例)。
- 限制输出长度 (
max_tokens
):- 在 API 请求中设置
max_tokens
参数,可以限制模型生成响应的最大 Token 数量。这不仅可以防止意外生成过长的文本,也能直接控制输出 Token 的成本。 - 需要注意的是,如果
max_tokens
设置得太小,可能会导致输出不完整 (finish_reason
会是length
)。
- 在 API 请求中设置
- 使用
tiktoken
预估成本:- 在发送 API 请求前,使用
tiktoken
计算 Prompt 的 Token 数量,可以提前预估输入成本。
- 在发送 API 请求前,使用
- 监控 API
usage
字段:- OpenAI API 的响应中通常会包含一个
usage
对象,其中详细说明了该次请求消耗的prompt_tokens
(输入Token)、completion_tokens
(输出Token) 和total_tokens
(总Token)。 - 记录和分析这些数据,可以帮助您了解应用的实际 Token 消耗情况。
// API 响应中 usage 字段示例 { // ... 其他响应内容 ... "usage": { "prompt_tokens": 56, "completion_tokens": 150, "total_tokens": 206 } }
- OpenAI API 的响应中通常会包含一个
- 设置预算与告警 (OpenAI Platform):
- 在 OpenAI 平台的账户设置中,您可以设置每月的使用量上限 (硬限制或软限制) 和消费预警。当支出接近或达到设定的阈值时,您会收到通知。
- 批处理请求 (Batching):
- 对于某些 API (如 Embeddings API),可以一次性提交多个输入项进行处理,这通常比逐个发送请求更高效,并可能在某些情况下降低总体开销 (例如,减少了 HTTP 请求的次数)。
- 缓存 API 响应:
- 对于那些输入相同、预期输出也基本不变的请求 (例如,对特定文档的摘要、常见问题的回答),可以考虑缓存 API 的响应结果。
- 使用 Redis 或其他缓存系统存储结果,当再次遇到相同请求时,直接从缓存返回,避免重复调用 API,从而节省成本和提高响应速度。
- 需要注意缓存的有效期和更新策略。
- 流式响应 (
stream=True
):- 对于 Chat Completions,使用流式响应本身不直接降低 Token 成本 (因为总 Token 数不变),但它可以改善用户体验,让用户更快看到结果。在某些交互场景下,用户可能在看到部分结果后就中止请求,从而间接节省了未生成部分的 Token 成本。
- 针对特定任务使用专用模型:
- 例如,如果只需要语音转文本,直接使用 Whisper API;如果只需要文本向量化,使用 Embeddings API。这些专用模型通常比使用通用的大型语言模型 (如 GPT-4) 来模拟这些功能更具成本效益和性能优势。
- 对于 Fine-tuning:
- 仔细准备和清洗训练数据,高质量的数据比大量低质量数据更重要。
- 监控微调过程中的指标,避免不必要的训练轮次。
- 评估微调后的模型是否真的比使用 Prompt Engineering 的基础模型有显著优势,以证明其成本合理性。
- 利用更低成本的 Embedding 模型和维度裁剪:
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
: 指定请求体的格式。- 对于发送 JSON 数据的 POST 请求 (大部分 OpenAI API 都如此),其值为
application/json
。 - 示例:
Content-Type: application/json
- 对于发送 JSON 数据的 POST 请求 (大部分 OpenAI API 都如此),其值为
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/
。
不同的功能对应不同的路径:- Chat Completions:
POST /v1/chat/completions
- Embeddings:
POST /v1/embeddings
- Image Generation (DALL·E):
POST /v1/images/generations
- Audio Transcriptions (Whisper):
POST /v1/audio/transcriptions
(这是一个multipart/form-data
请求,因为需要上传文件) - Fine-tuning Jobs:
POST /v1/fine_tuning/jobs
- Files:
POST /v1/files
(用于上传文件) - Assistants:
POST /v1/assistants
- Threads:
POST /v1/threads
- Moderations:
POST /v1/moderations
完整的 API 端点列表和各端点的具体参数可以在 OpenAI API 参考文档 中找到。
- Chat Completions:
-
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
。- 其他特定于 API 的字段。
示例 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
):- 这是标准的使用方式,API 调用会阻塞当前线程,直到收到响应。
- 适用于大多数常规脚本、Web 应用的后端请求处理 (在非异步框架中) 等。
from openai import OpenAI # 导入同步客户端 # 初始化同步客户端 # 它会自动查找 OPENAI_API_KEY 环境变量 client = OpenAI() # 后续章节将展示如何使用 client 对象调用各种 API # 例如: response = client.chat.completions.create(...) print("同步OpenAI客户端已准备就绪。")
-
异步客户端 (
AsyncOpenAI
):- 基于
asyncio
和httpx
实现,允许进行非阻塞的 API 调用。 - 适用于需要高并发处理大量 API 请求的场景,例如构建高性能的异步 Web 服务 (如 FastAPI, Starlette)、批处理任务等。
- 使用异步客户端需要在
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=