01MVP
MVP 指南

OpenAI API 调用原理:文本、语音、JSON 输出与兼容层

搞懂网站接入 ChatGPT 类能力的完整链路:客户端、服务端、对话状态、结构化输出、工具调用、语音链路,以及兼容 OpenAI API 的真实边界。

这篇写给想接入 AI 对话能力,但还没把底层链路想清楚的人。

很多人会说“我想在网站里调用 ChatGPT”。但工程上,你真正做的通常不是“接 ChatGPT 网页版”,而是:接一个模型 API,然后自己补上 UI、鉴权、上下文、工具调用、数据校验、日志、重试和成本控制。

截至 2026-03-14,如果你看 OpenAI 的官方文档,会发现它现在主要围绕 Responses APIRealtime APIStructured OutputsFunction 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 形状和字段名,不等于完全兼容模型能力、流式事件格式、音频能力和边界行为。

最小行动路径(先把主链路想明白)

  1. 先判断你做的是 文本聊天结构化输出工具型 Agent,还是 实时语音对话
  2. 不要把正式 API Key 直接放前端;前端请求先到你自己的服务端,或者由服务端签发短期令牌。
  3. 在你自己的服务端统一管理 system prompt、对话历史、用户身份、限流和日志。
  4. 只要结果要给程序消费,就不要只靠提示词“求它返回 JSON”,而要上 JSON Schema 或工具调用。
  5. 需要联网、查库、调业务系统时,不要让模型“编”,而要把能力包装成 tool
  6. 对长对话一定要做上下文裁剪、摘要或状态归档,不然成本、延迟和错误率都会上去。

如果你只记住一件事

模型只是推理层。真正的产品闭环,是 输入层 + 状态层 + 工具层 + 输出校验层 一起工作。

系统梳理

全景地图

场景常见接口延迟要求是否适合严格 JSON备注
普通网页聊天Responses/v1/chat/completions适合最常见起点
表单抽取 / 分类 / 工作流Responses + Structured Outputs,或兼容层的 json_schema非常适合重点是稳定和可校验
工具调用型 AgentResponses / 兼容层 tools中到高适合本质是“模型做决策,系统做执行”
实时语音助手Realtime APISTT -> LLM -> TTS前台链路通常不直接追求严格 JSON重点是低延迟和打断控制

主题一:先分清 ChatGPT 产品和 API

一句话:你的网站不会去“嵌入 ChatGPT 官网”,而是调用底层模型接口,再自己拼出一套产品。

一个最常见的链路长这样:

浏览器聊天框
  -> 你的后端
  -> 模型 API
  -> 你的工具层 / 数据库 / 搜索 / CRM
  -> 你的后端
  -> 浏览器展示

这里面至少有 4 个角色:

  • 前端:负责输入框、消息列表、录音、播放、打字机效果。
  • 你的服务端:负责鉴权、拼 prompt、管理会话、调用模型、执行工具、做日志和限流。
  • 模型 API:负责理解输入、生成文本、决定要不要调用工具。
  • 业务系统:数据库、搜索、订单系统、知识库、第三方接口。

最常见误区

不要把长期有效的 OpenAI Key 直接写进前端。文本接口通常走你自己的后端;实时语音如果要浏览器直连,也应该先由你自己的后端签发短期令牌。

主题二:一次标准文本调用,到底发生了什么

一句话:所谓“调用 ChatGPT”,工程上就是一次带上下文的推理请求,加上可能发生的工具回环。

一个标准流程通常是:

  1. 用户在前端发来一句话,比如“帮我总结这个工单”。
  2. 你的后端拿到请求后,补上:
    • system prompt
    • 最近几轮对话
    • 当前用户 ID / 会话 ID
    • 业务约束(例如只能返回中文、只能输出 JSON)
  3. 后端把这些内容发给模型。
  4. 模型返回两类结果之一:
    • 直接回答文本
    • 要求调用某个工具,例如 search_ticketquery_order
  5. 如果是工具调用,你的后端执行工具,把结果再喂给模型。
  6. 模型生成最终答复,后端再把结果流式或一次性返回给前端,并把这轮消息持久化。

