ChatGPT网站源码解析:从零搭建AI对话平台的技术实践
自建 AI 对话平台的三座大山
把大模型搬到网页上,看似“调个接口”而已,真正落地时 90% 的时间都花在踩这三颗雷:- 实时性:用户一句“你好”发出去,恨不得下一秒就听到回复。网络、推理、渲染任何一环卡 500 ms,体验直接“掉线”。
- 上下文:多轮对话要记忆,Token 却越滚越大。既要保证“记得住”,又得防止“撑爆”显存与钱包。
- 成本:GPT-4 每 1k tokens 0.03$,一上午就能烧掉一杯星巴克。缓存、截断、降级策略没做好,月底账单教你做人。
技术选型:后端三兄弟与前端两姐妹
后端- Flask:生态最老,同步阻塞,并发一上来就“红温”,适合原型,不适合生产。
- Django:全家桶,ORM、Admin 一把梭,AI 服务里 70% 功能用不上,启动慢、内存高。
- FastAPI:异步原生,pydantic 自动校验,Starlette 支持 WebSocket/SSE,压测 QPS 比 Flask 高 4~6 倍,本文直接锁它。
前端
- Vue:模板友好,上手快,但生态对流式渲染、Markdown 数学公式支持零散。
- React:配合 react-markdown + remark-math,SSE 数据一块一块进来就能即时渲染,社区方案成熟,选型不纠结。
核心实现拆解
下面代码均摘自生产仓库,可直接跑,已按 PEP8/ESLint 排雷。3.1 FastAPI 骨架 + JWT 鉴权
先装依赖
pip install fastapi[all] python-jose redis再写 main.py
from fastapi import FastAPI, Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer from jose import jwt import redis, uuid, time app = FastAPI(title="ChatGPT-lite") pool = redis.Redis(host="127.0.0.1", port=6379, decode_responses=True, max_connections=50) SECRET = "YOUR_VERY_SECRET_KEY" oauth = OAuth2PasswordBearer(tokenUrl="login") def verify_token(token: str = Depends(oauth)): try: payload = jwt.decode(token, SECRET, algorithms=["HS256"]) uid = payload["uid"] except Exception: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token") return uid @app.post("/login") def login(username: str, password: str): # 仅示例,生产请走 DB+bcrypt if username != "admin" or password != "123456": raise HTTPException(401, "Bad credentials") token = jwt.encode({"uid": "007", "exp": time.time()+3600}, SECRET, algorithm="HS256") return {"access_token": token, "token_type": "bearer"}3.2 SSE 流式输出(含异常守护)
from fastapi.responses import StreamingResponse import httpx, json, asyncio @app.get("/chat") def chat(prompt: str, uid: str = Depends(verify_token)): async def event_stream(): try: async with httpx.AsyncClient(timeout=httpx.Timeout(connect=5, read=60)) as client: async_chunk = await client.stream( "POST", "https://api.openai.com/v1/chat/completions", headers={"Authorization": f"Bearer {OPENAI_KEY}"}, json={"model":"gpt-3.5-turbo", "messages":[{"": [{"role":"user","content":prompt}]}, "stream":True}, ) async for line in async_chunk.aiter_lines(): if line.startswith("data: "): chunk = line[6:] if chunk == "[DONE]": break delta = json.loads(chunk)["choices"][0]["delta"].get("content", "") yield f"data: {json.dumps({'text':delta})}\n\n" except asyncio.TimeoutError: yield f"data: {json.dumps({'text':'[超时] 请稍后再试'})}\n\n" except Exception as e: yield f"data: {json.dumps({'text':'[异常] 服务暂不可用'})}\n\n" return StreamingResponse(event_stream(), media_type="text/event-stream")3.3 React 前端 Markdown 渲染优化
关键:每收到一段 SSE data,就把新字符串拼到 lastDelta,用 useMemo 做局部刷新,避免整页重渲染。import { useEffect, useState, useMemo } from 'react'; import ReactMarkdown from 'react-markdown'; import remarkMath from 'remark-math'; import rehypeKatex from 'rehype-katex'; export default function Chat() { const [text, setText] = useState(''); useEffect(() => { const es = new EventSource(`/chat?prompt=${encodeURIComponent(prompt)}`); es.onmessage = (e) => { const { text: delta } = JSON.parse(e.data); setText(prev => prev + delta); }; return () => es.close(); }, [prompt]); const md = useMemo(() => text, [text]); return <ReactMarkdown remarkPlugins={[remarkMath]} rehypePlugins={[rehypeKatex]}>{md}</ReactMarkdown>; }性能调优实战
4.1 压测数据(Locust 1 进程 200 并发,持续 30 s)- FastAPI + gunicorn + 1 worker:平均 RT 451 ms,P95 1.2 s,QPS 42。
- 开 4 worker 并加 redis 连接池:平均 RT 降至 210 ms,QPS 118,CPU 占用 68%,显存稳。
4.2 Redis 对话上下文内存优化
- 只存最近 5 轮:LPUSH + LTRIM 0 9,O(1) 复杂度。
- 对每句做 token 计数,超限用“滑动窗口”抛弃最早一句,保证总量 ≤ 3k tokens。
- key 带 uid + 日期,设 TTL 3600 s,自动过期防内存泄漏。
避坑指南
- 限流策略误配置:OpenAI 官方 3/min 3500 rpm 是“组织级”,不是“IP 级”。别把桶大小设 3500,否则多用户一冲就 429。推荐 redis + token bucket,单 IP 60 桶/60 s。
- 线程阻塞:如果沿用 sync 库 requests 调 GPT,一阻塞就是 30 s,整 worker 被拖。一定用 httpx/asyncio,并加 read timeout。
- 跨域安全:前端 3000 端口调 8000 端口,'Access-Control-Allow-Origin' 设 '*' 会泄露鉴权接口。正确姿势:
from fastapi.middleware.cors import CORSMiddleware app.add_middleware(CORSMiddleware, allow_origins=["http://localhost:3000"], allow_credentials=True, ...)
架构图(文本版)
浏览器 ←SSE→ Nginx(443) ←→ FastAPI(8000) ←→ Redis(限流/上下文)
↘
OpenAI HTTPS API开放式问题
模型越大效果越好,可推理延迟与成本指数级上涨。你是愿意牺牲 20% 的 IQ 换来 50% 的 RT 下降,还是通过缓存+蒸馏让 7B 小模型也能“够用”?欢迎在 GitHub 仓库 提 Issue 分享你的折中方案。动手实验推荐
如果你想把“耳朵-大脑-嘴巴”一条龙体验补齐,而不仅停留在文字聊天,可以试试从0打造个人豆包实时通话AI动手实验。我跟着教程把语音流式对话跑通,发现把 ASR、LLM、TTS 串成一条低延迟管线,其实比纯文字版更有挑战,也更贴近真实场景。源码全公开,改两行配置就能换上自己的音色,小白也能顺利体验。