跳至内容
为什么个人 AI 项目也该尽早加一层 LLM Gateway

为什么个人 AI 项目也该尽早加一层 LLM Gateway

July 18, 2026

一开始做 AI 功能,最直觉的写法是:业务代码里直接 from openai import OpenAI,过几天再加 Anthropic,再过几天补一个国内模型或本地 Ollama。短期能跑;一旦 Web、桌面、IDE 插件、定时脚本一起上,密钥、超时、降级和计费会散成几坨。

当 Web、桌面、IDE 插件与定时任务同时接入模型能力时,直接在业务里拼接多家 SDK 很快会失控。更稳妥的做法是先建立一层统一网关:对外提供稳定的 OpenAI 兼容接口,对内完成鉴权、路由、降级与观测。

客户端经 Gateway 打到多后端

1. 没网关时,代码会怎么烂掉

下面是真实项目里很常见的「第一版」——看起来短,维护三个月后会很痛:

# bad_app.py —— 业务里直接握多家 Key
import os
from openai import OpenAI
import anthropic

def chat(provider: str, prompt: str) -> str:
    if provider == "openai":
        c = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
        r = c.chat.completions.create(
            model="gpt-4o-mini",
            messages=[{"role": "user", "content": prompt}],
            timeout=60,
        )
        return r.choices[0].message.content or ""

    if provider == "anthropic":
        c = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
        r = c.messages.create(
            model="claude-sonnet-4-0",
            max_tokens=1024,
            messages=[{"role": "user", "content": prompt}],
        )
        return r.content[0].text

    if provider == "ollama":
        c = OpenAI(
            api_key="ollama",
            base_url="http://127.0.0.1:11434/v1",
        )
        r = c.chat.completions.create(
            model="qwen2.5:14b",
            messages=[{"role": "user", "content": prompt}],
        )
        return r.choices[0].message.content or ""

    raise ValueError(provider)

问题不在「能不能通」,而在横切能力散落:

横切能力散落写法的后果
密钥每个仓库、每个客户端各存一份,轮换一次改四处
超时 / 重试Web 一套、脚本一套,行为不一致
降级业务 if/else 越写越长,还经常忘测
成本只能月末看账单,不知道哪个功能在烧
协议差异流式、tool calling 字段各家不同,客户端重复适配

Gateway 要收口的,正是这张表。

2. 目标架构:客户端只认识一个 base_url

约定对外契约:

  • URL:https://api.your-domain.cloud/v1(或本机 http://127.0.0.1:4000/v1
  • 鉴权:客户端只拿虚拟 Keysk-xxx),真实 Provider Key 只放网关环境变量
  • 协议:优先 OpenAI Chat Completions;需要 Anthropic Messages 的再单独开兼容路由

这样桌面端、插件、CI 脚本都能复用同一套 SDK,只改两行:

# good_client.py —— 业务只打网关
from openai import OpenAI

client = OpenAI(
    api_key="sk-local-dev",           # 网关发的虚拟 Key
    base_url="http://127.0.0.1:4000/v1",
)

resp = client.chat.completions.create(
    model="auto",  # 网关侧映射到真实模型
    messages=[
        {"role": "system", "content": "你是简洁的中文技术助手。"},
        {"role": "user", "content": "用三句话解释什么是 LLM Gateway。"},
    ],
    temperature=0.3,
)
print(resp.choices[0].message.content)
print("usage:", resp.usage)

3. 实践:用 LiteLLM Proxy 搭最小可运行网关

一种常见且成熟的实现是 LiteLLM Proxy:用 YAML 声明模型,经 Docker 或本机进程拉起后,对外提供 OpenAI 兼容接口。

3.1 config.yaml

model_list:
  # 本地优先:开发机 / 内网 GPU
  - model_name: local-qwen
    litellm_params:
      model: ollama/qwen2.5:14b
      api_base: http://host.docker.internal:11434
      rpm: 30

  # 云端兜底:本地挂了再走
  - model_name: cloud-mini
    litellm_params:
      model: openai/gpt-4o-mini
      api_key: os.environ/OPENAI_API_KEY

  # 对外暴露的「逻辑模型名」
  - model_name: auto
    litellm_params:
      model: ollama/qwen2.5:14b
      api_base: http://host.docker.internal:11434

