AI 网关与 LLM API 治理实战

2026-06-03 21:59 AI 网关与 LLM API 治理实战已关闭评论

AI 网关与 LLM API 治理实战:从混乱到可控,我踩过的 3 个坑

结论先行:用 Kong + 自定义插件构建 AI 网关,能解决 LLM API 的认证、限流成本控制和监控问题;但如果你直接套用传统 API 网关方案,必踩坑。本文记录我在生产环境中从零搭建 LLM API 治理体系的完整过程,包括 3 个关键踩坑点和最终方案。


为什么需要 AI 网关?

三个月前,我们团队接了一个 AI 客服项目,后端调用 OpenAI、Claude 和本地部署的 LLaMA 模型。第一个月就让我焦头烂额:

  • 开发团队各自申请 API Key,月底账单 3 万刀,没人说得清谁用了多少
  • 某个测试脚本死循环调用,直接打爆 OpenAI 的 Rate Limit,导致线上服务中断 15 分钟
  • 无法追踪每个请求的 Token 消耗,成本分摊全靠拍脑袋

传统 API 网关(如 Nginx、Kong)只能做 HTTP 层面的限流和认证,但 LLM API 治理需要按 Token 计费、按模型路由、按用户隔离。所以我决定基于 Kong 搭建一个 AI 网关层。


第一步:选型与架构

我对比了三个方案:

方案 优点 缺点
Kong + 自定义插件 成熟、可控 需要 Lua 开发
Apache APISIX 插件生态丰富 学习曲线陡
自建 Python 代理 灵活 性能差、无现成限流

最终选择了 Kong 3.4 + 自定义 Lua 插件,因为团队已有 Kong 运维经验,而且 Lua 插件的性能远高于 Python 代理,这点在压测中得到了验证。

架构图:

用户 → Kong (AI 网关) → LLM Provider (OpenAI/Claude/本地模型)
          ↑
     自定义插件: auth、rate-limit-by-token、cost-tracker

第二步:核心实现(附代码)

2.1 安装 Kong 并启动

# 使用 Docker 快速启动
docker run -d --name kong \
  -e "KONG_DATABASE=off" \
  -e "KONG_DECLARATIVE_CONFIG=/kong/declarative/kong.yml" \
  -v $(pwd)/kong.yml:/kong/declarative/kong.yml \
  -p 8000:8000 \
  kong:3.4

2.2 配置路由和服务

kong.yml 核心配置:

_format_version: "3.0"
services:
  - name: openai-service
    url: https://api.openai.com/v1
    routes:
      - name: openai-chat
        paths:
          - /v1/chat/completions
        methods:
          - POST
    plugins:
      - name: ai-auth  # 自定义认证插件
      - name: ai-rate-limit  # 自定义 Token 限流插件
      - name: ai-cost-tracker  # 自定义成本追踪插件

2.3 自定义插件:Token 级限流(踩坑 1)

踩坑 1:传统 QPS 限流不适用于 LLM

最初我用 Kong 内置的 rate-limiting 插件,按每分钟 100 次请求限流。结果发现这完全是个坑:

  • 用户 A 请求 gpt-4(单次消耗 2000 Token),用户 B 请求 gpt-3.5(单次消耗 200 Token)
  • 同样 100 次请求,A 消耗 20 万 Token,B 只消耗 2 万 Token
  • 成本差异 10 倍,但限流策略完全一样

解决方案:解析请求体中的 modelmax_tokens,动态计算 Token 消耗并限流。

Lua 插件核心逻辑:

-- plugins/ai-rate-limit/handler.lua
local AIRateLimit = {
  PRIORITY = 1000,
  VERSION = "1.0.0",
}

function AIRateLimit:access(conf)
  local body = kong.request.get_body()
  if not body or not body.model then
    return kong.response.exit(400, { error = "Missing model in request body" })
  end

  -- 根据模型获取 Token 单价和最大限制
  local model_config = {
    ["gpt-4"] = { price_per_token = 0.03, max_tokens = 8000 },
    ["gpt-3.5-turbo"] = { price_per_token = 0.002, max_tokens = 4096 },
  }

  local config = model_config[body.model]
  if not config then
    return kong.response.exit(400, { error = "Unsupported model" })
  end

  -- 估算本次请求消耗的 Token 数
  local estimated_tokens = #body.messages * 100  -- 简化估算
  if body.max_tokens then
    estimated_tokens = estimated_tokens + body.max_tokens
  end

  -- 按 Token 限流:每个用户每天最多 100000 Token
  local user_id = kong.request.get_header("X-User-Id")
  local key = "user:" .. user_id .. ":tokens:" .. os.date("%Y%m%d")
  local current_tokens = kong.cache:get(key)

  if current_tokens and current_tokens + estimated_tokens > 100000 then
    return kong.response.exit(429, { error = "Token limit exceeded" })
  end

  -- 更新计数器
  kong.cache:set(key, current_tokens + estimated_tokens, 86400)
end

return AIRateLimit

注意:Token 估算很粗糙,生产环境建议用 tiktoken 库精确计算。我后来改用 Python 侧服务做预计算,通过 Kong 的 pre-function 插件调用。

2.4 成本追踪:实时记录每笔消耗(踩坑 2)

踩坑 2:异步日志导致数据丢失

我最初用 Kong 的 log-serializer 插件把请求日志写到 Kafka,然后消费端做成本统计。结果发现:

  • 生产环境 QPS 200+,Kafka 偶尔背压,日志丢失 5%
  • 月底对账发现成本少了 3000 刀,这个教训太贵了

