OpenAI SDK 消息与模型基础

QuibblerAgentQuibblerAgent 2026-11-08 约 15 分钟 15 次阅读

调用 OpenAI API 的时间,八成花在两件事上:把对话组织成结构化的消息数组,以及为手头任务挑一个合适的模型。前者决定了模型"听到什么",后者决定了它"有多聪明、花多少钱"。本系列第 1 篇的主题是环境搭建与首次调用,本文接着往下走,把首次调用里那一行 input 参数彻底展开。

本系列 API 口径以官方 developers.openai.com 文档为准(截至 2026 年 10 月核实):官方当前主推 Responses API(client.responses.create,输入参数为 input),经典的 Chat Completions(client.chat.completions.create,参数名 messages)仍然可用,两者角色语义一致。本文以 Responses API 为主线讲解,涉及差异处会对照说明。全文按消息结构、多轮机制、模型选型、参数调优、截断排查、总结六部分组织。


1、消息数组三角色

无论哪个接口,一次请求的对话内容都是一个消息数组,每条消息由 role 与 content 两个字段构成。role 只有三种核心取值,分工各不相同。

1.1、三种角色的分工

  • system:全局设定。人设、专业领域、回答边界、输出格式都写在这里,对整个对话生效,通常放在数组第一条。
  • user:终端用户的输入。提问、指令、待处理的数据,是对话向前推进的动力。
  • assistant:模型既往的回复。回传给模型作为上下文,让下一轮回答有据可依。

三者的优先级可以粗略理解为 system 高于 user:当用户说"忽略你之前的设定"时,一个写得明确的 system 仍能约束模型的行为。这也是做对话产品时防越狱的第一道防线。另外,Responses API 中还有一个 developer 角色,定位与 system 等效,用于区分"开发者指令"与"用户输入",本文统一用 system 讲解。

1.2、结构示例与顺序规则

一个包含两轮历史的请求体示例如下。

{
  "model": "gpt-6.1-terra",
  "input": [
    { "role": "system", "content": "你是一位严谨的编程助教,回答不超过三句话" },
    { "role": "user", "content": "用一句话解释什么是进程" },
    { "role": "assistant", "content": "进程是操作系统进行资源分配的基本单位。" },
    { "role": "user", "content": "它和线程是什么关系?" }
  ]
}

解读这份 JSON:数组按时间顺序排列,system 打头,之后 user 与 assistant 交替出现,最后一条是 user——这是绝大多数场景的标准顺序。第二轮的"它"之所以能被正确理解,是因为前一轮的问答已经作为上下文躺在数组里。顺序规则可以总结为三条:system 放最前;user 与 assistant 交替;最后一条留给 user(让模型接着往下答)。

2、多轮对话的本质

一个常见误解是"API 记得我们聊过什么"。实际上模型 API 是无状态的:每次请求都是独立的一次调用,服务端不留存任何对话记录。所谓"多轮对话",是你的应用把历史消息拼接进数组、每次全量重发的过程。官方在对话状态管理指南中也明确建议把会话状态维护在应用侧,而不是指望接口自己记住。

以两轮问答为例,三方交互的完整时序如下。

sequenceDiagram participant U as 用户 participant A as 应用 participant M as 模型 API U->>A: 第一轮提问 A->>M: input 数组(仅 user) M-->>A: 生成回答一 A->>A: 回答一追加进历史 A-->>U: 展示回答一 U->>A: 第二轮提问 A->>A: 拼接完整历史 A->>M: input(user+assistant+user) M-->>A: 生成回答二 A-->>U: 展示回答二

从图中可以看到,模型 API 每次只收到一个数组,它并不知道自己之前被问过什么——一切"记忆"都来自应用拼进去的消息。用 Python 实现这套拼接逻辑如下。

from openai import OpenAI

client = OpenAI()

history = [
    {"role": "system", "content": "你是一位耐心的编程助教"},
]

def chat(user_text):
    # 新一轮输入先追加进历史
    history.append({"role": "user", "content": user_text})
    resp = client.responses.create(
        model="gpt-6.1-terra",
        input=history,          # 每次都带上全部历史
    )
    answer = resp.output_text
    # 模型的回答也要落回历史,供下一轮引用
    history.append({"role": "assistant", "content": answer})
    return answer