router_settings:
  # 主模型失败时按顺序切换
  fallbacks:
    - auto: ["cloud-mini"]
    - local-qwen: ["cloud-mini"]

general_settings:
  master_key: sk-change-me-in-prod
  # 生产务必换掉,并用环境变量注入

3.2 docker-compose.yml(轻量,适合 4G 小机做「网关本身」)

注意:网关进程很轻;不要把 14B 本地模型硬塞进只有 4G 内存的同机。本地推理放开发机或独立 GPU 机,云主机只跑代理 + 反代。

services:
  litellm:
    image: ghcr.io/berriai/litellm:main-latest
    ports:
      - "127.0.0.1:4000:4000"   # 只绑本机,外面走 Nginx
    volumes:
      - ./config.yaml:/app/config.yaml:ro
    environment:
      OPENAI_API_KEY: ${OPENAI_API_KEY}
      ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}
    command: ["--config", "/app/config.yaml", "--port", "4000"]
    restart: unless-stopped

启动与冒烟:

export OPENAI_API_KEY=sk-...
docker compose up -d

curl -s http://127.0.0.1:4000/v1/chat/completions \
  -H "Authorization: Bearer sk-change-me-in-prod" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "auto",
    "messages": [{"role":"user","content":"ping"}]
  }' | jq '.choices[0].message.content, .model, .usage'

4. 案例:一次「IDE 插件偶发超时」的排障

背景(复现过多次的模式):

  • 桌面 / IDE 插件直接打 Anthropic
  • Web 后台直接打 OpenAI
  • 夜间脚本打 Ollama
  • 某天 Anthropic 区域性抖动,插件大面积超时,Web 却正常——用户以为「我们的产品坏了」

上了网关之后的改法:

  1. 三端全部改 base_url 到网关
  2. 逻辑模型统一叫 coding / chat,由网关映射
  3. coding 主走 Claude,fallback 到 gpt-4o-mini,再不行提示「降级中」
  4. 网关日志固定打:request_id, model, upstream, latency_ms, status

示例:给每次调用挂上可追踪 ID(客户端侧):

import uuid
from openai import OpenAI

client = OpenAI(api_key="sk-local-dev", base_url="http://127.0.0.1:4000/v1")
rid = str(uuid.uuid4())

resp = client.chat.completions.create(
    model="auto",
    messages=[{"role": "user", "content": "总结这段报错:Connection reset by peer"}],
    extra_headers={"X-Request-Id": rid},
)
print(rid, resp.model, resp.usage)

网关侧用 access log / 应用日志按 X-Request-Id 检索,就能回答:「这次是上游 429,还是我们自己 60s 砍断的?」——没有这一层,只能对着三个控制台猜。

5. 和「进化手记」方向怎么对齐

我们站点主线是前端 → AI 应用 → 兼容代理 → 桌面 / 部署。Gateway 正好是中间那块「插座标准」:

  • lingma-proxy 这类项目,本质是把 OpenAI / Anthropic / 国内协议收成稳定调用面
  • 主域放内容站,api. 子域挂网关,密钥与业务仓库分离(见后文基础设施篇)
  • 客户端(Web / Tauri / 插件)只依赖契约,不依赖某一家 SDK 细节

6. 什么时候上、第一版做到哪

建议上线的信号(两条即够):

  • 已经在用 ≥2 个供应商,或「云 + 本地」混用
  • 同一个能力要被 ≥2 个客户端调用

第一版最小清单(刻意做薄):

  1. 统一入口 + 虚拟 Key
  2. 至少一条主备 fallback
  3. 请求日志:model / latency / tokens / status
  4. Provider Key 移出业务仓库
  5. 公网只经 HTTPS 反代,网关端口不直暴

语义缓存、复杂预算、多租户可以后补。先把调用面收口,后面谈工程化才有抓手。