Python 项目接入 OpenAI API 实战:从发消息到结构化输出

2026-04-21 33 预计阅读时间:1 分钟
来源:realpython.com AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:11 分钟

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.rolemessage.contentresponse.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-minigpt-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}")

运行前确保:

  1. OPENAI_API_KEY 环境变量已设置。
  2. openai 库版本 ≥ 1.0,pydantic 已安装。
  3. 模型用 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.APIErroropenai.RateLimitError,做重试或降级
延迟 长文本生成用 stream=True,逐 chunk 返回给用户

API 调用本身不难,难的是把"能跑的 demo"变成"稳定运行的服务"。角色约束、结构化输出、token 监控这三件事做到位,你的 Python 项目才算真正接入了 ChatGPT,而不是接入了它的随机性。


相关推荐