# 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 - **配置**: gopkg.in/yaml.v3 - **验证**: go-playground/validator/v10 - **迁移**: goose ## 项目结构 ``` backend/ ├── cmd/ │ └── server/ │ └── main.go # 主程序入口(依赖注入) ├── internal/ │ ├── config/ # 配置管理 │ │ ├── config.go # 配置加载/保存/验证 │ │ └── 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/ # 数据库迁移 │ ├── 001_initial_schema.sql │ └── 002_add_indexes.sql ├── tests/ # 测试 │ ├── helpers.go │ ├── integration/ │ ├── unit/ │ └── testdata/ ├── Makefile ├── go.mod └── README.md ``` ## 架构 采用三层架构(handler → service → repository),通过依赖注入连接: ``` handler(HTTP 请求处理) → service(业务逻辑) → repository(数据访问) ``` ## 运行方式 ### 安装依赖 ```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 ./server ``` 环境变量命名规则:将配置路径转换为大写,用下划线连接,加 `NEX_` 前缀: - `server.port` → `NEX_SERVER_PORT` - `database.path` → `NEX_DATABASE_PATH` - `log.level` → `NEX_LOG_LEVEL` ### 命令行参数 所有配置项都支持命令行参数: ```bash ./server --server-port 9000 --log-level debug --database-path /tmp/test.db ``` CLI 参数命名规则:将配置路径转换为 kebab-case,用连字符连接: - `server.port` → `--server-port` - `database.path` → `--database-path` - `log.level` → `--log-level` 完整参数列表: ``` 服务器配置: --server-port int 服务器端口(默认 9826) --server-read-timeout duration 读超时(默认 30s) --server-write-timeout duration 写超时(默认 30s) 数据库配置: --database-path string 数据库文件路径(默认 ~/.nex/config.db) --database-max-idle-conns int 最大空闲连接数(默认 10) --database-max-open-conns int 最大打开连接数(默认 100) --database-conn-max-lifetime duration 连接最大生命周期(默认 1h) 日志配置: --log-level string 日志级别:debug/info/warn/error(默认 info) --log-path string 日志文件目录(默认 ~/.nex/log) --log-max-size int 单个日志文件最大大小 MB(默认 100) --log-max-backups int 保留的旧日志文件最大数量(默认 10) --log-max-age int 保留旧日志文件的最大天数(默认 30) --log-compress 是否压缩旧日志文件(默认 true) 通用选项: --config string 配置文件路径(默认 ~/.nex/config.yaml) ``` ### 使用示例 ```bash # 1. 使用默认配置 ./server # 2. 临时修改端口(不修改配置文件) ./server --server-port 9000 # 3. 测试场景(临时数据库) ./server --database-path /tmp/test.db --log-level debug # 4. Docker 部署(环境变量) docker run -d \ -e NEX_SERVER_PORT=9000 \ -e NEX_DATABASE_PATH=/data/nex.db \ -e NEX_LOG_LEVEL=info \ nex-server # 5. 使用自定义配置文件 ./server --config /path/to/custom.yaml # 6. 混合使用(优先级:CLI > ENV > File) export NEX_LOG_LEVEL=debug ./server --server-port 9000 # port 用 CLI,log.level 用 ENV ``` 数据文件: - `~/.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 # 或直接使用 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 ``` 请求示例: ```json { "model": "gpt-4", "messages": [ {"role": "user", "content": "Hello"} ], "stream": false } ``` #### Anthropic 协议代理 ``` POST /anthropic/v1/messages GET /anthropic/v1/models ``` 请求示例: ```json { "model": "claude-3-opus", "max_tokens": 1024, "messages": [ {"role": "user", "content": [{"type": "text", "text": "Hello"}]} ] } ``` **协议转换**:网关支持任意协议间的双向转换。客户端使用 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 字段说明:** - `protocol` 标识上游供应商使用的协议类型,可选值:`"openai"`(默认)、`"anthropic"` - 同协议透传时,请求体和响应体原样转发,零序列化开销 **重要说明:** - `base_url` 应配置到 API 版本路径,不包含具体端点 - OpenAI: `https://api.openai.com/v1` - GLM: `https://open.bigmodel.cn/api/paas/v4` - 其他 OpenAI 兼容供应商根据其文档配置版本路径 #### 模型管理 - `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` - 供应商 ID - `model_name` - 模型名称 - `start_date` - 开始日期(YYYY-MM-DD) - `end_date` - 结束日期(YYYY-MM-DD) - `group_by` - 聚合维度(provider/model/date) ## 开发 ### 构建 ```bash make build ``` ### 代码检查 ```bash make lint ``` ### 环境要求 - Go 1.26 或更高版本 ## 公共库使用指南 ### pkg/errors — 结构化错误 使用预定义的错误类型,配合 `errors.Is` / `errors.As` 判断错误: ```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 } ``` 可用函数:`NewAppError`、`Wrap`、`WithContext`、`WithMessage`、`AsAppError` 预定义错误:`ErrModelNotFound`、`ErrProviderNotFound`、`ErrInvalidRequest`、`ErrRequestCreate`、`ErrRequestSend`、`ErrResponseRead` 等 ### 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} } ``` 禁止直接在业务代码中使用 `zap.L()` 全局 logger,应通过构造函数注入。 ### pkg/validator — 请求验证 ```go import "nex/backend/pkg/validator" v := validator.Get() err := v.Validate(myStruct) ``` ## 编码规范 - **JSON 解析**:使用 `encoding/json` 标准库(`json.Unmarshal` / `json.Marshal`),不手动扫描字节 - **字符串拼接**:使用 `strings.Join`,不手写循环拼接 - **错误判断**:使用 `errors.Is` / `errors.As`,不使用字符串匹配(`strings.Contains(err.Error(), ...)`) - **日志使用**:通过依赖注入 `*zap.Logger`,不直接调用 `zap.L()` - **字符串分割**:使用 `strings.SplitN(key, "/", 2)` 等精确分割,不使用索引切片