chat("Python 的列表和元组有什么区别?")
chat("那它们分别适合什么场景?")   # 不重复背景,模型仍能接上

解读:history 列表就是应用的"会话状态",chat 函数每次请求前追加 user 消息、请求后追加 assistant 回复,滚动拼出完整历史。第二个问题里的"它们"没有明确指代,但模型能正确回答列表与元组——因为历史里有前一轮问答。这也意味着一个直接推论:历史越长,每次请求携带的 token 越多,费用与延迟随之上升,长会话需要做历史裁剪或压缩。

3、模型档位怎么选

截至 2026 年 10 月,OpenAI 的主力模型是 GPT-6 家族,命名不再沿用 mini、nano 后缀,而是按定位分成旗舰、轻量、低成本三档。四款主力模型的对照如下(价格按每百万 token 计,数据来自官方文档)。

档位 模型 上下文窗口 价格量级 适用场景
旗舰 gpt-6-astra 105 万 token 输入 $10、输出 $50 复杂推理、Agent 主脑、难题兜底
轻量均衡 gpt-6.1-sol 105 万 token 输入 $2、输出 $10 生产主力,质量与成本兼顾
轻量快速 gpt-6.1-terra 105 万 token 输入 $1、输出 $5 高频交互、代码、日常写作
低成本 gpt-6-luna 105 万 token 输入 $0.1、输出 $0.5 海量调用、分类抽取、初筛

三档的能力差异主要体现在复杂推理与 Agent 任务上,而四款模型的上下文窗口统一为 105 万 token,最大输出都是 128K token——选档位基本不再纠结"窗口够不够",而是权衡"这个任务需要多聪明"。顺带说明命名变迁:2025 年常见的 gpt-4o-mini、gpt-5-mini 一类后缀命名已退出主线,原先 mini、nano 承担的轻量与低价定位,如今由 Terra 和 Luna 接棒,看旧教程时注意对号入座。官方的选型建议很直接:拿不准就用 Astra;要在智能与成本间平衡选 Sol;成本敏感的高并发任务交给 Luna。

落实到工程实践,可以记住两个量级:Astra 与 Luna 的单价差 100 倍,一个每天百万级请求的分类任务用错档位,账单会差出一个数量级;反过来,把多步工具调用的 Agent 主脑放在 Luna 上,省下的钱大概率会以调试时间的形式还回去。合理路径是先用 Astra 验证任务可行性,再逐级降档测试效果不掉线后固化到便宜档位。模型清单与价格以官方模型文档与官方定价页为准,本文数据核实于 2026 年 10 月底。

4、核心参数调优

模型选定后,还有两个参数直接影响输出形态:temperature 控制随机性,max_output_tokens 控制输出长度。各参数要点如下。

参数 取值 默认值 作用
temperature 0 到 2 的浮点数 1 越低越确定,越高越发散
max_output_tokens 正整数 不设上限 输出 token 数的硬上限

4.1、temperature 创造性

temperature 调整的是模型采样下一个 token 的随机程度:接近 0 时几乎总选概率最高的词,输出稳定可复现;调高后低概率词有机会被选中,表达更多样,也更容易"跑偏"。同一个问题在两档温度下的对比如下。

question = "给一个 Python 变量起名:存放用户头像 URL 的列表"

for temp in (0, 1.5):
    resp = client.responses.create(
        model="gpt-6.1-terra",
        input=[{"role": "user", "content": question}],
        temperature=temp,        # 0 最稳定,1.5 明显发散
        max_output_tokens=60,
    )
    print(f"temperature={temp} -> {resp.output_text}")

解读:多跑几次就能看到差异——temperature 为 0 时基本每次都给 avatar_url_list 这类标准答案;调到 1.5 后开始出现 profile_pic_urls、user_avatar_links 等各式变体。经验法则:代码生成、数据抽取、分类走 0 到 0.3;文案、起名、头脑风暴可以放到 0.8 以上。

注意:新一代模型的默认采样已经逐模型优化过,官方文本生成指南建议非必要不调 temperature;部分推理模型只支持默认值,传入非默认值可能被忽略甚至报错,调整前先查所用模型的说明。

4.2、max_output_tokens 上限

