微服务与 AI 网关设计

统一模型入口:路由、缓存、限流、容灾的架构实践

Maurice | 灵阙学院


一、为什么需要 AI 网关

当应用对接多个 LLM Provider 时,每个调用点直接集成 SDK 会导致以下问题:

  1. Key 散落:API Key 分散在各服务中,轮转和审计困难
  2. 无统一限流:单个服务超额调用导致全局 Key 被封
  3. 无容灾:Provider 宕机时需要手动切换,业务中断
  4. 无成本可见:Token 消耗分散在各处,无法统一计量

AI 网关将这些横切关注点集中到一个架构层解决。

┌─────────────────────────────────────────────────────────────┐
│                     AI 网关架构全景                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Service A ──┐                                              │
│  Service B ──┼── AI Gateway ──┬── OpenAI                    │
│  Service C ──┤    │           ├── Anthropic                 │
│  Agent D  ──┘    │           ├── Google                     │
│                  │           ├── 自部署 (vLLM)              │
│              ┌───┴────┐      └── SiliconFlow               │
│              │ 路由    │                                     │
│              │ 缓存    │                                     │
│              │ 限流    │                                     │
│              │ 重试    │                                     │
│              │ 计量    │                                     │
│              │ 日志    │                                     │
│              └────────┘                                     │
└─────────────────────────────────────────────────────────────┘

二、核心能力拆解

2.1 模型路由

请求进入 → 解析 model 字段 → 路由决策
│
├─ 直接映射: "gpt-4o" → OpenAI gpt-4o
├─ 别名映射: "fast" → gpt-4o-mini, "premium" → claude-opus
├─ 条件路由: if (tokens > 10000) → use claude (200K context)
├─ A/B 路由:  50% → gpt-4o, 50% → claude-sonnet (效果对比)
└─ 权重路由: 70% OpenAI + 30% 自部署 (成本优化)

路由配置示例:

routes:
  - model: "chat-balanced"
    strategy: "weighted"
    targets:
      - provider: openai
        model: gpt-4o-mini
        weight: 60
      - provider: siliconflow
        model: Qwen/Qwen2.5-72B-Instruct
        weight: 40

  - model: "chat-premium"
    strategy: "fallback"
    targets:
      - provider: anthropic
        model: claude-sonnet-4-20250514
        priority: 1
      - provider: openai
        model: gpt-4o
        priority: 2

2.2 多 Provider Failover

                    ┌─── Provider A (主) ───── 成功 → 返回
                    │       失败/超时
请求 → Failover ────┼─── Provider B (备) ───── 成功 → 返回
      Controller    │       失败/超时
                    └─── Provider C (兜底) ─── 成功 → 返回
                                                失败 → 报错
class FailoverChain:
    def __init__(self, providers: list[ProviderConfig]):
        self.providers = providers
        self.circuit_breakers: dict[str, CircuitBreaker] = {}

    async def execute(self, request: ChatRequest) -> ChatResponse:
        errors = []
        for provider in self.providers:
            cb = self.circuit_breakers[provider.name]
            if cb.is_open:
                continue  # 跳过已熔断的 Provider

            try:
                response = await provider.chat(
                    request, timeout=provider.timeout_seconds
                )
                cb.record_success()
                return response
            except (TimeoutError, ProviderError) as e:
                cb.record_failure()
                errors.append((provider.name, str(e)))

        raise AllProvidersFailedError(errors)

2.3 限流策略

限流维度 说明 实现方式
全局 RPM 每分钟总请求数 Token Bucket
Per-Key RPM 单个 API Key 的请求限制 滑动窗口
Per-User TPM 单用户每分钟 Token 消耗 令牌桶 + Token 计数
Per-Model RPM 单个模型的请求限制 与 Provider 限额对齐
并发数 同时在途请求数 信号量
class TokenBucketLimiter:
    def __init__(self, rpm: int, tpm: int):
        self.request_bucket = TokenBucket(
            capacity=rpm, refill_rate=rpm / 60
        )
        self.token_bucket = TokenBucket(
            capacity=tpm, refill_rate=tpm / 60
        )

    async def acquire(self, estimated_tokens: int) -> bool:
        if not self.request_bucket.consume(1):
            return False
        if not self.token_bucket.consume(estimated_tokens):
            self.request_bucket.refund(1)
            return False
        return True

