深入了解 OpenAI API

#2025/07/30 #大模型开发

目录

基本概念

  • 提示词就是发送给模型的输入文本,用于指示模型执行特定任务。
  • 词元是单词或单词的一部分,100 个词元约等于 75 个英文单词。调用 OpenAI 模型的请求是基于词元数量收费的

{%}|472

入门:OpenAI Python 库

OpenAI 服务访问与 API 密钥

在 API keys 页面中,单击 Create new secret key(创建新密钥),写到环境变量中

# 打开~/.zshrc
zed ~/.zshrc
# 为当前会话设置环境变量 OPENAI_API_KEY
export OPENAI_API_KEY=sk-(...)
# 确认环境变量已设置
echo $OPENAI_API_KEY

请注意,切勿将这些命令行代码推送到公共代码库中。

Hello World

python 环境准备

# 安装 pyenv
brew install pyenv

# 查看可安装的版本
pyenv install --list

# 安装特定版本
pyenv install 3.11.5

# 设置全局默认版本
pyenv global 3.11.5

# 设置项目本地版本
pyenv local 3.11.5

使用以下命令通过 pip 安装 Python 库:

pip3 install openai

# 本地需要开启翻墙代理,所以需要安装
pip3 install "httpx[socks]"

接下来,在 Python 中访问 OpenAI API:

from openai import OpenAI
client = OpenAI()

# 使用 gpt-3.5-turbo 模型调用 OpenAI 的 chat.completions 端点
response = client.chat.completions.create(
    model="gpt-3.5-turbo",
    messages=[
        {"role": "user", "content": "Hello World!"}
    ]
)

# 输出响应内容
print(response.choices[0].message.content)

现在你的脚本可以正常运行了,但是遇到了 API 配额限制

 Error code: 429 - {'error': {'message': 'You exceeded your current quota, please check your plan and billing details. For more information on this error, read the docs: https://platform.openai.com/docs/guides/error-codes/api-errors.', 'type': 'insufficient_quota', 'param': None, 'code': 'insufficient_quota'}}

你将看到以下输出:

所以,需要找到合适的 ak

Hello there! How may I assist you today?

秘钥本地管理

前面的代码片段没有明确提供 OpenAI API 密钥。这是因为 OpenAI 库会自动查找名为 OPENAI_API_KEY 的环境变量。

你也可以手动在代码中设置 API 密钥,如下所示:

# 加载 API 密钥
openai.api_key = os.getenv("OPENAI_API_KEY")

我们建议遵循常见的环境变量约定,将密钥存储在.env 文件中,并确保该文件被列入.gitignore,从而避免其被提交到版本控制系统中。

在 Python 中,你可以使用 load_dotenv 函数加载.env 文件并导入 OpenAI 库:

from dotenv import load_dotenv
load_dotenv()
from openai import OpenAI

加载.env 文件后,不要忘记 OpenAI 库的导入语句,否则 OpenAI 设置将无法正确应用。

 需要注意的是,dotenv 包不是标准 Python 库的一部分,因此你需要使用 pip install python-dotenv 命令安装。

使用聊天补全模型

from openai import OpenAI
client = OpenAI()

# 对于 GPT-3.5 Turbo,使用的端点是 chat.completions
client.chat.completions.create(
    # 对于 GPT-3.5 Turbo,模型名称是"gpt-3.5-turbo"
    model="gpt-3.5-turbo",
    # 对话以消息列表的形式传递
    messages=[
        {"role": "system", "content": "You are a helpful teacher."},
        {
            "role": "user",
            "content": "Are there other measures than time complexity for an algorithm?",
        },
        {
            "role": "assistant",
            "content": "Yes, there are other measures besides time complexity for an algorithm, such as space complexity.",
        },
        {"role": "user", "content": "What is it?"},
    ],
)
  • 输入消息采用对话格式,可以发送多条消息给模型,如上面的 messages 数组
  • API 并不会在上下文中存储之前的消息
    • 因此,问题“What is it?”(那是什么?)指代的是前一个答案中的方法,这个问题只有在模型理解该答案时才有意义
  • 每次请求时,你必须重新发送完整的对话内容,以模拟一个完整的聊天会话。