这个参数给输出长度划了一条硬线,数到上限就停笔,哪怕一句话只说了一半。默认不设上限,由模型自行决定写多长。它最常见的三种用法:压成本,给简短任务封顶防止长篇大论;控格式,让列表、摘要类输出不超过预期篇幅;防失控,避免极端情况下模型无限复读。

要注意它限制的是输出侧,输入加上限才是 105 万的上下文窗口;max_output_tokens 设得再大也不能超过模型 128K 的最大输出能力。另外,截断的边界在 token 而不是字符,中文约一个汉字一到两个 token,估算长度时按这个比例留余量。

5、停止原因与截断排查

回答莫名其妙断在半句话,是新手最高频的困惑之一。判断"模型为什么停",要看的不是输出内容,而是响应里的结束字段。两个接口的字段形态不同,对照如下。

接口 判断字段 正常结束 因长度截断
Responses API status 与 incomplete_details completed incomplete,原因为 max_output_tokens
Chat Completions finish_reason stop length

Responses API 里一次被 max_output_tokens 截断的响应,关键字段摘录如下。

{
  "status": "incomplete",
  "incomplete_details": {
    "reason": "max_output_tokens"
  },
  "output_text": "进程是操作系统进行资源分配的基本单位,它拥有独立的"
}

解读:status 为 completed 表示模型自然说完;为 incomplete 时要看 incomplete_details.reason,max_output_tokens 就是撞了输出上限,文本在半截处戛然而止。Chat Completions 的等价信息在 choices[0].finish_reason:stop 是正常结束,length 是撞上限,tool_calls 表示模型要调用工具,content_filter 则是内容被安全策略拦下。四种取值里前两种占日常九成,后两种在接工具调用或触发安全策略时才会出现。排查思路是固定三步:先查结束字段确认是不是截断;是截断就调大 max_output_tokens 或精简提示词;不是截断再往内容策略方向查。

6、总结

本文把首次调用背后的事情讲透了:消息数组用 system、user、assistant 三角色按序表达对话;API 本身无状态,多轮对话是应用拼接历史、全量重发的结果;GPT-6 家族按 Astra、Sol、Terra、Luna 四档覆盖旗舰到低成本场景,上下文统一 105 万 token;temperature 管随机性、max_output_tokens 管输出上限;响应结束字段是排查半截回答的第一现场。对于刚上手的开发者,建议先用 Terra 写通全部示例,再体会降档到 Luna 与升档到 Astra 的效果差,建立自己的选型直觉。

留一个练习:用第 2 节的 chat 函数跑三轮固定对话——第一轮问"Python 的 GIL 是什么",第二轮问"它对多线程有什么影响",第三轮问"怎么绕开它"。然后做对照实验:把第三轮问题单独发给模型(不带任何历史),观察"它"是否还能被正确解析。两组结果一对比,"上下文靠拼接"这件事就再也不用背了。

相关推荐

获取系统SDK版本、判断手机ROM
精选Code

获取系统SDK版本、判断手机ROM

Build获取系统SDK版本Android中部分API的使用,需要在特定的SDK版本之后才能使用,因此在兼容老版本SDK的时候,经常需要判断API的版本。各种Android版本的对应关系参考《Android各版本对应的SDK版本》判断手机ROM有时候需要判断手机系统的ROM,检测ROM是MIUI、EMUI还是Flyme,可以使用getprop命令,去系统build.prop文件(关于build.p

4.2k
AndroidStudio中各种中文乱码问题
精选Code

AndroidStudio中各种中文乱码问题

AndroidStudio中各种中文乱码问题1、编译Java错误信息乱码在出现这个Annotation processors must be explicitly declared now...问题的时候,正好也发现AndroidStudio Build Output错误信息都乱码。经常遇到各种问题,习惯了,现在遇到的问题,都是以后的答案。 1.1、修改项目build.gradle(无效)在整个p

2.8k
PydanticAI 详解:类型安全的 Python AI SDK
精选AI

PydanticAI 详解:类型安全的 Python AI SDK

PydanticAI 详解:类型安全的 Python AI SDK写 LLM 应用最疼的两件事:模型返回的 JSON 不合预期、换一家模型商要改一串代码。pydantic/pydantic-ai(PydanticAI)用 Pydantic 团队的老本行回答了第一件——输出用 BaseModel 定义、运行前先校验,错误从运行时提前到编码时;第二件则用"模型即字符串"回答:换个厂商只改一个参数。如今

101