共计 2053 个字符,预计需要花费 6 分钟才能阅读完成。
背景痛点
构建一个高效稳定的 ChatGPT 网站,开发者通常会遇到几个关键挑战:

- API 延迟问题 :直接调用 OpenAI API 时,响应时间受网络波动和 API 负载影响明显,尤其是在国际网络环境下。
- 上下文管理 :长对话场景需要维护历史消息,但 GPT 模型有 token 限制(如 4096 个 token),需设计合理的截断策略。
- 流式响应体验 :传统请求 - 响应模式会让用户等待完整结果,而逐字返回的流式响应能显著提升体验。
技术选型
API 接入方式对比
- 直接调用 OpenAI API
- 优点:实现简单,无需额外基础设施
-
缺点:暴露 API 密钥风险高,无法灵活添加缓存 / 重试逻辑
-
自建 Node.js 代理层
- 优点:可集中管理密钥、实现请求优化
- 缺点:需要维护服务器成本
前端框架选择
- 纯前端(如 React)
- 适合简单 Demo,但 SEO 不友好
- Next.js 服务端渲染
- 支持 SSR 优化首屏加载,同时保留 CSR 交互性
核心实现
Next.js 前端实现
// pages/index.tsx
export default function ChatPage() {const [messages, setMessages] = useState<Message[]>([]);
// 流式响应处理
const handleStream = async (chunk: string) => {setMessages(prev => [...prev.slice(0, -1), {...prev[prev.length-1],
content: prev[prev.length-1].content + chunk
}]);
};
}
Node.js 代理中间件
// api/proxy.ts
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// 密钥验证
if (!verifyApiKey(req.headers.authorization)) {return res.status(401).json({error: 'Unauthorized'});
}
// 请求转发
const openaiRes = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {'Authorization': `Bearer ${process.env.OPENAI_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'gpt-3.5-turbo',
messages: req.body.messages,
stream: true // 启用流式
})
});
// 流式转发
openaiRes.body.pipe(res);
}
Markdown 流式解析
// utils/parseStream.ts
function parseStream(
stream: ReadableStream,
onChunk: (text: string) => void
) {const reader = stream.getReader();
const decoder = new TextDecoder();
async function process() {const { done, value} = await reader.read();
if (done) return;
const text = decoder.decode(value);
const chunks = text.split('data:')
.filter(chunk => chunk.trim() !== '[DONE]');
chunks.forEach(chunk => {
try {const data = JSON.parse(chunk);
onChunk(data.choices[0].delta?.content || '');
} catch {/* 忽略解析错误 */}
});
process(); // 递归处理下一批数据}
process();}
性能优化
- 请求合并 :当用户快速连续发送消息时,合并多个请求为一个批次
- 错误重试 :对 5xx 错误实现指数退避重试机制
- 内存缓存 :对常见问题答案缓存 5 -10 秒,减少 API 调用
安全考量
- API 密钥保护 :永远不要在前端暴露密钥,应通过后端代理
- 输入过滤 :清理用户输入中的敏感信息和恶意脚本
- 速率限制 :按用户 /IP 限制每分钟请求次数
避坑指南
- 流式响应中断
- 现象:长响应时连接意外关闭
-
方案:添加心跳检测,超时自动重连
-
token 超额
- 现象:长对话返回截断结果
-
方案:实时计算 token 数,主动截断旧消息
-
移动端兼容性
- 现象:iOS 设备流式渲染卡顿
- 方案:减少 DOM 操作频率,使用 requestAnimationFrame
进一步优化建议
尝试实现对话历史本地存储(使用 IndexedDB),并思考这些优化方向:
- 长对话场景下,如何智能压缩历史消息(如删除无关问答)
- 基于用户设备性能动态调整流式响应速度
- 对专业领域问题添加本地知识库混合应答
正文完
