OpenAI API 兼容
从 Bit 到 AGI · 第十章:自己训练一个模型 · OpenAI API 兼容
vLLM 自带 OpenAI 兼容 HTTP API。本篇教你用同一套 SDK 代码切换 OpenAI 官方与你的自训模型,并配置 LiteLLM 做统一网关。
直觉:通用电源插座
OpenAI API 成了事实标准:URL + Authorization: Bearer + JSON body。兼容层像万能转接头——客户端不变,后端从 api.openai.com 换成本地 localhost:8000。
定义
核心端点(vLLM 实现):
| 端点 | 用途 |
|---|---|
POST /v1/chat/completions | 对话(需 chat template) |
POST /v1/completions | 纯续写 |
GET /v1/models | 列出模型 ID |
请求体字段:
{
"model": "checkpoints/best-hf",
"messages": [{"role": "user", "content": "你好"}],
"max_tokens": 128,
"temperature": 0.7
}例子
例 1:curl 调用 completions
curl http://localhost:8000/v1/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-local" \
-d '{
"model": "checkpoints/best-hf",
"prompt": "比特是信息的最小单位,",
"max_tokens": 100,
"temperature": 0.7
}'例 2:OpenAI Python SDK 指向本地
pip install openai
python << 'EOF'
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="sk-local", # vLLM 不校验,但 header 必填
)
resp = client.completions.create(
model="checkpoints/best-hf",
prompt="深度学习是",
max_tokens=128,
temperature=0.7,
)
print(resp.choices[0].text)
print(f"usage: {resp.usage}")
EOF例 3:LiteLLM 统一路由
pip install litellm
cat > deploy/litellm_config.yaml << 'EOF'
model_list:
- model_name: mini-gpt
litellm_params:
model: openai/checkpoints/best-hf
api_base: http://localhost:8000/v1
api_key: sk-local
- model_name: gpt-4o
litellm_params:
model: gpt-4o
api_key: os.environ/OPENAI_API_KEY
EOF
litellm --config deploy/litellm_config.yaml --port 4000客户端改 base_url=http://localhost:4000 即可在 mini-gpt 与 gpt-4o 间切换。
例 4:流式输出
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1", api_key="sk-local")
stream = client.completions.create(
model="checkpoints/best-hf",
prompt="Transformer 的核心是",
max_tokens=200,
stream=True,
)
for chunk in stream:
print(chunk.choices[0].text or "", end="", flush=True)
print()量化
| 场景 | 延迟 | 吞吐 |
|---|---|---|
| 非流式 128 tokens | 0.5–1.5 s | — |
| 流式首 token | 50–150 ms | — |
| 并发 4 路 | P99 ~2 s | ~400 tok/s 合计 |
Base 模型无 chat template 时,/v1/chat/completions 可能格式异常——预训练模型优先用 /v1/completions;SFT 后再配 chat template。
在 tokenizer_config.json 里添加 chat_template(Jinja2)后,chat 端点才能正确渲染 <|user|> 等标记。详见微调 SFT。
动手试一试
对比官方与本地(需 OPENAI_API_KEY):
python << 'EOF'
import os, time
from openai import OpenAI
def bench(client, model, prompt):
t0 = time.perf_counter()
r = client.completions.create(model=model, prompt=prompt, max_tokens=64)
return time.perf_counter() - t0, r.choices[0].text[:80]
local = OpenAI(base_url="http://localhost:8000/v1", api_key="sk-local")
dt, text = bench(local, "checkpoints/best-hf", "AI is")
print(f"local {dt:.2f}s: {text!r}")
EOF与 OpenAI 官方差异
| 特性 | OpenAI 官方 | vLLM 自托管 |
|---|---|---|
logprobs | 支持 | 部分版本支持 |
| Function calling | 完整 | 取决于模型 SFT |
| Vision | GPT-4o | 需多模态模型 |
| Rate limit | 有 | 自行限流 |
| 计费 | 按 token | 仅 GPU 成本 |
生产环境在 vLLM 前加 nginx 限流与 API key 校验:
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
location /v1/ {
limit_req zone=api burst=20;
proxy_pass http://127.0.0.1:8000;
}多客户端接入示例
Node.js:
import OpenAI from "openai";
const client = new OpenAI({ baseURL: "http://localhost:8000/v1", apiKey: "sk-local" });
const r = await client.completions.create({ model: "checkpoints/best-hf", prompt: "Hello", max_tokens: 32 });
console.log(r.choices[0].text);curl 流式:
curl -N http://localhost:8000/v1/completions \
-H "Content-Type: application/json" \
-d '{"model":"checkpoints/best-hf","prompt":"Hi","max_tokens":50,"stream":true}'每条 SSE 以 data: {...} 开头,以 data: [DONE] 结束。
常见误区
- model 名不一致:SDK 里的
model必须与 vLLM 加载路径或--served-model-name一致。 - 忘记 api_key header:vLLM 仍要求 Bearer token 存在。
- Base 模型走 chat 端点:应先用 completions 或 SFT 后加 template。
- HTTPS 未配置:公网暴露应用 TLS 终止在 Caddy/nginx。
- timeout 过短:长生成 512 token 可能超 60 s,调大 proxy_read_timeout。
端到端实践要点
OpenAI 兼容 API 是事实标准:改 base_url 即可让现有应用连自托管模型。vLLM 实现了 completions 与 chat 端点;Base 模型用 completions,Instruct 模型用 chat 并确保 chat template 一致。LiteLLM 在多个 backend 间路由,适合「开发用 mini-gpt、生产 fallback gpt-4o」的混合策略。
流式 SSE 改善用户体验:首 token 200 ms 内返回,用户感觉比等整段 JSON 快得多。注意 proxy timeout 与 client timeout 设到 120 s 以上,长生成 512 token 可能超 60 s。公网暴露务必 HTTPS + 鉴权;sk-local 只是占位,生产用随机 UUID key 存 Secrets Manager。
logprobs、function calling、vision 等高级特性取决于 vLLM 版本与模型 SFT 格式,不要假设与 OpenAI 100% 一致。对接前先读 vLLM release notes。最后,在 staging 用同一 SDK 代码连 staging URL 跑集成测试,再切 production DNS——避免直接在生产 debug。
命令流水线复盘
确认 vLLM 在 :8000;pip install openai;运行本篇 Python SDK 脚本;试 stream=True 流式;可选 litellm --config deploy/litellm_config.yaml --port 4000 起网关。Node 客户端改 baseURL 同样可用。Base 模型用 completions 端点;SFT 后用 chat.completions。下一篇MCP 集成在 API 之上包 MCP tool。
排错速查
404 model:served-model-name 与请求不一致。401:缺 Authorization header。空 completion:prompt 为空或 max_tokens=0。流式断连:nginx proxy 未关 buffering。chat 乱码:Base 模型勿用 chat 端点。
验收标准
本篇完成后,你应能在终端用一条命令或脚本验证关键产出存在且非空:test -s 查文件、curl 查 HTTP、python -c 查 import 与 shape。把验收命令写进项目 Makefile 或 scripts/verify.sh,每次改数据或权重后跑一遍,避免带着 silent failure 进入下一篇。全章二十篇都通过验收,即表示你已具备从零训小模型到部署 API 的完整能力。
与前后章衔接
本章每篇都假设你已读完第一至九章:比特与文件格式(第一章)解释 train.bin 为何是字节流;信息论(第三章)解释为何 PPL 衡量压缩效率;Transformer(第五章)解释预训练在优化什么;Tokenizer(第六章)与本章 BPE 训练一一对应;Training(第七章)给出 loss、LoRA、Checkpoint 理论;Inference(第八章)对应推理与部署;GPU(第九章)对应硬件与显存估算。若某命令报错,先查依赖版本(Python 3.11+、CUDA 驱动、PyTorch 与 vLLM 匹配),再查路径是否与 mini-gpt-lab 目录约定一致。整条流水线可在单卡 4090 上于一个周末跑通;失败时回到从失败中恢复对照症状表,不要跳过日志与 checkpoint 排查。
完成本篇后建议把关键命令与输出路径记录到 docs/run-log.md,便于复现与团队协作。
练习扩展
若时间充裕,尝试修改一个超参或配置项,观察下游指标变化并记录到 docs/run-log.md:例如清洗 threshold、词表大小、学习率、save 间隔或 vLLM 的 max-model-len。单次只改一处,才能归因。对比实验最好开 W&B 同名 project 下不同 run,用 Notes 字段标注「baseline / variant B」。这些习惯与工业界 ML 实验管理一致,比单纯跑通命令更有长期价值。
建议每篇末尾自检:关键产出文件存在、体积合理、随机抽检内容可读,再点下一篇链接。
遇到问题先查日志与磁盘空间,再查本章排错速查表。
保持 mini-gpt-lab 目录结构与命名一致,可显著减少路径类错误。
全章命令均在 bash + Python venv 下验证;Windows 用户请用 WSL2 或调整路径分隔符。
建议 fork 本章 mini-gpt-lab 仓库模板,在 README 中勾选已完成篇目,便于跟踪进度。
遇阻可在本章总结页的命令速查中对照当前阶段。耐心排查,多数错误来自路径与环境。
小结
- vLLM 暴露
/v1/completions与/v1/chat/completions。 - OpenAI SDK 改
base_url即可对接自训模型。 - LiteLLM 做多模型网关;流式用
stream=True。
→ 下一篇:MCP 集成