1
0
Files
nex/backend/README.md

403 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AI Gateway Backend
AI 网关后端服务,提供统一的大模型 API 代理接口。
## 功能特性
- 支持 OpenAI 协议(`/openai/v1/...`
- 支持 Anthropic 协议(`/anthropic/v1/...`
- 支持 Hub-and-Spoke 跨协议双向转换OpenAI ↔ Anthropic
- 同协议透传(零语义损失、零序列化开销)
- 支持流式响应SSE
- 支持 Function Calling / Tools
- 支持 Thinking / Reasoning
- 支持扩展层接口Models、Embeddings、Rerank
- 多供应商配置和路由
- 用量统计
- 结构化日志zap + lumberjack
- YAML 配置管理
- 请求验证
- 中间件支持(请求 ID、日志、恢复、CORS
## 技术栈
- **语言**: Go 1.26+
- **HTTP 框架**: Gin
- **ORM**: GORM
- **数据库**: SQLite
- **日志**: zap + lumberjack
- **配置**: Viper + pflag多层配置CLI > 环境变量 > 配置文件 > 默认值)
- **验证**: go-playground/validator/v10
- **迁移**: goose
## 项目结构
```
backend/
├── cmd/
│ └── server/
│ └── main.go # 主程序入口(依赖注入)
├── internal/
│ ├── config/ # 配置管理
│ │ ├── config.go # Viper 多层配置加载/验证
│ │ └── models.go # GORM 数据模型
│ ├── domain/ # 领域模型
│ │ ├── provider.go
│ │ ├── model.go
│ │ ├── stats.go
│ │ └── route.go
│ ├── handler/ # HTTP 处理器
│ │ ├── middleware/ # 中间件
│ │ │ ├── request_id.go
│ │ │ ├── logging.go
│ │ │ ├── recovery.go
│ │ │ └── cors.go
│ │ ├── proxy_handler.go # 统一代理处理器
│ │ ├── provider_handler.go
│ │ ├── model_handler.go
│ │ └── stats_handler.go
│ ├── conversion/ # 协议转换引擎
│ │ ├── canonical/ # Canonical Model
│ │ │ ├── types.go # 核心请求/响应类型
│ │ │ ├── stream.go # 流式事件类型
│ │ │ └── extended.go # 扩展层 Models
│ │ ├── openai/ # OpenAI 协议适配器
│ │ │ ├── types.go
│ │ │ ├── adapter.go
│ │ │ ├── decoder.go
│ │ │ ├── encoder.go
│ │ │ ├── stream_decoder.go
│ │ │ └── stream_encoder.go
│ │ ├── anthropic/ # Anthropic 协议适配器
│ │ │ ├── types.go
│ │ │ ├── adapter.go
│ │ │ ├── decoder.go
│ │ │ ├── encoder.go
│ │ │ ├── stream_decoder.go
│ │ │ └── stream_encoder.go
│ │ ├── adapter.go # ProtocolAdapter 接口 + Registry
│ │ ├── stream.go # StreamDecoder/Encoder/Converter
│ │ ├── middleware.go # Middleware 接口和 Chain
│ │ ├── engine.go # ConversionEngine 门面
│ │ ├── errors.go # ConversionError
│ │ ├── interface.go # InterfaceType 枚举
│ │ └── provider.go # TargetProvider
│ ├── provider/ # 供应商客户端
│ │ └── client.go
│ ├── repository/ # 数据访问层
│ │ ├── provider_repo.go # 接口定义
│ │ ├── provider_repo_impl.go
│ │ ├── model_repo.go
│ │ ├── model_repo_impl.go
│ │ ├── stats_repo.go
│ │ └── stats_repo_impl.go
│ └── service/ # 业务逻辑层
│ ├── provider_service.go # 接口定义
│ ├── provider_service_impl.go
│ ├── model_service.go
│ ├── model_service_impl.go
│ ├── routing_service.go
│ ├── routing_service_impl.go
│ ├── stats_service.go
│ └── stats_service_impl.go
├── pkg/ # 公共包
│ ├── errors/ # 结构化错误
│ │ ├── errors.go
│ │ └── wrap.go
│ ├── logger/ # 日志系统
│ │ ├── logger.go
│ │ ├── rotate.go
│ │ └── context.go
│ └── validator/ # 验证器
│ └── validator.go
├── migrations/ # 数据库迁移
│ ├── 20260401000001_initial_schema.sql
│ ├── 20260401000002_add_indexes.sql
│ └── 20260419000001_add_provider_protocol.sql
├── tests/ # 集成测试
│ ├── helpers.go
│ └── integration/
├── Makefile
├── go.mod
└── README.md
```
## 架构
采用三层架构handler → service → repository通过依赖注入连接
```
handlerHTTP 请求处理)
→ service业务逻辑
→ repository数据访问
```
代理请求通过 ConversionEngine 进行协议转换:
```
Client Request (clientProtocol)
→ ProxyHandler 路由到上游 provider
→ ConversionEngine 请求转换 (clientProtocol → providerProtocol)
→ ProviderClient 发送请求
→ ConversionEngine 响应转换 (providerProtocol → clientProtocol)
→ Client Response
```
同协议时自动透传,跳过序列化开销。
## 运行方式
### 安装依赖
```bash
go mod download
```
### 启动服务
```bash
go run cmd/server/main.go
```
服务将在端口 9826 启动。首次启动会自动创建配置文件和运行数据库迁移。
## 配置
配置支持多种方式:配置文件、环境变量、命令行参数,优先级为:**CLI 参数 > 环境变量 > 配置文件 > 默认值**
### 配置文件
配置文件位于 `~/.nex/config.yaml`,首次启动自动生成。
```yaml
server:
port: 9826
read_timeout: 30s
write_timeout: 30s
database:
path: ~/.nex/config.db
max_idle_conns: 10
max_open_conns: 100
conn_max_lifetime: 1h
log:
level: info
path: ~/.nex/log
max_size: 100 # MB
max_backups: 10
max_age: 30 # 天
compress: true
```
### 环境变量
所有配置项都支持环境变量,使用 `NEX_` 前缀:
```bash
export NEX_SERVER_PORT=9000
export NEX_DATABASE_PATH=/data/nex.db
export NEX_LOG_LEVEL=debug
```
命名规则:配置路径转大写 + 下划线 + `NEX_` 前缀(如 `server.port``NEX_SERVER_PORT`)。
### 命令行参数
```bash
./server --server-port 9000 --log-level debug --database-path /tmp/test.db
```
命名规则:配置路径转 kebab-case + `--` 前缀(如 `server.port``--server-port`)。
完整参数列表:
```
服务器: --server-port, --server-read-timeout, --server-write-timeout
数据库: --database-path, --database-max-idle-conns, --database-max-open-conns, --database-conn-max-lifetime
日志: --log-level, --log-path, --log-max-size, --log-max-backups, --log-max-age, --log-compress
通用: --config (指定配置文件路径)
```
### 使用示例
```bash
# 默认配置
./server
# 临时修改端口
./server --server-port 9000
# 测试场景
./server --database-path /tmp/test.db --log-level debug
# Docker 部署
docker run -d -e NEX_SERVER_PORT=9000 -e NEX_LOG_LEVEL=info nex-server
# 自定义配置文件
./server --config /path/to/custom.yaml
```
数据文件:
- `~/.nex/config.yaml` - 配置文件
- `~/.nex/config.db` - SQLite 数据库
- `~/.nex/log/` - 日志目录
## 测试
```bash
# 运行所有测试
make test
# 生成覆盖率报告
make test-coverage
```
## 数据库迁移
```bash
# 使用 Makefile
make migrate-up DB_PATH=~/.nex/config.db
make migrate-down DB_PATH=~/.nex/config.db
make migrate-status DB_PATH=~/.nex/config.db
# 创建新迁移
make migrate-create
# 或直接使用 goose
goose -dir migrations sqlite3 ~/.nex/config.db up
```
## API 文档
### 代理接口
使用 `/{protocol}/v1/{path}` URL 前缀路由:
#### OpenAI 协议
```
POST /openai/v1/chat/completions
GET /openai/v1/models
POST /openai/v1/embeddings
POST /openai/v1/rerank
```
#### Anthropic 协议
```
POST /anthropic/v1/messages
GET /anthropic/v1/models
```
**协议转换**:网关支持任意协议间的双向转换。客户端使用 OpenAI 协议请求,上游供应商可以是 Anthropic 协议(反之亦然)。同协议时自动透传,零序列化开销。
### 管理接口
#### 供应商管理
- `GET /api/providers` - 列出所有供应商
- `POST /api/providers` - 创建供应商
- `GET /api/providers/:id` - 获取供应商
- `PUT /api/providers/:id` - 更新供应商
- `DELETE /api/providers/:id` - 删除供应商
```json
{
"id": "openai",
"name": "OpenAI",
"api_key": "sk-...",
"base_url": "https://api.openai.com/v1",
"protocol": "openai"
}
```
**Protocol 字段**:标识上游供应商使用的协议类型,可选值 `"openai"`(默认)、`"anthropic"`
**base_url 说明**:应配置到 API 版本路径,不包含具体端点(如 OpenAI: `https://api.openai.com/v1`GLM: `https://open.bigmodel.cn/api/paas/v4`)。
#### 模型管理
- `GET /api/models` - 列出模型(支持 `?provider_id=xxx` 过滤)
- `POST /api/models` - 创建模型
- `GET /api/models/:id` - 获取模型
- `PUT /api/models/:id` - 更新模型
- `DELETE /api/models/:id` - 删除模型
```json
{
"id": "gpt-4",
"provider_id": "openai",
"model_name": "gpt-4"
}
```
#### 统计查询
- `GET /api/stats` - 查询统计
- `GET /api/stats/aggregate` - 聚合统计
查询参数:`provider_id``model_name``start_date`YYYY-MM-DD`end_date``group_by`provider/model/date
#### 健康检查
- `GET /health` - 返回 `{"status": "ok"}`
## 开发
```bash
make build # 构建
make lint # 代码检查
make deps # 整理依赖
```
环境要求Go 1.26 或更高版本
## 公共库使用指南
### pkg/errors — 结构化错误
```go
import (
"errors"
pkgErrors "nex/backend/pkg/errors"
)
return pkgErrors.ErrRequestSend.WithCause(err)
var appErr *pkgErrors.AppError
if errors.As(err, &appErr) {
// appErr.Code, appErr.HTTPStatus, appErr.Message
}
```
### pkg/logger — 日志系统
构造函数接受 `*zap.Logger` 参数nil 时回退到 `zap.L()`
```go
func NewMyService(repo Repository, logger *zap.Logger) *MyService {
if logger == nil {
logger = zap.L()
}
return &MyService{repo: repo, logger: logger}
}
```
### pkg/validator — 请求验证
```go
import "nex/backend/pkg/validator"
v := validator.Get()
err := v.Validate(myStruct)
```
## 编码规范
- **JSON 解析**:使用 `encoding/json` 标准库,不手动扫描字节
- **字符串拼接**:使用 `strings.Join`,不手写循环拼接
- **错误判断**:使用 `errors.Is` / `errors.As`,不使用字符串匹配
- **日志使用**:通过依赖注入 `*zap.Logger`,不直接调用 `zap.L()`
- **字符串分割**:使用 `strings.SplitN` 等精确分割,不使用索引切片