三、语义缓存设计

传统缓存基于精确字符串匹配,但 LLM 场景中语义相同的不同措辞应该命中同一缓存。

3.1 架构

查询进入 → Embedding → 向量检索 (相似度 > 阈值?)
                          │
                    是 ────┼──── 否
                    │             │
               返回缓存      调用 LLM
               (省钱)        结果写入缓存

3.2 实现要点

class SemanticCache:
    def __init__(
        self,
        similarity_threshold: float = 0.95,
        ttl_seconds: int = 3600,
    ):
        self.threshold = similarity_threshold
        self.ttl = ttl_seconds
        self.vectorstore = Redis(
            index_name="semantic_cache",
            embedding=OpenAIEmbeddings(model="text-embedding-3-small"),
        )

    async def get(self, prompt: str) -> str | None:
        results = self.vectorstore.similarity_search_with_score(
            prompt, k=1
        )
        if results and results[0][1] >= self.threshold:
            entry = results[0][0]
            if not self._is_expired(entry):
                return entry.metadata["response"]
        return None

    async def set(self, prompt: str, response: str):
        self.vectorstore.add_texts(
            texts=[prompt],
            metadatas=[{
                "response": response,
                "created_at": time.time(),
            }],
        )

3.3 缓存策略

场景 是否适合缓存 原因
FAQ 查询 适合 (高命中) 问题有限且重复率高
RAG 问答 适合 (中命中) 相似问题指向相同文档
代码生成 谨慎 细微差异可能导致错误代码
创意写作 不适合 用户期望多样化输出
实时数据 不适合 结果时效性强

四、API Key 管理

4.1 安全架构

┌──────────────────────────────────────────────┐
│  API Key 安全层级                             │
├──────────────────────────────────────────────┤
│                                              │
│  应用层: 使用内部 Gateway Token              │
│     │    (不含任何 Provider Key)             │
│     ▼                                        │
│  网关层: Gateway Token → Provider Key 映射   │
│     │    Key 存储在 Vault/KMS               │
│     ▼                                        │
│  Provider: 实际的 API Key                    │
│                                              │
│  原则:                                       │
│  - 应用代码中永远不出现 Provider Key         │
│  - Key 轮转不影响应用服务                    │
│  - 每个应用发放独立的 Gateway Token          │
│  - 审计日志记录每次 Key 使用                 │
└──────────────────────────────────────────────┘

4.2 Key 池化与轮转

class KeyPool:
    """多 Key 轮转,分散限流压力"""

    def __init__(self, keys: list[ProviderKey]):
        self.keys = keys
        self.usage: dict[str, KeyUsage] = {}

    def get_key(self, provider: str) -> str:
        available = [
            k for k in self.keys
            if k.provider == provider
            and not self._is_rate_limited(k)
            and k.enabled
        ]
        if not available:
            raise NoAvailableKeyError(provider)
        # 选择用量最低的 Key
        return min(available, key=lambda k: self.usage[k.id].rpm_used)

五、使用计量与计费

5.1 计量数据模型

每次请求记录:
{
    "request_id": "req_abc123",
    "timestamp": "2026-02-27T10:30:00Z",
    "user_id": "user_001",
    "app_id": "chatbot_v2",
    "model": "gpt-4o",
    "provider": "openai",
    "input_tokens": 1250,
    "output_tokens": 380,
    "cached": false,
    "latency_ms": 2340,
    "cost_usd": 0.0068,
    "status": "success"
}

5.2 成本聚合