聊天补全端点的输入选项:一些参数选项

讨论如何使用 chat.completions 端点及其 create 方法。

create 方法参数:

{%}|512

消息对象结构

{%}|528

消息的 name 参数似乎在 OpenAI 的文档中少有记录,甚至在某些模型中得不到支持

"content": [{"type": "image_url", "image_url":{"url": your_url}}]

在这里, your_url 表示图像的 URL,而有些示例代码会使用:

"content": [{"image": image, "resize":768}]

其中 image 是一个 Base64 编码的图像。

对话和词元的长度

如前所述,对话的总长度与总词元数相关。这将对以下方面产生影响。

  • 成本
  • 响应用时
  • 可用性
    • 并可以通过 max_tokens 参数控制输出词元的数量
    • OpenAI 提供了一个名为 tiktoken 的库,其中包含了分词器的实现,也可以计算字符串中有多少词元

其他可选参数

{%}|536

调整 temperaturetop_p

temperaturetop_p 的作用是,在生成连贯的文本与引入变化创造性之间调整平衡。调整这些参数可以显著影响生成文本的特征。

温度影响分布本身:提高温度使分布变得更加集中,高概率词元的可能性提高,而低概率词元的可能性降低。

下图显示了在补全句子“The cat is”(猫是)时,温度为 1 和温度为 0.1 的分布差异。

{%}|464

图 2-6:温度对词元概率分布的影响

top_p 参数指的是 top-p 采样,也称为核采样(nucleus sampling)。模型将只考虑一部分词元,其总概率质量加起来等于指定的 top-p 值,而不是考虑所有可能的词元。

在前面的例子中,0.1 的 top-p 采样和 1 的温度,将只保留前三个词元,因为“a”“still”和“not”的概率总和超过了 0.1,如图 2-7 所示。

{%}|536

图 2-7:top-p 采样对词元概率分布的影响

因此,在一致的输出和创造性的输出之间进行选择时,这两个参数非常有用。我们在这里推荐一些示例,但在你自己的项目上进行试验总是最好的。

  • 对于需要保持内容和风格一致的事实性输出,请选择低 temperature 和低 top_p
    • 例如,在代码生成中, temperature = 0.1, top_p = 0.1
  • 对于事实重要但表述方式不那么重要的输出,使用较低的 top_p ,但提高 temperature ,以获得更自然和引人入胜的输出。
    • 例如,在聊天机器人响应中, temperature = 1, top_p = 0.1
  • 对于需要创造力的输出,使用高 temperature 和低 top_p
    • 例如,在创意写作中, temperature = 1.2top_p = 0.5

即使在最低的 top-p 和温度值下,OpenAI 也并不保证确定性,但输出更有可能高度一致。 seed 参数是获得确定性输出的解决方案

聊天补全端点的输出结果格式

现在你已经有了查询基于聊天的模型所需的信息,让我们看看如何使用这些结果。

以下是“Hello World”示例的完整响应:

ChatCompletion(
    id='chatcmpl-8qIU4dbIioE3addR1M6ofppFRlUmR',
    choices=[
        Choice(
            finish_reason='stop', index=0,
            message=ChatCompletionMessage(
                content='Hello there! How can I assist you today?',
                role='assistant', function_call=None, tool_calls=None),
            logprobs=None)],
    created=1707474799, model='gpt-3.5-turbo-0613',
    object='chat.completion', system_fingerprint=None,
    usage=CompletionUsage(
        completion_tokens=10, prompt_tokens=10, total_tokens=20))

生成的输出详见表 2-4。

表 2-4:聊天补全模型输出的描述

{%}|600

 如果你想在多个响应间进行选择,使用了一个大于 1 的 n 参数,你会发现 prompt_tokens 值不会改变,但 completion_tokens 的值将大致乘以 n