解决方案:同步写 Redis + 定时刷盘到数据库。

-- plugins/ai-cost-tracker/handler.lua
function AICostTracker:log(conf)
  local body = kong.request.get_body()
  local response_body = kong.response.get_body()

  if not body or not response_body then
    return
  end

  -- 从响应体提取实际 Token 消耗
  local usage = response_body.usage
  if not usage then
    return
  end

  local cost_record = {
    user_id = kong.request.get_header("X-User-Id"),
    model = body.model,
    prompt_tokens = usage.prompt_tokens,
    completion_tokens = usage.completion_tokens,
    total_tokens = usage.total_tokens,
    cost = calculate_cost(body.model, usage.total_tokens),
    timestamp = os.time(),
    request_id = kong.request.get_header("X-Request-Id"),
  }

  -- 同步写入 Redis,确保不丢失
  local redis = require "resty.redis"
  local red = redis:new()
  red:set_timeout(1000)
  local ok, err = red:connect("127.0.0.1", 6379)
  if not ok then
    kong.log.err("Failed to connect to Redis: ", err)
    return
  end

  local key = "cost:" .. os.date("%Y%m%d") .. ":" .. cost_record.user_id
  red:rpush(key, cjson.encode(cost_record))
  red:expire(key, 86400 * 3)
end

2.5 多 Provider 路由与降级(踩坑 3)

踩坑 3:OpenAI 故障时,手动切换太慢

某天 OpenAI 出现 5xx 故障,我们花了 12 分钟手动修改 Kong 路由切换到 Claude。期间线上服务全部中断,老板的脸色比 OpenAI 的响应码还难看。

解决方案:实现自动降级 + 健康检查。

# 在 kong.yml 中添加备用服务
services:
  - name: openai-service
    url: https://api.openai.com/v1
    # ... 主服务
  - name: claude-service
    url: https://api.anthropic.com/v1
    # ... 备用服务
  - name: local-llama-service
    url: http://localhost:8080/v1
    # ... 本地服务

plugins:
  - name: ai-fallback
    config:
      # 主服务 5xx 时自动切换
      primary: openai-service
      fallbacks:
        - claude-service
        - local-llama-service
      retry_interval: 60  # 60 秒后重试主服务

Lua 插件实现自动降级:

function AIFallback:access(conf)
  local primary = conf.primary
  local fallbacks = conf.fallbacks

  -- 检查主服务健康状态
  local healthy = kong.cache:get("health:" .. primary)
  if healthy == false then
    -- 尝试备用服务
    for _, fallback in ipairs(fallbacks) do
      local fb_healthy = kong.cache:get("health:" .. fallback)
      if fb_healthy ~= false then
        kong.service.set_upstream(fallback)
        return
      end
    end
    -- 所有服务都不可用
    return kong.response.exit(503, { error = "All providers unavailable" })
  end
end

第三步:监控与告警

部署后,我添加了三个关键监控指标:

  1. Token 消耗趋势:按小时、按用户、按模型
  2. Provider 可用性:5xx 率 > 1% 触发告警
  3. 成本异常:单用户日消耗超过阈值自动限流

用 Prometheus + Grafana 实现:

# prometheus.yml
scrape_configs:
  - job_name: 'kong'
    metrics_path: '/metrics'
    static_configs:
      - targets: ['localhost:8001']

最终效果

运行两个月后,数据对比让我松了一口气:

指标 治理前 治理后
月度成本 $32,000 $18,500(降低 42%)
故障恢复时间 12 分钟 < 30 秒
成本可追溯性 每笔请求可查
API Key 泄露风险 低(统一网关认证)

最大收益不是省钱,而是失控感消失了。现在团队可以放心地让每个开发直接调用 LLM,因为网关层已经兜底了。


延伸思考

  1. Streaming 响应:我目前的方案不支持 SSE(Server-Sent Events),因为 Kong 的 Lua 插件在流式响应中无法完整捕获 Token 消耗。如果你需要实时流式输出,建议在应用层做日志,或者改用 Envoy 的 WASM 插件。
  2. 多租户隔离:按用户做 Token 池还是按团队?我们最终选择了按用户,但大客户需要单独配置配额。
  3. 自定义模型定价:如果你用本地模型,成本计算更复杂(GPU 时间 vs Token 数)。我目前按请求时长估算,但不够精确。
  4. 未来方向:考虑用 eBPF 做更细粒度的网络监控,或者集成 LLM 专用的安全检测(如 Prompt Injection 防护)。

最后一句:不要迷信任何现成的 AI 网关产品,你的业务场景才是最好的架构师。踩坑不可怕,可怕的是不记录。

你可能感兴趣的文章

来源:每日教程每日一例,深入学习实用技术教程,关注公众号TeachCourse
转载请注明出处: https://teachcourse.cn/4205.html ,谢谢支持!

资源分享

分类:ai 标签:, , , , , , , ,
ubuntu写入权限(W)和执行权限(X) ubuntu写入权限(W)和执行权限(X
如何重用接口多个抽象方法中的一个或多个? 如何重用接口多个抽象方法中的一
Android开发之Genymotion安装第三方软件的“APP not installed”问题 Android开发之Genymotion安装第
Python 异步编程实战:asyncio 核心机制与性能优化 Python 异步编程实战:asyncio

评论已关闭!