mirror of
https://github.com/RaisFast/raisfast.git
synced 2026-09-23 16:02:25 +00:00
825 lines
26 KiB
Markdown
825 lines
26 KiB
Markdown
# 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<ProviderRequest>;
|
||
|
||
/// 构建上游 HTTP 请求
|
||
fn build_http_request(
|
||
&self,
|
||
base_url: &str,
|
||
api_key: &str,
|
||
body: ProviderRequest,
|
||
) -> AppResult<reqwest::Request>;
|
||
|
||
/// 处理非流式响应
|
||
async fn handle_response(
|
||
&self,
|
||
resp: reqwest::Response,
|
||
) -> AppResult<UnifiedChatResponse>;
|
||
|
||
/// 处理流式响应(返回 SSE 流)
|
||
fn handle_stream_response(
|
||
&self,
|
||
resp: reqwest::Response,
|
||
) -> AppResult<Pin<Box<dyn Stream<Item = Result<SSEChunk>> + 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<ChatMessage>,
|
||
pub temperature: Option<f64>,
|
||
pub top_p: Option<f64>,
|
||
pub max_tokens: Option<i64>,
|
||
pub stream: bool,
|
||
pub stop: Option<Vec<String>>,
|
||
pub tools: Option<Vec<Tool>>,
|
||
pub response_format: Option<ResponseFormat>,
|
||
}
|
||
|
||
pub struct ChatMessage {
|
||
pub role: MessageRole, // system, user, assistant, tool
|
||
pub content: MessageContent, // text or array of content parts
|
||
pub tool_calls: Option<Vec<ToolCall>>,
|
||
pub tool_call_id: Option<String>,
|
||
}
|
||
|
||
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<Pin<Box<dyn Stream<Item = Result<Event, axum::Error>> + 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::<SSEChunk>(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 全文搜索
|
||
```
|