视觉能力::多模态

gpt-4-turbo 支持 PNG(.png)、JPEG(.jpeg 和.jpg)、WEBP(.webp)和非动画 GIF(.gif)格式图像,每张图像最大为 20 MB

以下是使用视觉能力的一个例子:

url = """https://upload.wikimedia.org/wikipedia/commons/f/f0/Ophiopteris_antipodum.JPG"""
response = client.chat.completions.create(
    model="gpt-4-turbo",
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": "Give the name of the animal in the image.",
                },
                {"type": "image_url", "image_url": {"url": url}},
            ],
        }
    ],
)
response.choices[0].message.content

例如:给一张图片,要求 GPT-4 告诉我们这是什么动物。它正确地识别出:“这是一只海星(starfish)

{%}|536

以下函数将图像文件转换为 Base64 格式:

from base64 import b64encode

def encode_image(image_path):
    with open(image_path, "rb") as image_file:
        image_data = image_file.read()
        base64_image = b64encode(image_data).decode("utf-8")
    return base64_image

这是此函数的一个用例,假设图像保存在名为 image.jpg 的文件中:

base64_image = encode_image("image.jpg")
response = client.chat.completions.create(
    model="gpt-4-turbo",
    messages=[
        {
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": "Give the name of the animal in this image.",
                },
                {
                    "type": "image_url",
                    "image_url": {
                        "url": f"data:image/jpeg;base64,{base64_image}"
                    },
                },
            ],
        }
    ],
)

请求 JSON 输出

这个方案相当简单:将 response_format 参数设置为 {"type": "json_object"} 。此外,指示模型在消息中使用 JSON 输出格式。例如:

from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
	model="gpt-3.5-turbo-1106",
	response_format={"type": "json_object"},
	messages=[
		{
			"role": "system",
			"content": "Convert the user's query in a JSON object"
		},
		{
			"role": "user",
			"content": "I am looking for blue or red shoes, leather,
				size 7."
		},
	],
)

此代码片段将返回以下内容:

{
	"color": ["blue", "red"],
	"material": "leather",
	"size": 7
}

[!info]
其他需要时查文档就好了,知道可以要求输入的格式就好

使用其他文本补全模型

文本补全聊天补全之间有一个重要区别:

  • 二者都生成文本
  • 但聊天补全是针对对话进行优化的。在下面的代码片段中, chat.completions 端点的主要不同在于提示词格式。基于聊天的模型必须采用对话格式,
  • 而补全则使用一个单一的提示词
from openai import OpenAI
client = OpenAI()

# 调用 openai 库的 completions 端点
response = client.completions.create(
    model="gpt-3.5-turbo-instruct",
    prompt="Hello World!"
)
# 输出响应
print(response.choices[0].text)

前面的代码片段将输出类似以下内容的补全结果:

"\n\nIt's a pleasure to meet you. I'm new to the world"

文本补全端点的输入选项

completions.create 的输入选项集与此前提到的聊天端点的非常相似

{%}|608

文本补全端点的输出结果格式

现在,你已拥有查询基于文本的模型所需的所有信息,你会发现结果与聊天端点的结果非常相似。以下是将其用于我们的“Hello World”示例的输出:

Completion(
    id='cmpl-8qOqLVuCC4GRuBe5XUKTXJj9EWtpv',
    choices=[
        CompletionChoice(
            finish_reason='length',
            index=0,
            logprobs=null,
            text="<br />\n\nHi there! It's great to see you.",
        )
    ],
    created=1707499245,
    model='gpt-3.5-turbo-instruct',
    object='text_completion',
    usage=CompletionUsage(
        completion_tokens=15, prompt_tokens=3, total_tokens=18
    )
)

 这个输出与我们从聊天模型中得到的非常相似。唯一的区别在于 Choice 对象:没有包含 contentrole 属性的消息,而是一个简单的 text 属性,包含模型生成的补全内容。

注意事项

