summaryrefslogtreecommitdiff
path: root/README.md
blob: 96d25403371cb18799299f6d9a8329acc8f1d6e6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# AIGW

AIGW 是一个轻量、无状态的 AI API 中转后端。当前阶段聚焦上游接入与 API 分发:提供 OpenAI Chat Completions 和 Anthropic Messages 兼容入口,支持公开模型名映射、多上游路由、加权分流、故障转移、SSE 直通、客户密钥鉴权以及异步用量事件。

它借鉴了 ZenMux 的双协议、`provider/model` 模型命名、统一错误、请求 ID、路由和可观测性边界,但没有复制其业务实现。

## 当前能力

- OpenAI:`POST /v1/chat/completions`、`GET /v1/models`
- Anthropic:`POST /anthropic/v1/messages`、`GET /anthropic/v1/models`
- ZenMux 风格别名:所有入口同时提供 `/api/...` 路径
- 一个公开模型可配置多个同协议上游,低 `priority` 优先,同级按 `weight` 分流
- 在 429、502、503、504 或连接失败时,于响应开始前自动尝试下一条路由
- SSE 增量直通、主动断连传播、共享 HTTP/2 连接池
- `Authorization: Bearer` 和 `x-api-key` 客户鉴权
- 统一 JSON 错误、`X-AIGW-Request-ID`、Prometheus 文本指标
- 非阻塞用量事件,包含租户、项目、模型、上游、尝试次数、耗时和 token 用量

## 快速运行

需要 Go 1.26。先准备配置和环境变量:

```bash
cp config.example.json config.json
export AIGW_API_KEYS='[{"key":"sk-local-change-me","key_id":"local-key","tenant_id":"tenant-demo","project_id":"project-default","scopes":["inference"]}]'
export OPENAI_API_KEY='your-upstream-key'
export ANTHROPIC_API_KEY='your-upstream-key'
go run ./cmd/aigw -config config.json
```

如果当前只有一种上游,从 `config.json` 删除未使用的 provider 和对应 model,避免启动时要求该密钥。

不使用真实密钥的本地体验方式:

```bash
# terminal 1
go run ./cmd/mockupstream

# terminal 2
export MOCK_UPSTREAM_KEY='local-only'
export AIGW_API_KEYS='[{"key":"sk-local-change-me","key_id":"local-key","tenant_id":"tenant-demo","project_id":"project-default","scopes":["inference"]}]'
go run ./cmd/aigw -config config.local.json
```

本地网关会监听 `http://127.0.0.1:18081`,公开模型为 `demo/openai` 和 `demo/anthropic`。`cmd/mockupstream` 只用于开发验证,不应部署到生产环境。

OpenAI 调用示例:

```bash
curl http://127.0.0.1:8080/v1/chat/completions \
  -H 'Authorization: Bearer sk-local-change-me' \
  -H 'Content-Type: application/json' \
  -d '{"model":"openai/gpt-4.1-mini","messages":[{"role":"user","content":"hello"}],"stream":true}'
```

Anthropic 调用示例:

```bash
curl http://127.0.0.1:8080/anthropic/v1/messages \
  -H 'x-api-key: sk-local-change-me' \
  -H 'anthropic-version: 2023-06-01' \
  -H 'Content-Type: application/json' \
  -d '{"model":"anthropic/claude-sonnet","max_tokens":256,"messages":[{"role":"user","content":"hello"}]}'
```

## 配置路由

每条 route 把一个对外模型映射到一个上游模型:

```json
{
  "id": "vendor/model-public",
  "owned_by": "vendor",
  "routes": [
    {"provider": "provider-a", "upstream_model": "model-v3", "priority": 0, "weight": 80},
    {"provider": "provider-b", "upstream_model": "model-v3", "priority": 0, "weight": 20},
    {"provider": "provider-c", "upstream_model": "model-v2", "priority": 10, "weight": 100}
  ]
}
```

这里 A/B 承担约 80/20 的首选流量,C 只作为更低优先级的后备。所有上游密钥仅通过 `api_key_env` 指向的环境变量读取。客户密钥从 `AIGW_API_KEYS` JSON 数组读取,进程内只保存 SHA-256 摘要。

## 生产边界

当前版本可以作为第一阶段的数据面,但还不是完整商业计费平台:

- 用量事件写到结构化日志,队列满时会丢弃并增加 `aigw_usage_events_dropped_total`。正式扣费前必须替换为持久、幂等的账本写入或消息队列。
- 客户密钥和模型目录在控制面模式下从 PostgreSQL 载入到原子内存快照;Redis 只是可选的变更广播加速层,故障时通过 PostgreSQL generation 轮询收敛。
- 当前只把 OpenAI 入口发给 OpenAI 兼容上游、Anthropic 入口发给 Anthropic 兼容上游,不做跨协议转换。
- 自动故障转移可能在极少数网络错误下造成上游重复执行。正式计费时需要上游幂等能力、请求去重策略和重复成本对账。
- `/metrics` 应仅在内网暴露;公网 TLS、WAF 和连接层限速应放在负载均衡器或边缘代理。

## PostgreSQL + Redis 控制面

控制面模式使用 [config.control.example.json](config.control.example.json)。PostgreSQL 保存事实数据:租户、项目、API Key 摘要、加密后的供应商密钥、模型和路由。Redis 保存当前 generation 并广播变更事件;它不是客户账本,PG 才是控制面事实来源。Redis 短暂不可用或未配置时,网关仍从 PostgreSQL 启动并通过周期轮询收敛,恢复后会在后台自动重连订阅。管理写入先提交 PG 并替换本机快照,Redis 广播失败只记录告警,不会把已提交的写入报告为失败。

凭证密钥必须是 base64 编码的 32 字节随机值。供应商 API Key 使用 AES-256-GCM 加密后写入 PostgreSQL,运行时解密到当前内存快照,管理接口永远不会返回它。

开发环境可以直接启动:

```bash
export AIGW_CREDENTIAL_KEY="$(openssl rand -base64 32)"
export AIGW_ADMIN_TOKEN="$(openssl rand -hex 32)"
docker compose up --build
```

然后打开 `http://127.0.0.1:8080/admin/`,输入 `AIGW_ADMIN_TOKEN`。第一套资源的创建顺序是:Tenant → Project → API key → Provider → Model route。创建客户 API Key 时,明文只在成功响应中出现一次。

生产环境建议把 `auto_migrate` 改为 `false`,先执行:

```bash
AIGW_DATABASE_URL="postgres://..." go run ./cmd/migrate
```

管理 API 仅使用一个 bootstrap token,应该放在内网、VPN 或反向代理后。下一阶段需要把它替换为管理员用户、角色和审计日志。

完整的扩展边界见 [架构说明](docs/architecture.md)。

## 验证

```bash
go test ./...
go vet ./...
```