一个 Key 不够用?把 N 个 Grok API Key 组成一个 Pool,统一入口、自动轮询、自动熔断、统一监控。
实战踩坑笔记,LiteLLM / Nginx+Lua / 自建网关三套方案任你选。
直接拿一个 Key 怼生产环境,迟早出事。单 Key 有三大硬伤:
官方按 tier 限 RPM / TPM / 每日配额,一个 Key 很快触顶,报 429 限流
Key 失效、被风控、余额耗尽 → 全线停摆,连个备胎都没有
哪个调用方花了多少钱、何时打满配额,完全看不到
高并发打一个 Key,很容易触发官方滥用检测,直接封号
Pool 的核心价值:
客户端只认一个入口地址;轮询、熔断、重试全部在 Pool 层完成。
适用场景:
不管你选哪套方案,底层都是这几个机制。理解了它,出问题才知道怎么查。
请求按顺序轮流分派:1→Key A、2→Key B、3→Key C、4→Key A…… 最简单,所有 Key 用量平均。LiteLLM 叫 simple-shuffle,Nginx 默认 upstream 就是轮询。
不同 Key 配额不同(例如一个 100 RPM、一个 60 RPM),按权重分配,配额大的多接一些。LiteLLM 用 weighted 策略,在 model_info 配 rpm 即可。
定时或按请求探测每个 Key 状态:429(限流)、401(Key 失效)、503(上游挂)→ 即刻标记为「冷却」,不再分派请求给它,直至冷却期结束。
连续失败 N 次才触发熔断,暂停该 Key M 分钟;成功一次就重置计数。这个机制防「抖」——不会因为一次 429 就废了一个好 Key。LiteLLM 参数:allowed_fails + cooldown_time。
相同请求(相同 prompt)直接回缓存,完全不经过上游,节省配额省钱。适合固定 prompt 的场景(例如翻译、摘要模板)。注意:对话类请求缓存命中率低,要小心缓存了不该缓存的内容。
每个 Key 独立记录:请求数、token 用量、延迟、状态码、失败原因。这些是第 8 节做告警的数据基础——没有日志就如同盲目开车。
| 机制 | 作用 | 关键配置 | 建议 |
|---|---|---|---|
| 轮询 | 请求均匀分发 | simple-shuffle / upstream 默认 | ✅ 必开 |
| 加权 | 按配额比例分配 | rpm / weight | 🟡 Key 配额不一致才需要 |
| 健康检查 | 剔除失效 Key | enable_pre_call_checks | ✅ 必开 |
| 熔断 | 防抖动 + 自动恢复 | allowed_fails: 3 / cooldown_time: 60 | ✅ 必开 |
| 缓存 | 节省配额省钱 | Redis / 内存缓存 | ❌ 按场景选 |
| 日志监控 | 用量 / 告警数据源 | Prometheus / 日志轮替 | ✅ 必开 |
四套方案都是开源 / 免费,功能与维护成本相差很远。直接看表:
| 方案 | 优点 | 缺点 | 适合谁 | 部署难度 | 建议 |
|---|---|---|---|---|---|
| A. LiteLLM Proxy 推荐 ⭐ |
轮询/加权/熔断/重试/fallback 全部内置,改 YAML 即可;OpenAI 兼容,客户端零改动;自带 /metrics 监控 |
Python 进程食 ~200-400MB 内存;概念略多,新手要看文档 | 认真部署、团队共用、要监控告警 | 中 | ✅ 首选 |
| B. Nginx + Lua / OpenResty | 极轻量、零额外进程;与现有 Nginx 架构融合;性能天花板最高 | 轮询/熔断/健康检查全部要自己写 Lua;无自带监控面板;Debug 靠日志 | 已有 Nginx 基础、只想要简单轮询、机器资源紧张 | 中 | 🟡 轻量备选 |
| C. 自订 Python FastAPI 网关 | 逻辑 100% 可控;想加什么功能都可以(缓存/计费/审计);代码就是文档 | 全部自己写自己维护;健壮性靠你;出 bug 没有人救 | 想完全掌控、学习用途、有特殊业务逻辑 | 高 | 🟡 进阶向 |
| D. One API / New API | Web 管理界面友好,鼠标点几下加 Key;中文社区活跃;多模型聚合(Grok/OpenAI/Claude 通食) | 功能重,带用户体系/计费/额度一堆东西;Grok 要按 OpenAI 兼容手动配;更新频繁有兼容风险 | 中文团队、要图形界面管理、顺便接多个模型厂商 | 低-中 | 🟡 团队向备选 |
以下步骤在 Ubuntu 22.04 / Debian 12 上亲测可行。用 Docker 或 venv 都可以,venv 适合机器资源紧张。
# Ubuntu 22.04+ / Python 3.10+
sudo apt update && sudo apt install -y python3.11 python3.11-venv curl jq
python3.11 -m venv /opt/litellm/venv
source /opt/litellm/venv/bin/activate
pip install -U "litellm[proxy]"docker run -d --name litellm -p 4000:4000 -v $(pwd):/app ghcr.io/berriai/litellm:main-latest --config /app/config.yaml,之后的 config.yaml 一样适用。# ── 模型路由表:同一个对外名 grok-main 挂多个 Key ──
model_list:
- model_name: grok-main # 对外暴露名,客户端请求用这个
litellm_params:
model: grok/grok-3 # 真实上游模型(xAI 命名,看官方 docs)
api_key: os.environ/GROK_API_KEY_1
rpm: 60 # 这个 Key 每分钟上限,加权轮询依据
model_info:
tier: primary
- model_name: grok-main
litellm_params:
model: grok/grok-3
api_key: os.environ/GROK_API_KEY_2
rpm: 60
model_info:
tier: primary
- model_name: grok-main
litellm_params:
model: grok/grok-3
api_key: os.environ/GROK_API_KEY_3
rpm: 30
model_info:
tier: primary
- model_name: grok-fallback # 兜底模型:主模型全挂时用
litellm_params:
model: grok/grok-3-mini
api_key: os.environ/GROK_API_KEY_1
# ── Router 核心:轮询 + 熔断 + 重试 + fallback ──
router_settings:
routing_strategy: simple-shuffle # 轮询分派(或 least-busy / weighted)
enable_pre_call_checks: true # 发送前先查配额,减少无效请求
cooldown_time: 60 # 熔断后冷却秒数(M 分钟恢复)
allowed_fails: 3 # 连续失败 N 次触发熔断
num_retries: 2 # 失败自动换 Key 重试次数
request_timeout: 60 # 单请求超时(秒)
fallbacks: [{"grok-main": ["grok-fallback"]}] # 全部 Key 挂晒→兜底模型
litellm_settings:
drop_params: true
set_verbose: true
telemetry: false # 关闭遥测,隐私友好# 三个独立账号的 Grok API Key(不要用同一个账号开的多个 Key!原因见第 7 节)
GROK_API_KEY_1=YOUR_GROK_API_KEY_1
GROK_API_KEY_2=YOUR_GROK_API_KEY_2
GROK_API_KEY_3=YOUR_GROK_API_KEY_3
# 保护 Pool 入口的 Master Key(客户端请求时要带)
LITELLM_MASTER_KEY=YOUR_MASTER_KEYchmod 600 /opt/litellm/.env,不要给其他用户读到。详见第 6 节。[Unit]
Description=LiteLLM Proxy (Grok API Pool)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/litellm
EnvironmentFile=/opt/litellm/.env
ExecStart=/opt/litellm/venv/bin/litellm --config /opt/litellm/config.yaml --port 4000
Restart=always
RestartSec=5
NoNewPrivileges=true
[Install]
WantedBy=multi-user.targetsudo chown -R www-data:www-data /opt/litellm
sudo systemctl daemon-reload
sudo systemctl enable --now litellm
sudo systemctl status litellm --no-pager# 1) 健康检查
curl -s http://127.0.0.1:4000/health/liveliness
# 2) 连续发 5 次请求,看日志确认 Key 有轮换
for i in 1 2 3 4 5; do
curl -s http://127.0.0.1:4000/v1/chat/completions \
-H "Authorization: Bearer YOUR_MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"grok-main","messages":[{"role":"user","content":"hi"}],"max_tokens":10}'
echo
done
# 3) 看日志:留意每个请求用了哪个 Key(provider/API Key 尾几位)
journalctl -u litellm -f --no-pager/v1/models 校验模型名,务必让对外模型名(model_name)与列表一致;若客户端工具集包含 web_search 而上游 console 通道会注入同名原生工具,需要在网关层去重,否则整个请求会被 400 拒绝(详见第 9 节)。机器只有 512MB RAM 又不想装 Python?用 OpenResty(Nginx + LuaJIT)做轻量网关。轮询、Key 注入、熔断全部用 Lua 写,食内存几乎为零。
# Ubuntu / Debian 官方源
sudo apt install -y openresty # 或者按 openresty.org 加官方源
# 检查 Lua 模块
openresty -V 2>&1 | grep -o 'lua-nginx-module'# Grok API Pool —— Nginx + Lua 轻量网关
# 部署前替换: YOUR_GROK_API_KEY_1..3 / YOUR_POOL_ENTRY_KEY / your-domain.com
lua_shared_dict grok_pool 1m; # 跨 worker 共享熔断状态
init_by_lua_block {
-- Key 池(占位符,部署前替换)
KEYS = { "YOUR_GROK_API_KEY_1", "YOUR_GROK_API_KEY_2", "YOUR_GROK_API_KEY_3" }
RR = 0 -- round-robin 游标
MAX_FAILS = 3 -- 连续失败熔断阈值
COOLDOWN_SEC = 60 -- 冷却秒数
}
upstream grok_backend {
server api.x.ai:443;
keepalive 16; -- 连接复用,降低握手开销
}
server {
listen 443 ssl;
server_name your-domain.com;
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
location /v1/ {
rewrite_by_lua_block {
-- 1) 校验调用方 Key(保护 Pool 入口)
if ngx.var.http_authorization ~= "Bearer YOUR_POOL_ENTRY_KEY" then
return ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
-- 2) 轮询选 Key,跳过冷却中的;全部冷却则取第一个硬试
local d = ngx.shared.grok_pool
local now = os.time()
local chosen = KEYS[1]
ngx.ctx.key_idx = 1
for i = 1, #KEYS do
local idx = (RR % #KEYS) + 1
RR = RR + 1
local cooldown = d:get("cd:" .. idx) or 0
if cooldown <= now then
chosen = KEYS[idx]
ngx.ctx.key_idx = idx
break
end
end
-- 3) 注入上游要用的 Key(覆盖调用方传的)
ngx.req.set_header("Authorization", "Bearer " .. chosen)
}
proxy_pass https://grok_backend/v1/;
proxy_set_header Host api.x.ai;
proxy_ssl_name api.x.ai;
proxy_ssl_server_name on;
proxy_buffering off; -- 流式响应必须关缓冲
proxy_read_timeout 300s;
proxy_connect_timeout 10s;
header_filter_by_lua_block {
-- 4) 429/401/503 → 记失败;连续 MAX_FAILS 次 → 冷却 COOLDOWN_SEC
local status = ngx.status
if status == 429 or status == 401 or status == 503 then
local idx = ngx.ctx.key_idx
if idx then
local d = ngx.shared.grok_pool
local fails = (d:get("f:" .. idx) or 0) + 1
if fails >= MAX_FAILS then
d:set("cd:" .. idx, os.time() + COOLDOWN_SEC)
d:set("f:" .. idx, 0)
else
d:set("f:" .. idx, fails)
end
end
end
}
}
}# 语法检查 + 重载
openresty -t
sudo systemctl reload openresty
# 测试:带入口 Key 请求
curl -s https://your-domain.com/v1/chat/completions \
-H "Authorization: Bearer YOUR_POOL_ENTRY_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"grok-3","messages":[{"role":"user","content":"hi"}],"max_tokens":10}'error_log;SSE 流式要记得 proxy_buffering off;Key 数量变化要改配置重载。想要熔断/健康检查/监控全套,老老实实返去 LiteLLM。想完全掌控逻辑?用 FastAPI + httpx 写一个 ~120 行的轮询池网关,核心流程:Key 池初始化 → 请求分发 → 响应处理 → 失败重试 → 冷却恢复。以下代码亲测可跑,填占位符即用。
# gateway.py —— Grok API Pool 网关(FastAPI + httpx 异步)
# 启动: uvicorn gateway:app --host 0.0.0.0 --port 8000
# 部署前替换: 环境变量 GROK_KEYS 填真实 Key,逗号分隔
import asyncio
import os
import time
import httpx
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse, StreamingResponse
# ── 配置(全部走环境变量,不要硬编码)──
API_KEYS = os.getenv("GROK_KEYS", "").split(",") # 多个 Key 逗号分隔
GROK_BASE = os.getenv("GROK_BASE", "https://api.x.ai/v1")
MODEL = os.getenv("GROK_MODEL", "grok-3")
MAX_FAILS = int(os.getenv("MAX_FAILS", "3")) # 熔断阈值
COOLDOWN = int(os.getenv("COOLDOWN_SEC", "60")) # 冷却秒数
TIMEOUT = float(os.getenv("TIMEOUT_SEC", "60"))
ENTRY_KEY = os.getenv("POOL_ENTRY_KEY", "YOUR_POOL_ENTRY_KEY")
# ── Key 池状态:失败次数 + 冷却截止时间 ──
state = {k: {"fails": 0, "cooldown_until": 0.0} for k in API_KEYS}
lock = asyncio.Lock()
rr = 0 # round-robin 游标
app = FastAPI(title="Grok API Pool Gateway")
def _healthy(key):
return state[key]["cooldown_until"] < time.time()
async def _pick_key():
# 轮询 + 跳过冷却中的 Key;全部冷却就全部重试(最坏情况)
global rr
async with lock:
now = time.time()
keys = [k for k in API_KEYS if _healthy(k)]
if not keys:
keys = API_KEYS
k = keys[rr % len(keys)]
rr += 1
return k
def _on_fail(key, reset=False):
# 失败计数;连续 MAX_FAILS 次 → 熔断冷却
s = state[key]
if reset:
s["fails"] = 0
s["cooldown_until"] = 0.0
else:
s["fails"] += 1
if s["fails"] >= MAX_FAILS:
s["cooldown_until"] = time.time() + COOLDOWN
s["fails"] = 0
@app.get("/v1/models")
async def models():
return {"object": "list", "data": [{"id": MODEL, "object": "model"}]}
@app.post("/v1/chat/completions")
async def chat(request: Request):
# 入口鉴权:调用方要带自己的 Key
if request.headers.get("Authorization") != "Bearer " + ENTRY_KEY:
return JSONResponse({"error": {"message": "unauthorized"}}, status_code=401)
body = await request.json()
headers = {"Content-Type": "application/json"}
# 流式请求:单次直通(重试会破坏 SSE 顺序,不做)
if body.get("stream"):
async def gen():
async with httpx.AsyncClient(timeout=TIMEOUT) as client:
async with client.stream(
"POST", f"{GROK_BASE}/chat/completions",
json=body, headers=headers,
) as resp:
async for chunk in resp.aiter_bytes():
yield chunk
key = await _pick_key()
headers["Authorization"] = f"Bearer {key}"
return StreamingResponse(gen(), media_type="text/event-stream")
# 非流式:失败换 Key 重试,直到全部 Key 试完
for _ in range(len(API_KEYS) + 1):
key = await _pick_key()
headers["Authorization"] = f"Bearer {key}"
try:
async with httpx.AsyncClient(timeout=TIMEOUT) as client:
resp = await client.post(
f"{GROK_BASE}/chat/completions", json=body, headers=headers
)
if resp.status_code in (429, 401, 503):
_on_fail(key) # 限流/凭证错/上游挂 → 换 Key
continue
if resp.status_code >= 500:
_on_fail(key)
continue
_on_fail(key, reset=True) # 成功 → 重置计数
return JSONResponse(content=resp.json(), status_code=resp.status_code)
except httpx.TimeoutException:
_on_fail(key)
except Exception:
_on_fail(key)
return JSONResponse(
{"error": {"message": "all keys unavailable"}}, status_code=503
)fastapi
uvicorn[standard]
httpxFROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY gateway.py .
EXPOSE 8000
CMD ["uvicorn", "gateway:app", "--host", "0.0.0.0", "--port", "8000"]services:
grok-gateway:
build: .
ports:
- "127.0.0.1:8000:8000" # 只绑本机,外面经反向代理入
environment:
GROK_KEYS: "YOUR_GROK_API_KEY_1,YOUR_GROK_API_KEY_2,YOUR_GROK_API_KEY_3"
GROK_BASE: "https://api.x.ai/v1"
GROK_MODEL: "grok-3"
POOL_ENTRY_KEY: "YOUR_POOL_ENTRY_KEY"
MAX_FAILS: "3"
COOLDOWN_SEC: "60"
restart: unless-stoppeddocker compose up -d --build
# 测试
curl -s http://127.0.0.1:8000/v1/chat/completions \
-H "Authorization: Bearer YOUR_POOL_ENTRY_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"grok-3","messages":[{"role":"user","content":"hi"}],"max_tokens":10}'这一节是红线——真 Key 流出一次,轻则被盗刷,重则连累成个账号。以下规范逐条执行。
| 敏感数据 | 占位符 | 说明 |
|---|---|---|
| Grok API Key | YOUR_GROK_API_KEY_1 / _2 / _3 | 每个独立账号一个 Key,编号区分 |
| Pool 入口 Key | YOUR_POOL_ENTRY_KEY | 客户端调用时要带的共享密钥 |
| LiteLLM Master Key | YOUR_MASTER_KEY | 保护 /chat/completions 入口 |
| Bot Token | YOUR_BOT_TOKEN | 接 Telegram Bot 时用 |
| 域名 | your-domain.com | 证书、server_name 一律用这个 |
| 服务器 IP | <YOUR_SERVER_IP> | 防火墙 / 白名单场景用 |
grep -rn "sk-" . --include="*.py" --include="*.yaml" --include="*.env"chmod 600 .env,并确保属主是服务运行用户api.x.ai 与内部端口server {
listen 80;
server_name your-domain.com;
# 强制跳 HTTPS(certbot 会帮你写好,这里示意)
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name your-domain.com;
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
# LiteLLM 自带 Master Key 鉴权,这里再加一层 IP 白名单(可选)
allow <YOUR_SERVER_IP>/32;
deny all;
location / {
proxy_pass http://127.0.0.1:4000; # 指去 LiteLLM
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_buffering off; # 流式响应
proxy_read_timeout 300s;
}
}sudo certbot --nginx -d your-domain.com,免费自动续期,无需自己处理证书。实话实说:Pool 不是万能的,以下限制部署前就要想清楚。
| 限制 | 说明 | 应对 |
|---|---|---|
| 只能分散,不能突破 | 总吞吐 = N 个 Key 配额之和,单 Key 限速依然存在 | 按需加 Key,不要指望一个 Pool 变无限 |
| 同账号多 Key 可能共享配额 | xAI 政策以官方为准——部分 tier 下同一账号开的多个 Key 可能共用同一配额池,那 Pool 就等于白建 | ❌ 部署前用不同账号各开一个 Key(实测最稳) |
| 高并发触发滥用检测 | 多 Key 同时高频打,可能触发官方风控,成批账号被审查/封禁 | 控制总 QPS,观察配额消耗曲线,不要一次拉满 |
| 额外单点延迟 | 经 Pool 多一跳,通常 +5~20ms,可接受但不是 0 | Pool 与调用方尽量同区域部署 |
| 流式/长连接要特殊处理 | SSE 流式要关缓冲、调大超时;长连接会话要会话保持 | Nginx 开 proxy_buffering off;LiteLLM 配 request_timeout |
| 法律与 ToS 风险 | Key 共享、转售、多 Key 聚合是否违反 xAI 服务条款,要自己确认 | 🟡 自用共享注意合规,商用转售掂量清楚 |
部署完成只是开始。Key 会失效、配额会打满、账号会被风控——所以要有监控与维护流程。
/var/log/litellm/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
copytruncate
}journalctl -u litellm,配 journald.conf 的 SystemMaxUse=500M 防日志撑爆磁盘。| 指标名 | 含义 | 建议告警阈值 |
|---|---|---|
litellm_total_requests | 总请求数 | - |
litellm_token_usage_total | token 用量(按模型分 label) | 日用量对比基线 |
litellm_remaining_quota | 各 Key 剩余配额 | 🟡 低于 20% 告警 |
litellm_deployment_failures | 各 Key 失败次数 | ❌ > 0 持续 5 分钟告警 |
litellm_deployment_latency | 延迟直方图 | P95 > 5s 告警 |
scrape_configs:
- job_name: litellm
metrics_path: /metrics
static_configs:
- targets: ["127.0.0.1:4000"]enabled: false 或注释),观察无新请求后再删 Key,不要直接删除model_list,跑一轮实测(第 3.5 节)再正式启用litellm_remaining_quota,低于 20% 推 Telegram / 邮件#!/usr/bin/env bash
# 用 curl 查 Prometheus,剩低 <20% 就推告警(占位符自行替换)
THRESHOLD=20
QUOTA=$(curl -s "http://127.0.0.1:9090/api/v1/query" \
--data-urlencode 'query=litellm_remaining_quota' | jq -r '.data.result[0].value[1]' 2>/dev/null)
[ -z "$QUOTA" ] && exit 0
if [ "$QUOTA" -lt "$THRESHOLD" ]; then
curl -s "https://api.telegram.org/botYOUR_BOT_TOKEN/sendMessage" \
-d "chat_id=YOUR_CHAT_ID" \
-d "text=⚠️ Grok Key 剩余配额得 $QUOTA%"
fistatus.json,页面 fetch('status.json?cb='+Date.now()) 每 60 秒拉一次;自托管页面(nginx 直出)能真正做到每分钟新鲜,GitHub Pages 因构建与 CDN 延迟通常滞后 1~2 分钟flock 防止任务重叠;状态数据原子写入(tmp + mv),避免页面读到半截 JSON这些都是从实战中捡回来的教训,直接照抄即可。
🔍 原因:请求入面的 model 名与 model_list 的 model_name 不一致,或者 litellm_params.model 写错了上游模型名。
✅ 解决:① 用 curl http://127.0.0.1:4000/v1/models 看实际暴露的模型名;② 确认 litellm_params.model 用官方命名(例如 grok/grok-3);③ 改完 config 要重启:sudo systemctl restart litellm。还不行就开 set_verbose: true 看完整报错。
🔍 原因:① keepalive 连接复用,同一连接上的请求都走同一条路;② 没有真正做「按请求选 Key」,只是 upstream 层面的多 server(这个与 Key 轮换无关);③ Lua 游标 RR 在每个 worker 各自计数,请求少时好像「只是用同一个」。
✅ 解决:确认轮询逻辑在 rewrite_by_lua / balancer_by_lua 按请求执行;用 lua_shared_dict 存游标做跨 worker 计数;测试时并发 10 以上先看得出轮换(curl 单发很容易连续命中同一 worker)。
🔍 原因:① 未启用 enable_pre_call_checks(发送前不查配额);② allowed_fails 阈值太高,未到熔断线;③ 429 之后没有将失败记录下去(Nginx/Lua 版常见——没有写 header_filter_by_lua)。
✅ 解决:LiteLLM 开 enable_pre_call_checks: true,配 allowed_fails: 3 + cooldown_time: 60;自建/Nginx 版确保 429/401/503 都有计数逻辑。改完看日志确认该 Key 进入冷却。
🔍 原因:① 容器 DNS 解析不到 api.x.ai(公司/机房 DNS 污染);② 容器出不到网(无外网路由);③ 全局代理环境变量 HTTP_PROXY 令容器走去不存在的代理。
✅ 解决:① 加 DNS:compose 入面 dns: ["1.1.1.1", "8.8.8.8"];② 测试 docker exec litellm curl -v https://api.x.ai 看卡在哪步;③ 检查 env | grep -i proxy,不要继承宿主代理;④ 公司网络封锁就经自己服务器代理出去。
🔍 原因:① 中间代理缓冲了 SSE 数据(Nginx 默认 proxy_buffering on);② 读超时太短,SSE 长连接被掐断;③ 网关把流式请求当普通请求处理,等成个 body 先返回。
✅ 解决:Nginx 加 proxy_buffering off + proxy_read_timeout 300s;LiteLLM 调大 request_timeout;自建网关确保 stream: true 走 StreamingResponse 直通,不要做重试(重试会打乱 SSE 顺序)。
🔍 原因:部分上游(例如 Grok console 通道)会自动注入原生 web_search 工具;如果客户端工具集自带同名 function 工具(Agent 类客户端几乎必然携带),上游会拒绝整个请求——400 invalid-argument。典型症状:普通 curl 测试正常,但接上 Agent 后所有请求 400 并静默降级到备用模型。
✅ 解决:① 在网关层过滤——把客户端发送的 function 版 web_search 转换为原生类型并去重(chenyme/grok2api 可修改 convertChatTools 后重新构建镜像,改完用独立 tag 部署以免被自动更新覆盖);② 或让客户端对该模型禁用 web_search 工具;③ 自建网关则在转发前对工具列表去重。改完记得重启网关进程(长驻进程会缓存旧配置,见下一题)。
🔍 原因:① 长驻网关 / 代理进程在启动时缓存配置,修改 config 后未重启所以不生效;② 部分客户端的命令行参数(如 -m / --model)不做别名解析,直接把参数名当作模型名发送;③ 配置的模型名不在 /v1/models 列表内,客户端校验直接拒绝。
✅ 解决:① 修改配置后重启网关进程(systemctl restart 或 docker compose up -d);② 别名用交互式命令切换(如 /model 别名),不要依赖 -m 参数;③ 先用 curl /v1/models 确认模型名真实存在,切换后核对日志中的 base_url 与模型名是否与预期一致。
🔍 原因:cron 环境 PATH 极简(通常只有 /usr/bin:/bin),浏览器自动化库(如 DrissionPage)的自动探测找不到可执行文件。手动执行正常、定时执行失败,九成是这个原因。
✅ 解决:① 在脚本内显式扫描常见浏览器路径并调用 set_browser_path 指定;② 创建符号链接 /usr/bin/chromium → /usr/bin/chromium-browser;③ 脚本内全部使用绝对路径,并用 flock 防止任务重叠。
🔍 原因:真实 chat 探测会消耗配额;每分钟一次意味着每天 1440 次调用,会把 Pool 的总配额吃掉大半。
✅ 解决:探测节流——真实探测每 30~60 分钟一次即可;日常监控改读 admin API / 网关日志(请求数、错误数、配额窗口),页面只展示最近一次探测结果。页面每分钟刷新的是「状态数据」,不是「探测请求」。
| 指令 | 作用 |
|---|---|
pip install -U "litellm[proxy]" | 安装 LiteLLM Proxy |
litellm --config config.yaml --port 4000 | 启动 LiteLLM(手动测试用) |
sudo systemctl enable --now litellm | 注册 systemd 服务并启动 |
journalctl -u litellm -f | 实时看 LiteLLM 日志 |
curl http://127.0.0.1:4000/health/liveliness | LiteLLM 健康检查 |
curl http://127.0.0.1:4000/v1/models | 列出对外模型名 |
curl -X POST http://127.0.0.1:4000/v1/chat/completions -H "Authorization: Bearer YOUR_MASTER_KEY" ... | 发测试请求 |
openresty -t && sudo systemctl reload openresty | Nginx 语法检查 + 重载 |
docker compose up -d --build | 起 Docker 版网关 |
docker exec litellm curl -v https://api.x.ai | 容器内测上游连通性 |
openssl rand -hex 16 | 生成随机密钥(Master Key 用) |
chmod 600 .env | 收紧 .env 权限 |
logrotate -f /etc/logrotate.d/litellm | 手动触发日志轮替 |
grep -rn "sk-" . --include="*.py" --include="*.yaml" | 提交前自查有无泄漏真 Key |
sudo certbot --nginx -d your-domain.com | 免费 HTTPS 证书一条龙 |
curl http://127.0.0.1:PORT/v1/models | 确认对外模型名(客户端校验依据) |
/model 别名(客户端内交互命令) | 切换模型别名(部分 CLI 的 -m 参数不做别名解析) |
systemctl restart 网关 / docker compose up -d | 修改配置后重启长驻网关进程使其生效 |
/opt/litellm/
├── config.yaml # 路由配置(多 Key / 熔断 / fallback)
├── .env # 密钥(chmod 600,不入 Git)
├── venv/ # Python 虚拟环境
└── (Docker 方案则用 compose 管理)
/etc/systemd/system/litellm.service # systemd 单元
/etc/logrotate.d/litellm # 日志轮替
/etc/nginx/conf.d/grok-entry.conf # 对外入口(HTTPS + 反代)| 项目 | 参考值 | 备注 |
|---|---|---|
| 模型 | grok-3 / grok-3-mini 等 | 官方命名随时变,以 /v1/models 与 docs 为准 |
| Context Window | grok-3 约 128K token(参考) | 以官方 docs 最新值为准 |
| Rate Limit | 视 tier 而定(RPM / TPM) | 在 console.x.ai 控制台看自己账号实际配额 |
| 定价 | 按 token 计费 | 以官方 pricing 页为准,Pool 层统一记录用量方便对账 |
| 配额刷新 | 通常按 UTC 日重置 | 监控脚本留意时区,不要在重置前一刻误报 |
grep -rn "YOUR_" 自查通过.env 已 chmod 600 且入了 .gitignore本附錄補充「如何有效自動創建 Grok 帳戶」及建議頻率。所有指令與腳本均以實測為準。完整 16 步驟流程 + 智能工具鏈已收錄於獨立頁面:📄 完整註冊與系統工作流(本頁只保留精華摘要,避免雙線維護)。
cron-batch.sh(包裝 grok-pool-register.sh + 匯入)。#!/bin/bash
# 由 Hermes cron 呼叫,預設每批 5 個
exec ~/grok-pool/cron-batch.sh "${@:-5}"
tr -d '\r\n' 會把多個 token 連成一行\r(保留 \n 分隔):sed -i 's/\r$//' accounts_*.txtbrowser_runtime.py 加入常見路徑掃描(同 cpa_xai/browser_session.py 做法)。python3 ~/grok-pool/reapply-picker-patch.py(冪等)。systemd-run --user --on-active=90s --unit=grok-restart bash ~/grok-pool/restart-gateway.sh(已修正 PATH 與 venv python)。當 console 配額接近 80% 時,建議暫停新增批次 48 小時,觀察現有帳戶的實際消耗。健康監察頁會在配額 gauge 顯示警戒線。
(crontab -l 2>/dev/null; echo ""; echo "# Grok 帳戶自動註冊批次"; \
echo "0 4 * * * /home/YOUR_USERNAME/.hermes/scripts/grok-pool-batch.sh 5"; \
echo "0 16 * * * /home/YOUR_USERNAME/.hermes/scripts/grok-pool-batch.sh 5") | crontab -
cd /home/YOUR_USERNAME/grok-pool && ./cron-batch.sh 5
ls -t ~/grok-pool/logs/batch_*.log | head -1 | xargs tail -30
python3 /home/YOUR_USERNAME/grok-pool/reapply-picker-patch.py
systemd-run --user --on-active=90s --unit=grok-restart \
bash /home/YOUR_USERNAME/grok-pool/restart-gateway.sh
完整端到端流程(Cron → 註冊 → 匯入 → 監控 → 智能調度)請見獨立頁面:
grok-pool-register.sh 只呼叫 /accounts/console/import。grok-4.3 受 Team Fingerprint RPM 硬鎖(~60/min,log 關鍵字 upstream_team_model_rate_limited)——加帳戶無法突破,需靠隊列整形 + 降級鏈。tools 透傳)——Hermes 可直接驅動池做真 agent。smart_gateway.py(熔斷 + 速率整形 + 降級鏈)、poc_agent.py(端到端 ReAct)、gateway_monitor.py(429 監控)、verify_phase1.py(驗收測試)。把 Grok API Key 接入 Telegram bot(或通过任何 Telegram 渠道传递 Key)之前,先读完这一节。核心原则只有一条:Key 一旦出现在任何 Telegram 聊天记录里,就按已泄露处理。以下为 2026-08-06 实测整理。
/help、/keys 等指令回显 Key 或配置内容。.env(chmod 600)或环境变量,代码里用 os.environ 读取;仓库里只放带 YOUR_XAI_API_KEY 占位符的模板。.env 与 bot_token 一并加入 .gitignore,提交前 git status 确认没有密钥文件。base_url 指向你的 Pool 网关(LiteLLM Proxy 或自建 gateway),不要直连 api.x.ai;网关负责多 Key 轮询与熔断,bot 只持有网关地址。Authorization / Key 明文chat_id,避免公开 bot 被他人调用消耗你的配额.env 已 chmod 600 且入 .gitignoreconsole.x.ai 吊销并创建新 Key,旧 Key 同步从池中移除(LiteLLM key 列表 / gateway 配置)。@BotFather 的 /revoke 重置。本附錄记录本指南配套的监控与告警方案:全部数据来自被动来源(网关自带审计账本 + 管理 API + 本地 SQLite),不消耗任何上游配额。仪表板:system-monitor。2026-08-06 实测。
/healthz(免费),聚合审计统计,写入 status.json(nginx 静态目录公开,仪表板每 60 秒 fetch)。max_tokens=10)验证端到端推理 —— 这是唯一消耗配额的探针,一天约 48 次,远低于单日配额。pool_history.db),仪表板展示最近 48 小时曲线。grok2api 自带审计账本 request_audits 表(SQLite,docker volume 内),每一条请求都记录 status_code / error_code / duration_ms / first_token_ms / total_tokens / client_key_name / account_name。collector 直接读表聚合,零上游成本:
upstream_rate_limited_resource_exhausted 等)。duration_ms、p95 首 token 延迟。provider_accounts 的 auth_status / failure_count / cooldown_until / last_error。通过管理 API 改运行参数(无需重启,restartRequired: [])。先 GET 拿全量配置与 revision,改完再 PUT:
# 1) 登录拿 token
curl -X POST http://127.0.0.1:8001/api/admin/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"YOUR_ADMIN_PASSWORD"}'
# 2) GET 全量配置(拿到 revision)
# 3) 修改以下字段后再 PUT {"config": {...}, "revision": "0"}
routing:
maxAttempts: 10 # 默认 999 —— 降级时 retry 风暴会打爆上游(429 雪崩)
capacityWait: 1.5s # 默认 500ms —— 账号全忙时排队代替硬撞
accounts:
autoCleanReauthEnabled: true # 自动清理需要重新登录的死账号
clientKeyDefaults.rpmLimit=120 / maxConcurrent=8,可逐 key 收紧。server.maxConcurrentRequests=1024,过高可调低,让网关在负载时快速返回 429 而不是无限排队。base_url: http://127.0.0.1:8001/v1),把 --listen 0.0.0.0:8001 改为 --listen 127.0.0.1:8001 后 docker compose up -d —— 公网根本没有入口可以 spike,比任何边缘限流都干净。| 条件 | 阈值 | 说明 |
|---|---|---|
| 网关挂 | api_ok=false 或 status.json 超过 180 秒 | 立即报警 |
| 1h 错误率爆升 | 错误率 > 50% 且请求 ≥ 8 | 上游故障 / 风控 |
| 429 风暴 | 1h 内 429 ≥ 15 | RPM 被限,检查账号与 maxAttempts |
| 配额用尽 | used_pct ≥ 80% | Console 配额预警 |
| reauth 突增 | reauth 账号 ≥ 8 | 批量掉登录,需重新导入 |
| probe 失败 | 端到端探针返回失败 | 推理链路异常 |
防噪音设计:同一条件需连续 2 次检查成立才报警;同类报警 30 分钟内不重复;条件消失即重置计数。cron:*/5 * * * * flock -n /tmp/grok-pool-watch.lock /usr/bin/python3 /home/YOUR_USERNAME/grok-pool/pool-watch.py。
Grok 免費帳戶除聊天外,還附帶 Imagine 圖像(5/日)與影片(2/日)配額,經 Console 帳戶池(79 帳戶)可達每日 ~395 張圖 / ~158 段片,全部 $0。2026-08-08 實測。關鍵在於路由必須走 Console/ 前綴——默認的 grok-imagine-image 會路由到 Web 帳戶(datacenter IP 被 anti-bot 擋 → 502 upstream_unavailable)。
| 能力 | Model(必須 Console/ 前綴) | 每帳戶/日 | 79 帳戶池 |
|---|---|---|---|
| Chat | Console/grok-4.3 | 10 請求 | 790 請求 |
| Image | Console/grok-imagine-image | 5 張 | 395 張 |
| Image→Video | Console/grok-imagine-video | 2 段 | 158 段 |
curl -X POST https://your-domain/v1/images/generations \
-H "Authorization: Bearer YOUR_GROK_API_KEY" -H "Content-Type: application/json" -d '{
"model": "Console/grok-imagine-image", ← 必须 Console/ 前缀
"prompt": "Tokyo alley, film look, 8k",
"size": "1024x1536", ← 2:3 直向; 1:1=1024x1024
"n": 1
}'curl -X POST https://your-domain/v1/videos/generations \
-H "Authorization: Bearer YOUR_GROK_API_KEY" -H "Content-Type: application/json" -d '{
"model": "Console/grok-imagine-video",
"prompt": "woman walks then jumps, slow motion",
"image": {"url": "data:image/jpeg;base64,"}, ← 必须 HTTPS 或 data URL
"aspect_ratio": "2:3" ← 关键! 否则固定 16:9 会压扁直向图
}'
# 返回 {"request_id":"video_xxx"} → 轮询状态
curl "https://your-domain/v1/videos/video_xxx" -H "Authorization: Bearer YOUR_GROK_API_KEY"
# {"status":"done","video":{"url":".../v1/videos/video_xxx/content"}} → 下载
curl "https://your-domain/v1/videos/video_xxx/content" -H "Authorization: Bearer YOUR_GROK_API_KEY" -o out.mp4 Console/ 前綴:無前綴 → Web route → DC IP 502 upstream_unavailable。http://127.0.0.1 會被拒(视频首图必须是 HTTPS URL 或 image data URL)。用 base64 -w0 img.png 轉 data URL。aspect_ratio 必填:默認輸出 16:9(1280×720),直向圖(2:3)會被強行壓扁;指定 "2:3" → 720×1088 正常。/content 端點無 header 返回 401 invalid_api_key。:8000,本機實際端口不同時替換即可。quotaWindows 每個帳戶有 console / console_image / console_video 三個窗口;統計「可用聊天配額」時只加 mode=='console',否則 image/video 滿配額會混入,造成「有額度卻 429」的假象。用法:
python3 grok_gen.py image "prompt" [-ar 2:3|16:9|1:1|9:16] [-o out.png]
python3 grok_gen.py video "動作描述" [-i input.png] [-ar 2:3] [-o out.mp4]
核心邏輯(關鍵三行):
body = {"model": "Console/grok-imagine-image", "prompt": prompt, "size": sizes.get(ar)}
body = {"model": "Console/grok-imagine-video", "prompt": prompt,
"image": {"url": "data:...;base64,..."}, "aspect_ratio": ar}
# video 下载: urllib Request 带 Authorization header → /videos/{id}/content/v1/images/generations、/v1/videos/generations),可直接接入現有 OpenAI SDK;配額每日 UTC 窗口重置,79 帳戶 staggered 輪換。腳本已整合入 ~/grok-pool/tools/grok_gen.py。