在大规模使用 API 之前,你应该考虑两个重要因素:成本和数据隐私

其他 OpenAI API 和功能

嵌入 (Embedding)

  • 将高维或离散数据映射到低维连续向量空间的特定技术
  • 嵌入的原理是以某种方式有意义地表示文本字符串,以捕捉它们的语义相似度
  • 嵌入具有这样的特性:如果两个文本具有相似的含义,它们的向量表示在向量空间中会相接近。
    • 例如,图 2-9 展示了三个句子在二维嵌入中的表现。
      • 尽管句子“The cat chased the mouse around the house”(猫在房子里追老鼠)和“Around the house, the mouse was pursued by the cat”(在房子里,老鼠被猫追)的语法结构不同,但它们传达了相同的一般含义,因此它们应该具有相近的嵌入表示。
      • 句子“The astronaut repaired the spaceship in orbit”(宇航员在轨道上修理了飞船)与前面句子的主题(猫和老鼠)无关,而是讨论了一个完全不同的主题(宇航员和飞船),因此它应该具有显著不同的嵌入表示。

{%}|600

OpenAI 推出了三个嵌入模型。text-embedding-ada-002 是在 2022 年 12 月底推出的模型。最近,OpenAI 推出了两个新模型:一个更小、更便宜且高效的模型,名为 text-embedding-3-small;以及一个更大、更强但也更昂贵的模型,名为 text-embedding-3-large。

嵌入端点使用起来非常简单:

result = client.embeddings.create(
    model="text-embedding-ada-002",
    input="your input text"
)

嵌入通过以下方式访问:

result.data[0].embedding

审核

OpenAI 提供了一个审核模型,用于检查内容是否符合使用政策,

  • 仇恨
  • 仇恨 / 威胁
  • 骚扰
  • 骚扰 / 威胁
  • 自残
  • 自残 / 说明
  • 自残 / 意图
  • 性内容
  • 涉及未成年人的性内容
  • 暴力
  • 暴力 / 详细描绘

审核模型的端点是 moderations.create,只有两个可用参数:模型和输入文本。内容审核模型有两个。默认是 text-moderation-latest 10 ,它会随时自动更新,以确保你始终使用最准确的模型。另一个模型是 text-moderation-stable

以下是使用审核模型的示例:

from openai import OpenAI
client = OpenAI()

# 调用 openai 审核端点的 text-moderation-latest 模型
response = client.moderations.create(
model="text-moderation-latest",
input="I want to kill my neighbor."
)

文本转语音

目前 OpenAI 有两个可用 TTS 模型:tts-1 和 tts-1-hd。tts-1 是标准模型,经过优化以提高速度

from openai import OpenAI
client = OpenAI()

response = client.audio.speech.create(
    model="tts-1",
    voice="echo",
    input="I won't be home tonight. Could you please take the dog \
        for a walk?"
)
response.stream_to_file("speech.mp3")

详细参数如下图:
{%}|600

语音转文本

Whisper 是一个多功能的语音识别模型。有开源版本,可以搜索

这是转录 API 的一个使用示例:

from openai import OpenAI
client = OpenAI()

transcript = client.audio.transcriptions.create(
    model="whisper-1",
    file=open("speech.mp3", "rb")
)
transcript.text

表 2-9:转录端点输入的描述

{%}|416

图像 API

OpenAI 提供的图像 API 提供了三种方法来处理文本中的图像。

生成

使用此方法,你可以在提示词中描述你想要实现的内容,并据此生成图像。这种方法可以通过 DALL · E 2 和 DALL · E 3 实现。

编辑

此方法允许根据提示词编辑现有图像。它在不改变图像核心内容的情况下对现有图像进行修改。在本书撰写时,这种方法仅在 DALL · E 2 中可用。

变体

此方法可以从现有图像创建变体,通过探索不同的艺术方向来增强创造力,同时保持原始图像的本质。在本书撰写时,这种方法也仅在 DALL · E 2 中提供。