-- 按应用/日期聚合成本
SELECT
    app_id,
    DATE(timestamp) AS date,
    SUM(input_tokens) AS total_input_tokens,
    SUM(output_tokens) AS total_output_tokens,
    SUM(cost_usd) AS total_cost,
    COUNT(*) AS request_count,
    AVG(latency_ms) AS avg_latency
FROM llm_usage_log
WHERE timestamp >= NOW() - INTERVAL '7 days'
GROUP BY app_id, DATE(timestamp)
ORDER BY total_cost DESC;

六、现有方案对比

维度 LiteLLM Portkey Cloudflare AI Gateway
部署方式 自托管 (开源) SaaS + 自托管 SaaS (Cloudflare)
Provider 数量 100+ 200+ 主流 ~15
OpenAI 兼容 完整 完整 完整
语义缓存 内置 内置 内置
限流 内置 内置 内置
Failover 内置 内置 内置
计量/审计 内置 内置 (高级) 内置
评估/Guardrails 基础 高级
开源协议 MIT 部分开源 不适用
适合场景 全场景自托管 企业级 SaaS CDN 用户

6.1 LiteLLM 快速部署

# litellm_config.yaml
model_list:
  - model_name: "gpt-4o"
    litellm_params:
      model: "openai/gpt-4o"
      api_key: "os.environ/OPENAI_API_KEY"

  - model_name: "gpt-4o"  # 同名 = 负载均衡
    litellm_params:
      model: "azure/gpt-4o-deployment"
      api_key: "os.environ/AZURE_API_KEY"
      api_base: "https://my-endpoint.openai.azure.com"

  - model_name: "claude-sonnet"
    litellm_params:
      model: "anthropic/claude-sonnet-4-20250514"
      api_key: "os.environ/ANTHROPIC_API_KEY"

router_settings:
  routing_strategy: "latency-based-routing"
  num_retries: 3
  timeout: 30
  fallbacks:
    - {"gpt-4o": ["claude-sonnet"]}
litellm --config litellm_config.yaml --port 4000

七、生产架构参考

┌──────────────────────────────────────────────────────────────┐
│                                                              │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐                     │
│  │Service A│  │Service B│  │Agent C  │   Application Layer  │
│  └────┬────┘  └────┬────┘  └────┬────┘                     │
│       └────────────┼────────────┘                            │
│                    ▼                                         │
│  ┌────────────────────────────────┐                         │
│  │  Load Balancer (Nginx/Envoy)   │                         │
│  └────────────┬───────────────────┘                         │
│               ▼                                              │
│  ┌────────────────────────────────┐    ┌──────────────┐     │
│  │  AI Gateway (LiteLLM)          │───→│  Redis        │    │
│  │  - Auth + Rate Limit           │    │  - Cache      │    │
│  │  - Route + Failover            │    │  - Rate State │    │
│  │  - Log + Meter                 │    └──────────────┘     │
│  └──────┬──────────┬──────────┬───┘                         │
│         ▼          ▼          ▼                              │
│  ┌──────────┐ ┌─────────┐ ┌──────────┐                     │
│  │  OpenAI  │ │Anthropic│ │Self-host │  Provider Layer     │
│  └──────────┘ └─────────┘ │(vLLM)   │                      │
│                            └──────────┘                     │
│                                                              │
│  ┌────────────────────────────────┐                         │
│  │  PostgreSQL (usage_log)        │  Persistence Layer      │
│  │  + Grafana Dashboard           │                         │
│  └────────────────────────────────┘                         │
└──────────────────────────────────────────────────────────────┘

八、实施路线

阶段 目标 产出 周期
Phase 1 统一入口 + Failover LiteLLM 部署 + 基础路由 3 天
Phase 2 计量 + 限流 用量仪表盘 + 预算告警 1 周
Phase 3 语义缓存 Redis 向量缓存 + 命中率监控 1 周
Phase 4 高级路由 A/B 测试 + 智能路由 + 成本优化 2 周

Maurice | maurice_wen@proton.me