OpenAI API 调用原理:文本、语音、JSON 输出与兼容层
搞懂网站接入 ChatGPT 类能力的完整链路:客户端、服务端、对话状态、结构化输出、工具调用、语音链路,以及兼容 OpenAI API 的真实边界。
这篇写给想接入 AI 对话能力,但还没把底层链路想清楚的人。
很多人会说“我想在网站里调用 ChatGPT”。但工程上,你真正做的通常不是“接 ChatGPT 网页版”,而是:接一个模型 API,然后自己补上 UI、鉴权、上下文、工具调用、数据校验、日志、重试和成本控制。
截至 2026-03-14,如果你看 OpenAI 的官方文档,会发现它现在主要围绕 Responses API、Realtime API、Structured Outputs、Function Calling 这些能力来组织;但在行业里,很多第三方平台和中转层依然广泛兼容 /v1/chat/completions 这套请求格式。所以这篇会把两条线一起讲清楚。
核心要点速览
TL;DR(先拿走结论)
- 网站里的 AI 对话,本质上不是“调用 ChatGPT 产品”,而是“调用模型接口 + 自己管理产品逻辑”。
- 现在常见有两套思路:
OpenAI 原生接口(如Responses/Realtime)和兼容 OpenAI 的聊天接口(最常见是/v1/chat/completions)。 - 如果你希望模型稳定返回机器可读结果,优先级一般是:
Structured Outputs / JSON Schema>tool / function calling>JSON mode>纯提示词要求返回 JSON。 - 语音对话最常见有两种架构:
Realtime 低延迟直连,或STT -> 文本模型 -> 工具 -> TTS的可控管线。 - “兼容 OpenAI API”通常兼容的是
HTTP 形状和字段名,不等于完全兼容模型能力、流式事件格式、音频能力和边界行为。
最小行动路径(先把主链路想明白)
- 先判断你做的是
文本聊天、结构化输出、工具型 Agent,还是实时语音对话。 - 不要把正式
API Key直接放前端;前端请求先到你自己的服务端,或者由服务端签发短期令牌。 - 在你自己的服务端统一管理
system prompt、对话历史、用户身份、限流和日志。 - 只要结果要给程序消费,就不要只靠提示词“求它返回 JSON”,而要上
JSON Schema或工具调用。 - 需要联网、查库、调业务系统时,不要让模型“编”,而要把能力包装成
tool。 - 对长对话一定要做上下文裁剪、摘要或状态归档,不然成本、延迟和错误率都会上去。
如果你只记住一件事
模型只是推理层。真正的产品闭环,是 输入层 + 状态层 + 工具层 + 输出校验层 一起工作。
系统梳理
全景地图
| 场景 | 常见接口 | 延迟要求 | 是否适合严格 JSON | 备注 |
|---|---|---|---|---|
| 普通网页聊天 | Responses 或 /v1/chat/completions | 中 | 适合 | 最常见起点 |
| 表单抽取 / 分类 / 工作流 | Responses + Structured Outputs,或兼容层的 json_schema | 中 | 非常适合 | 重点是稳定和可校验 |
| 工具调用型 Agent | Responses / 兼容层 tools | 中到高 | 适合 | 本质是“模型做决策,系统做执行” |
| 实时语音助手 | Realtime API 或 STT -> LLM -> TTS | 高 | 前台链路通常不直接追求严格 JSON | 重点是低延迟和打断控制 |
主题一:先分清 ChatGPT 产品和 API
一句话:你的网站不会去“嵌入 ChatGPT 官网”,而是调用底层模型接口,再自己拼出一套产品。
一个最常见的链路长这样:
浏览器聊天框
-> 你的后端
-> 模型 API
-> 你的工具层 / 数据库 / 搜索 / CRM
-> 你的后端
-> 浏览器展示这里面至少有 4 个角色:
前端:负责输入框、消息列表、录音、播放、打字机效果。你的服务端:负责鉴权、拼 prompt、管理会话、调用模型、执行工具、做日志和限流。模型 API:负责理解输入、生成文本、决定要不要调用工具。业务系统:数据库、搜索、订单系统、知识库、第三方接口。
最常见误区
不要把长期有效的 OpenAI Key 直接写进前端。文本接口通常走你自己的后端;实时语音如果要浏览器直连,也应该先由你自己的后端签发短期令牌。
主题二:一次标准文本调用,到底发生了什么
一句话:所谓“调用 ChatGPT”,工程上就是一次带上下文的推理请求,加上可能发生的工具回环。
一个标准流程通常是:
- 用户在前端发来一句话,比如“帮我总结这个工单”。
- 你的后端拿到请求后,补上:
system prompt- 最近几轮对话
- 当前用户 ID / 会话 ID
- 业务约束(例如只能返回中文、只能输出 JSON)
- 后端把这些内容发给模型。
- 模型返回两类结果之一:
- 直接回答文本
- 要求调用某个工具,例如
search_ticket、query_order
- 如果是工具调用,你的后端执行工具,把结果再喂给模型。
- 模型生成最终答复,后端再把结果流式或一次性返回给前端,并把这轮消息持久化。
很多人以为“模型会记住上下文”,其实大多数情况下,是你每次都在重新发送上下文,或者依赖服务端状态机制帮你记。按当前 OpenAI 官方的 conversation state 文档能力来看,服务端可以帮助你维护部分状态;但从产品设计角度,我仍然建议你把“应用自己的会话状态”当成主数据源,这样迁移到兼容 API 或多供应商时更稳。这一点属于基于官方能力现状做出的工程建议。
推荐做法
把“模型会话状态”和“应用业务状态”分开。前者服务推理,后者服务产品可靠性、审计和迁移。
主题三:为什么很多产品都要求模型返回 JSON
一句话:因为自然语言适合给人看,JSON 才适合给程序接。
常见原因有 4 个:
- 前端要稳定渲染卡片、表格、按钮,而不是猜模型这一轮的文案结构。
- 后端要把结果塞进数据库、工作流、CRM、审核系统。
- 你想区分“模型说的话”和“模型建议执行的动作”。
- 你需要做校验、重试、兜底和监控。
这 4 种方案,可靠性依次递增:
| 方案 | 你怎么做 | 可靠性 | 适用场景 |
|---|---|---|---|
| 纯提示词约束 | 提示词里写“请返回 JSON” | 最低 | 临时 demo |
| JSON mode | 让输出必须是合法 JSON | 中 | 结构简单、容错高 |
| Structured Outputs / JSON Schema | 明确字段、类型、必填项 | 高 | 正式生产 |
| Tool / Function Calling | 模型不是“回答结构”,而是“选择动作 + 参数” | 很高 | 查系统、触发工作流 |
示意 payload 可以长这样。下面这个写法更接近很多“兼容 OpenAI”接口的通用样子:
{
"model": "gpt-4.1",
"messages": [
{
"role": "system",
"content": "你是一个客服工单分类助手,只返回符合 schema 的 JSON。"
},
{
"role": "user",
"content": "用户说付款后没有收到激活邮件。"
}
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "ticket_result",
"strict": true,
"schema": {
"type": "object",
"properties": {
"category": {
"type": "string"
},
"priority": {
"type": "string",
"enum": ["low", "medium", "high"]
},
"should_escalate": {
"type": "boolean"
}
},
"required": ["category", "priority", "should_escalate"],
"additionalProperties": false
}
}
}
}但就算用了严格 schema,也不要省掉服务端校验。推荐做法依然是:
- 模型按 schema 生成;
- 你的服务端再用
Zod或JSON Schema validator校验; - 校验失败就重试、降级或走人工兜底。
另一个常见误区
“让模型返回 JSON”和“让模型稳定返回你想要的 JSON”不是一回事。前者是格式问题,后者是约束、校验和失败处理问题。
主题四:Tool / Function Calling 的本质是什么
一句话:模型负责“决定该调什么”,真正去调接口、查数据库、扣库存、发邮件的,永远应该是你的系统。
例如你定义一个工具:
{
"type": "function",
"name": "query_order",
"description": "根据订单号查询订单状态",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string"
}
},
"required": ["order_id"],
"additionalProperties": false
}
}典型回环是:
- 你把工具 schema 发给模型;
- 模型判断“这题我不能瞎编,需要查订单”,于是返回
query_order({ order_id: "..." }); - 你的后端执行真实查询;
- 你把查询结果再发回模型;
- 模型把“机器结果”翻译成“用户能看懂的话”。
这就是为什么业内很多人会说:tool calling 才是真正可接业务系统的接口层,因为它把“语言生成”和“系统执行”分开了。
主题五:语音对话为什么比文本复杂一倍
一句话:文本只有“发消息 -> 回消息”,语音还要多处理录音、切片、打断、回声、说话结束判断和音频播放。
语音产品最常见有两种架构。
方案 A:Realtime 低延迟直连
链路大致是:
麦克风
-> 浏览器采集音频
-> WebRTC / WebSocket 实时发送
-> Realtime 会话
-> 模型理解 / 转写 / 决策
-> 可选工具调用
-> 实时音频返回
-> 浏览器播放这个方案的优点是:
- 延迟低,体验更像真人对话;
- 支持说话打断;
- 适合口语陪练、语音助手、销售陪聊、AI 电话等场景。
但复杂点也很明显:
- 浏览器和服务端要处理实时连接;
- 你要决定用
WebRTC还是WebSocket; - 要处理
VAD(语音活动检测),也就是“用户什么时候算说完了”; - 认证一般不是前端直接拿正式 Key,而是由你的后端创建短期会话令牌。
方案 B:STT -> 文本模型 -> 工具 -> TTS
链路大致是:
麦克风
-> 录音上传
-> 语音转文字(STT)
-> 文本模型推理 / JSON / Tool Calling
-> 文本转语音(TTS)
-> 返回音频播放这个方案的优点是:
- 工程更稳,调试更容易;
- 更容易插入严格 JSON、工具调用、审核、缓存和人工兜底;
- 更适合客服、表单采集、业务工作流、电话质检这类“结果正确性”比“极致实时性”更重要的场景。
一个很实用的行业判断是:
| 你更在意什么 | 更推荐 |
|---|---|
| 像真人聊天一样顺滑、可打断、低延迟 | Realtime |
| 输出结构稳定、可审计、易插业务系统 | STT -> LLM -> TTS |
按当前 OpenAI 官方文档和模型页能力来看,Realtime 路线非常适合做实时语音交互;但如果你业务里很依赖严格结构化输出,很多团队仍然会把“结构化决策”放在文本 / 工具层来做,而不是要求音频主链路直接吐最终 JSON。这一点是结合当前官方能力描述做出的工程推断。
主题六:什么叫“兼容 OpenAI API”,它到底兼容了什么
一句话:大多数所谓兼容层,兼容的是 请求格式,不是 行为语义。
业内最常见的兼容目标是这套形状:
- 端点长得像
/v1/chat/completions - 请求里有
model - 对话输入是
messages - 支持
stream: true - 支持
tools/functions - 部分支持
response_format
所以你在代码里经常能看到一种非常“通用”的封装:
const client = createLLMClient({
baseURL: process.env.LLM_BASE_URL,
apiKey: process.env.LLM_API_KEY,
});
const result = await client.chat.completions.create({
model: "some-model",
messages,
stream: true,
tools,
response_format,
});这也是为什么很多 SaaS 会说“支持 OpenAI 兼容接口”:因为这样前端 SDK、后端封装、Agent 框架和中转层都更容易接。
但真实世界里的差异非常大:
| 看起来一样的地方 | 真正可能不一样的地方 |
|---|---|
都叫 messages | 多模态内容数组格式可能不同 |
都有 tools | 有的 provider 只做浅兼容,参数校验并不严格 |
都支持 stream | SSE 事件名称、delta 结构、结束信号可能不同 |
都有 response_format | 有的不支持严格 json_schema,只支持弱 JSON |
| 都支持同一个 SDK 形状 | 模型能力、上下文长度、速率限制、报错风格完全不同 |
选兼容层时最容易踩的坑
不要只看“能不能跑起来”,还要看它是否真的兼容你要用的那部分能力:流式输出、工具调用、严格 JSON、图片输入、语音、Realtime、速率限制、错误码、账单统计。
横向对比:如果你现在要落地,应该怎么选
| 需求 | 推荐主链路 |
|---|---|
| 做一个普通网页聊天框 | 先用 Responses 或兼容的 /v1/chat/completions |
| 要把模型结果写入数据库 / 工作流 | 优先 Structured Outputs / JSON Schema |
| 要查订单、查知识库、调 CRM | 用 tool / function calling |
| 要做网页语音助手、强调低延迟 | 优先 Realtime |
| 要做可审计的客服 / 语音工作流 | 优先 STT -> LLM -> TTS |
主题七:工程上真正决定稳定性的,不是模型名,而是这份清单
一句话:同一个模型,接法不同,产品稳定性可以差很多。
上线前至少把下面这些补齐:
鉴权:正式 Key 只放服务端;实时场景用短期令牌。上下文管理:不要无限拼历史消息,要做摘要和裁剪。结果校验:JSON 一定服务端二次校验。超时 / 重试:工具超时、上游抖动、流式中断都要兜底。日志:记录 prompt 版本、模型名、延迟、失败率、工具调用链。成本控制:长对话、语音和多轮工具回环都很烧钱。降级策略:上游炸了时,至少能退回纯文本回答或人工接管。
深度补充
原理与背景(为什么调用链路会长这样)
你可以把整套系统想成这样:
模型像 CPU,负责计算下一步该说什么;API像操作系统调用,规定怎么喂输入、怎么拿输出;你的服务端像调度层,负责状态、权限、工具和可靠性;前端像交互壳层,负责把复杂过程变成用户感知到的“像在聊天”。
所以用户看到的是一句一句对话,工程里实际跑的是:
输入处理 -> 上下文拼装 -> 模型推理 -> 工具回环 -> 输出校验 -> 前端渲染
语音只是在这个基础上,再加一层音频输入输出协议。
局限与边界
- “兼容 OpenAI”不代表和 OpenAI 行为完全一样,尤其在
streaming、tools、json_schema、multimodal、realtime上差异很大。 - 即使用了严格 JSON,模型也仍然可能因为拒答、长度截断、上游错误而给不出你想要的结果。
- 语音系统除了模型问题,还有麦克风权限、网络抖动、噪声、回声消除、设备兼容这些传统音视频问题。
- 长对话不做状态治理,迟早会在成本、速度和稳定性上出问题。
延伸资源
- OpenAI 官方:
Conversation state - OpenAI 官方:
Function calling - OpenAI 官方:
Structured outputs - OpenAI 官方:
Realtime guide - OpenAI 官方:
Realtime model - 站内延伸:
全球 API 供应商怎么选