模型 API、消息角色与结构化输出
# 模型 API、消息角色与结构化输出
理解模型 API 的请求和响应结构,分清 Instructions、用户输入、流式输出与结构化输出,并完成一次可运行的 Python 调用。
# 先记住一句话
模型 API 是应用与模型服务之间的协议边界:应用提交模型、指令和输入,模型返回输出、状态与用量;返回成功只代表请求完成,不代表业务结果一定正确。
# 模型 API 解决什么问题
直接部署大型模型需要 GPU、推理框架、容量管理和版本运维。模型 API 把这些能力封装成远程服务,应用通过 HTTP 或官方 SDK 调用。
业务代码
↓ 调用本地 ModelClient
提供方 SDK
↓ HTTPS 请求
模型 API
↓ 调度推理服务
模型生成结果
↓ 返回状态、输出和 Usage
业务代码校验并继续处理
2
3
4
5
6
7
8
9
SDK 只是对 HTTP API 的封装。它可以帮助序列化参数、鉴权和解析响应,但不会替你完成用户授权、业务校验、幂等写入或质量评测。
# Instructions、Input 和消息角色
| 内容 | 含义 | 应放什么 |
|---|---|---|
| Instructions / Developer | 应用提供的稳定规则 | 职责、边界、输出要求 |
| User | 当前用户的请求 | 问题、文章、任务参数 |
| Assistant | 模型此前生成的消息 | 需要保留的对话结果 |
| Tool | 工具执行后返回的真实结果 | 查询数据、执行状态、错误 |
高优先级指令可以约束模型行为,但不能代替服务端权限。例如 Instructions 可以写 “只能读取当前用户的笔记” ,真正的数据库查询仍必须带用户条件。
不同提供方对 System 和 Developer 的命名或优先级可能不同。接入时应读取对应 API 文档,不要把一个 SDK 的字段名当作行业统一规范。
# 最小完整调用
下面示例使用 OpenAI Python SDK 和 Responses API。先安装依赖,并把密钥放入环境变量:
pip install openai
export OPENAI_API_KEY="你的 API Key"
2
不要把真实密钥写进源码、笔记或 Git 仓库。
# 文件位置:examples/summarize.py
from openai import OpenAI
# SDK 默认从 OPENAI_API_KEY 环境变量读取密钥。
client = OpenAI()
def summarize_note(note: str) -> str:
if not note.strip():
raise ValueError("笔记内容不能为空")
response = client.responses.create(
# 示例模型来自当前官方文档;生产项目应使用配置并固定经过评测的版本。
model="gpt-6-astra",
instructions="请把技术笔记压缩成三句话,不补充原文没有的事实。",
input=note,
)
# output 可能含有多种项目,使用 SDK 聚合后的 output_text 更安全。
if not response.output_text:
raise RuntimeError("模型没有返回文本结果")
return response.output_text
if __name__ == "__main__":
result = summarize_note("连接池应长期复用,并设置最大连接数和等待监控。")
print(result)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
OpenAI 官方文档特别说明,output 数组可能同时包含消息、工具调用和推理信息,不能假定文本永远位于 output[0].content[0].text。官方 SDK 提供的 output_text 会聚合文本输出,适合普通文本场景。
# 普通文本和结构化输出怎样选择
- 给人阅读的回答、解释和文章可以返回普通文本;
- 需要写数据库、渲染固定 UI 或驱动后续代码时,应优先使用结构化输出;
- 只在 Prompt 中要求 “返回 JSON” 不能保证字段完整和类型正确;
- Structured Outputs 会按照提供的 JSON Schema 约束结构,但业务含义仍需应用校验。
下面把模型输出直接解析成 Pydantic 对象:
# 文件位置:examples/extract_topic.py
from openai import OpenAI
from pydantic import BaseModel, Field
class NoteTopic(BaseModel):
title: str = Field(description="笔记主题")
keywords: list[str] = Field(description="2~5 个检索关键词")
needs_review: bool = Field(description="内容是否存在需要人工确认的信息")
client = OpenAI()
def extract_topic(note: str) -> NoteTopic:
response = client.responses.parse(
model="gpt-6-astra",
input=[
{"role": "system", "content": "从技术笔记中提取主题,不补充外部事实。"},
{"role": "user", "content": note},
],
# SDK 根据 Pydantic 模型生成并解析结构化输出。
text_format=NoteTopic,
)
if response.output_parsed is None:
raise RuntimeError("模型未返回可解析的主题")
return response.output_parsed
if __name__ == "__main__":
topic = extract_topic("Go 的 sql.DB 是并发安全的连接池,应长期共享。")
print(topic.model_dump())
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Schema 只能保证 needs_review 是布尔值,不能保证模型对内容风险的判断一定正确。关键业务条件还要使用程序规则、真实数据或人工审核。
# 流式输出是什么
非流式调用要等模型完成后一次性返回。流式调用会在生成过程中持续发送事件,常见传输方式是 Server-Sent Events(SSE)。
请求开始
↓
response.created
↓
response.output_text.delta 多次到达
↓
response.completed 或错误事件
2
3
4
5
6
7
客户端不能只拼接文本增量,还要处理完成、拒绝、取消和错误事件。用户断开时应取消上游模型请求,防止页面已经关闭,服务端仍继续消耗 Token。
# 错误应该怎样分类
| 类型 | 例子 | 处理方式 |
|---|---|---|
| 参数错误 | Schema 不支持、输入超限 | 修正请求,不要盲目重试 |
| 认证与权限 | Key 无效、项目无权限 | 停止并检查配置 |
| 限流 | 请求数或 Token 超额 | 读取重试提示并退避 |
| 暂时性故障 | 超时、服务端错误 | 有上限地重试 |
| 安全拒绝 | 模型拒绝处理内容 | 作为明确业务状态返回 |
| 解析失败 | 输出缺失或不符合预期 | 记录原始状态并安全失败 |
模型调用可能已经执行成功,但客户端因为网络中断没有收到结果。会产生费用或副作用的操作需要幂等键、请求记录或提供方查询能力,不能把所有超时都直接重放。
# 项目面试怎样表达
我把模型 SDK 封装在独立客户端中,业务层只传任务输入并接收领域结果。调用时设置模型版本、超时和输出预算;普通文本使用聚合字段读取,程序要消费的结果使用 Schema 约束并再次做业务校验。日志记录请求 ID、模型、状态、延迟和 Token,但不记录密钥与完整敏感输入。
# 高频面试题与回答
1. 为什么不能直接读取 output 数组的第一个元素?参考答案
因为一个响应可能包含文本消息、工具调用和推理信息等多种项目,第一个元素不保证就是文本。普通文本场景应使用 SDK 提供的聚合字段,复杂场景则按项目类型逐项处理。
2. Structured Outputs 能保证答案正确吗?参考答案
它主要保证输出符合指定 Schema,例如字段存在、类型和枚举合法,但不能保证字段内容在业务上真实正确。金额、权限和资源状态等关键数据仍要由程序或可信数据源校验。
3. 流式输出为什么不会降低模型总计算量?参考答案
流式输出只是把已经生成的增量更早发送给客户端,改善首字显示时间和用户体验。模型仍然要逐 Token 完成生成,因此总计算量和最终 Token 数不会因为使用流式传输自动减少。
4. 模型 API 返回 200 是否代表业务成功?参考答案
不代表。它只说明 API 请求成功完成,模型仍可能拒绝、返回空内容、产生事实错误或给出不满足业务规则的值。应用还要检查响应状态、解析结果、业务约束和必要证据。
# 接下来学什么
下一篇学习 Temperature、Top P 与输出预算,理解生成参数能控制什么,以及它们为什么不能替代业务校验。