ChatGPT网站源码实战:从零搭建高可用对话系统的关键技术与避坑指南

📅 发布时间:2026/7/10 0:03:37 👁️ 浏览次数:
ChatGPT网站源码实战:从零搭建高可用对话系统的关键技术与避坑指南
ChatGPT网站源码实战从零搭建高可用对话系统的关键技术与避坑指南背景痛点自建 ChatGPT 网站的三座大山高并发响应延迟传统同步阻塞式调用 OpenAI 接口请求排队导致 P99 延迟动辄 2 s前端白屏时间长用户流失率飙升。对话状态丢失HTTP 无状态每次请求都要把历史消息再传一遍报文体膨胀 5~10 倍带宽浪费且易触网关 413 限制。流式输出卡顿后端一次性await response.text()再转发首字节时间TTFB高前端未做ReadableStream渐进渲染出现“一口气蹦出”的顿挫感。架构对比为什么放弃 SSR转向 Next.js FastAPI维度纯后端渲染SSR前后端分离Next.js FastAPI首屏服务端拼 HTML白屏到可交互 1.2 s静态骨架 SSR 仅用于 SEO可交互 400 ms流式输出需整页刷新无法局部更新WebSocket / SSE 直接推流到组件零刷新并发单进程渲染CPU 密集FastAPI 异步协程Next.js Edge Function 边缘缓存运维同仓库混合部署耦合高独立容器CI/CD 互不阻塞结论把“渲染”交给 Next.js把“业务 并发”交给 FastAPI二者通过 Redis 与 WebSocket 解耦性能与可维护性兼得。核心实现1. Redis 对话上下文缓存Python设计目标单聊会话 ≤ 50 轮JSON ≤ 128 KB过期自动淘汰防止僵尸 Key# cache_schema.py import json, time, redis from typing import List, Dict r redis.Redis(host127.0.0.1, decode_responsesTrue) TTL 30 * 60 # 30 min class ChatContext: 时间复杂度O(1) 读写全部走 Redis hash expire def __init__(self, uid: str): self.key fchat:{uid} def push(self, role: str, content: str): # 先读后写保证原子性 pipe r.pipeline() msgs json.loads(r.get(self.key) or []) msgs.append({role: role, content: content, ts: int(time.time())}) # 保留最近 50 条防止无限膨胀 msgs msgs[-50:] pipe.set(self.key, json.dumps(msgs, ensure_asciiFalse)) pipe.expire(self.key, TTL) pipe.execute() def get(self) - List[Dict]: return json.loads(r.get(self.key) or [])2. WebSocket 流式分块传输TypeScript后端FastAPI推送片段# ws_stream.py from fastapi import WebSocket import openai, json, asyncio async def stream_chat(websocket: WebSocket, uid: str): await websocket.accept() context ChatContext(uid).get() # 拉取历史 try: response await openai.ChatCompletion.acreate( modelgpt-3.5-turbo, messagescontext, streamTrue, max_tokens1024 ) async for chunk in response: delta chunk.choices[0].delta.content or await websocket.send_text(json.dumps({type: delta, payload: delta})) await websocket.send_text(json.dumps({type: done})) except Exception as e: await websocket.send_text(json.dumps({type: error, payload: str(e)})) finally: await websocket.close()前端Next.js接收片段// hooks/useStream.ts export default function useStream() { const [full, setFull] useState(); const socket useRefWebSocket | null(null); const start (uid: string) { if (socket.current?.readyState WebSocket.OPEN) return; socket.current new WebSocket(wss://api.xxx.dev/ws/${uid}); socket.current.onmessage (e) { const { type, payload } JSON.parse(e.data); if (type delta) setFull((v) v payload); if (type done) socket.current?.close(); }; }; return { full, start }; }3. JWT 鉴权 限流FastAPI 依赖注入# auth.py from fastapi import Depends, HTTPException from fastapi.security import HTTPBearer import jwt, time, redis r redis.Redis() security HTTPBearer() def verify_token(token: str Depends(security)): try: payload jwt.decode(token.credentials, SECRET, algorithms[HS256]) uid payload[uid] # 滑动窗口 60 s 限 30 次 pipe r.pipeline() now int(time.time()) key frate:{uid} pipe.zREMRANGEBYSCORE(key, 0, now - 60) # 清理过期 pipe.zCARD(key) pipe.zADD(key, {now: now}) pipe.expire(key, 60) _, cnt, *_ pipe.execute() if cnt 30: raise HTTPException(status_code429, detailrate limited) return uid except jwt.PyJWTError: raise HTTPException(status_code401, detailinvalid token)性能优化1. 压测对比环境4 vCPU 8 GFastAPI Uvicorn 4 Workers工具locust100 并发虚拟用户持续 5 min指标优化前无缓存 同步优化后Redis 流式P50 延迟2.1 s0.8 sP99 延迟3.4 s1.1 s成功率92 %99.8 %带宽节省—↓ 35 %历史消息免重复上传2. 缓存 TTL 与内存平衡经验公式日均活跃用户 DAU × 平均会话时长(min) ÷ 60 × 128 KB ≈ 内存峰值示例1 w DAU × 5 min ÷ 60 × 128 KB ≈ 106 MB单机 2 G 内存绰绰有余。TTL 设置 30 min可在业务低峰期通过redis --maxmemory-policy allkeys-lru兜底淘汰。避坑指南WebSocket 泄漏检测定时任务扫描CLIENT LIST过滤idle 300 s的连接主动CLOSE同时前端在beforeunload发送{type:bye}心跳后端即时清理。AI 响应超时重试设置openai.timeout 15 s首次失败立即返回 503 与retry-after: 3头部前端指数退避重试最多 2 次防止雪崩。敏感词过滤采用双通道同步AC 自动机算法时间复杂度 O(n)1000 词库 1 ms 内完成异步BERT 小模型二次审核召回率 98 %误判率 1 %。同步拒绝优先异步复核追加封号。延伸思考文本对话系统已跑通下一步“语音 图片”多模态如何无损接入是否继续复用 WebSocket 二进制帧如何设计统一的消息 ID 保证图文音同序展示GPU 资源峰值是文本的 3 倍弹性伸缩策略如何制定欢迎评论区交换思路。我按上述方案落地后仍感觉手写缓存、鉴权、流控等模块颇为琐碎。若你也想快速验证却又不想重复造轮子可以体验从0打造个人豆包实时通话AI动手实验它把 ASR、LLM、TTS 整条链路封装成可插拔组件WebSocket 与 Redis 最佳实践已内置源码公开改两行配置就能跑起自己的高并发对话服务。对中级全栈而言既省时间又能把注意力放在业务创新上值得一试。