从 REST 到 SSE,从认证到容错 —— 构建生产级大模型 API 的完整指南
与传统 REST API 不同,LLM API 面临高延迟、流式输出、Token 计量、上下文窗口约束等独特挑战。设计一个「好用的」LLM API 需要在以下维度做权衡:
最基础也是最关键的决定 —— 用什么协议传输数据。
| 协议 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| REST (JSON) | 同步短回答、管理类接口 | 简单、工具链成熟、缓存友好 | 不支持原生流式,轮询浪费资源 |
| SSE | 流式对话生成 | 单向推送、浏览器原生支持、断线重连 | 仅服务端→客户端,不支持二进制 |
| WebSocket | 实时双向通信、语音对话 | 全双工、低延迟、二进制支持 | 需自行处理重连逻辑,复杂 |
| gRPC | 内部微服务调用 | 强类型、高性能、双向流 | 浏览器支持弱,调试不便 |
REST + SSE 组合:短请求用 REST,长生成用 SSE。内部服务用 gRPC。90% 的 LLM 平台(OpenAI、Anthropic、Gemini)都走这条路。
保护 API 不被滥用,同时保持接入简单。
| 方案 | 适用场景 | 说明 |
|---|---|---|
| API Key (Bearer Token) | 最通用 | 简单,放在 Authorization: Bearer sk-xxx |
| OAuth 2.0 | 第三方应用 | 授权码流程,适合让用户授权 |
| JWT + 签名 | 分发密钥 | 服务端签发,客户端带签名请求,防篡改 |
| mTLS | 企业级 | 双向证书认证,高安全场景 |
一个良好的请求结构应同时支持简单场景和高级定制。
// POST /v1/chat/completions
{
"model": "gpt-4o", // 模型选择(必填)
"messages": [ // 对话历史(必填)
{ "role": "system", "content": "..." },
{ "role": "user", "content": "..." }
],
"stream": true, // 是否流式输出
"temperature": 0.7, // 随机性控制
"max_tokens": 4096, // 最大输出 token 数
"top_p": 0.95, // 核采样阈值
"tools": [...], // Function Calling 工具定义
"user": "user_123" // 终端用户标识(用于监控/滥用检测)
}
// 非流式 — 一次返回完整结果
{
"id": "chatcmpl-xxx",
"object": "chat.completion",
"created": 1717401600,
"model": "gpt-4o",
"choices": [{
"index": 0,
"message": { "role": "assistant", "content": "..." },
"finish_reason": "stop"
}],
"usage": { "prompt_tokens": 42, "completion_tokens": 128, "total_tokens": 170 }
}
// 流式 (SSE) — 逐 token 推送
data: {"id":"chatcmpl-xxx","choices":[{"delta":{"content":"你"}}]}
data: {"id":"chatcmpl-xxx","choices":[{"delta":{"content":"好"}}]}
data: {"id":"chatcmpl-xxx","choices":[{"delta":{"content":"!"}}]}
data: {"id":"chatcmpl-xxx","choices":[{"delta":{},"finish_reason":"stop"}]}
data: [DONE]
delta 字段只包含增量,前端需要自己拼接。最后一条带 finish_reason 表示结束。有些平台会在最后一条额外返回 usage 信息。
控制模型「创造力」与「确定性」的平衡。
| 参数 | 作用 | 典型范围 | 使用建议 |
|---|---|---|---|
temperature | 控制随机性,越高越「放飞」 | 0 ~ 2 | 代码/事实类用 0~0.3,创意写作用 0.7~1.0 |
top_p (核采样) | 只从累计概率 ≥ p 的 token 中采样 | 0 ~ 1 | 一般设 0.9~0.95,与 temperature 二选一 |
top_k | 只从概率最高的 k 个 token 中采样 | 1 ~ N | Gemini 等部分模型支持 |
max_tokens | 生成 token 上限 | 1 ~ 上下文窗口 | 防止无限生成、控制成本 |
stop | 停止序列 | 字符串数组 | 如 ["\n\n", "###"] 遇到就停 |
presence_penalty | 惩罚已出现的 token | -2 ~ 2 | 正值鼓励说新内容,避免重复 |
frequency_penalty | 按频率惩罚重复 | -2 ~ 2 | 比 presence_penalty 更激进 |
seed | 随机种子 | 整数 | 确保可复现输出 |
让 LLM 能调用外部工具,这是 Agent 的基石。
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的天气信息",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "城市名" },
"unit": { "type": "string", "enum": ["celsius","fahrenheit"] }
},
"required": ["city"]
}
}
}
description 写得越具体,LLM 越不容易选错工具。字段级描述比顶层描述更有价值。支持 strict 模式确保输出格式合规。建议限制同时可用的工具数量(≤ 25 个为宜)。
错误信息应该让调用方能「程序化处理」,而非只能人工阅读。
| HTTP 状态码 | 含义 | 典型场景 |
|---|---|---|
200 | 成功 | 正常返回 |
400 | 请求参数错误 | temperature 超出范围、messages 格式错误 |
401 | 认证失败 | API Key 缺失/无效/过期 |
402 | 余额不足 | 账户欠费 |
403 | 权限不足 | 地区限制、模型未开放 |
429 | 速率限制 | 请求过于频繁 —— 返回 Retry-After 头 |
500 | 服务端错误 | 模型推理异常 —— 客户端应重试 |
503 | 服务不可用 | 过载、维护 —— 返回 Retry-After |
// 错误响应体规范
{
"error": {
"type": "invalid_request_error", // 机器可读的错误类型
"code": "context_length_exceeded", // 具体错误码
"message": "上下文超过 128k token 限制", // 人类可读的描述
"param": "messages" // 出错的参数名(可选)
}
}
保护后端资源,保证公平使用。
| 维度 | 说明 | 示例 |
|---|---|---|
| RPM (每分钟请求数) | 限制调用频率 | 免费用户 20 RPM,付费 500 RPM |
| TPM (每分钟 Token 数) | 按 token 消耗限制 | 免费用户 40K TPM,付费 1M TPM |
| 并发数 | 同时进行的请求上限 | 排队机制,超限返回 429 |
| QPD (每天配额) | 日总请求上限 | 适用于固定的配额套餐 |
X-RateLimit-Limit、X-RateLimit-Remaining、X-RateLimit-Reset 等标准头,方便客户端做自适应限速。
随着模型迭代,API 必然要演进。如何兼容旧版?
| 策略 | 示例 | 评价 |
|---|---|---|
| URL 路径版本 | /v1/chat → /v2/chat | 最直观,OpenAI 采用 |
| Header 版本 | API-Version: 2024-06-01 | 灵活,Anthropic 采用(日期版) |
| 模型名版本 | 用 model 参数区分行为 | 适合 model 级别的差异 |
| 查询参数版本 | ?version=2 | 简单但不够 RESTful |
Sunset 和 Deprecation 提前通知。
LLM 的上下文窗口有限(从 4K 到 1M+ tokens),API 需要帮助用户管理。
context_window 和 remaining_tokenstruncation_strategy: "auto" | "last_messages" 参数/tokenize 或 /count_tokens 端点,让客户端在发送前知晓 token 消耗现代 LLM 能处理图片、音频、视频。API 如何优雅地支持?
// Content Part 模式 —— 一条消息可混合文本+图片
{
"role": "user",
"content": [
{ "type": "text", "text": "这张图片里有什么?" },
{ "type": "image_url", "image_url": { "url": "https://..." } },
{ "type": "image_url", "image_url": { "url": "data:image/jpeg;base64,..." } }
]
}
low / high / auto 控制图片分辨率 — 决定 token 消耗生产环境的必备能力。
对相似的问题(非完全相同)返回缓存结果。实现方式:用 Embedding 向量计算问题相似度,超过阈值命中缓存。代价是牺牲一点准确性换取 50%~80% 的延迟和成本降低。
通过 X-Idempotency-Key 请求头实现:相同 key 的重复请求返回已缓存的结果,避免网络重试导致重复计费。服务端保留时效一般设为 24 小时。
id:便于排查问题user 参数区分终端用户,精确计费通过 response_format: { "type": "json_schema", "json_schema": {...} } 约束模型输出格式。底层通常通过 grammar-based sampling 或 constrained decoding 实现,保证输出 100% 符合 Schema。
| 设计维度 | 核心决策 | 业界主流做法 |
|---|---|---|
| 协议 | REST + SSE | OpenAI / Anthropic / Gemini 一致 |
| 认证 | Bearer Token (API Key) | 最通用,OAuth 仅授权场景 |
| 流式 | SSE,delta 增量 | 逐 token 推送,EventSource 原生支持 |
| 错误 | 统一 error 对象 + 标准 HTTP 码 | type + code + message + param |
| 限流 | RPM + TPM 双维度 | 429 + Retry-After 头 |
| 版本 | URL 路径 (/v1) 或日期 Header | OpenAI 用路径,Anthropic 用日期 |
| 工具调用 | JSON Schema 定义 function | OpenAI / Gemini 统一标准 |
| 多模态 | Content Part (type: text/image_url) | OpenAI 首创,已成事实标准 |
| 缓存 | 语义缓存 + 请求幂等 | 高 QPS 场景必备 |
| 输出格式 | structured outputs (json_schema) | OpenAI / Gemini / Anthropic 均已支持 |
最终的 API 设计不是单纯的「技术选型」,而是在开发者体验、系统可靠性、成本控制、安全性之间找到平衡。好的 API 让开发者感觉「理所当然就该这样」——那说明你设计对了。