LLM 网关 & 密钥管理
SayClaw 通过 One API 作为统一 LLM 网关,实现主密钥保护、令牌独立计费、模型路由、请求日志和自动限额。所有 OC 实例使用 litellm provider 经网关访问模型,不直连 OpenAI/Anthropic/Google。
最后更新:2026-03-11
重大变更:
- 已从 LiteLLM Proxy 迁移至 One API(Go 原生,MySQL 存储,零 Prisma 依赖)。
- API Key 管理模块重构:Master Key 与 OneAPI Channel 自动同步,Sub Key 与 OneAPI Token 自动派生。
1. 架构总览
┌──────────────────────────────────────────────────────┐
│ SayClaw Admin 后台 │
│ ┌────────────┐ ┌──────────┐ ┌───────────────────┐ │
│ │ 主 Key 保险库│ │ 模型/渠道 │ │ 令牌管理 │ │
│ └─────┬──────┘ └─────┬────┘ └────────┬──────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌────────────────────────────────────────────────┐ │
│ │ One API(Go / Docker / jp-1:3001) │ │
│ │ • 渠道(Channel) = 真实 Provider API Key │ │
│ │ • 令牌(Token) = 实例虚拟 Key,独立额度 │ │
│ │ • 请求日志 → MySQL (oneapi.logs) 实时写入 │ │
│ │ • 自动 Fallback(渠道优先级 + 权重) │ │
│ └──────────────────────┬─────────────────────────┘ │
└─────────────────────────┼────────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ OC 实例 A │ │ OC 实例 B │ │ OC 实例 C │
│ provider: │ │ provider: │ │ provider: │
│ litellm │ │ litellm │ │ litellm │
│ key=sk-令牌A│ │ key=sk-令牌B│ │ key=sk-令牌C│
└────────────┘ └────────────┘ └────────────┘
核心原则: OC 实例只持有 One API 虚拟令牌,永远看不到真实 Provider Key。
2. 为什么从 LiteLLM 迁移到 One API?
LiteLLM 的问题(已弃用)
| 问题 | 影响 |
|---|---|
| Prisma query-engine 反复崩溃为 zombie 进程 | LLM 请求本身可能成功,但消费日志(SpendLogs)写入失败并被丢弃 |
| Python + Prisma ORM + PostgreSQL | 架构复杂,调试困难,资源消耗大 |
database_type: "postgres" 不支持原生直连 | 只支持 dynamo_db,无法绕过 Prisma |
| SpendLog 异步批量写入(10s flush) | engine 挂了期间所有日志丢失,无法恢复 |
One API 优势
| 特性 | 说明 |
|---|---|
| Go 单二进制 | 零 Python、零 Prisma、零 zombie 风险 |
| 原生 MySQL | 直接用现有 sayclaw-mysql,日志实时写入 |
| 60k+ Star | 中国社区最活跃的 AI 网关,文档完善 |
| 内置功能 | 渠道管理、令牌系统、额度控制、用量统计、请求日志 |
| OpenAI 兼容 | OC 实例零改动(换 baseUrl + key 即可) |
3. 部署架构
组件清单
| 组件 | 位置 | 端口 | 说明 |
|---|---|---|---|
| One API | jp-1 (35.243.76.69) | 3001 | Docker 容器 sayclaw-oneapi |
| MySQL | jp-1 | 3306 | 数据库 oneapi(容器 sayclaw-mysql) |
| Admin 面板 | http://35.243.76.69:3001 | — | One API 自带 Web 管理界面 |
Docker 启动命令
docker run -d --name sayclaw-oneapi --network host \
--restart unless-stopped \
-e SQL_DSN="sayclaw:SayClaw_App_2026!@tcp(127.0.0.1:3306)/oneapi" \
-e SESSION_SECRET="sayclaw-oneapi-secret-2026" \
-e TZ=Asia/Tokyo \
-v /opt/sayclaw-oneapi/data:/data \
justsong/one-api:latest \
--port 3001
MySQL 表结构(One API 自动创建)
| 表名 | 说明 |
|---|---|
channels | 渠道(Provider API Key 配置) |
tokens | 令牌(实例虚拟 Key) |
logs | 请求日志(实时写入) |
users | 用户管理 |
abilities | 模型-渠道映射 |
options | 系统配置 |
4. 渠道配置(Channel = 主 Key)
渠道是 One API 中管理真实 Provider API Key 的核心概念。每个渠道对应一个 Provider 的 API Key。
当前渠道
| # | 渠道名 | 类型 | 优先级 | 支持模型 |
|---|---|---|---|---|
| 1 | anthropic | 14 (Anthropic) | 10 | claude-opus-4-6, claude-sonnet-4-6, claude-haiku-3-5 |
| 2 | openai | 1 (OpenAI) | 5 | gpt-4o, gpt-4o-mini |
| 3 | 24 (Google Gemini) | 5 | gemini-2.5-flash, gemini-2.5-pro, gemini-3-flash-preview, gemini-3.1-pro-preview |
渠道管理
- 优先级:数值越大优先使用(Anthropic=10 优先于 OpenAI=5)
- 权重:同优先级多渠道时按权重负载均衡
- 自动禁用:连续失败后自动禁用渠道,定期自动测试恢复
- 多 Key 轮转:同一 Provider 可创建多个渠道实现 Key 轮转
- 自动同步:在 Admin 后台添加/修改主密钥时,会自动调用 OneAPI 接口创建或更新对应的 Channel。
添加渠道 API
curl -X POST http://localhost:3001/api/channel/ \
-b <cookie> \
-H "Content-Type: application/json" \
-d '{
"name": "anthropic",
"type": 14,
"key": "sk-ant-xxx",
"models": "claude-opus-4-6,claude-sonnet-4-6,claude-haiku-3-5",
"priority": 10,
"weight": 1
}'
5. 令牌系统(Token = 实例 Key)
每个 OC 实例分配一个 One API 令牌,作为访问网关的凭证。
当前令牌分配
| 实例 | 令牌名 | 额度 |
|---|---|---|
| oc-xialong-main | oc-xialong-main | 无限 |
| oc-ai-jp-2-01 ~ 10 | oc-ai-jp-2-01 ~ 10 | 无限 |
| oc-ai-jp-3-01 ~ 08 | oc-ai-jp-3-01 ~ 08 | 无限 |
令牌与实例名一一对应,映射保存在
/opt/sayclaw-oneapi/token-mapping.txt
令牌管理
- 额度控制:可设置
remain_quota(剩余额度)或unlimited_quota: true - 过期时间:可设置
expired_time,到期自动失效 - 模型限制:可指定令牌只允许访问特定模型
- 子网限制:可限制令牌只从特定 IP 使用
- 自动派生:在 Admin 后台为实例创建子密钥时,会自动调用 OneAPI 接口生成 Token,并将
oneapi_key_id关联存储在数据库中。
创建令牌 API
curl -X POST http://localhost:3001/api/token/ \
-b <cookie> \
-H "Content-Type: application/json" \
-d '{
"name": "oc-ai-jp-2-02",
"remain_quota": 0,
"unlimited_quota": true
}'
6. OC 实例配置
openclaw.json
{
"models": {
"providers": {
"litellm": {
"baseUrl": "http://35.243.76.69:3001", // One API 地址
"api": "openai-completions",
"models": [
{ "id": "claude-opus-4-6", "name": "Claude Opus 4.6" },
{ "id": "claude-sonnet-4-6", "name": "Claude Sonnet 4.6" },
{ "id": "claude-haiku-4-5", "name": "Claude Haiku 4.5" },
{ "id": "gpt-4o", "name": "GPT-4o" },
{ "id": "gpt-4o-mini", "name": "GPT-4o Mini" },
{ "id": "gemini-2.5-flash", "name": "Gemini 2.5 Flash" },
{ "id": "gemini-2.5-pro", "name": "Gemini 2.5 Pro" }
]
}
}
},
"agents": {
"defaults": {
"model": {
"primary": "litellm/claude-sonnet-4-6",
"fallbacks": ["litellm/claude-haiku-4-5", "litellm/gemini-2.5-flash"]
}
}
}
}
注意: OpenClaw 的 provider 名仍然叫
litellm(历史兼容),实际指向 One API。
auth-profiles.json
{
"version": 1,
"profiles": {
"litellm:default": {
"type": "api_key",
"provider": "litellm",
"key": "sk-xxxx" // One API 令牌
}
}
}
文件位置:/opt/oc-home-NN/.openclaw/agents/main/agent/auth-profiles.json
7. 请求日志 & 审计
日志表:oneapi.logs
One API 在每次请求完成后同步写入 MySQL,零延迟。
-- One API 自动创建,关键字段:
SELECT
id,
FROM_UNIXTIME(created_at) AS time,
token_name, -- 令牌名 = 实例名(如 oc-ai-jp-2-02)
model_name, -- 实际使用的模型
prompt_tokens,
completion_tokens,
quota, -- 消耗额度(One API 内部单位)
channel_id, -- 使用的渠道 ID
elapsed_time, -- 请求耗时(ms)
type, -- 2=成功, 其他=失败
content -- 日志详情
FROM oneapi.logs
ORDER BY id DESC;
Admin API 审计接口
GET /api/v1/audit/request-logs
参数:
keyword— 搜索令牌名/模型名/用户名model— 按模型过滤status—success或errorstart_date/end_date— 日期范围page/page_size— 分页
返回字段:
| 字段 | 说明 |
|---|---|
instance_id | 令牌名(= 实例名) |
user_email | 关联用户邮箱(通过 sayclaw_portal.user_instances + users JOIN) |
model | 使用的模型 |
input_tokens | 输入 token 数 |
output_tokens | 输出 token 数 |
cost_usd | 费用(quota / 500000) |
latency_ms | 请求耗时 |
status | success / error |
用户关联逻辑
oneapi.logs.token_name (如 oc-ai-jp-2-02)
↓ = instance_id
sayclaw_portal.user_instances.instance_id → user_id
↓ JOIN
sayclaw_portal.users.id → email, name
聊天消息双向关联
系统打通了 LLM 调用日志与用户聊天消息之间的双向关联:
- 聊天历史展示用量:
GET /me/chat/history接口中,assistant 消息通过instance_id+ ±5s 时间窗口匹配oneapi.logs,返回prompt_tokens/completion_tokens/cost_usd/latency_ms/model。 - 用量日志展示消息:
GET /me/usage/logs接口中,LLM 调用日志反向关联chat_messages,返回user_message/ai_message预览。
查询示例
-- 某实例今日花费
SELECT model_name, COUNT(*) AS calls, SUM(quota) AS total_quota,
SUM(prompt_tokens) AS prompt_tk, SUM(completion_tokens) AS completion_tk
FROM oneapi.logs
WHERE token_name = 'oc-ai-jp-2-02' AND created_at >= UNIX_TIMESTAMP(CURDATE())
GROUP BY model_name;
-- 所有实例月度排行
SELECT token_name, COUNT(*) AS calls, SUM(quota) AS total_quota
FROM oneapi.logs
WHERE created_at >= UNIX_TIMESTAMP(DATE_FORMAT(NOW(), '%Y-%m-01'))
GROUP BY token_name ORDER BY total_quota DESC;
8. Fallback & 路由策略
渠道级 Fallback(One API 自动)
One API 根据渠道优先级和权重自动路由:
- 请求模型
claude-sonnet-4-6→ 匹配 Anthropic 渠道(priority=10) - 如果 Anthropic 失败 → 自动禁用该渠道 → 尝试备用渠道
- 定期自动测试已禁用渠道 → 恢复可用后重新启用
OC 实例级 Fallback
"model": {
"primary": "litellm/claude-sonnet-4-6",
"fallbacks": ["litellm/claude-haiku-4-5", "litellm/gemini-2.5-flash"]
}
当 One API 返回错误时,OpenClaw 按 fallback 列表依次尝试。
建议: fallback 链中不混用不同计费等级的模型(如 claude-sonnet → claude-haiku → gemini-flash,而非 → gpt-4o),避免意外高额消费。
9. 主 Key 保险库(Admin 侧)
主 Key 仍然通过 SayClaw Admin 管理,加密存储在 llm_master_keys 表。
One API 渠道中填入的是解密后的真实 Key。
管理规则
- 主 Key 加密存储在 MySQL(AES-256-GCM),Admin 后台仅展示前缀
- 只配置到 One API 渠道,不下发给任何 OC 实例
- 支持多 Key 轮转(同一 Provider 创建多个渠道)
- 创建/轮换/吊销均有审计日志
DB 表:llm_master_keys(Admin 侧,不变)
CREATE TABLE llm_master_keys (
id VARCHAR(36) PRIMARY KEY,
provider VARCHAR(50) NOT NULL COMMENT 'openai/anthropic/google',
name VARCHAR(100) NOT NULL COMMENT '密钥名称',
encrypted_key TEXT NOT NULL COMMENT 'AES-256-GCM 加密后的原始 key',
key_prefix VARCHAR(20) NOT NULL COMMENT '前缀用于识别(如 sk-ant-...)',
is_active TINYINT(1) DEFAULT 1,
monthly_budget_usd DECIMAL(10,2) DEFAULT 0,
current_spend_usd DECIMAL(10,2) DEFAULT 0,
created_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
10. 可用模型
| 供应商 | Model ID | 渠道类型 |
|---|---|---|
| Anthropic | claude-opus-4-6 | 14 |
| Anthropic | claude-sonnet-4-6 | 14 |
| Anthropic | claude-haiku-3-5 | 14 |
| OpenAI | gpt-4o | 1 |
| OpenAI | gpt-4o-mini | 1 |
| gemini-2.5-flash | 24 | |
| gemini-2.5-pro | 24 | |
| gemini-3-flash-preview | 24 | |
| gemini-3.1-pro-preview | 24 |
新增模型流程:
- 在 One API 渠道中添加模型名到
models字段 - 更新 OC 实例
openclaw.json的 models 列表 - (可选)在
ai_models表注册元信息
11. 运维手册
常用命令
# 查看 One API 状态
docker ps --filter name=oneapi
# 查看日志
docker logs --tail 50 sayclaw-oneapi
# 重启
docker restart sayclaw-oneapi
# 查看最近请求日志
docker exec sayclaw-mysql mysql -uroot -p'SayClaw_MySQL_2026!' oneapi \
-e "SELECT FROM_UNIXTIME(created_at), token_name, model_name, prompt_tokens, completion_tokens FROM logs ORDER BY id DESC LIMIT 10;"
健康检查
# API 可达性
curl -s http://localhost:3001/api/status
# 模型调用测试
curl -s -X POST http://localhost:3001/v1/chat/completions \
-H "Authorization: Bearer <admin-token>" \
-H "Content-Type: application/json" \
-d '{"model":"claude-sonnet-4-6","messages":[{"role":"user","content":"hi"}],"max_tokens":3}'
故障排查
| 症状 | 排查 | 处理 |
|---|---|---|
| 实例报 401 | 令牌过期或被删 | One API 后台检查令牌状态 |
| 实例报 404 模型 | 渠道未配该模型 | 渠道 models 字段加入 |
| 日志不出现 | MySQL 连接异常 | docker logs sayclaw-oneapi 查错 |
| 请求超时 | 渠道 Key 限额 | 检查 Provider 仪表盘 |
12. 迁移记录
从 LiteLLM 到 One API(2026-03-10)
| 步骤 | 说明 | 状态 |
|---|---|---|
| 部署 One API | Docker, 端口 3001, MySQL oneapi | ✅ |
| 创建渠道 | Anthropic(14) / OpenAI(1) / Google(24) | ✅ |
| 生成令牌 | 19 个实例各一个 Token | ✅ |
| 切换 jp-2 实例 | 10 个实例 baseUrl + key 更新 | ✅ |
| 切换 jp-3 实例 | 8 个实例 baseUrl + key 更新 | ✅ |
| Admin API 审计 | 改查 oneapi.logs + 用户关联 | ✅ |
| 停用 LiteLLM | 容器停止 + watchdog cron 移除 | ✅ |
| 数据验证 | 请求 → 日志 1 秒内出现 | ✅ |
历史遗留
llm_sub_keys表:LiteLLM 时代的子 Key 管理,数据保留但不再使用llm_request_logs表:LiteLLM 时代的请求日志,已被oneapi.logs取代- PostgreSQL
litellm库:LiteLLM SpendLogs 历史数据保留,不再写入
13. 安全约束
- 渠道中的真实 API Key 仅存在于 One API 数据库和 Admin 后台
- OC 实例只持有 One API 虚拟令牌,无法反推真实 Key
- One API 管理面板建议设置强密码,限制访问 IP
- 令牌创建/停用/删除操作通过 One API 审计日志追溯
- 主 Key 在
llm_master_keys表中加密存储(AES-256-GCM)