ChatGPT 开发指南:用 OpenAI Responses API 构建对话应用
很多人说“开发一个 ChatGPT”,实际要做的通常不是复制 ChatGPT 产品,而是通过 OpenAI API,在自己的网页、企业工具或应用中加入对话式 AI。本指南以 Node.js 为例,从一次最小调用开始,逐步搭出一个可继续扩展的服务端应用。
完成后,你会知道请求应该放在哪里、如何保留多轮上下文、何时使用流式输出、如何让模型调用自己的业务函数,以及上线前必须补齐哪些安全和成本控制。本文依据 2026 年 7 月 13 日核验的 OpenAI 官方开发文档撰写;模型名称、功能支持和限制可能变化,正式上线前应再次查看文末官方来源。
1、先分清 ChatGPT 产品与 OpenAI API
ChatGPT 是面向用户的产品,OpenAI API 是开发者把模型能力接入自己软件的接口。二者的界面、账号使用方式和计费管理不能简单等同。本指南讨论的是 API 应用开发,不是修改 ChatGPT 网页,也不需要抓取或模拟 ChatGPT 的前端请求。
一个最小应用通常分为三层:
- 浏览器或客户端负责收集用户输入和展示结果。
- 你自己的服务端负责保存密钥、校验用户、组织提示词并调用 OpenAI。
- OpenAI Responses API 负责生成响应,必要时请求服务端执行工具。
这张图最重要的信息是安全边界:OpenAI API Key 只能保存在服务端。如果把密钥写进浏览器 JavaScript、移动端安装包或公开仓库,其他人可能提取并滥用它。
2、准备 Node.js 项目与 API Key
本文示例需要较新的 Node.js 版本,并使用 OpenAI 官方 JavaScript SDK。新建一个空目录后执行:
npm init -y
npm install openai
npm pkg set type=module
在 OpenAI 平台创建 API Key,然后把它设置为环境变量。PowerShell 当前会话可以这样设置:
$env:OPENAI_API_KEY="你的_API_Key"
不要把真实密钥写入教程代码、提交到 Git,或发送给聊天机器人协助排错。生产环境应使用部署平台的 Secret 管理功能,并为开发、测试、生产分别配置项目和权限。
3、完成第一次 Responses API 调用
新建 app.js:
import OpenAI from "openai";
const client = new OpenAI();
const response = await client.responses.create({
model: "gpt-5.6",
instructions: "你是中文产品助手。回答要准确、简洁;不确定时明确说明。",
input: "用三点说明,为什么 API Key 不能放在浏览器前端。",
});
console.log(response.output_text);
运行:
node app.js
官方最新模型指南在本文核验时建议新项目从 gpt-5.6 开始。它是会变化的配置,不应散落在业务代码中。实际项目可通过环境变量设置模型默认值:
const model = process.env.OPENAI_MODEL || "gpt-5.6";
官方 SDK 提供的 response.output_text 会聚合响应中的文本。不要长期假设文本固定存在于 output[0].content[0].text,因为 output 还可能包含工具调用等其他项目。
3.1、instructions 与 input 怎么分工
instructions放应用级规则,例如角色、语言、边界和输出原则。input放当前用户任务与材料。- 来自用户的文本不要直接拼进开发者规则,否则容易让权限边界变得模糊。
如果结果不稳定,先明确任务、材料、限制和验收标准,再考虑更换模型。可继续阅读站内的提示词基础教程,把产品需求转换为可测试的指令。
4、把脚本改成真正的服务端接口
浏览器不应该直接调用 OpenAI。下面用 Express 建立一个最小后端:
npm install express
import express from "express";
import OpenAI from "openai";
const app = express();
const client = new OpenAI();
app.use(express.json({ limit: "32kb" }));
app.post("/api/chat", async (req, res) => {
const message = String(req.body?.message || "").trim();
if (!message || message.length > 4000) {
return res.status(400).json({ error: "请输入 1 到 4000 个字符。" });
}
try {
const response = await client.responses.create({
model: process.env.OPENAI_MODEL || "gpt-5.6",
instructions: "你是中文产品助手。不要编造事实;信息不足时先提问。",
input: message,
});
res.json({ text: response.output_text, responseId: response.id });
} catch (error) {
console.error("OpenAI request failed", error);
res.status(502).json({ error: "AI 服务暂时不可用,请稍后重试。" });
}
});
app.listen(3000, () => {
console.log("Server running at http://localhost:3000");
});
这只是教学起点。上线前还必须加入用户鉴权、请求频率限制、超时、日志脱敏、内容安全策略和费用上限。不要把底层错误对象原样返回给浏览器,因为其中可能含有不适合公开的调试信息。
5、实现多轮对话状态
模型请求本身不会自动知道某个浏览器用户之前说过什么。Responses API 支持多种上下文管理方式,其中一种简洁做法是把上一轮 response.id 作为下一轮的 previous_response_id:
const first = await client.responses.create({
model: "gpt-5.6",
input: "给我的读书应用取三个中文名字。",
store: true,
});
const second = await client.responses.create({
model: "gpt-5.6",
previous_response_id: first.id,
input: "保留第二个,再给它写一句介绍。",
store: true,
});
console.log(second.output_text);
在真实服务中,必须把响应 ID 与当前登录用户、会话 ID 绑定,不能接受浏览器随意提交一个响应 ID 就继续对话。还要设置会话过期和删除机制,并说明数据如何保留。
另一个选择是由应用自己保存并重新发送必要的消息。这样更容易控制数据存储,但你需要负责裁剪历史、控制上下文长度,并保留 API 后续处理所需的完整响应项目。无论选择哪种方案,都不要把“无限历史记录”当成默认设计;更稳妥的做法是保留近期对话,把较早内容压缩成经过核验的摘要。
6、用流式输出改善等待体验
普通请求会等模型生成完成后一次返回。长回答可能让页面像“卡住”一样。开启流式输出后,服务端可以通过 Server-Sent Events(SSE)逐步接收有明确类型的事件,并把文本增量转发给前端。
const stream = await client.responses.create({
model: "gpt-5.6",
input: "为一个记账应用写一段 300 字的新手说明。",
stream: true,
});
for await (const event of stream) {
if (event.type === "response.output_text.delta") {
process.stdout.write(event.delta);
}
}
流式输出改善的是“开始看到结果的时间”,不等于总生成时间一定更短。前端还要处理断线、取消、重复事件、最终完成状态和错误状态。涉及内容审核时也要谨慎:部分内容在完整输出前已经显示给用户,审核链路需要与产品风险相匹配。
7、用工具调用连接真实业务数据
模型只负责判断是否需要工具并生成参数,真正的数据库查询、日历写入或订单操作仍由你的服务端执行。基本循环是:
- 请求中提供工具名称、用途和 JSON Schema 参数。
- 模型返回函数调用请求。
- 服务端验证参数、检查用户权限并执行函数。
- 服务端用
function_call_output把结果交回模型。 - 模型根据工具结果生成面向用户的答案。
一个只读天气工具可以这样声明:
const tools = [
{
type: "function",
name: "get_weather",
description: "查询指定城市的当前天气。仅用于用户明确询问天气时。",
parameters: {
type: "object",
properties: {
city: { type: "string", description: "城市名称,例如杭州" },
},
required: ["city"],
additionalProperties: false,
},
strict: true,
},
];
const response = await client.responses.create({
model: "gpt-5.6",
input: "杭州今天适合骑车吗?",
tools,
});
接下来应遍历 response.output,识别 function_call 项,按工具名路由到你允许的函数,并校验参数。不要把模型生成的函数名、SQL、URL 或金额当成可信指令直接执行。
7.1、高影响操作必须二次确认
退款、发邮件、删除数据、创建订单等写操作,应满足至少三道条件:
- 当前用户本来就有执行权限;
- 服务端对参数做独立校验,并限制可操作范围;
- 执行前向用户展示动作、对象和关键参数,获得明确确认。
工具描述不是安全策略。即使提示词写着“不要误操作”,真正的权限控制仍必须在服务端代码、数据库规则和审计日志中实现。
8、用结构化输出替代脆弱的文本解析
如果下游代码需要固定字段,不要要求模型“返回一段看起来像 JSON 的文本”后就直接解析。Structured Outputs 可以让响应遵循提供的 JSON Schema,适合信息抽取、分类和表单生成。
const response = await client.responses.create({
model: "gpt-5.6",
input: "把这条反馈分类:登录后页面一直转圈,希望尽快修复。",
text: {
format: {
type: "json_schema",
name: "feedback_result",
strict: true,
schema: {
type: "object",
properties: {
category: {
type: "string",
enum: ["bug", "feature", "billing", "other"],
},
urgency: {
type: "string",
enum: ["low", "medium", "high"],
},
summary: { type: "string" },
},
required: ["category", "urgency", "summary"],
additionalProperties: false,
},
},
},
});
const result = JSON.parse(response.output_text);
结构符合 Schema 不代表事实一定正确。比如模型可以合法输出 urgency: "high",但是否真的紧急仍取决于你的业务规则。因此要分别验证“格式正确”和“业务判断正确”。
9、建立错误、限流与成本控制
一个能演示的脚本与一个能上线的服务,差别往往在异常路径。至少处理以下情况:
| 情况 | 应用应该怎么做 |
|---|---|
| 输入为空或过长 | 在调用 API 前拒绝并给出可执行提示 |
| 身份无效 | 返回 401 或 403,不调用模型 |
| 触发速率限制 | 使用带随机抖动的指数退避,限制最大重试次数 |
| 上游超时 | 允许用户取消,记录请求 ID,返回可重试状态 |
| 模型拒绝请求 | 识别拒绝状态,不把它伪装成系统故障 |
| 工具执行失败 | 返回结构化错误给模型,禁止无限循环调用 |
| 输出不满足业务规则 | 服务端校验,必要时进入人工复核 |
失败请求也可能占用速率配额,所以不能无间隔地不断重发。应用还应为单个用户设置日、周或月用量上限,监控异常增长,并在平台项目中配置预算提醒和支出限制。
成本评估不要只看单次 API 价格。还应记录每类任务的输入规模、输出长度、重试次数、工具调用次数和人工修正时间。模型选择可参考站内的模型任务评测方法,用自己的代表性任务做固定测试,而不是只比较一次演示结果。
10、补齐安全与隐私设计
OpenAI 官方安全最佳实践建议使用内容审核、对抗测试和人工复核等措施。具体组合取决于应用风险,但下面几项不应省略:
- 输入边界:限制长度、文件类型、URL 和允许的业务范围。
- 提示注入防护:把外部网页和文档当作不可信数据,不让其中的文字改变系统权限。
- 最小工具权限:能只读就不给写入权限,能按用户限定就不开放全库访问。
- 敏感信息处理:提交前删除不必要的密码、密钥、身份信息和公司机密。
- 人工复核:医疗、法律、财务、高影响决策和代码执行等场景保留人工检查。
- 审计与删除:记录必要的操作证据,同时设置保留期限和删除流程。
测试时既要覆盖正常问题,也要主动尝试“忽略之前规则”“输出隐藏指令”“读取其他用户数据”等对抗输入。测试目标不是证明模型永不出错,而是确认错误不会越过服务端权限边界。
11、用评测决定是否可以上线
不要用“感觉回答不错”作为发布标准。建立一组来自真实业务、但已经去除敏感信息的测试样本,并为每条样本记录:
- 任务是否完成;
- 事实是否正确;
- 输出格式是否合规;
- 工具是否选择正确;
- 是否出现越权或泄露;
- 响应时间、用量和人工修改时间。
每次修改模型、指令、Schema 或工具描述后重新运行同一组样本。先在测试项目和小流量环境验证,再逐步扩大范围。模型更新时,至少同时测试当前推理设置和相邻设置,不要只替换模型字符串就直接推到生产环境。
12、上线前检查清单
- API Key 只存在于服务端 Secret 或环境变量中。
- 开发、测试、生产使用独立项目和权限。
- 用户身份、请求长度、频率与总用量都有限制。
- 错误响应不会泄露密钥、内部提示或堆栈信息。
- 多轮会话数据与正确用户绑定,并有过期和删除机制。
- 工具参数经过服务端校验,高影响操作需要再次确认。
- 结构化输出同时经过 Schema 与业务规则验证。
- 流式输出支持取消、断线和最终状态处理。
- 代表性任务已经完成回归测试和对抗测试。
- 平台预算提醒、应用日志和异常告警已经启用。
13、下一步怎么扩展
先让最小文本接口稳定运行,再按真实需求增加能力。需要连续对话时加入会话状态;等待体验不好时加入流式输出;必须访问实时数据时再加入只读工具;下游程序需要可靠字段时使用结构化输出。不要一开始就把搜索、写操作、长记忆和多个工具全部开放。
如果你还没有形成稳定的指令模板,下一步先完成ChatGPT 提示词实战,为每个 API 任务写清输入、限制与验收标准,再把它们纳入自动评测。