引言
2023 年 3 月 1 日,一个普通的星期三。OpenAI 在博客上悄无声息地发布了一则公告:ChatGPT API 正式开放。
没有发布会,没有直播,甚至没有一条推文预告。但这个消息像一颗炸弹一样在开发者社区引爆了。在那个周末,成千上万的开发者在几小时内构建出了自己的 AI 应用。有人在 Twitter 上说:「我等了三个月的 GPT-3 API waitlist 没通过,现在 ChatGPT API 只需要一行代码就能调用了。」
API 的价格更是令人震惊:每 1000 个 token 只需 $0.002——比 GPT-3 便宜了 10 倍。
这是一场基础设施级别的变革。在此之前,只有少数经过审批的研究者才能调用 GPT-3 API;在此之后,任何一个会写 Python 的开发者都能在 10 分钟内接入一个强大的语言模型。
从 GPT-3 到 ChatGPT API
GPT-3 的痛点
GPT-3 的 completions API 使用的是续写模式——你给它一段文字开头,它接着往下写。看起来灵活,实际上很难控制:
GPT-3 的调用方式(completions):
Prompt: "将以下英文翻译为中文:
Hello, how are you?"
→ 模型可能输出:
"你好,你好吗?"
或者 "翻译:你好..."(它在解释而不是翻译)
或者 "I am fine..."(它开始对话了)
问题:模型不知道你想要什么,
它只是「接着写」。开发者不得不花大量精力设计提示词模板,用各种「魔法前缀」来引导模型行为。这种方式脆弱、不稳定,而且每个模型版本都需要重新调试。
革命性的 messages 数组
ChatGPT API 引入了一个改变游戏规则的设计:messages 数组。不再是让模型续写,而是给它一组对话历史,让它扮演对话中的角色。
# ChatGPT API 的核心设计
response = openai.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "你是一个翻译助手。"},
{"role": "user", "content": "将以下英文翻译为中文:Hello"},
{"role": "assistant", "content": "你好"},
{"role": "user", "content": "How are you?"}
]
)三种角色的含义:
| 角色 | 含义 | 类比 |
|---|---|---|
system | 设定 AI 的行为规则 | 公司的员工手册 |
user | 用户的输入 | 客户的提问 |
assistant | AI 的回复 | 员工的回答 |
messages 数组的工作方式:
┌─────────────────────────────────┐
│ system: 你是一个翻译助手 │ ← 全局指令
├─────────────────────────────────┤
│ user: Hello │ ← 历史对话
│ assistant: 你好 │
├─────────────────────────────────┤
│ user: How are you? │ ← 当前提问
├─────────────────────────────────┤
│ → 模型生成下一轮 assistant 回复 │
└─────────────────────────────────┘
关键洞察:每一条消息都是「上下文」的一部分。
模型看到的是完整的对话历史,而不是孤立的一句话。第一个 API 调用
完整代码示例
# 安装:pip install openai
from openai import OpenAI
client = OpenAI(api_key="sk-your-api-key-here")
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "你是一个有帮助的助手。"},
{"role": "user", "content": "什么是上下文工程?"}
],
temperature=0.7, # 创造性:0(确定)到 2(随机)
max_tokens=500 # 最大输出长度
)
print(response.choices[0].message.content)用 curl 调用
curl https://api.openai.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-your-api-key" \
-d '{
"model": "gpt-3.5-turbo",
"messages": [
{"role": "system", "content": "你是一个有帮助的助手。"},
{"role": "user", "content": "什么是上下文工程?"}
]
}'响应结构
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"model": "gpt-3.5-turbo",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "上下文工程是..."
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 25,
"completion_tokens": 150,
"total_tokens": 175
}
}注意 usage 字段——它告诉你这次调用消耗了多少 token。这是理解 API 成本的关键。
流式输出:让用户等得不那么焦虑
为什么需要流式输出?
大语言模型生成文本是逐个 token 产出的。如果等全部生成完再返回,用户可能要盯着空白屏幕等 10-30 秒。流式输出(Streaming)让模型一边生成一边返回,用户体验好得多。
# 流式输出
stream = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "写一首关于编程的诗"}],
stream=True # 关键参数
)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)非流式 vs 流式:
非流式: [等待 15 秒] → "白日依山尽,黄河入海流..."
流式: "白" → "日" → "依" → "山" → "尽" → ...
用户体验差距巨大——就像下载进度条 vs 黑屏等待API 端点与模型选择
各家 API 对比
| 提供商 | 端点 | 代表模型 | 特点 |
|---|---|---|---|
| OpenAI | /v1/chat/completions | GPT-4o, o1 | 生态最完善 |
| Anthropic | /v1/messages | Claude 4 Sonnet | 长上下文,安全对齐 |
/v1/models:generateContent | Gemini 2.5 Pro | 多模态原生 | |
| DeepSeek | /v1/chat/completions | DeepSeek-V3 | 开源,性价比高 |
OpenAI 兼容格式
一个好消息是:大多数 API 提供商都采用了 OpenAI 兼容的接口格式。这意味着你只需要改 base_url 和 api_key 就能切换模型:
# 用同一个 SDK 调用不同模型
from openai import OpenAI
# OpenAI
client_openai = OpenAI(api_key="sk-...")
# DeepSeek(兼容 OpenAI 格式)
client_deepseek = OpenAI(
api_key="sk-...",
base_url="https://api.deepseek.com"
)
# 本地模型(Ollama,也兼容 OpenAI 格式)
client_local = OpenAI(
api_key="ollama",
base_url="http://localhost:11434/v1"
)本节小结
| 概念 | 要点 |
|---|---|
| ChatGPT API | 2023.3 发布,$0.002/1K tokens,引爆 AI 应用浪潮 |
| messages 数组 | system/user/assistant 三角色,取代了 GPT-3 的续写模式 |
| API 响应 | 包含 content 和 usage(token 消耗),是成本追踪的关键 |
| 流式输出 | stream=True,逐 token 返回,大幅改善用户体验 |
| OpenAI 兼容格式 | 多数提供商兼容,切换模型只需改 base_url |
思考题
- 为什么 ChatGPT 的 messages 数组设计比 GPT-3 的续写模式更适合构建应用?想想你在使用 ChatGPT 网页版时的体验。
- 如果你来设计一个客服机器人,system 消息应该包含哪些内容?
- 流式输出和非流式输出分别适合什么场景?有没有必须用非流式的场景?