很多人以为“模型会记住上下文”,其实大多数情况下,是你每次都在重新发送上下文,或者依赖服务端状态机制帮你记。按当前 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,也不要省掉服务端校验。推荐做法依然是:

  1. 模型按 schema 生成;
  2. 你的服务端再用 ZodJSON Schema validator 校验;
  3. 校验失败就重试、降级或走人工兜底。

另一个常见误区

“让模型返回 JSON”和“让模型稳定返回你想要的 JSON”不是一回事。前者是格式问题,后者是约束、校验和失败处理问题。

主题四:Tool / Function Calling 的本质是什么

一句话:模型负责“决定该调什么”,真正去调接口、查数据库、扣库存、发邮件的,永远应该是你的系统。

例如你定义一个工具:

{
  "type": "function",
  "name": "query_order",
  "description": "根据订单号查询订单状态",
  "parameters": {
    "type": "object",
    "properties": {
      "order_id": {
        "type": "string"
      }
    },
    "required": ["order_id"],
    "additionalProperties": false
  }
}

典型回环是:

  1. 你把工具 schema 发给模型;
  2. 模型判断“这题我不能瞎编,需要查订单”,于是返回 query_order({ order_id: "..." })
  3. 你的后端执行真实查询;
  4. 你把查询结果再发回模型;
  5. 模型把“机器结果”翻译成“用户能看懂的话”。

这就是为什么业内很多人会说: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 只做浅兼容,参数校验并不严格
都支持 streamSSE 事件名称、delta 结构、结束信号可能不同
都有 response_format有的不支持严格 json_schema,只支持弱 JSON
都支持同一个 SDK 形状模型能力、上下文长度、速率限制、报错风格完全不同

选兼容层时最容易踩的坑

不要只看“能不能跑起来”,还要看它是否真的兼容你要用的那部分能力:流式输出工具调用严格 JSON图片输入语音Realtime速率限制错误码账单统计

横向对比:如果你现在要落地,应该怎么选

需求推荐主链路
做一个普通网页聊天框先用 Responses 或兼容的 /v1/chat/completions
要把模型结果写入数据库 / 工作流优先 Structured Outputs / JSON Schema
要查订单、查知识库、调 CRMtool / function calling
要做网页语音助手、强调低延迟优先 Realtime
要做可审计的客服 / 语音工作流优先 STT -> LLM -> TTS

主题七:工程上真正决定稳定性的,不是模型名,而是这份清单

一句话:同一个模型,接法不同,产品稳定性可以差很多。

上线前至少把下面这些补齐:

  1. 鉴权:正式 Key 只放服务端;实时场景用短期令牌。
  2. 上下文管理:不要无限拼历史消息,要做摘要和裁剪。
  3. 结果校验:JSON 一定服务端二次校验。
  4. 超时 / 重试:工具超时、上游抖动、流式中断都要兜底。
  5. 日志:记录 prompt 版本、模型名、延迟、失败率、工具调用链。
  6. 成本控制:长对话、语音和多轮工具回环都很烧钱。
  7. 降级策略:上游炸了时,至少能退回纯文本回答或人工接管。

深度补充

原理与背景(为什么调用链路会长这样)

你可以把整套系统想成这样:

  • 模型 像 CPU,负责计算下一步该说什么;
  • API 像操作系统调用,规定怎么喂输入、怎么拿输出;
  • 你的服务端 像调度层,负责状态、权限、工具和可靠性;
  • 前端 像交互壳层,负责把复杂过程变成用户感知到的“像在聊天”。

所以用户看到的是一句一句对话,工程里实际跑的是:

输入处理 -> 上下文拼装 -> 模型推理 -> 工具回环 -> 输出校验 -> 前端渲染

语音只是在这个基础上,再加一层音频输入输出协议。

局限与边界

  • “兼容 OpenAI”不代表和 OpenAI 行为完全一样,尤其在 streamingtoolsjson_schemamultimodalrealtime 上差异很大。
  • 即使用了严格 JSON,模型也仍然可能因为拒答、长度截断、上游错误而给不出你想要的结果。
  • 语音系统除了模型问题,还有麦克风权限、网络抖动、噪声、回声消除、设备兼容这些传统音视频问题。
  • 长对话不做状态治理,迟早会在成本、速度和稳定性上出问题。

延伸资源

目录