7.2 KiB
7.2 KiB
zzrouter - 模型 API 代理服务设计文档
概述
zzrouter 是一个 AI 模型 API 代理服务,支持多个模型提供商的统一访问,并提供 API Key 轮换、请求日志等功能。
核心需求
| 项目 | 决策 |
|---|---|
| 模型提供商 | OpenAI 兼容格式 + Anthropic |
| API Key 轮换策略 | 轮询 (Round-robin) |
| 客户端 Key 存储 | SQLite 数据库 |
| 提供商 Key 存储 | SQLite 数据库 |
| 对外 API 格式 | OpenAI 兼容 + 原生格式双支持 |
| 流式响应 | 支持 (SSE) |
| 管理方式 | 直接操作数据库 |
| 日志统计 | 完整版 (token、延迟、错误) |
架构
┌─────────────┐
│ Client │
└──────┬──────┘
│ API Key 认证
▼
┌─────────────────────────────────┐
│ FastAPI 服务 │
│ ┌─────────────────────────┐ │
│ │ 认证中间件 │ │
│ └───────────┬─────────────┘ │
│ ▼ │
│ ┌─────────────────────────┐ │
│ │ 路由层 │ │
│ │ /v1/chat/* (OpenAI) │ │
│ │ /v1/anthropic/* │ │
│ └───────────┬─────────────┘ │
│ ▼ │
│ ┌─────────────────────────┐ │
│ │ LiteLLM + Key 轮询 │ │
│ └───────────┬─────────────┘ │
│ ▼ │
│ ┌─────────────────────────┐ │
│ │ 日志记录 (异步批量) │ │
│ └─────────────────────────┘ │
└─────────────────────────────────┘
│
▼
┌─────────────┐
│ SQLite │
└─────────────┘
数据库结构
-- 客户端 API Key
CREATE TABLE client_keys (
id INTEGER PRIMARY KEY,
key TEXT UNIQUE NOT NULL,
name TEXT,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT NOW()
);
-- 模型提供商
CREATE TABLE providers (
id INTEGER PRIMARY KEY,
name TEXT UNIQUE NOT NULL,
base_url TEXT NOT NULL,
api_type TEXT NOT NULL, -- 'openai' 或 'anthropic'
is_active BOOLEAN DEFAULT TRUE
);
-- 提供商 API Key (一对多)
CREATE TABLE provider_keys (
id INTEGER PRIMARY KEY,
provider_id INTEGER REFERENCES providers(id),
key TEXT NOT NULL,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT NOW()
);
-- 请求日志
CREATE TABLE request_logs (
id INTEGER PRIMARY KEY,
client_key_id INTEGER REFERENCES client_keys(id),
provider_id INTEGER REFERENCES providers(id),
model TEXT,
prompt_tokens INTEGER,
completion_tokens INTEGER,
latency_ms INTEGER,
success BOOLEAN,
error_message TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
API 路由
认证方式: Bearer Token (Authorization: Bearer <client_key>)
路由结构:
├── /v1/chat/completions # OpenAI 兼容格式 (自动路由)
├── /v1/models # 列出可用模型
│
├── /v1/openai/chat/completations # OpenAI 原生格式 (透传)
├── /v1/openai/models
│
├── /v1/anthropic/messages # Anthropic 原生格式 (透传)
├── /v1/anthropic/models
│
└── /health # 健康检查
模型映射逻辑
用于 /v1/chat/completions 根据模型名自动路由:
gpt-*,o1-*→ OpenAIclaude-*→ Anthropic- 其他可配置映射
项目结构
backend/
├── main.py # 入口
├── pyproject.toml
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI 应用
│ ├── config.py # 配置
│ ├── database.py # 数据库连接
│ │
│ ├── models/ # 数据库模型
│ │ ├── __init__.py
│ │ ├── client_key.py
│ │ ├── provider.py
│ │ ├── provider_key.py
│ │ └── request_log.py
│ │
│ ├── services/ # 业务逻辑
│ │ ├── __init__.py
│ │ ├── auth.py # 客户端 Key 验证
│ │ ├── key_selector.py # Key 轮询选择
│ │ ├── logger.py # 异步日志写入
│ │ └── router.py # 模型路由逻辑
│ │
│ ├── providers/ # 提供商适配器
│ │ ├── __init__.py
│ │ └── litellm_wrapper.py # LiteLLM 封装
│ │
│ └── routes/ # API 路由
│ ├── __init__.py
│ ├── chat.py
│ ├── openai.py
│ ├── anthropic.py
│ └── health.py
│
frontend/ # 暂时空置
请求流程
Client Request: POST /v1/chat/completions
Headers: Authorization: Bearer <client_key>
Body: { "model": "claude-3-opus", "messages": [...], "stream": true }
│
▼
1. Auth Middleware - 验证 client_key
│
▼
2. Router Service - model → provider 映射
│
▼
3. Key Selector - 轮询选择 provider_key
│
▼
4. LiteLLM Wrapper - 执行请求
│
▼
5. Response Handler - 流式/非流式响应
│
▼
6. Async Logger - 批量写入日志
错误处理
认证错误
| 场景 | 响应 |
|---|---|
| 缺少 Authorization | 401 {"error": "Missing API key"} |
| 无效的 client_key | 401 {"error": "Invalid API key"} |
| client_key 已禁用 | 403 {"error": "API key disabled"} |
提供商错误
| 场景 | 响应 |
|---|---|
| 模型不存在/不支持 | 400 {"error": "Unsupported model: xxx"} |
| 提供商无可用 Key | 503 {"error": "No available API key for provider"} |
| 上游 API 限流 | 429 转发上游错误 |
| 上游 API 错误 | 500 转发上游错误 |
流式响应错误
- 中途失败 → SSE
data: {"error": "..."}\n\n后关闭连接 - 记录部分成功的 token 使用量
依赖
dependencies = [
"fastapi>=0.135.1",
"uvicorn[standard]>=0.41.0",
"litellm>=1.50.0",
"aiosqlite>=0.20.0",
]
初始化示例
-- 预置提供商
INSERT INTO providers (name, base_url, api_type) VALUES
('openai', 'https://api.openai.com/v1', 'openai'),
('anthropic', 'https://api.anthropic.com/v1', 'anthropic');
-- 添加提供商 API Key
INSERT INTO provider_keys (provider_id, key) VALUES
(1, 'sk-xxx...'), -- OpenAI key 1
(1, 'sk-yyy...'), -- OpenAI key 2
(2, 'sk-ant-xxx...'); -- Anthropic key
-- 添加客户端 Key
INSERT INTO client_keys (key, name) VALUES
('zzrouter-client-xxx', 'my-laptop');