单文件FastAPI网关通过UPSTREAMS字典配置代理deepseek/qwen/glm等模型,代码可直接复用。
整个系统就是一个 FastAPI 应用,所有请求都打到同一个路径:
@app.post("/v1/chat/completions")
async def chat_completions(request: Request):
caller = verify_key(request)
body = await request.json()
model = body["model"]
# Resolve the model string to an upstream provider
cfg = resolve_upstream(model) # {"base_url": ..., "api_key": ..., "model": ...}
# Forward the request, swapping in the real upstream model name
body["model"] = cfg["model"]
resp = await client.post(
f"{cfg['base_url']}/chat/completions",
headers={"Authorization": f"Bearer {cfg['api_key']}"},
json=body,
)
return resp.json()
UPSTREAMS 字典是整个系统的核心:
UPSTREAMS = {
"deepseek-chat": {"base_url": "https://api.deepseek.com/v1", "api_key": os.getenv("DEEPSEEK_API_KEY"), "model": "deepseek-chat"},
"qwen-max": {"base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", "api_key": os.getenv("QWEN_API_KEY"), "model": "qwen-max"},
"glm-4-plus": {"base_url": "https://open.bigmodel.cn/api/paas/v4", "api_key": os.getenv("ZHIPU_API_KEY"), "model": "glm-4-plus"},
"kimi-k3": {"base_url": "https://api.moonshot.cn/v1", "api_key": os.getenv("MOONSHOT_API_KEY"), "model": "kimi-k3"},
# ... 11 more
}
新增一个模型只需要在配置里加一项,不需要改代码。Kimi K3 上线时,接入它只用了 20 分钟——字典里三行,显示名映射里一行,供应商映射里一行,外加一个 models.json 条目。
一个只会转发请求的网关没什么稀奇的。真正复杂的是周围的各个层:
认证 —— API Key 用 SHA-256 哈希。每次日志里的 caller_id 都是 sha256(api_key)[:8],所以 Key 本身永远不会出现在日志文件里。
限速 —— 每个 Key 一个滑动窗口,存在内存里。默认 60 RPM。
配额 —— 这是最棘手的。免费用户每月 50 万 token。检查和扣减必须是原子操作,否则并发请求会钻空子。我吃过这个亏——19 个用户在限额检查移入 _USERS_LOCK 临界区之前就冲破了 50 万。
流式响应 —— SSE 直通。Token 随生随发。first_token_ms 这个指标告诉你上游模型实际开始响应的速度,这比总延迟对用户体验的影响更大。
缓存 —— 只用一个响应头。X-Cache-TTL: 3600。一小时内相同的 prompt 走缓存返回。零 token,零上游费用。1200ms 变成 200ms。
可观测性 —— 一个内存环缓冲(200 条)加一个 JSONL 文件(5000 条)用于持久化。每条请求日志都记录 request_id、caller、model、latency、tokens、status。
环缓冲在启动时从磁盘加载 200 条记录,管理后台从内存读取。有一段时间管理后台显示零日志,因为过滤器把 type="response" 类型的条目(最常见的类型)排除了,导致什么都没有可显示。
修复方法是加一个兜底:优先用详细日志,没有详细日志就用基本日志。环缓冲里有 200 条记录时绝不返回零结果。听起来很简单,其实一开始并不是这样。
转发逻辑只有 50 行。生产级别的功能——认证、配额、限速、流式响应、缓存、可观测性、错误处理——有 5000 行。"在我机器上能跑"和"给付费用户用"之间的差距,永远比你以为的更大,而且这个差距永远出现在外层,而不是核心。
想试试吗?网关已经在线运行:
→ aibridge-api.com/playground.html(15 个模型,无需注册) → aibridge-api.com/prompts.html(24 个提示词,无需注册)
免费额度:每月 50 万 token。无需信用卡。



