为什么需要 AI API 网关?

如果你在做 AI 应用开发,大概率遇到过这些问题:

  • OpenAI 抽风了,服务挂了 2 小时,你的应用跟着挂
  • DeepSeek 发布新版本,API 过载,请求全超时
  • 老板突然说"我们试试 Claude",你得改一堆代码
  • 不同模型的 API 格式不一样,适配起来头疼

这些问题的本质是:你的应用直接耦合了某个具体的模型 API。

解决方案很简单——在你的应用和模型之间加一层网关,统一接口、自动路由、故障转移。

完整代码:100 行的 AI 网关

直接上代码,Python + FastAPI,能跑的那种。

import os
import time
import httpx
import asyncio
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

app = FastAPI(title="AI API Gateway")

# 模型配置:按优先级排列
MODEL_PROVIDERS = [
    {
        "name": "deepseek",
        "base_url": "https://api.deepseek.com/v1",
        "api_key": os.getenv("DEEPSEEK_API_KEY", ""),
        "model": "deepseek-chat",
        "timeout": 30,
        "max_retries": 2,
    },
    {
        "name": "openai",
        "base_url": "https://api.openai.com/v1",
        "api_key": os.getenv("OPENAI_API_KEY", ""),
        "model": "gpt-4o-mini",
        "timeout": 30,
        "max_retries": 1,
    },
    {
        "name": "anthropic",
        "base_url": "https://api.anthropic.com/v1",
        "api_key": os.getenv("ANTHROPIC_API_KEY", ""),
        "model": "claude-3-5-sonnet-20241022",
        "timeout": 30,
        "max_retries": 1,
    },
]

# 健康状态追踪
provider_health = {p["name"]: {"healthy": True, "last_fail": 0} for p in MODEL_PROVIDERS}
COOLDOWN_SECONDS = 60  # 故障冷却时间


async def call_provider(provider: dict, messages: list) -> dict:
    """调用单个模型供应商"""
    headers = {
        "Authorization": f"Bearer {provider['api_key']}",
        "Content-Type": "application/json",
    }

    # Anthropic 的 API 格式略有不同
    if provider["name"] == "anthropic":
        headers = {
            "x-api-key": provider["api_key"],
            "anthropic-version": "2023-06-01",
            "Content-Type": "application/json",
        }
        payload = {
            "model": provider["model"],
            "max_tokens": 4096,
            "messages": messages,
        }
        url = f"{provider['base_url']}/messages"
    else:
        payload = {
            "model": provider["model"],
            "messages": messages,
            "max_tokens": 4096,
        }
        url = f"{provider['base_url']}/chat/completions"

    async with httpx.AsyncClient(timeout=provider["timeout"]) as client:
        resp = await client.post(url, json=payload, headers=headers)
        resp.raise_for_status()
        return resp.json()


def normalize_response(provider_name: str, raw: dict) -> dict:
    """统一不同供应商的响应格式"""
    if provider_name == "anthropic":
        content = raw.get("content", [{}])[0].get("text", "")
        model = raw.get("model", "")
    else:
        content = raw["choices"][0]["message"]["content"]
        model = raw.get("model", "")

    return {
        "content": content,
        "model": model,
        "provider": provider_name,
    }


@app.post("/v1/chat")
async def chat(request: Request):
    """统一聊天接口——自动路由到可用的模型"""
    body = await request.json()
    messages = body.get("messages", [])
    preferred = body.get("provider", None)  # 可选:指定供应商

    errors = []

    for provider in MODEL_PROVIDERS:
        name = provider["name"]
        health = provider_health[name]

        # 跳过指定供应商以外的
        if preferred and name != preferred:
            continue

        # 跳过冷却中的供应商
        if not health["healthy"] and time.time() - health["last_fail"] < COOLDOWN_SECONDS:
            continue

        try:
            raw = await call_provider(provider, messages)
            provider_health[name]["healthy"] = True
            return JSONResponse(normalize_response(name, raw))

        except Exception as e:
            provider_health[name] = {"healthy": False, "last_fail": time.time()}
            errors.append(f"{name}: {str(e)}")
            continue

    return JSONResponse(
        {"error": "All providers failed", "details": errors},
        status_code=503,
    )


@app.get("/health")
async def health():
    """查看各供应商健康状态"""
    return {name: info for name, info in provider_health.items()}

怎么用

1. 安装依赖

pip install fastapi uvicorn httpx

2. 设置 API Key

export DEEPSEEK_API_KEY="sk-xxx"
export OPENAI_API_KEY="sk-xxx"
export ANTHROPIC_API_KEY="sk-xxx"

3. 启动

uvicorn gateway:app --port 8000

4. 调用

# 自动路由(优先 DeepSeek,失败自动切换)
curl -X POST  
  -H "Content-Type: application/json" 
  -d '{"messages": [{"role": "user", "content": "你好"}]}'

# 指定供应商
curl -X POST  
  -H "Content-Type: application/json" 
  -d '{"messages": [{"role": "user", "content": "你好"}], "provider": "openai"}'

# 查看健康状态
curl 

核心设计思路

1. 优先级路由

MODEL_PROVIDERS 数组的顺序就是优先级。DeepSeek 排第一是因为性价比最高,OpenAI 和 Claude 作为备用。

2. 自动故障转移

某个供应商报错后,标记为"不健康"并进入 60 秒冷却期。在此期间跳过该供应商,直接尝试下一个。冷却结束后自动恢复。

3. 响应格式统一

不管后端用的是哪个模型,返回格式都是统一的 {content, model, provider},调用方完全不需要关心底层用的是谁。

进阶优化方向

这个 100 行版本是最小可用版。生产环境中你可能还需要:

功能说明
流式响应SSE 支持,实现打字机效果
请求日志记录每次请求的模型、耗时、token 数
成本追踪按 token 计费,统计各模型花费
负载均衡多个 API Key 轮询,避免单 Key 限流
缓存层相同请求命中缓存,省钱又快
鉴权给网关加 API Key,防止滥用

如果你不想自己造轮子,市面上也有现成的模型聚合方案:

  • OpenRouter:国外主流,模型多,但国内访问不稳定
  • Ofox.ai:国内可直接用,支持主流模型,适合国内开发者
  • One-API / New-API:开源自建方案,需要自己部署维护

总结

搭一个 AI API 网关并不难,核心就三件事:统一接口、自动路由、故障转移

100 行代码就能解决 90% 的场景。剩下的 10%(流式、缓存、监控),要么自己加,要么用现成的聚合平台。

重点是:别让你的应用跟任何一个模型供应商绑死。今天 DeepSeek 最强,明天可能是别人。保持灵活,才能永远用上最好的。


觉得有用?点个赞收个藏,这是我继续写的最大动力。有问题评论区见。

本站提供的所有下载资源均来自互联网,仅提供学习交流使用,版权归原作者所有。如需商业使用,请联系原作者获得授权。 如您发现有涉嫌侵权的内容,请联系我们 邮箱:alixiixcom@163.com