一、先说说背景:为什么非得搞个网关?
2026 年,我们团队内部的 AI 工具链大概长这样:
-
写代码用 Codex(编程上下文理解最好)
-
处理文档用 GPT-4o(多模态支持完善)
-
内部知识库用 本地 Qwen(数据不出域)
-
成本敏感场景用 DeepSeek-V3(便宜量大)
看起来美好,实际接入时全是坑:
-
协议碎片化:OpenAI 是
/v1/chat/completions,Anthropic 是/v1/messages,Codex 虽然兼容 OpenAI 协议,但不同模型的model字段命名规则完全不同。 -
密钥管理灾难:每个项目里散落着不同的
OPENAI_API_KEY,轮换一次要改十几个仓库。 -
网络抽风:直接调用官方 API,超时、断连、IP 受限是家常便饭。
-
成本盲盒:没有用量监控,月底看账单像开盲盒。
最痛的一次:某个后端服务因为直接持有 OpenAI 密钥,被同事误传到 GitHub,第二天 token 就被刷光了。
痛定思痛,我们在应用层和模型层之间,加了一层AI API 网关(也叫模型网关)。这篇文章聊聊架构设计和落地踩坑。
二、网关架构:四个核心模块
整体链路很简单,但每个模块都有讲究:
plain
┌─────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 你的应用 │────▶│ AI API 网关 │────▶│ 各模型服务商 │
│ (Codex/自研 │ │ ·协议转换 │ │ (OpenAI/Claude/ │
│ 系统/脚本) │ │ ·密钥托管 │ │ DeepSeek等) │
└─────────────┘ │ ·智能路由 │ └─────────────────┘
│ ·用量审计 │
└─────────────────┘
2.1 协议转换层:屏蔽模型差异
Codex 用的是 OpenAI 协议,但如果我们想让它无感切换到 Claude 或 DeepSeek,网关就要做请求翻译。
Python
# 简化版:OpenAI 请求 → 多模型适配
class ModelAdapter:
def convert_request(self, body: dict, target_provider: str) -> dict:
"""将标准 OpenAI 请求转译为不同厂商格式"""
messages = body.get("messages", [])
model = body.get("model", "gpt-4o")
if target_provider == "anthropic":
# Claude 的 messages 格式与 OpenAI 略有差异
return {
"model": model.replace("gpt-4o", "claude-sonnet-4"),
"messages": messages,
"max_tokens": body.get("max_tokens", 4096),
"temperature": body.get("temperature", 0.7),
"stream": body.get("stream", True)
}
elif target_provider == "deepseek":
return {
"model": model.replace("gpt-4o", "deepseek-chat"),
"messages": messages,
"stream": body.get("stream", True)
}
# 默认透传 OpenAI 格式
return body
关键点:content 字段的兼容性。Claude 支持 content 为数组(text + image 混合),早期 OpenAI 只接受字符串,网关层必须做扁平化处理。
2.2 智能路由层:不只是转发
网关应该根据策略选最优上游,而不是无脑转发:
class SmartRouter:
def __init__(self):
self.providers = {
"o3": [
{"name": "azure-east", "latency": 120, "cost_per_1k": 0.005, "healthy": True},
{"name": "openai-official", "latency": 200, "cost_per_1k": 0.005, "healthy": True},
],
"claude-sonnet-4": [
{"name": "aws-bedrock", "latency": 150, "cost_per_1k": 0.003, "healthy": True},
{"name": "official", "latency": 180, "cost_per_1k": 0.003, "healthy": False}
]
}
def route(self, model: str, strategy: str = "cost") -> dict:
candidates = [p for p in self.providers.get(model, []) if p["healthy"]]
if not candidates:
raise Exception(f"模型 {model} 无可用上游")
if strategy == "latency":
return min(candidates, key=lambda x: x["latency"])
elif strategy == "cost":
return min(candidates, key=lambda x: x["cost_per_1k"])
return candidates[0] # 轮询
三种策略:
-
开发环境:按延迟优先,响应快最重要
-
生产环境:按成本优先,或设置"成本上限 + 故障转移"
-
关键业务:多上游并发,取最快返回
2.3 密钥与配额管理:唯一可信的持有方
网关作为唯一持有上游真实密钥的实体,客户端只拿到带权限的短期 Token:
Python
class KeyManager:
def __init__(self):
# 上游真实密钥,只存服务端,永不暴露
self.upstream_keys = {
"openai": "sk-real-xxxx",
"claude": "sk-ant-yyyy"
}
self.client_tokens = {}
def issue_token(self, user_id: str, quota: int, allowed_models: list) -> str:
"""签发带模型权限和额度限制的客户端 Token"""
token = f"gw_{uuid.uuid4().hex[:16]}"
self.client_tokens[token] = {
"user_id": user_id,
"remaining_quota": quota,
"rate_limit": "100/min",
"allowed_models": allowed_models
}
return token
def validate(self, token: str, model: str) -> bool:
if token not in self.client_tokens:
return False
client = self.client_tokens[token]
return model in client["allowed_models"] and client["remaining_quota"] > 0
收益:
-
上游密钥零暴露
-
支持按项目、按模型、按用户细粒度控权
-
额度耗尽自动熔断,防止半夜被刷爆
2.4 可观测性:网关是最佳埋点位置
class MetricsCollector:
def log(self, trace_id: str, model: str, provider: str,
input_tokens: int, output_tokens: int, latency_ms: int):
cost = self.calculate_cost(model, provider, input_tokens, output_tokens)
# 实际可对接 Prometheus + Grafana
print(f"[{trace_id}] {model}@{provider} | "
f"tokens={input_tokens}+{output_tokens} | "
f"latency={latency_ms}ms | cost=${cost:.4f}")
三、实战:让 Codex 走网关
Codex 原生支持 OpenAI 协议,接入网关非常轻量。
3.1 配置环境变量
# 临时生效
export OPENAI_BASE_URL="https://www.aegisy.cc/v1"
export OPENAI_API_KEY="你的网关客户端Token"
# 或者写入 ~/.zshrc 持久化
echo 'export OPENAI_BASE_URL="https://www.aegisy.cc/v1"' >> ~/.zshrc
3.2 网关侧的模型映射配置
# .env
CODEX_DEFAULT=o3
CODEX_MINI=o4-mini
FALLBACK_MODEL=claude-sonnet-4
# 上游配置
OPENAI_BASE_URL=https://www.aegisy.cc/v1
OPENAI_API_KEY=sk-your-real-key
效果:Codex 默认走 o3,轻量任务走 o4-mini,遇到长上下文或复杂推理时网关自动切到 claude-sonnet-4。业务代码零改动,只改网关配置。
四、企业级落地:三个必须考虑的点
4.1 高可用
-
多上游冗余:至少配 2-3 个不同渠道,一个挂了自动切
-
熔断降级:连续错误率 > 5% 时自动暂停该上游,避免雪崩
-
缓存层:Embedding 结果做 Redis 缓存,降低 50%+ 成本
4.2 合规与审计
-
日志脱敏:请求日志中自动过滤手机号、身份证号等 PII
-
数据留存:敏感业务 7 天,普通业务 30 天
-
跨境合规:境外上游 API 需确保网关部署在合规区域
五、踩坑记录(真实经验)
-
流式响应的 Buffer 不匹配:SSE 流式传输时,网关→客户端、网关→上游的 Buffer 大小如果不一致,会出现"卡顿"或"断流"。建议统一用 4KB Buffer。
-
Token 计费偏差:不同上游计费规则不同(有些按字符、有些按 Token),网关层最好自己用
tiktoken重新计算,避免账单对不上。 -
模型切换的上下文兼容性:Codex 的
system消息和tool_calls格式与 Claude 有细微差异,协议转换时要特别注意字段映射。
六、总结
AI API 网关不是"为了中转而中转",而是企业级 AI 架构中必然出现的一层:
-
解耦:业务代码与模型供应商解耦,换模型只改网关配置
-
治理:统一管控密钥、配额、审计,避免"各自为政"
-
优化:路由策略 + 缓存机制,压低成本的同时保证体验
-
扩展:新模型上线,业务端零感知
如果你也在用 Codex、Cursor 或自研 AI 应用,建议尽早把网关层纳入架构设计。不要等到密钥泄露、账单爆炸、网络抽风时才想起来补这一层。