# LLM API 网关技术参考 > 基于 one-api / new-api 的完整技术流程分析,作为 raisfast 实现 LLM Gateway 的参考文档。 ## 1. 产品定位 LLM API 网关(也称"API 中转"或"API 聚合")的核心价值: - **统一入口**:客户端只需对接一个 API 地址,网关路由到不同上游 - **Key 管理**:统一颁发 API Key,控制额度、过期、模型权限 - **负载均衡**:多渠道分发,失败自动切换 - **格式转换**:OpenAI / Claude / Gemini 等格式互转 - **计费统计**:按 token 用量计费,支持缓存 token 折扣 ## 2. 端到端请求流程 ``` ┌──────────┐ ┌──────────────────────────────────────────────────────┐ ┌──────────┐ │ Client │────▶│ raisfast Gateway │────▶│ Upstream │ │ │◀────│ │◀────│ (OpenAI/ │ │ │ │ RateLimit → Auth → Distribute → Relay → Response │ │ Claude/ │ └──────────┘ └──────────────────────────────────────────────────────┘ │ Gemini) │ └──────────┘ ``` ### 2.1 请求阶段 ``` POST /v1/chat/completions Authorization: Bearer sk-xxxxx { "model": "gpt-4", "messages": [{"role":"user","content":"hello"}], "stream": true } ``` ### 2.2 中间件链 ``` 请求进入 │ ├─ Step 1: Rate Limit(IP 限流) │ · 滑动窗口,按 IP 计数 │ · 默认 180 req / 3 min │ · Redis 或内存存储 │ ├─ Step 2: Token Auth(认证 + 预检) │ · 提取 Bearer token → sk-xxx │ · 查缓存 → DB 回退 │ · 检查:enabled? expired? quota > 0? │ · IP 白名单验证 │ · 模型白名单验证(token 级别) │ · 预扣额度(估算值,防止超额) │ ├─ Step 3: Distribute(渠道选择) │ · 根据 user_group + model 查 abilities 表 │ · 优先级 + 随机算法 │ · 选中的渠道信息注入 context │ ├─ Step 4: Relay(请求转发) │ · 格式转换(按上游类型) │ · 构建 HTTP 请求 │ · 设置超时 │ · 发送到上游 │ ├─ Step 5: Response(响应处理) │ · 非流式:解析 JSON → 提取 usage → 返回 │ · 流式 SSE:逐行转发 → 累积 token → 结算 │ ├─ Step 6: Billing(计费结算) │ · 计算实际用量 │ · 结算预扣额度 │ · 记录日志 │ └─ 完成 失败重试(Step 4-5 之间): · 429 / 5xx → 自动重试 · 400 / 客户端错误 → 不重试 · 每次重试换一个不同渠道 · 超过重试次数 → 返回错误 + 退还额度 ``` ## 3. 数据库 Schema ### 3.1 核心表 #### channels — 上游渠道 ```sql CREATE TABLE channels ( id BIGINT PRIMARY KEY, document_id VARCHAR(36) NOT NULL UNIQUE, tenant_id VARCHAR(36), -- 多租户 -- 渠道基本信息 name VARCHAR(255) NOT NULL, provider_type SMALLINT NOT NULL DEFAULT 1, -- 1=OpenAI, 3=Azure, 14=Anthropic, 15=Gemini ... status SMALLINT NOT NULL DEFAULT 1, -- 1=enabled, 2=manual_disabled, 3=auto_disabled base_url VARCHAR(1024), -- 上游 API 地址 api_key TEXT NOT NULL, -- 上游 API Key(可存多个,逗号分隔) -- 路由配置 models TEXT NOT NULL, -- 支持的模型列表(逗号分隔) model_mapping TEXT, -- {"客户端模型":"上游模型"} JSON priority BIGINT NOT NULL DEFAULT 0, -- 路由优先级,越高越优先 weight INT NOT NULL DEFAULT 0, -- 权重(预留) channel_group VARCHAR(255) DEFAULT 'default', -- 渠道分组 -- 高级配置 config TEXT, -- 渠道特定配置 JSON system_prompt TEXT, -- 强制注入的 system prompt header_override TEXT, -- 请求头覆盖 JSON param_override TEXT, -- 请求参数覆盖 JSON status_code_mapping TEXT, -- 错误码映射 JSON -- 统计 used_quota BIGINT NOT NULL DEFAULT 0, response_time INT, -- 平均响应时间 ms test_time TIMESTAMPTZ, -- 审计 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); ``` #### tokens — API Key 管理 ```sql CREATE TABLE tokens ( id BIGINT PRIMARY KEY, document_id VARCHAR(36) NOT NULL UNIQUE, tenant_id VARCHAR(36), user_id BIGINT NOT NULL REFERENCES users(id), -- Key 信息 name VARCHAR(255) NOT NULL, key VARCHAR(64) NOT NULL UNIQUE, -- sk-xxxx status SMALLINT NOT NULL DEFAULT 1, -- 1=enabled, 2=disabled, 3=expired, 4=exhausted -- 额度控制 remain_quota BIGINT NOT NULL DEFAULT 0, used_quota BIGINT NOT NULL DEFAULT 0, unlimited_quota BOOLEAN NOT NULL DEFAULT FALSE, -- 访问控制 expired_at TIMESTAMPTZ, -- 过期时间 allowed_models TEXT, -- 允许的模型列表(空=全部) allowed_ips TEXT, -- IP 白名单 CIDR token_group VARCHAR(255), -- Token 级分组覆盖 -- 审计 accessed_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); ``` #### abilities — 路由交叉表 ```sql -- 预计算的 (group × model × channel) 关系 -- 避免 runtime 时复杂查询 CREATE TABLE abilities ( channel_group VARCHAR(255) NOT NULL, -- 用户分组 model VARCHAR(255) NOT NULL, -- 模型名 channel_id BIGINT NOT NULL REFERENCES channels(id), enabled BOOLEAN NOT NULL DEFAULT TRUE, priority BIGINT NOT NULL DEFAULT 0, PRIMARY KEY (channel_group, model, channel_id) ); CREATE INDEX idx_abilities_group_model ON abilities(channel_group, model, enabled); ``` #### gateway_logs — 请求日志 ```sql CREATE TABLE gateway_logs ( id BIGINT PRIMARY KEY, tenant_id VARCHAR(36), -- 请求信息 request_id VARCHAR(64), -- X-Request-ID user_id BIGINT NOT NULL, token_id BIGINT, channel_id BIGINT, model_name VARCHAR(255), is_stream BOOLEAN NOT NULL DEFAULT FALSE, -- 用量 prompt_tokens INT NOT NULL DEFAULT 0, completion_tokens INT NOT NULL DEFAULT 0, cache_tokens INT NOT NULL DEFAULT 0, -- 缓存命中 token -- 计费 quota BIGINT NOT NULL DEFAULT 0, -- 本次消耗额度 group_ratio REAL, -- 分组倍率 model_ratio REAL, -- 模型倍率 -- 性能 elapsed_time INT, -- 耗时 ms status_code INT, -- 上游返回的 HTTP 状态码 -- 审计 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_gateway_logs_user ON gateway_logs(user_id, created_at); CREATE INDEX idx_gateway_logs_token ON gateway_logs(token_id, created_at); CREATE INDEX idx_gateway_logs_channel ON gateway_logs(channel_id, created_at); CREATE INDEX idx_gateway_logs_model ON gateway_logs(model_name, created_at); ``` #### pricing — 模型定价 ```sql CREATE TABLE pricing ( id BIGINT PRIMARY KEY, document_id VARCHAR(36) NOT NULL UNIQUE, tenant_id VARCHAR(36), model_name VARCHAR(255) NOT NULL, -- 模型名 -- 计费方式 billing_mode VARCHAR(20) NOT NULL DEFAULT 'ratio', -- ratio=倍率, price=固定价 model_ratio REAL NOT NULL DEFAULT 1.0, -- 模型倍率 completion_ratio REAL NOT NULL DEFAULT 1.0, -- 补全倍率 cache_ratio REAL, -- 缓存 token 倍率(默认 = model_ratio) model_price REAL, -- 每次请求固定价格 billing_expr TEXT, -- 表达式计费(高级) created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); ``` #### user_groups — 用户分组倍率 ```sql CREATE TABLE user_groups ( id BIGINT PRIMARY KEY, name VARCHAR(255) NOT NULL UNIQUE, -- default, vip, enterprise ratio REAL NOT NULL DEFAULT 1.0, -- 分组倍率 created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); ``` ## 4. 渠道选择算法 ### 4.1 基础算法(one-api) ``` 输入: user_group, model 输出: channel 1. 查 abilities 表: WHERE channel_group = user_group AND model = model AND enabled = true 2. 取最高 priority 的记录集合 3. 在同 priority 集合内随机选一个 4. 返回对应 channel ``` ### 4.2 增强算法(new-api) ``` 输入: user_group, model, session_id 输出: channel 1. 会话亲和检查: · 如果 session_id 存在,查缓存看上次用的哪个 channel · 同一会话优先路由到同一 channel(保持上下文一致) 2. auto group 模式: · 如果 user_group = "auto" · 按优先级尝试多个 group · 第一个有可用 channel 的 group 生效 3. 多 Key 轮询: · 如果 channel 配置了多个 api_key · 按 round-robin 轮询使用 4. 降级: · 首次尝试: 最高 priority · 重试时: 忽略 priority,任何可用 channel ``` ### 4.3 raisfast 建议的改进 ``` 1. 加权随机(weighted random)— 代替简单随机 · channel.weight 字段实际生效 · 响应时间越短 → 动态权重越高 2. 最少连接(least connections) · 跟踪每个 channel 的活跃连接数 · 选连接数最少的 3. 健康检查 · 后台定时 ping 上游 · 异常自动降权或禁用 · 恢复后自动提权 4. 地理路由(预留) · 根据 channel 的 region 和客户端位置 · 优先路由到最近的 region ``` ## 5. 格式转换 ### 5.1 Adaptor 接口设计 ```rust /// LLM 提供商适配器接口 #[async_trait] pub trait ProviderAdaptor: Send + Sync { /// 提供商名称 fn provider_name(&self) -> &str; /// 将统一请求转换为上游格式 fn convert_request( &self, unified: &UnifiedChatRequest, channel: &Channel, ) -> AppResult; /// 构建上游 HTTP 请求 fn build_http_request( &self, base_url: &str, api_key: &str, body: ProviderRequest, ) -> AppResult; /// 处理非流式响应 async fn handle_response( &self, resp: reqwest::Response, ) -> AppResult; /// 处理流式响应(返回 SSE 流) fn handle_stream_response( &self, resp: reqwest::Response, ) -> AppResult> + Send>>>; /// 从响应中提取 token 用量 fn extract_usage(&self, response: &ProviderResponse) -> TokenUsage; /// 该提供商支持的模型列表 fn supported_models(&self) -> &[&str]; } ``` ### 5.2 统一请求结构 ```rust /// 统一的 Chat 请求(OpenAI 兼容格式作为内部标准) pub struct UnifiedChatRequest { pub model: String, pub messages: Vec, pub temperature: Option, pub top_p: Option, pub max_tokens: Option, pub stream: bool, pub stop: Option>, pub tools: Option>, pub response_format: Option, } pub struct ChatMessage { pub role: MessageRole, // system, user, assistant, tool pub content: MessageContent, // text or array of content parts pub tool_calls: Option>, pub tool_call_id: Option, } pub struct TokenUsage { pub prompt_tokens: i64, pub completion_tokens: i64, pub cache_tokens: i64, // 缓存命中的 token 数 pub total_tokens: i64, } ``` ### 5.3 OpenAI → Claude 转换要点 ``` OpenAI 格式 Claude 格式 ───────────── ────────── messages: [ system: "system prompt", ← 提取到顶层 {role:"system", content:"..."}, messages: [ {role:"user", content:"hi"} {role:"user", content:"hi"} ] ] max_tokens: 可选 max_tokens: 必填(默认 4096) stop: ["\n"] stop_sequences: ["\n"] ← 字段名不同 tools: [{type:"function",...}] tools: [{name:"",...}] ← 格式不同 tool_choice: "auto" tool_choice: {type:"auto"} stream: true stream: true temperature: 0.7 temperature: 0.7 ``` ### 5.4 OpenAI → Gemini 转换要点 ``` OpenAI 格式 Gemini 格式 ───────────── ────────── POST /v1/chat/completions POST /v1/models/{model}:generateContent { { "model": "gpt-4", "contents": [ "messages": [ {"role":"user","parts":[{"text":"hi"}]} {role:"user", content:"hi"} ], ], "systemInstruction": {"parts":[{"text":"..."}]}, "temperature": 0.7 "generationConfig": { } "temperature": 0.7 } } ``` ### 5.5 透传优化(零拷贝) ``` 当上游是 OpenAI 兼容 且 没有模型映射 且 没有 system prompt 注入时: → 直接转发原始请求体,不解析不序列化 → 节省 CPU 和延迟 ``` ## 6. 流式转发(SSE) ### 6.1 架构 ``` Upstream ──SSE──▶ raisfast ──SSE──▶ Client ┌──────────────┐ │ SSE Proxy │ │ │ │ line_scanner ◀─── 逐行读取上游 │ │ │ │ ▼ │ │ parse_chunk │─── 提取 usage │ │ │ │ ▼ │ │ forward │─── 逐行转发客户端 │ │ │ │ ▼ │ │ accumulate │─── 累积 token 计数 └──────────────┘ ``` ### 6.2 SSE 协议 ``` 上游返回(chunked transfer encoding): data: {"id":"chatcmpl-xxx","choices":[{"delta":{"content":"Hello"},"index":0}]} data: {"id":"chatcmpl-xxx","choices":[{"delta":{"content":" world"},"index":0}]} data: {"id":"chatcmpl-xxx","choices":[{"delta":{},"index":0}],"usage":{"prompt_tokens":10,"completion_tokens":5}} data: [DONE] ``` ### 6.3 Token 计数策略 ``` 优先级: 1. 上游返回 usage 字段(最准确) 2. 强制 stream_options: {"include_usage": true} 让上游返回 usage 3. tiktoken 本地估算(退路) 流式场景的计费流程: 1. 请求开始:预扣额度 = max_tokens × model_ratio × group_ratio 2. 流式传输中:累积 completion_tokens 3. 流结束:结算 = (prompt_tokens + completion_tokens × completion_ratio) × group_ratio 4. 差额退还 ``` ### 6.4 Rust 实现(建议) ```rust use axum::response::sse::{Event, Sse}; use futures::stream::Stream; use reqwest::Response; pub async fn proxy_sse( upstream_resp: Response, ) -> Sse> + Send>>> { let stream = async_stream::stream! { let mut lines = upstream_resp.lines(); while let Some(line) = lines.next_line().await.unwrap_or(None) { if line.starts_with("data: ") { let data = &line[6..]; if data == "[DONE]" { yield Ok(Event::default().data("[DONE]")); break; } // 提取 usage(如果有) if let Ok(chunk) = serde_json::from_str::(data) { if let Some(usage) = chunk.usage { // 记录 token 用量 record_usage(usage).await; } } yield Ok(Event::default().data(data.to_string())); } } }; Sse::new(Box::pin(stream)) } ``` ## 7. 计费系统 ### 7.1 额度计算公式 ``` quota = group_ratio × model_ratio × ( prompt_tokens + completion_tokens × completion_ratio - cache_tokens × (1 - cache_ratio) ) 其中: - group_ratio: 用户分组倍率(VIP 打折) - model_ratio: 模型倍率(GPT-4 比 GPT-3.5 贵) - completion_ratio: 补全倍率(GPT-4 补全 = 2x prompt) - cache_ratio: 缓存折扣(缓存 token 更便宜) ``` ### 7.2 预扣 + 结算 ``` 时间线: ─────────────────────────────────────────────────────▶ │ │ │ │ 预扣额度(estimate) │ 实际用量(usage) │ 结算(refund) │ remain_quota -= 预估值 │ │ remain_quota += (预估 - 实际) │ │ │ │ 如果余额不足预扣 → 拒绝 │ │ 如果中途失败 → 全额退还 ``` ### 7.3 各模型倍率参考 ``` 模型 model_ratio completion_ratio ───────────────────── ────────── ─────────────── gpt-3.5-turbo 0.75 1.0 gpt-4 15.0 2.0 gpt-4-turbo 5.0 2.0 gpt-4o 2.5 2.0 claude-3-haiku 0.5 1.25 claude-3-sonnet 1.5 1.25 claude-3-opus 7.5 1.25 claude-3.5-sonnet 1.5 1.25 gemini-pro 1.0 1.0 gemini-1.5-pro 1.75 1.0 gemini-1.5-flash 0.075 1.0 deepseek-chat 0.14 1.0 deepseek-reasoner 0.55 1.0 ``` ## 8. 失败重试与容错 ### 8.1 重试策略 ``` RetryTimes = 可配置(默认 0) 重试条件: 429 (Too Many Requests) → 重试(上游限流) 500 (Internal Server Error) → 重试 502 (Bad Gateway) → 重试 503 (Service Unavailable) → 重试 504 (Gateway Timeout) → 重试 400 (Bad Request) → 不重试(客户端问题) 401 (Unauthorized) → 不重试(Key 无效) 403 (Forbidden) → 不重试 404 (Not Found) → 不重试 每次重试: 1. 选一个不同的 channel(排除上次失败的) 2. 重新预扣额度 3. 重新发送请求 ``` ### 8.2 渠道健康监控 ``` 每个 channel 维护一个成功率队列(最近 N 次请求): success_rate = successes / total_requests 如果 success_rate < threshold(默认 0.8): → 自动禁用该 channel(status = auto_disabled) 后台定时检查: → 定期 ping 被禁用的 channel → 恢复后自动启用(status = enabled) ``` ### 8.3 raisfast 改进建议 ``` 1. 熔断器(Circuit Breaker): · Closed → Open → Half-Open → Closed · 比简单成功率监控更优雅 2. 指数退避重试: · retry 1: 100ms 后 · retry 2: 200ms 后 · retry 3: 400ms 后 3. 优先级感知: · 记录每个 channel 的平均延迟 · 同优先级内优先选延迟低的 4. 渠道预热: · 新添加的 channel 先做健康检查 · 确认可用后才加入路由池 ``` ## 9. Provider 类型常量 ``` const OPENAI: 1 const AZURE: 3 const CUSTOM: 4 // 自定义 URL const ANTHROPIC: 14 const GEMINI: 15 const BAIDU: 16 const ZHIPU: 17 const ALI: 18 const XUNFEI: 19 const AWS: 20 const COHERE: 21 const DEEPSEEK: 22 const MOONSHOT: 23 const BAICHUAN: 24 const MINIMAX: 25 const GROQ: 26 const OLLAMA: 27 const TONGYI_QWEN: 28 // 通义千问 const YI: 29 // 零一万物 const STEP: 30 // 阶跃星辰 const DOUBAO: 31 // 字节豆包 const COZE: 32 const CLOUDFLARE: 33 const DEEPL: 34 const TOGETHER_AI: 35 const DIFY: 36 const XAI: 37 // Grok const SILICONFLOW: 38 // 硅基流动 const VERTEX_AI: 39 // Google Vertex AI ``` ## 10. raisfast 实现建议 ### 10.1 模块划分 ``` src/ ├── gateway/ │ ├── mod.rs -- 模块入口 │ ├── router.rs -- /v1/* 路由注册 │ ├── middleware/ │ │ ├── rate_limit.rs -- IP 限流 │ │ ├── auth.rs -- Token 认证 │ │ └── distribute.rs -- 渠道选择 │ ├── relay/ │ │ ├── mod.rs -- 转发入口 + 重试逻辑 │ │ ├── sse.rs -- SSE 流式代理 │ │ ├── billing.rs -- 预扣/结算 │ │ └── usage.rs -- Token 计数 │ ├── adaptor/ │ │ ├── mod.rs -- trait ProviderAdaptor │ │ ├── openai.rs -- OpenAI 透传 │ │ ├── anthropic.rs -- Claude 格式转换 │ │ ├── gemini.rs -- Gemini 格式转换 │ │ ├── azure.rs -- Azure OpenAI │ │ └── custom.rs -- 自定义 OpenAI 兼容 │ ├── channel.rs -- 渠道管理 (CRUD) │ ├── token.rs -- API Key 管理 (CRUD) │ ├── ability.rs -- 路由交叉表维护 │ ├── pricing.rs -- 模型定价 │ └── health.rs -- 渠道健康检查 ├── models/ │ ├── channel.rs -- Channel model │ ├── gateway_token.rs -- GatewayToken model │ ├── gateway_log.rs -- GatewayLog model │ └── pricing.rs -- Pricing model ``` ### 10.2 Feature Flag ```toml [features] gateway = ["reqwest/stream"] ``` ### 10.3 依赖 ```toml [dependencies] reqwest = { version = "0.12", features = ["stream", "json"] } async-stream = "0.3" tokio-stream = "0.1" ``` ### 10.4 性能目标(vs Go) ``` Go (one-api) Rust (raisfast) 并发连接数 ~1,000 ~10,000+ 内存占用 100-200MB 10-20MB SSE 延迟抖动 GC 导致 ~50ms <1ms 冷启动 ~1s <50ms 二进制大小 ~50MB ~10MB 单核 QPS ~3,000 ~15,000+ ``` ### 10.5 与现有模块集成 ``` gateway 模块复用: · users 表 → 用户管理 · rbac → 权限控制 · audit_log → 审计日志 · api_tokens → Key 管理(扩展 gateway_token) · plugin_system → 格式转换可作为插件实现 · worker/cron → 渠道健康检查定时任务 · webhook → 用量告警 · media → 图片生成接口 · tenant → 多租户隔离 ``` ## 11. API 路由设计 ### 11.1 中转接口(兼容 OpenAI) ``` # Chat POST /v1/chat/completions POST /v1/completions # Embeddings POST /v1/embeddings # Images POST /v1/images/generations POST /v1/images/edits POST /v1/images/variations # Audio POST /v1/audio/transcriptions POST /v1/audio/translations POST /v1/audio/speech # Models GET /v1/models # Rerank (new-api) POST /v1/rerank ``` ### 11.2 管理接口 ``` # 渠道管理 GET /api/v1/gateway/channels POST /api/v1/gateway/channels PUT /api/v1/gateway/channels/:id DELETE /api/v1/gateway/channels/:id POST /api/v1/gateway/channels/:id/test # Key 管理 GET /api/v1/gateway/tokens POST /api/v1/gateway/tokens PUT /api/v1/gateway/tokens/:id DELETE /api/v1/gateway/tokens/:id # 定价管理 GET /api/v1/gateway/pricing POST /api/v1/gateway/pricing PUT /api/v1/gateway/pricing/:id # 统计 GET /api/v1/gateway/logs GET /api/v1/gateway/stats/overview GET /api/v1/gateway/stats/models GET /api/v1/gateway/stats/channels # 用户分组 GET /api/v1/gateway/groups PUT /api/v1/gateway/groups/:id ``` ## 12. 与 one-api/new-api 的差异化 ``` one-api/new-api 的不足 raisfast 的改进 ───────────────────── ──────────────── Go GC 导致 SSE 抖动 无 GC,延迟稳定 单机架构,扩展靠 Redis 多租户原生支持 格式转换硬编码 插件化 adaptor 定价硬编码或单表 灵活表达式定价 无桌面端 Tauri 桌面管理 无 Content Type 可组合内容建模 无工作流 工作流引擎 无搜索 Tantivy 全文搜索 ```