OpenAI 的 ChatGPT API 已经从实验性接口变成了不少项目的核心依赖。但如果你只是把用户输入丢给 API、再把返回字符串贴到页面上,很快就会遇到问题——回复不稳定、格式不可控、成本飙升。这篇文章从 Python 的 openai 库出发,讲清楚三件事:怎么发请求、怎么用角色约束 AI 行为、怎么拿到程序能直接消费的结构化输出。
安装与认证:先把钥匙装好
pip install openai
新版 openai 库(≥1.0)全面改用了异步友好、类型标注更完善的接口,和旧版 0.x 的调用方式差异很大。如果你从老项目迁移,注意看下文的新写法。
认证方式有两种,推荐用环境变量,避免把 key 硬编码进代码:
export OPENAI_API_KEY="sk-xxxxxxxxxxxxxxxxxxxxxxxx"
或者在代码里显式传入(适合临时测试,不要提交到 Git):
from openai import OpenAI
client = OpenAI(api_key="sk-xxxxxxxxxxxxxxxxxxxxxxxx")
设好环境变量后,OpenAI() 可以无参数直接实例化,它会自动读取 OPENAI_API_KEY。
第一次对话:发一条消息拿到回复
最简调用只需要指定模型和消息列表:
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "user", "content": "用一句话解释什么是向量数据库"}
]
)
print(response.choices[0].message.content)
输出类似:
向量数据库是专门存储和检索高维向量数据的系统,常用于语义搜索和推荐场景。
response 对象的结构值得看一眼:response.choices 是一个列表,每个元素有 message.role 和 message.content;response.usage 记录了 token 消耗。多轮对话时,choices 通常只有一个,但 n 参数可以让你一次拿多个候选回复。
用角色控制行为:system 消息才是真正的"调参"
API 的 messages 列表支持三种角色:
| 角色 | 作用 | 典型用法 |
|---|---|---|
system |
设定 AI 的全局行为边界 | "你是一个只输出 JSON 的翻译引擎" |
user |
发起请求 | 用户输入、业务数据 |
assistant |
AI 的历史回复 | 多轮对话中回传上下文 |
很多人只用 user 角色,结果每次都要在 prompt 里重复"请用中文回答""不要解释"。把这类约束放进 system 消息,一次设定、全程生效:
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "system",
"content": "你是一个技术文档翻译引擎,只输出翻译结果,不添加解释或评论。输出语言为中文。"
},
{
"role": "user",
"content": "A vector database stores and retrieves high-dimensional vectors for semantic search."
}
],
temperature=0.3 # 降低随机性,翻译场景需要稳定输出
)
print(response.choices[0].message.content)
temperature 从 0 到 2,越低输出越确定。翻译、抽取这类"要准确"的任务建议 0.1–0.3;创意写作可以放到 0.7 以上。
多轮对话的实现就是把历史 assistant 回传回去:
messages = [
{"role": "system", "content": "你是一个 Python 编程助手。"},
{"role": "user", "content": "怎么读取 CSV 文件?"},
{"role": "assistant", "content": "可以用 pandas:import pandas as pd; df = pd.read_csv('file.csv')"},
{"role": "user", "content": "如果文件编码不是 UTF-8 呢?"}
]
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages
)
注意:每轮对话的 messages 都会完整发送,token 消耗是累加的。长对话要做截断或摘要,否则成本会失控。
拿到结构化输出:让 AI 返回程序能直接解析的数据
自由文本对人类友好,对程序是灾难。如果你要 AI 返回分类标签、翻译结果、数据记录,强制它输出 JSON 是最实用的做法。
方式一:在 system 消息里约束格式
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "system",
"content": (
"你是一个情感分析引擎。对用户输入的文本判断情感倾向。"
"只输出合法 JSON,格式为:{\"label\": \"positive\"|\"negative\"|\"neutral\", \"confidence\": 0.0-1.0}"
"不要输出任何其他内容。"
)
},
{"role": "user", "content": "这个新功能终于上线了,等了好久!"}
],
temperature=0.1
)
import json
result = json.loads(response.choices[0].message.content)
print(result["label"]) # positive
print(result["confidence"]) # 0.92
这种方式简单,但 AI 有时还是会"加戏"——在 JSON 前后加注释或多余空格。用 json.loads 前可以先做 .strip() 清理。
方式二:使用 response_format 参数(更可靠)
gpt-4o-mini 和 gpt-4o 支持 response_format 参数,直接让 API 保证输出合法 JSON:
response = client.chat.completions.create(
model="gpt-4o-mini",
response_format={"type": "json_object"},
messages=[
{
"role": "system",
"content": (
"分析用户文本的情感倾向,输出 JSON,包含 label 和 confidence 字段。"
"label 取值:positive / negative / neutral。confidence 取值 0.0 到 1.0。"
)
},
{"role": "user", "content": "快递又延误了,第三次了"}
],
temperature=0.1
)
result = json.loads(response.choices[0].message.content)
print(result) # {'label': 'negative', 'confidence': 0.88}
用 response_format={"type": "json_object"} 时,system 或 user 消息里必须明确提到"JSON",否则 API 会报错。这是 OpenAI 的硬性要求。
方式三:Structured Outputs(最严格,支持 schema 校验)
2024 年 OpenAI 推出了 Structured Outputs 功能,可以传入一个 JSON Schema,API 保证输出严格符合该 schema:
from openai import OpenAI
from pydantic import BaseModel
client = OpenAI()
class SentimentResult(BaseModel):
label: str
confidence: float
response = client.beta.chat.completions.parse(
model="gpt-4o-mini",
messages=[
{
"role": "system",
"content": "分析用户文本的情感倾向。label 取 positive/negative/neutral,confidence 取 0.0-1.0。"
},
{"role": "user", "content": "今天天气不错,适合出门散步"}
],
response_format=SentimentResult,
)
result = response.choices[0].message.parsed
print(result.label) # positive
print(result.confidence) # 0.85
print(type(result)) # SentimentResult (pydantic model)
client.beta.chat.completions.parse 会直接把 JSON 反序列化成你定义的 Pydantic model,省掉手动 json.loads 和字段校验。如果输出不符合 schema,parsed 字段会是 None,你可以检查 response.choices[0].message.refusal 看拒绝原因。
这是目前最可靠的方式,适合生产环境。注意它还在 beta 阶段,接口路径可能变化。
实战组合:一个完整的分类+抽取脚本
把上面三块拼起来,写一个能跑的小脚本——对一批用户评论做情感分类和关键词抽取:
import json
from openai import OpenAI
from pydantic import BaseModel
client = OpenAI()
class CommentAnalysis(BaseModel):
sentiment: str # positive / negative / neutral
confidence: float # 0.0 - 1.0
keywords: list[str] # 关键词列表
comments = [
"新版本加载速度快了不少,体验流畅",
"客服态度很差,问题三天没解决",
"功能还行,就是界面有点丑"
]
for comment in comments:
response = client.beta.chat.completions.parse(
model="gpt-4o-mini",
messages=[
{
"role": "system",
"content": (
"分析用户评论的情感倾向和关键词。"
"sentiment 取 positive/negative/neutral。"
"confidence 取 0.0-1.0。"
"keywords 提取 2-5 个核心词。"
)
},
{"role": "user", "content": comment}
],
response_format=CommentAnalysis,
temperature=0.1,
)
parsed = response.choices[0].message.parsed
if parsed:
print(f"评论: {comment}")
print(f" 情感: {parsed.sentiment} (置信度 {parsed.confidence})")
print(f" 关键词: {', '.join(parsed.keywords)}")
print()
else:
print(f"解析失败: {comment}")
# 回退到手动解析
raw = response.choices[0].message.content
print(f" 原始输出: {raw}")
运行前确保:
OPENAI_API_KEY环境变量已设置。openai库版本 ≥ 1.0,pydantic已安装。- 模型用
gpt-4o-mini(便宜)或gpt-4o(更准),不要用已停用的gpt-4-32k等。
上线前的检查清单
| 项目 | 建议 |
|---|---|
| API Key 管理 | 环境变量或密钥管理服务,绝不硬编码 |
| 模型选择 | 日常任务用 gpt-4o-mini,复杂推理用 gpt-4o |
| Token 预算 | 用 response.usage 监控,设置上限告警 |
| 输出可靠性 | 生产环境优先用 Structured Outputs + Pydantic schema |
| 多轮上下文 | 超过 8–10 轳要截断或做摘要,否则 token 爆炸 |
| 错误处理 | 捕获 openai.APIError、openai.RateLimitError,做重试或降级 |
| 延迟 | 长文本生成用 stream=True,逐 chunk 返回给用户 |
API 调用本身不难,难的是把"能跑的 demo"变成"稳定运行的服务"。角色约束、结构化输出、token 监控这三件事做到位,你的 Python 项目才算真正接入了 ChatGPT,而不是接入了它的随机性。