109 KiB
EasyAI AI 网关中台设计文档
1. 建设目标
将现有 easyai-server-main / easyai-integration-api 中与 integration-platform 强相关的能力拆分为一个独立项目:
- 独立运行:后端 Go、复用 Agent memory 的 PostgreSQL 18(
easyai-pgvector)、前端 React + TSX。 - 前端 UI:使用
shadcn-ui+ Tailwind CSS + Radix UI +lucide-react,保持控制台形态与 EasyAI 运维后台一致。 - 独立部署:HTTP API、任务执行器、任务事件持久化与进度回调均由本服务承载;接入 EasyAI 主站时,业务前端实时推送继续复用
server-main现有 WebSocket 网关。 - 身份双模式:支持 Gateway 本地账号注册登录,也支持沿用
easyai-server-main的 JWT claim、角色权限和 OpenAPIsk-*校验方式。 - 能力对接:作为 Chat、生图、生视频等模型能力网关,供
easyai-server-main和前端直接调用。 - 渐进迁移:新服务稳定后,再让
server-main的OpenaiService/IntegrationPlatformService切为远程调用门面。
2. 迁移边界
2.1 迁入 AI Gateway
| 能力 | 新服务职责 |
|---|---|
| 平台管理 | 平台 CRUD、启停、优先级、凭证配置、可用性检测、默认折扣系数 |
| 基准模型库 | 维护全量 provider、基准模型、模型能力、图像/视频/文本能力标签、基准定价和默认限流模板 |
| 模型管理 | 平台模型、模型类型、能力标签、上下文窗口、计费配置;支持从基准模型继承、按折扣继承或自由覆盖 |
| 模型路由 | 复用现有 assignClientsByModelName 的平台过滤、并发负载、优先级、队列等待语义 |
| 生成任务 | Chat、图片生成、图片编辑、视频生成、语音、Embedding 等任务入口 |
| 队列执行 | 按平台/模型/方法维度做并发控制、等待队列、超时、重试;队列状态必须持久化,服务异常重启后可恢复 |
| 测试模式 | 支持 simulation / dry-run,不向真实平台提交任务,仍完整走路由、队列、限流、重试、进度、结果归一流程 |
| 计费预估 | 在任务创建前或前端配置面提供 estimated billing,且必须使用与真实路由一致的有效模型价格 |
| 任务结果 | 保存任务请求、状态、结果摘要、billings,向主服务发送结算事件 |
| 推送 | 承接任务级进度事件,提供类似原 RxJS Observable 的中间过程返回能力;业务实时进度通过配置回调地址回调 server-main 内部任务进度接口,再由主服务复用原 WebSocket 网关推给业务前端 |
2.2 接入 server-main 时仍保留在 server-main
| 能力 | 原因 |
|---|---|
| 用户、组织、租户、角色 | EasyAI 主站模式下仍是主账号体系;Gateway 保存同步副本和策略快照 |
| 余额、资源包、扣费流水 | 需要复用现有账单锁、组织扣费、消费记录 |
| API Key 创建与撤销 | 接入模式下 sk-* 生命周期属于主服务用户体系;独立模式由 Gateway 本地 API Key 模块承接 |
| 文件上传 | 复用 server-main 已开放的文件上传接口,OSS/COS/S3 密钥和上传实现继续只落在主服务 |
| 业务 WebSocket 网关 | 现有前端已经订阅主服务 WebSocket 通道,Gateway 通过任务进度回调接入,不直接替换业务推送链路 |
| 对话、绘图历史、工作流历史 | 与产品域模型绑定,Gateway 只返回任务结果和结算载荷 |
3. 总体架构
flowchart LR
subgraph client [调用方]
FE[React / easyai-main-web]
MAIN[easyai-server-main]
OPENAPI[OpenAPI Client]
end
subgraph gw [API Gateway / Ingress]
HTTP[HTTP Routes]
PUSH[SSE / WebSocket Routes]
end
subgraph aigw [EasyAI AI Gateway]
API[Go HTTP API]
AUTH[Auth Middleware]
ROUTER[Model Router]
QUEUE[Queue + Runtime]
CALLBACK[Task Progress Callback]
VENDOR[Vendor Clients]
ADMIN[React Admin Console]
end
subgraph data [Data]
PG[(Agent memory PostgreSQL<br/>easyai-pgvector)]
REDIS[(Redis later)]
end
subgraph servermain [server-main]
USER[User / Org]
APIKEY[API Key Verify]
WSGW[WebSocket Gateway]
BILL[Billing + Ledger]
FILES[Open File Upload]
end
FE --> HTTP --> API
FE <-->|business progress ws| WSGW
ADMIN --> PUSH
OPENAPI --> HTTP --> API
MAIN -->|internal HTTP| API
ADMIN --> API
API --> AUTH
AUTH -->|JWT local verify| API
AUTH -->|sk-* delegate| APIKEY
API --> ROUTER --> QUEUE --> VENDOR
API --> PG
QUEUE --> PG
QUEUE --> CALLBACK -->|POST task progress callback to server-main| WSGW
QUEUE -->|settlement event| BILL
API -->|POST /v1/files/upload| FILES
4. Monorepo 方案
本脚手架采用 Nx + pnpm + go.work:
Nx负责任务编排、缓存、前后端统一命令:dev、build、test、migrate。go.work管理 Go 模块,避免把 Go 项目硬塞进 Node 包管理。pnpm-workspace.yaml管理 React 前端与 TypeScript contracts。- 前端控制台使用
shadcn-ui作为组件体系,配套 Tailwind CSS、Radix UI primitive、lucide-react图标和class-variance-authority管理组件变体。
选择这个组合的原因:
- 与 EasyAI 现有 Nx 使用习惯接近,便于团队统一命令。
- Go 后端保持 Go 原生模块边界,后续可以拆
apps/worker、libs/go/*。 - 前端仍可复用 React 生态和 TSX 风格,contracts 可给前端、SDK、BFF 共用。
shadcn-ui代码是可拷贝进仓库的 TSX 组件,不绑定运行时 UI 框架,适合后续沉淀平台管理、队列监控、模型计费配置等复杂表单。
4.1 前端 UI 约束
- 基础组件统一从
apps/web/src/components/ui/*引入,不直接散落自定义 button/input/table 样式。 - 首期需要引入并固化:
Button、Input、Textarea、Select、Switch、Checkbox、Tabs、Dialog、Sheet、Table、Badge、Alert、Toast/Sonner、Tooltip、Progress、DropdownMenu、Form。 - 图标统一使用
lucide-react,按钮类动作优先用语义图标加 tooltip。 - 控制台不是营销页,布局以高密度管理视图为主:平台列表、模型配置、队列监控、任务详情、客户端运行状态、限流配置、重试策略。
- 主题 token 以 CSS variables 管理,后续可与 EasyAI 全局后台色板对齐。
5. 授权设计
5.1 身份模式与 JWT 用户授权
Gateway 支持三种身份模式:
standalone:Gateway 本地账号注册登录,签发 Gateway JWT。server-main:只接受server-mainJWT / OpenAPIsk-*校验结果,用户与租户从主服务同步。hybrid:同时接受 Gateway 本地账号和server-main身份,按source区分。
接入 server-main 时兼容主服务 JWT:
- secret:读取
CONFIG_JWT_SECRET,默认值与现有jwtConstants.secret保持一致。 - token 有效期:由
server-main签发控制,当前 access token 为600s,refresh token 为7d。 - claim 兼容:
sub:用户 IDusernameroletenantIdgatewayTenantIdtenantKeysourcegatewayUserIduserGroupId/userGroupKey/userGroupKeyssso_id- API Key 场景扩展:
apiKeyId、apiKeySecret、apiKeyName
Gateway 本地登录首期只签发短期 access token,不做 refresh token;接入 server-main 的刷新仍走主服务。
5.1.1 本地账号注册登录
首期普通账号只做用户名/邮箱 + 密码:
| Method | Path | Permission | 说明 |
|---|---|---|---|
POST |
/api/v1/auth/register |
public |
注册 Gateway 本地账号,创建或加入租户,返回 access token |
POST |
/api/v1/auth/login |
public |
本地账号登录,返回 access token |
注册字段:
username/email:至少一个必填。password:至少 8 位,落库为 bcrypt hash。tenantKey/tenantName:可选;未传时创建个人租户。
安全边界:
- 普通注册首期只给
user角色。 - 允许注册时填写已有
tenantKey只是脚手架行为;正式上线前需要租户邀请、域名校验或管理员审批。 server-main模式可关闭本地注册登录,只保留外部 token 入口。
5.2 权限等级
权限枚举与 server-main 保持一致:
| Permission | 用途 |
|---|---|
public |
健康检查、公开模型列表等 |
basic |
普通用户任务创建、任务查询 |
creat |
创作者级能力 |
power |
平台配置、模型配置、计费配置 |
manager |
超级管理与危险操作 |
角色映射与 server-main 一致:user、creator、operator、manager、admin。
5.3 OpenAPI API Key
对于 Authorization: Bearer sk-* 或 x-comfy-api-key:
- Gateway 不直接持有 API Key hash。
- Gateway 调用
server-main的内部校验接口。 server-main返回标准用户 claim。- Gateway 将 claim 放入请求上下文,后续任务、结算事件都带
apiKeyId/apiKeyName。
建议在 server-main 增加内部接口:
POST /internal/platform/auth/verify-api-key
Authorization: Bearer ${SERVER_MAIN_INTERNAL_TOKEN}
Content-Type: application/json
{ "apiKey": "sk-..." }
5.4 内部服务授权
server-main 调 Gateway 内部接口时使用服务令牌:
Authorization: Bearer ${SERVER_MAIN_INTERNAL_TOKEN}
X-EasyAI-Actor: easyai-server-main
内部令牌只用于服务间调用,不替代用户上下文。用户发起的任务仍应透传用户 JWT 或 API Key claim。
6. 接口规范与兼容路由
6.1 兼容优先原则
AI Gateway 的外部接口必须以“兼容现有 EasyAI 路由、请求 DTO、响应结构、错误码、流式语义”为第一目标,不能要求前端或 server-main 因拆分服务而大面积改调用路径。
- 管理面兼容:沿用原
integration-platform、integration/platform-api、integration-platform/snapshots路由。 - 站内生成接口兼容:沿用原
OpenaiController根路径接口,例如/chat/completions、/images/generations。 - OpenAPI 兼容:沿用
/v1/*路由,例如/v1/chat/completions、/v1/images/generations,API Key 语义与原来一致。 - 新增接口隔离:
/api/v1/*只用于 Gateway 自身控制台、调试、内部 SDK 或新能力,不作为迁移期替代原路由。 - 响应包装:对原来返回
ResOp的管理接口继续返回同形态;对 OpenAI-compatible 接口保持原 JSON / stream chunk 结构;对任务类接口保持原taskId、status、url、billings等字段语义。 - 路径匹配顺序:迁移
integration-platform时必须保留静态路径优先于:id/:action/:id的规则,避免models/list/enabled、dynamic-priority/:id、scene-script/:id/partial被动态路由误吞。
6.2 平台管理兼容路由
| Method | Path | Permission | 说明 |
|---|---|---|---|
POST |
/integration-platform |
power |
创建平台 |
POST |
/integration-platform/preset |
power |
创建预置平台 |
GET |
/integration-platform |
power |
平台列表 |
GET |
/integration-platform/list/ops-compact |
power |
运维精简列表 |
GET |
/integration-platform/queue-stats |
power |
队列统计 |
POST |
/integration-platform/clear-queue/:platformId |
power |
清理平台队列 |
POST |
/integration-platform/find-by-ids |
power |
按 ID 批量查平台 |
POST |
/integration-platform/find-by-model |
basic |
按模型查候选平台 |
GET |
/integration-platform/models/list/enabled |
basic |
启用模型列表 |
GET |
/integration-platform/models/list/all |
power |
全量模型列表 |
PATCH |
/integration-platform/dynamic-priority/:id |
power |
动态优先级 |
DELETE |
/integration-platform/dynamic-priority/:id |
power |
删除动态优先级 |
POST |
/integration-platform/disable/:id |
power |
禁用平台 |
POST |
/integration-platform/enable-model/:id |
power |
启用平台模型 |
PATCH |
/integration-platform/scene-script/:id/partial |
power |
局部更新场景脚本 |
PATCH |
/integration-platform/:action/:id |
power |
兼容启停等 action |
PATCH |
/integration-platform/:id |
power |
更新平台 |
DELETE |
/integration-platform/:id |
manager |
删除平台 |
GET |
/integration-platform/models/:type |
basic |
按类型查模型 |
GET |
/integration-platform/models/strict/:type |
basic |
按类型严格查模型 |
POST |
/integration-platform/models/estimatedBilling |
basic |
预估扣费 |
POST |
/integration-platform/models/estimatedBilling/:workflowId |
basic |
工作流预估扣费 |
POST |
/integration-platform/resetException/:id |
power |
重置异常状态 |
POST |
/integration-platform/:id/copy |
power |
复制平台 |
GET |
/integration-platform/:id |
power |
平台详情 |
兼容表中的旧权限用于保持原接口语义;在新 Gateway 第一阶段,所有写入 provider / platform 凭证、启停平台、删除平台、复制平台的接口都要额外要求全局管理员,即 manager/admin 角色。
6.3 平台 API 配置兼容路由
| Method | Path | Permission | 说明 |
|---|---|---|---|
POST |
/integration/platform-api |
power |
创建 API 配置 |
GET |
/integration/platform-api |
power |
API 配置列表 |
GET |
/integration/platform-api/:id |
power |
API 配置详情 |
PATCH |
/integration/platform-api/:id |
power |
更新 API 配置 |
DELETE |
/integration/platform-api/:id |
manager |
删除 API 配置 |
POST |
/integration/platform-api/execute |
power |
调试执行 |
GET |
/integration-platform/snapshots/platform/:platformId |
power |
平台快照 |
6.4 站内生成兼容路由
| Method | Path | 说明 |
|---|---|---|
GET |
/models/list |
站内模型列表 |
POST |
/chat/completions |
站内 Chat |
POST |
/chat/completions/cancel/:requestId |
取消 Chat |
POST |
/chat/structured-output |
结构化输出 |
POST |
/images/generations |
生图 |
POST |
/images/edits |
图片编辑 |
POST |
/video/generations |
生视频 |
POST |
/embeddings |
Embeddings |
GET |
/embeddings/models |
Embedding 模型 |
GET |
/ai/result/:taskId |
任务结果 |
6.5 OpenAPI 兼容路由
| Method | Path | 说明 |
|---|---|---|
POST |
/v1/chat/completions |
OpenAI-compatible Chat |
POST |
/v1/chat/completions/cancel/:requestId |
取消 Chat |
POST |
/v1/images/generations |
生图 |
POST |
/v1/images/edits |
图片编辑 |
POST |
/v1/video/generations |
生视频 |
GET |
/v1/ai/result/:taskId |
任务结果 |
GET |
/v1/ai/cancel/:taskId |
取消任务 |
POST |
/v1/embeddings |
Embeddings |
GET |
/v1/embeddings/models |
Embedding 模型 |
GET |
/v1/models |
OpenAI-compatible 模型列表 |
GET |
/v1/models/list |
EasyAI 模型列表 |
/v1/balance、/v1/files/upload、/v1/creatToken、/v1/removeToken 与用户余额、文件、API Key 生命周期强绑定,默认仍由 server-main 保留;若网关暴露同路径,只能做兼容代理或调用 server-main 内部接口。
6.6 Gateway 新增接口
| Method | Path | Permission | 说明 |
|---|---|---|---|
POST |
/api/v1/auth/register |
public |
Gateway 本地账号注册,invitationCode 可选 |
POST |
/api/v1/auth/login |
public |
Gateway 本地账号登录 |
GET |
/api/v1/me |
basic |
网关当前身份调试 |
GET |
/api/v1/catalog/providers |
power |
基准 provider 列表 |
GET |
/api/v1/catalog/base-models |
power |
基准模型库列表 |
GET |
/api/v1/catalog/base-models/:id |
power |
基准模型详情 |
GET |
/api/v1/tenants |
power |
网关租户列表,支持本地租户和 server-main 同步租户 |
GET |
/api/v1/tenants/:id |
power |
租户详情、来源、策略和同步状态 |
POST |
/api/v1/tenants/sync |
power |
从 server-main 拉取或接收租户同步 |
GET |
/api/v1/tenant-invitations |
power |
本地租户邀请码列表 |
POST |
/api/v1/tenant-invitations |
power |
创建本地注册邀请码 |
GET |
/api/v1/users |
power |
网关用户列表,支持本地用户和 server-main 同步用户 |
GET |
/api/v1/users/:id |
power |
用户详情、来源、角色、用户组和同步状态 |
POST |
/api/v1/users/sync |
power |
从 server-main 拉取或接收用户同步 |
GET |
/api/v1/user-groups |
power |
用户组策略列表 |
GET |
/api/v1/user-groups/:id |
power |
用户组详情、成员与策略 |
POST |
/api/v1/user-groups/:id/sync |
power |
同步用户组策略到 server-main |
GET |
/api/v1/wallet/accounts |
basic |
独立模式本地余额账户 |
GET |
/api/v1/wallet/transactions |
basic |
独立模式本地余额流水 |
GET |
/api/v1/recharge/orders |
basic |
独立模式充值订单 |
POST |
/api/v1/recharge/orders |
basic |
独立模式创建充值订单 |
GET |
/api/v1/api-keys |
basic |
独立模式 API Key 列表 |
POST |
/api/v1/api-keys |
basic |
独立模式创建 API Key |
PATCH |
/api/v1/api-keys/:id/disable |
basic |
禁用本地 API Key |
POST |
/api/v1/pricing/estimate |
basic |
使用 effective pricing resolver 做价格预估 |
GET |
/api/v1/pricing/rules |
power |
定价规则列表 |
GET |
/api/v1/tasks/:taskId |
basic |
Gateway 任务详情 |
GET |
/api/v1/tasks/:taskId/events |
basic |
SSE 任务进度 |
GET |
/api/v1/runtime/clients |
power |
客户端运行状态 |
GET |
/api/v1/runtime/queues |
power |
队列与限流状态 |
GET |
/api/v1/runtime/rate-limit-windows |
power |
TPM/RPM 当前窗口与并发 lease 状态 |
POST |
/api/v1/runtime/tasks/:taskId/replay |
power |
重放任务事件 |
POST |
/api/v1/runtime/tasks/:taskId/callbacks/replay |
power |
重放任务进度回调 outbox |
6.7 内部接口
| Method | Path | 调用方 | 说明 |
|---|---|---|---|
POST |
/internal/v1/settlements |
Gateway worker | 回调主服务结算失败时补偿 |
POST |
${TASK_PROGRESS_CALLBACK_URL} |
Gateway worker | 任务进度回调到 server-main 内部任务进度接口,实际路径由配置决定 |
POST |
/internal/platform/tenants/sync |
server-main | 同步租户增量、禁用状态和租户策略到 Gateway |
POST |
/internal/platform/users/sync |
server-main | 同步用户增量、禁用状态、角色和用户组关系到 Gateway |
POST |
/internal/platform/user-groups/sync |
server-main | 同步用户组、折扣和限流策略到 Gateway |
POST |
/internal/v1/task-callbacks |
server-main | 迁移期主服务回写历史或任务绑定 |
7. 数据模型
数据模型按“基准模型库 -> 平台实例 -> 平台模型覆盖 -> 任务运行态”分层:
- 基准模型库保存 provider、canonical model、能力 schema、默认能力、基准价格、默认限流模板,是所有平台模型的 fallback。
- 创建平台时可以设置
default_discount_factor,平台模型默认按“基准价格 x 折扣系数”计算;没有任何平台侧配置时,能力和价格都 follow 基准模型。 - 平台模型可以覆盖能力、定价和限流。覆盖只作用于该平台模型,不能反向污染基准模型库。
- 租户是隔离域:独立模式下 Gateway 管理
gateway_tenants;接入模式下同步server-main租户/组织,任务、用户、平台可见性、限流与计费策略都需要带租户上下文。 - 用户是身份域:独立部署时 Gateway 自己维护
gateway_users、登录、API Key、余额、充值订单和钱包流水;接入server-main时 Gateway 保存用户同步副本和策略快照,认证、余额、充值、订单仍可由server-main作为事实源。 - 用户组是策略作用域:不同用户组可以绑定充值折扣、模型计费折扣、TPM/RPM/并发、队列优先级、API Key 配额等策略。独立模式由 Gateway 完整执行;
server-main模式下充值和余额执行归主服务,Gateway 执行模型调用侧策略。 - estimated billing、真实任务 billings、控制台价格预览必须走同一个 effective pricing resolver。
7.0 身份运行模式
Gateway 需要支持三种身份运行模式,默认配置为 IDENTITY_MODE=hybrid:
| 模式 | 配置 | 用户事实源 | API Key / 登录 | 用户组策略 | 适用场景 |
|---|---|---|---|---|---|
| Standalone | IDENTITY_MODE=standalone |
Gateway gateway_users |
Gateway 本地用户、密码/SSO、API Key | Gateway 维护并执行充值折扣、调用折扣、限流和并发 | 独立商业化或单独部署 |
| Server-main gateway | IDENTITY_MODE=server-main |
server-main |
JWT 复用主服务 secret,sk-* 调用主服务校验 |
server-main 与 Gateway 同步;主服务执行充值/余额,Gateway 执行调用折扣和限流 |
EasyAI 主站拆分后的 AI 网关 |
| Hybrid | IDENTITY_MODE=hybrid |
Gateway + server-main |
两种方式并存,按 source 区分 |
按 source + group_key 合并,冲突以更高优先级策略为准 |
迁移灰度或私有化集成 |
本地注册规则:
- 普通注册默认开放,
invitationCode可选;不填邀请码时创建或复用tenantKey对应的 Gateway 本地租户,未填tenantKey时生成个人租户。 - 填写邀请码时必须命中
gateway_tenant_invitations的有效记录,注册用户加入邀请码指定租户,并可绑定邀请码指定用户组。 - 平台凭证、provider 凭证和全局模型配置暂时只允许全局管理员维护;租户侧先只做模型使用、用量查看和策略展示。
身份解析流程:
- 从 JWT / API Key / 内部调用 token 解析出外部身份。
- 根据
source + external_user_id在gateway_users找到或创建同步副本。 - 按用户、租户、API Key、组织命中
gateway_user_group_memberships。 - 根据用户组优先级和策略合并规则得到 effective policy。
- 创建任务时把
gateway_user_id、user_source、user_group_id、user_group_key、user_group_policy_snapshot写入gateway_tasks,后续重试和结算不受同步变更影响。
7.0.1 多租户模型
多租户支持不能只停留在 claim 的 tenantId 字符串,Gateway 需要有自己的租户表和执行上下文:
gateway_tenants保存租户事实或同步副本,使用source + external_tenant_id幂等同步。gateway_users.gateway_tenant_id关联 Gateway 租户,tenant_id/tenant_key保留与server-main兼容的外部标识。gateway_tasks固化gateway_tenant_id、tenant_id、tenant_key,确保任务恢复、重试、结算和审计不受后续租户变更影响。integration_platforms.visibility_scope区分global、tenant、private,租户专属平台只能被对应租户路由到。- 限流、定价、用户组和 quota 都可以以租户作为 scope;同一请求会同时命中 global、tenant、user_group、user、api_key 等策略。
- 控制台数据查询默认按当前用户租户过滤;
power/manager可跨租户查看和管理。
7.1 integration_platforms
保存平台实例与凭证:
provider:平台类型,如openai、runninghub、jimeng。name:运营可识别名称。base_urlauth_typecredentials:加密后的凭证 JSON,后续应接入 KMS。config:限流、超时、重试、平台私有配置。visibility_scope:global、tenant、private,用于多租户平台可见性。tenant_id/tenant_key:租户专属平台的归属。default_discount_factor:平台默认折扣系数,基于基准模型价计算平台有效价。prioritystatus
7.2 platform_models
保存平台模型配置:
platform_idbase_model_id:关联基准模型库,未配置能力/价格时 follow 基准模型。model_namemodel_type:chat、image、video、audio等。capabilities:平台模型有效能力,来自基准模型能力与平台覆盖合并。pricing_mode:inherit、inherit_discount、custom。discount_factor:模型级折扣,未设置时使用平台默认折扣。billing_config:平台模型有效计费配置,来自基准价、折扣和自定义覆盖。enabled
7.3 gateway_tasks
保存任务请求与状态:
kind:chat.completions、images.generations、videos.generationsuser_id:请求 claim 中的用户 ID,独立模式为 Gateway 用户 ID,接入模式为server-main用户 ID。gateway_user_id:Gateway 本地用户表 ID,用于关联同步副本和审计。user_source:gateway、server-main、sync。tenant_iduser_group_id/user_group_keyuser_group_policy_snapshot:任务创建时解析出的用户组策略快照,用于审计和重试稳定性。modelrequeststatus:queued、running、succeeded、failed、cancelledqueue_key:限流队列 key,例如${platformKey}-${model}或${provider}-${methodName}priorityidempotency_keyremote_task_idremote_task_payloadrun_mode:production、simulationsimulation_profilesimulation_seedlocked_bylocked_atheartbeat_atnext_run_atresultbillingserror
7.4 gateway_task_attempts
保存每一次客户端尝试,支持“上一个客户端失败,下一个客户端重试”的完整审计:
task_idattempt_noplatform_idclient_idmodel_idstatus:submitted、polling、succeeded、failed、skippedretryableerror_codeerror_messageremote_task_idsimulatedrequest_snapshotresponse_snapshotstarted_atfinished_at
7.5 gateway_task_events
保存任务执行过程中的事件,用于控制台 SSE / WebSocket 重放、进度回调 outbox 投递与服务重启后的进度恢复:
task_idseq:单任务内递增序号event_type:queue_status、node_status、progress、partial_result、completed、failedstatusprogressmessagepayloadcreated_at
客户端断线重连时可通过 Last-Event-ID 或 afterSeq 回放事件。
7.5.1 gateway_task_callback_outbox
保存投递给 server-main 内部任务进度接口的回调。Gateway 产生事件后先写 gateway_task_events,再写 callback outbox,由独立 worker 按配置的 TASK_PROGRESS_CALLBACK_URL 投递,失败后可重试。
task_idevent_id/seq:与gateway_task_events对应,用于幂等与顺序控制。callback_url:当前使用的回调地址快照,避免配置变化影响已排队事件。payload:回调给 server-main 的标准事件载荷。status:pending、delivering、delivered、failed、skippedattemptsnext_attempt_atlast_errordelivered_at
7.6 runtime_client_states
保存平台客户端的运行时状态:
client_idplatform_idprovidermethod_namequeue_keyrunning_countwaiting_countlimiter_ratiocooldown_untillast_assigned_atlast_errorupdated_at
该表用于恢复与观测;实时并发计数仍可通过事务锁、SKIP LOCKED、Redis token bucket 或内存计数加 PG 校验组合实现,但 PG 是最终事实源。
7.7 gateway_upload_assets
保存网关任务引用的上传资产:
task_idsource:server-main-open-upload、remote-url、multipart、simulationserver_main_file_id:主服务返回的文件 ID。object_keyurlcontent_typesizechecksummetadata
7.8 settlement_outbox
保存结算事件 outbox:
task_idevent_typepayloadstatusattemptsnext_attempt_at
结算事件必须按 eventId / taskId 幂等,不能因为 Gateway 重启、HTTP 回调失败或 MQ 抖动重复扣费。
7.9 表结构草案
以下为首期 PostgreSQL 表结构草案。字段类型以后续 migration 为准,但语义和索引边界应保持稳定。
7.9.1 model_catalog_providers
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | 基准 provider ID |
provider_key |
text |
unique, not null | 稳定 provider key,如 openai、runninghub、jimeng |
display_name |
text |
not null | 展示名称 |
provider_type |
text |
not null | openai_compatible、workflow_app、video_vendor、audio_vendor |
capability_schema |
jsonb |
not null, default {} |
provider 支持的能力字段定义 |
default_rate_limit_policy |
jsonb |
not null, default {} |
provider 默认 TPM/RPM/并发模板 |
status |
text |
not null | active、deprecated、hidden |
metadata |
jsonb |
not null, default {} |
文档链接、logo、排序等 |
created_at |
timestamptz |
not null | 创建时间 |
updated_at |
timestamptz |
not null | 更新时间 |
索引:
uniq_model_catalog_provider_key(provider_key)idx_model_catalog_provider_status(status)
7.9.2 base_model_catalog
保存全量基准模型。这个表不代表某个平台已经开通该模型,而是标准模型与价格来源。
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | 基准模型 ID |
provider_id |
uuid |
FK | 所属基准 provider |
provider_key |
text |
not null | 冗余 provider key,方便查询 |
canonical_model_key |
text |
unique, not null | Gateway 内部标准模型 key |
provider_model_name |
text |
not null | 供应商原始模型名 |
model_type |
text |
not null | chat、image、video、audio、embedding、music、digital_human、model_3d |
display_name |
text |
not null | 展示名称 |
capabilities |
jsonb |
not null, default {} |
上下文、多模态、参考图/视频/音频、尺寸、时长、质量等基准能力 |
base_billing_config |
jsonb |
not null, default {} |
基准计费配置 |
default_rate_limit_policy |
jsonb |
not null, default {} |
默认 TPM/RPM/并发限制模板 |
pricing_version |
int |
not null, default 1 |
基准价格版本 |
status |
text |
not null | active、deprecated、hidden |
metadata |
jsonb |
not null, default {} |
供应商文档、排序、标签 |
created_at |
timestamptz |
not null | 创建时间 |
updated_at |
timestamptz |
not null | 更新时间 |
索引:
uniq_base_model_catalog_key(canonical_model_key)idx_base_model_catalog_provider(provider_key, model_type, status)idx_base_model_catalog_capabilities使用GIN(capabilities)
7.9.3 model_pricing_rules
保存基准模型和平台模型的可版本化价格规则。平台模型没有自定义规则时,使用 base_model_catalog.base_billing_config 并应用折扣。
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | 价格规则 ID |
scope_type |
text |
not null | base_model、platform、platform_model |
scope_id |
uuid |
nullable | 对应基准模型、平台或平台模型 ID |
resource_type |
text |
not null | text_input、text_output、image、video、audio、music、digital_human、model |
unit |
text |
not null | 1k_tokens、image、5s、second、character_1k、item |
base_price |
numeric |
not null | 基准单价,单位使用 EasyAI resource / credit |
currency |
text |
not null, default resource |
resource、credit、cny、usd |
base_weight |
jsonb |
not null, default {} |
固定权重,如默认倍率 |
dynamic_weight |
jsonb |
not null, default {} |
分辨率、质量、时长、有无音频等动态权重 |
effective_from |
timestamptz |
nullable | 生效开始 |
effective_to |
timestamptz |
nullable | 生效结束 |
created_at |
timestamptz |
not null | 创建时间 |
updated_at |
timestamptz |
not null | 更新时间 |
索引:
idx_model_pricing_scope(scope_type, scope_id, resource_type)idx_model_pricing_effective(effective_from, effective_to)
7.9.4 gateway_tenants
保存 Gateway 可识别的租户。独立模式下它是租户事实表;接入 server-main 时它是租户/组织同步副本。
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | Gateway 租户 ID |
tenant_key |
text |
unique, not null | Gateway 内稳定租户 key |
source |
text |
not null | gateway、server-main、sync |
external_tenant_id |
text |
nullable | 外部租户/组织 ID |
name |
text |
not null | 租户名称 |
description |
text |
nullable | 说明 |
default_user_group_id |
uuid |
FK nullable | 默认用户组 |
plan_key |
text |
nullable | 套餐或商业计划 key |
billing_profile |
jsonb |
not null, default {} |
独立模式账务资料或接入模式同步摘要 |
rate_limit_policy |
jsonb |
not null, default {} |
租户级 TPM/RPM/并发/队列策略 |
auth_policy |
jsonb |
not null, default {} |
注册、邀请、SSO、域名限制等认证策略 |
metadata |
jsonb |
not null, default {} |
同步、运营、展示扩展信息 |
status |
text |
not null | active、disabled、locked、deleted |
synced_at |
timestamptz |
nullable | 最近同步时间 |
source_updated_at |
timestamptz |
nullable | 外部源更新时间 |
created_at |
timestamptz |
not null | 创建时间 |
updated_at |
timestamptz |
not null | 更新时间 |
deleted_at |
timestamptz |
nullable | 软删除时间 |
索引:
uniq_gateway_tenants_tenant_key(tenant_key)uniq_gateway_tenants_source_external(source, external_tenant_id)idx_gateway_tenants_status(status, created_at)
7.9.5 gateway_users
保存 Gateway 可识别的用户。独立模式下它是用户事实表,并与本地 API Key、钱包、充值订单形成闭环;接入 server-main 时它是用户同步副本和策略执行缓存,不迁入主服务余额、订单、充值流水。
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | Gateway 用户 ID |
user_key |
text |
unique, not null | Gateway 内稳定用户 key |
source |
text |
not null | gateway、server-main、sync |
external_user_id |
text |
nullable | 外部用户 ID,server-main 模式为主服务用户 ID |
username |
text |
not null | 登录名或同步用户名 |
display_name |
text |
nullable | 展示名 |
email |
text |
nullable | 邮箱 |
phone |
text |
nullable | 手机号 |
avatar_url |
text |
nullable | 头像 |
password_hash |
text |
nullable | 仅独立模式使用,接入主服务时为空 |
gateway_tenant_id |
uuid |
FK nullable | Gateway 租户 ID |
tenant_id |
text |
nullable | 外部租户 ID 或兼容 claim |
tenant_key |
text |
nullable | Gateway 租户 key |
default_user_group_id |
uuid |
FK nullable | 默认用户组 |
roles |
jsonb |
not null, default [] |
user、creator、operator、admin 等角色 |
auth_profile |
jsonb |
not null, default {} |
SSO、MFA、API Key 策略等认证资料 |
metadata |
jsonb |
not null, default {} |
同步、运营、展示扩展信息 |
status |
text |
not null | active、disabled、locked、deleted |
last_login_at |
timestamptz |
nullable | 最近登录时间 |
synced_at |
timestamptz |
nullable | 最近同步时间 |
source_updated_at |
timestamptz |
nullable | 外部源更新时间 |
created_at |
timestamptz |
not null | 创建时间 |
updated_at |
timestamptz |
not null | 更新时间 |
deleted_at |
timestamptz |
nullable | 软删除时间 |
索引:
uniq_gateway_users_user_key(user_key)uniq_gateway_users_source_external(source, external_user_id)idx_gateway_users_status(status, created_at)idx_gateway_users_tenant(tenant_id, tenant_key, status)
同步规则:
source='gateway':用户由 Gateway 创建和禁用,可独立使用;充值、余额、API Key 由 Gateway 本地模块承接。source='server-main':用户由主服务同步,Gateway 只保存执行模型调用所需字段;禁用、角色、用户组变更以主服务事件或同步任务为准。- 同一个外部用户以
source + external_user_id幂等 upsert,避免用户名变更造成重复用户。 - 用户被禁用后,新任务拒绝进入队列;已运行任务按任务策略快照继续或按管理员策略取消。
7.9.6 gateway_user_groups
保存用户组及其策略。用户组可以由 Gateway 管理,也可以从 server-main 同步;独立模式下 Gateway 执行全部策略,接入模式下充值和余额仍以 server-main 为事实源。
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | 用户组 ID |
group_key |
text |
unique, not null | 稳定用户组 key,如 free、pro、enterprise |
name |
text |
not null | 展示名称 |
description |
text |
nullable | 说明 |
source |
text |
not null | gateway、server-main、sync |
priority |
int |
not null, default 100 |
多组命中时优先级,越小越优先 |
recharge_discount_policy |
jsonb |
not null, default {} |
充值折扣/赠送资源策略;独立模式由 Gateway 执行,接入模式由 server-main 执行 |
billing_discount_policy |
jsonb |
not null, default {} |
模型调用计费折扣策略,用于 estimated billing 和 billings |
rate_limit_policy |
jsonb |
not null, default {} |
用户组 TPM/RPM/并发/队列策略 |
quota_policy |
jsonb |
not null, default {} |
日/月额度、API Key 额度、最大任务数 |
metadata |
jsonb |
not null, default {} |
展示、运营、同步元数据 |
status |
text |
not null | active、disabled |
created_at |
timestamptz |
not null | 创建时间 |
updated_at |
timestamptz |
not null | 更新时间 |
示例:
{
"recharge_discount_policy": {
"type": "tiered_bonus",
"tiers": [
{ "minAmount": 100, "bonusRatio": 0.05 },
{ "minAmount": 1000, "bonusRatio": 0.12 }
]
},
"billing_discount_policy": {
"defaultDiscountFactor": 0.9,
"modelTypeDiscounts": { "chat": 0.85, "image": 0.95 }
},
"rate_limit_policy": {
"rules": [
{ "metric": "rpm", "limit": 1200, "windowSeconds": 60 },
{ "metric": "tpm_total", "limit": 300000, "windowSeconds": 60 },
{ "metric": "concurrent", "limit": 50, "leaseTtlSeconds": 900 }
]
}
}
7.9.7 gateway_user_group_memberships
保存用户组成员关系。一个用户、租户或 API Key 可命中多个组,运行时按组优先级和策略合并规则解析有效策略。
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | 成员关系 ID |
group_id |
uuid |
FK | 用户组 ID |
principal_type |
text |
not null | user、tenant、api_key、organization |
principal_id |
text |
not null | 对应 Gateway 用户 ID、server-main 外部用户 ID、租户 ID、API Key ID 或组织 ID |
source |
text |
not null | gateway、server-main、sync |
priority |
int |
not null, default 100 |
关系级优先级 |
effective_from |
timestamptz |
nullable | 生效开始 |
effective_to |
timestamptz |
nullable | 生效结束 |
status |
text |
not null | active、disabled |
metadata |
jsonb |
not null, default {} |
同步和运营元数据 |
created_at |
timestamptz |
not null | 创建时间 |
updated_at |
timestamptz |
not null | 更新时间 |
索引:
uniq_user_group_membership(group_id, principal_type, principal_id)idx_user_group_membership_principal(principal_type, principal_id, status)idx_user_group_membership_effective(effective_from, effective_to)
成员解析规则:
- 独立模式优先使用
principal_type='user' + gateway_users.id。 - 接入
server-main时可使用principal_type='user' + external_user_id,并通过gateway_users.source='server-main'反查同步副本。 - 多个组同时命中时,先按
gateway_user_group_memberships.priority,再按gateway_user_groups.priority合并策略。
7.9.8 gateway_tenant_invitations
本地注册邀请码。邀请码不是普通注册的必填项,但填写后必须可校验,并决定用户加入哪个租户和用户组。
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | 邀请 ID |
tenant_id |
uuid |
FK | 加入的 Gateway 租户 |
invite_code |
text |
unique, not null | 注册邀请码 |
role |
text |
not null, default user |
注册后角色,默认普通用户 |
user_group_id |
uuid |
FK nullable | 注册后默认用户组 |
max_uses |
int |
nullable | 最大使用次数 |
used_count |
int |
not null | 已使用次数 |
expires_at |
timestamptz |
nullable | 过期时间 |
status |
text |
not null | active、disabled |
metadata |
jsonb |
not null | 渠道、备注、审批信息 |
7.9.9 gateway_api_keys
独立模式本地 API Key 表。server-main 接入模式下,sk-* 仍调用主服务校验;Gateway 只记录执行快照和策略。
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | API Key ID |
gateway_tenant_id / gateway_user_id |
uuid |
FK | 所属租户与用户 |
key_prefix |
text |
not null | 展示和快速定位前缀 |
key_hash |
text |
unique, not null | secret hash,不保存明文 |
name |
text |
not null | 用户命名 |
scopes |
jsonb |
not null | 可调用能力范围 |
user_group_id |
uuid |
nullable | API Key 级策略组 |
rate_limit_policy / quota_policy |
jsonb |
not null | Key 级限流和额度覆盖 |
status |
text |
not null | active、disabled、revoked |
expires_at / last_used_at |
timestamptz |
nullable | 过期与最近使用 |
7.9.10 gateway_wallet_accounts
独立模式本地钱包账户。Phase 1 先支持资源余额闭环,后续可扩展人民币、美元或企业额度。
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | 钱包账户 ID |
gateway_tenant_id / gateway_user_id |
uuid |
FK | 所属租户与用户 |
currency |
text |
not null | resource、credit、cny、usd |
balance |
numeric |
not null | 可用余额 |
frozen_balance |
numeric |
not null | 冻结余额 |
total_recharged |
numeric |
not null | 累计充值 |
total_spent |
numeric |
not null | 累计消费 |
status |
text |
not null | active、frozen、disabled |
7.9.11 gateway_wallet_transactions
独立模式钱包流水。任务扣费、充值到账、退款、人工调整都必须写流水,并使用 idempotency_key 防重。
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | 流水 ID |
account_id |
uuid |
FK | 钱包账户 |
direction |
text |
not null | credit、debit、freeze、unfreeze |
transaction_type |
text |
not null | recharge、billing、refund、adjustment |
amount |
numeric |
not null | 金额 |
balance_before / balance_after |
numeric |
not null | 变动前后余额 |
idempotency_key |
text |
nullable | 幂等 key |
reference_type / reference_id |
text |
nullable | 关联任务、订单或人工工单 |
7.9.12 gateway_recharge_orders
独立模式充值订单。第一阶段先闭合订单、折扣、入账和流水;支付渠道可以从手工确认开始,再接真实支付。
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | 充值订单 ID |
gateway_tenant_id / gateway_user_id |
uuid |
FK | 所属租户与用户 |
amount |
numeric |
not null | 入账基础额度 |
bonus_amount |
numeric |
not null | 用户组充值折扣或赠送额度 |
payable_amount |
numeric |
not null | 实际应付 |
currency |
text |
not null | 币种或资源类型 |
channel |
text |
not null | manual、wechat、alipay、stripe |
status |
text |
not null | created、pending、paid、closed、failed |
external_order_id |
text |
nullable | 第三方支付订单 |
idempotency_key |
text |
nullable | 创建订单幂等 key |
paid_at |
timestamptz |
nullable | 支付完成时间 |
7.9.13 integration_platforms
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | 平台实例 ID |
provider |
text |
not null | 平台类型,如 openai、runninghub、jimeng |
platform_key |
text |
unique, not null | 稳定平台 key,用于队列和客户端注册 |
name |
text |
not null | 展示名称 |
base_url |
text |
nullable | 平台 API 地址 |
auth_type |
text |
not null | bearer、api_key、basic、custom |
credentials |
jsonb |
not null, default {} |
加密后的凭证 JSON |
config |
jsonb |
not null, default {} |
超时、重试、限流、平台私有配置 |
visibility_scope |
text |
not null, default global |
global、tenant、private |
tenant_id |
text |
nullable | 租户外部 ID 或兼容 claim |
tenant_key |
text |
nullable | Gateway 租户 key |
default_pricing_mode |
text |
not null, default inherit_discount |
平台默认价格模式:inherit、inherit_discount、custom |
default_discount_factor |
numeric |
not null, default 1 |
平台默认折扣系数,创建平台时可统一基于基准模型打折 |
retry_policy |
jsonb |
not null, default {} |
平台级重试策略 |
rate_limit_policy |
jsonb |
not null, default {} |
平台级限流策略 |
priority |
int |
not null, default 100 |
静态优先级,越小越优先 |
dynamic_priority |
int |
nullable | 动态优先级覆盖 |
status |
text |
not null | enabled、disabled、exception |
disabled_reason |
text |
nullable | 禁用原因 |
cooldown_until |
timestamptz |
nullable | 熔断/冷却到期时间 |
created_at |
timestamptz |
not null | 创建时间 |
updated_at |
timestamptz |
not null | 更新时间 |
deleted_at |
timestamptz |
nullable | 软删除 |
索引:
idx_integration_platforms_provider_status(provider, status)idx_integration_platforms_status_priority(status, priority, dynamic_priority)idx_integration_platforms_cooldown(cooldown_until)idx_integration_platforms_tenant_scope(visibility_scope, tenant_id, tenant_key, status)
7.9.14 platform_models
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | 模型配置 ID |
platform_id |
uuid |
FK | 所属平台 |
base_model_id |
uuid |
FK nullable | 关联基准模型;为空时表示纯自定义模型 |
model_name |
text |
not null | 请求使用的模型名 |
model_alias |
text |
nullable | 对外展示/兼容别名 |
model_type |
text |
not null | chat、image、video、audio、embedding |
display_name |
text |
not null | 展示名称 |
capability_override |
jsonb |
not null, default {} |
相对基准模型的能力覆盖 |
capabilities |
jsonb |
not null, default {} |
解析后的有效能力,用于路由和前端展示 |
pricing_mode |
text |
not null, default inherit_discount |
inherit、inherit_discount、custom |
discount_factor |
numeric |
nullable | 模型级折扣,空值时继承平台默认折扣 |
billing_config_override |
jsonb |
not null, default {} |
自定义计费覆盖 |
billing_config |
jsonb |
not null, default {} |
解析后的有效计费配置,用于 estimated billing 和真实 billings |
permission_config |
jsonb |
not null, default {} |
平台维度权限过滤配置 |
retry_policy |
jsonb |
not null, default {} |
模型级重试策略 |
rate_limit_policy |
jsonb |
not null, default {} |
模型级限流策略 |
enabled |
boolean |
not null, default true |
是否启用 |
created_at |
timestamptz |
not null | 创建时间 |
updated_at |
timestamptz |
not null | 更新时间 |
索引与约束:
uniq_platform_model_type(platform_id, model_name, model_type)idx_platform_models_base(base_model_id)idx_platform_models_lookup(model_type, model_name, enabled)idx_platform_models_alias(model_alias)idx_platform_models_capabilities使用GIN(capabilities)
7.9.15 gateway_tasks
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | Gateway 任务 ID |
external_task_id |
text |
nullable | 与 server-main / 前端兼容的外部任务 ID |
kind |
text |
not null | chat.completions、images.generations 等 |
run_mode |
text |
not null | production、simulation |
user_id |
text |
not null | 请求 claim 中的用户 ID |
gateway_user_id |
uuid |
nullable | Gateway 用户表 ID |
user_source |
text |
not null, default gateway |
gateway、server-main、sync |
gateway_tenant_id |
uuid |
nullable | Gateway 租户 ID |
tenant_id |
text |
nullable | 外部租户 ID 或兼容 claim |
tenant_key |
text |
nullable | Gateway 租户 key |
api_key_id |
text |
nullable | OpenAPI Key ID |
user_group_id |
uuid |
nullable | 命中的用户组 ID |
user_group_key |
text |
nullable | 命中的用户组 key |
user_group_policy_snapshot |
jsonb |
not null, default {} |
用户组策略快照 |
model |
text |
not null | 请求模型 |
model_type |
text |
nullable | 模型类型 |
request |
jsonb |
not null | 原始请求快照 |
normalized_request |
jsonb |
not null, default {} |
参数处理后的请求 |
status |
text |
not null | queued、leased、running、polling、succeeded、failed_retryable、failed_final、cancelled |
queue_key |
text |
not null | 队列与限流 key |
priority |
int |
not null, default 100 |
任务优先级 |
idempotency_key |
text |
nullable | 幂等 key |
remote_task_id |
text |
nullable | 供应商任务 ID |
remote_task_payload |
jsonb |
nullable | 供应商任务载荷 |
simulation_profile |
jsonb |
nullable | 模拟配置 |
simulation_seed |
text |
nullable | 模拟种子 |
locked_by |
text |
nullable | worker ID |
locked_at |
timestamptz |
nullable | 租约时间 |
heartbeat_at |
timestamptz |
nullable | worker 心跳 |
next_run_at |
timestamptz |
not null | 下次可运行时间 |
attempt_count |
int |
not null, default 0 |
已尝试次数 |
max_attempts |
int |
not null, default 1 |
最大尝试次数 |
result |
jsonb |
nullable | 归一化结果 |
billings |
jsonb |
nullable | 计费结果 |
error_code |
text |
nullable | 错误码 |
error_message |
text |
nullable | 错误信息 |
created_at |
timestamptz |
not null | 创建时间 |
updated_at |
timestamptz |
not null | 更新时间 |
finished_at |
timestamptz |
nullable | 完成时间 |
索引:
idx_gateway_tasks_queue(status, next_run_at, priority, created_at)idx_gateway_tasks_lease(status, heartbeat_at)idx_gateway_tasks_user_created(user_id, created_at desc)idx_gateway_tasks_external(external_task_id)uniq_gateway_tasks_idempotency(user_id, idempotency_key)whereidempotency_key is not null
7.9.16 gateway_task_attempts
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | attempt ID |
task_id |
uuid |
FK | 所属任务 |
attempt_no |
int |
not null | 第几次尝试 |
platform_id |
uuid |
nullable | 尝试平台 |
platform_model_id |
uuid |
nullable | 尝试模型配置 |
client_id |
text |
nullable | 客户端实例 ID |
queue_key |
text |
not null | 限流 key |
status |
text |
not null | submitted、polling、succeeded、failed、skipped |
retryable |
boolean |
not null, default false |
是否可重试 |
simulated |
boolean |
not null, default false |
是否模拟 |
remote_task_id |
text |
nullable | 供应商任务 ID |
request_snapshot |
jsonb |
not null, default {} |
本次请求快照 |
response_snapshot |
jsonb |
nullable | 本次响应快照 |
error_code |
text |
nullable | 错误码 |
error_message |
text |
nullable | 错误信息 |
started_at |
timestamptz |
not null | 开始时间 |
finished_at |
timestamptz |
nullable | 结束时间 |
索引:
uniq_gateway_attempt_no(task_id, attempt_no)idx_gateway_attempts_task(task_id)idx_gateway_attempts_client(client_id, started_at desc)
7.9.17 gateway_task_events
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | 事件 ID |
task_id |
uuid |
FK | 所属任务 |
seq |
bigint |
not null | 单任务递增序号 |
event_type |
text |
not null | queue_status、progress、partial_result、completed、failed |
status |
text |
nullable | 任务状态 |
phase |
text |
nullable | routing、queued、submit、polling、upload |
progress |
numeric |
nullable | 0 到 1 |
message |
text |
nullable | 展示消息 |
payload |
jsonb |
not null, default {} |
事件载荷 |
simulated |
boolean |
not null, default false |
是否模拟 |
created_at |
timestamptz |
not null | 创建时间 |
索引:
uniq_gateway_events_seq(task_id, seq)idx_gateway_events_task_created(task_id, created_at)
7.9.18 gateway_task_callback_outbox
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | 回调 outbox ID |
task_id |
uuid |
FK | 任务 ID |
event_id |
uuid |
FK nullable | 对应 gateway_task_events.id |
seq |
bigint |
not null | 单任务事件序号 |
callback_url |
text |
not null | 投递地址快照,来自 TASK_PROGRESS_CALLBACK_URL |
payload |
jsonb |
not null | 回调载荷 |
status |
text |
not null | pending、delivering、delivered、failed、skipped |
attempts |
int |
not null, default 0 |
已投递次数 |
next_attempt_at |
timestamptz |
not null | 下次投递时间 |
last_error |
text |
nullable | 最近错误 |
delivered_at |
timestamptz |
nullable | 投递成功时间 |
created_at |
timestamptz |
not null | 创建时间 |
updated_at |
timestamptz |
not null | 更新时间 |
索引:
uniq_task_callback_seq(task_id, seq, callback_url)idx_task_callback_outbox_pending(status, next_attempt_at)idx_task_callback_outbox_task(task_id, seq)
7.9.19 runtime_client_states
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
client_id |
text |
PK | 客户端实例 ID |
platform_id |
uuid |
nullable | 所属平台 |
provider |
text |
not null | provider |
method_name |
text |
not null | 方法名 |
queue_key |
text |
not null | 队列 key |
running_count |
int |
not null, default 0 |
当前运行数 |
waiting_count |
int |
not null, default 0 |
当前等待数 |
limiter_ratio |
numeric |
not null, default 0 |
负载比 |
cooldown_until |
timestamptz |
nullable | 冷却截止 |
last_assigned_at |
timestamptz |
nullable | 上次分配时间 |
last_error |
text |
nullable | 最近错误 |
updated_at |
timestamptz |
not null | 更新时间 |
索引:
idx_runtime_client_queue(queue_key, cooldown_until)idx_runtime_client_platform(platform_id)
7.9.20 gateway_upload_assets
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | 资产 ID |
task_id |
uuid |
FK nullable | 关联任务 |
source |
text |
not null | 固定为 server-main-open-upload 或 remote-url / simulation |
server_main_file_id |
text |
nullable | 主服务返回的 file id |
url |
text |
not null | 主服务返回 URL |
object_key |
text |
nullable | 主服务返回对象 key |
content_type |
text |
nullable | MIME |
size |
bigint |
nullable | 文件大小 |
checksum |
text |
nullable | 校验和 |
metadata |
jsonb |
not null, default {} |
上传响应与业务元数据 |
created_at |
timestamptz |
not null | 创建时间 |
索引:
idx_gateway_upload_task(task_id)idx_gateway_upload_file(server_main_file_id)
7.9.21 gateway_retry_policies
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | 策略 ID |
scope_type |
text |
not null | global、provider、platform、model、method |
scope_key |
text |
not null | 作用域 key |
enabled |
boolean |
not null | 是否启用重试 |
policy |
jsonb |
not null | 重试策略 JSON |
created_at |
timestamptz |
not null | 创建时间 |
updated_at |
timestamptz |
not null | 更新时间 |
约束:
uniq_retry_policy_scope(scope_type, scope_key)
7.9.22 gateway_rate_limit_policies
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | 策略 ID |
scope_type |
text |
not null | global、provider、platform、client、base_model、platform_model、method、tenant、user_group、user、api_key |
scope_key |
text |
not null | 作用域 key |
policy |
jsonb |
not null | TPM、RPM、并发、队列长度、冷却策略 |
created_at |
timestamptz |
not null | 创建时间 |
updated_at |
timestamptz |
not null | 更新时间 |
约束:
uniq_rate_limit_policy_scope(scope_type, scope_key)
示例:
{
"rules": [
{ "metric": "tpm_total", "limit": 90000, "windowSeconds": 60, "consume": "reserve_then_reconcile" },
{ "metric": "rpm", "limit": 600, "windowSeconds": 60, "consume": "fixed_window" },
{ "metric": "concurrent", "limit": 20, "leaseTtlSeconds": 900 },
{ "metric": "queue_size", "limit": 1000 }
]
}
7.9.23 gateway_rate_limit_counters
保存一分钟窗口内的 TPM/RPM 使用量。Redis 可做加速,但 PG counter 是重启恢复和审计的事实源。
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
scope_type |
text |
not null | 限流作用域 |
scope_key |
text |
not null | 作用域 key |
metric |
text |
not null | tpm_total、tpm_input、tpm_output、rpm |
window_start |
timestamptz |
not null | 分钟窗口开始时间 |
limit_value |
numeric |
not null | 当前窗口限制 |
used_value |
numeric |
not null, default 0 |
已确认消耗 |
reserved_value |
numeric |
not null, default 0 |
已预占但待回填消耗 |
reset_at |
timestamptz |
not null | 窗口结束时间 |
updated_at |
timestamptz |
not null | 更新时间 |
约束:
pk_rate_limit_counter(scope_type, scope_key, metric, window_start)
7.9.24 gateway_concurrency_leases
保存并发请求租约,服务异常重启后可释放过期并发。
| 字段 | 类型 | 约束 | 说明 |
|---|---|---|---|
id |
uuid |
PK | 并发租约 ID |
task_id |
uuid |
FK | 任务 ID |
attempt_id |
uuid |
FK nullable | attempt ID |
scope_type |
text |
not null | 限流作用域 |
scope_key |
text |
not null | 作用域 key |
lease_value |
numeric |
not null, default 1 |
占用并发数 |
acquired_at |
timestamptz |
not null | 获取时间 |
expires_at |
timestamptz |
not null | 过期时间 |
released_at |
timestamptz |
nullable | 释放时间 |
索引:
idx_concurrency_leases_active(scope_type, scope_key, released_at, expires_at)idx_concurrency_leases_task(task_id)
8. 模型路由、限流与失败切换
迁移时不要做简单的 first-match。路由器需要复用现有语义:
- 先按模型名、模型类型、平台启用状态筛候选。
- 保留平台维度权限过滤,同名模型不能跨平台混用。
- 按平台优先级排序。
- 结合并发是否已满、limiter ratio、等待队列、lastAssignedAt 做负载均衡。
- 对 app-style provider,例如 RunningHub 模板类能力,使用
provider + methodName的队列 key。
一期脚手架只预留接口,迁移实现时把 IntegrationModelFactory.assignClientsByModelName 与 assignClientsByProviderMethod 的行为写成 Go 版单元测试再实现。
8.1 限流维度
限流策略需要支持以下维度组合:
- 全局:
global - Provider 级:
provider_key - 平台级:
platform_id - 客户端级:
client_id - 基准模型级:
base_model_id/canonical_model_key - 平台模型级:
platform_model_id/model_name/model_type - 方法级:
method_name,例如runAIApp$ - 租户/用户级:
gateway_tenant_id/tenant_id/tenant_key/user_id - 用户组级:
user_group_id/user_group_key - OpenAPI Key 级:
api_key_id
同一请求可能命中多个策略,默认以最严格策略为准;如后续需要不同优先级,可在 policy 中增加 mergeStrategy。
8.1.1 一分钟窗口核心指标
限流必须把 TPM、RPM、并发作为一等指标,而不是只看 running count:
| 指标 | 含义 | 适用能力 | 执行方式 |
|---|---|---|---|
tpm_total |
一分钟内输入 + 输出 token 总数 | Chat、Responses、Embedding、可 token 化文本任务 | 提交前按 prompt / max_tokens 预估并预占,完成后用实际 token 回填 |
tpm_input |
一分钟内输入 token 数 | Chat、Responses、Embedding | 提交前按 tokenizer 或近似估算预占 |
tpm_output |
一分钟内输出 token 数 | Chat、Responses | stream 场景可按 chunk 增量累计,完成后 reconcile |
rpm |
一分钟内请求数 | 所有同步/异步模型提交 | 任务 attempt 提交前扣减;失败切换的新 attempt 也算一次 provider 请求 |
concurrent |
同时运行或轮询中的请求数 | 所有 provider client | 通过 gateway_concurrency_leases 获取租约,完成、失败、取消或过期释放 |
queue_size |
等待队列长度 | 所有队列 | 超过后按策略返回兼容错误或降级到 simulation / fallback |
TPM 的预占策略:
- 文本任务:优先使用模型 tokenizer;没有 tokenizer 时用字符数估算,并把估算方式写入 event payload。
- Chat stream:提交前按
input_tokens + max_tokens预占,stream 中可累计输出 token,完成后用 usage 精确回填。 - 未提供
max_tokens的请求:使用模型默认输出上限或策略中的defaultOutputReserveTokens。 - 失败且未提交到供应商:释放预占 token;已提交但失败的 attempt 按供应商是否计费决定保留或部分释放。
- 生图/生视频通常不计 TPM,但如果 provider 对多模态 token 有限制,可在
capabilities.tokenPolicy中启用对应估算器。
RPM 和并发的边界:
- RPM 以“向 provider 发起一次 submit / sync request”为计数点;排队但未提交不消耗 RPM。
- 失败切换到下一个客户端时,新客户端 attempt 会重新消耗该客户端、平台、provider 维度的 RPM。
- 并发租约从 submit 前获取,到同步响应完成、异步任务进入可安全释放阶段或任务终态时释放;长轮询 provider 可拆成
submit_concurrent与poll_concurrent两类 scope。 - 服务重启后扫描过期 lease,结合
gateway_tasks.heartbeat_at判断释放或续租,避免并发永久泄漏。
除核心指标外,还保留每日/月度配额、错误冷却、余额不足拦截等策略,但这些属于调度前置校验,不替代 TPM/RPM/并发。
8.2 候选客户端选择
候选客户端排序规则必须可测试、可复现:
- 过滤禁用平台、禁用模型、不可用凭证、权限不满足的客户端。
- 过滤与请求能力不匹配的客户端,例如不支持视频参考、音频参考、多图输入时不能作为候选。
- 根据指定平台 / 指定客户端优先收窄候选。
- 按
priority、是否满载、limiter_ratio、等待队列长度、last_assigned_at、轻微 jitter 排序。 - 选中客户端后在同一事务内写入
last_assigned_at和任务锁,避免并发请求都抢到同一个客户端。
8.3 失败切换与重试策略
一个模型请求进来,如果多个客户端满足要求,必须具备“上一个客户端失败,下一个客户端重试”的能力,且可配置是否允许:
{
"retry": {
"enabled": true,
"failoverClients": true,
"maxAttempts": 3,
"maxSameClientAttempts": 1,
"backoff": { "type": "exponential", "baseMs": 500, "maxMs": 5000 },
"retryableStatusCodes": [408, 409, 429, 500, 502, 503, 504],
"retryableErrorCodes": ["timeout", "rate_limit", "temporary_unavailable"],
"nonRetryableErrorCodes": ["invalid_request", "insufficient_quota", "content_policy"],
"retryOnSubmitUnknown": false
}
}
策略可挂在全局、provider、platform、model、method 四层,优先级为:请求显式配置 > 模型配置 > 平台配置 > provider 默认 > 全局默认。
重试要求:
- 每次尝试都写入
gateway_task_attempts,包括跳过原因和失败分类。 - 已明确提交成功但轮询失败的任务,优先继续轮询原
remote_task_id,不能盲目重复提交造成供应商侧重复任务。 - 提交状态未知时,根据
retryOnSubmitUnknown决定是否切换客户端;默认保守处理,标记为unknown并进入人工/补偿检查。 - 非重试错误直接终止任务,不再切换客户端。
- 重试产生的新客户端必须仍满足同一请求的能力、权限和计费要求。
9. 基准模型库与定价体系
定价不直接散落在平台模型里。Gateway 先有一个基准模型库,基准模型库存所有 provider、模型、能力、默认限流模板和基准价格;平台创建和平台模型配置都基于这个库继承或覆盖。
9.1 基准模型库内容
每个基准模型至少包含:
- provider:如
openai、runninghub、jimeng。 - canonical model key:Gateway 内部稳定模型 key。
- provider model name:供应商真实请求模型名。
- model type:
chat、image、video、audio、embedding、music、digital_human、model_3d。 - capabilities:上下文窗口、stream、多模态输入、参考图、参考视频、参考音频、支持尺寸、质量、时长、有无音频、最大 batch 等。
- base billing config:文本、图像、视频、音频等不同能力的基准价格。
- default rate limit policy:provider 或模型默认 TPM/RPM/并发模板。
9.2 平台价格继承与覆盖
创建平台时有三种方式:
| 模式 | 配置 | 说明 |
|---|---|---|
inherit |
不填价格 | 完全 follow 基准模型价格和能力 |
inherit_discount |
default_discount_factor 或模型级 discount_factor |
有效价 = 基准价 x 折扣系数;能力默认 follow 基准模型,可局部覆盖 |
custom |
billing_config_override / capability_override |
平台模型完全自定义价格或能力,未覆盖字段仍从基准模型补齐 |
有效配置解析顺序:
- 平台模型
capability_override/billing_config_override。 - 平台模型
discount_factor。 - 平台
default_discount_factor。 - 用户组
billing_discount_policy,用于用户组级模型调用折扣。 - 基准模型
capabilities/base_billing_config。
如果没有配置平台模型价格,必须 follow 基准模型;不能出现空价格导致预估扣费为 0 的隐式行为。
接入 server-main 时,充值折扣不在 Gateway 内直接变更余额。Gateway 只保存 recharge_discount_policy 并与主服务同步;充值订单、资源包发放、余额流水仍由 server-main 作为事实源执行。独立模式下,充值、余额、订单和钱包流水由 Gateway 本地账务模块闭环。
9.3 各能力计价模型
文本类:
- 按
1k_tokens计价。 - 输入和输出分开计价:
text_input、text_output。 - 如果模型只配置总价,可 fallback 到
text_total,但 resolver 对外仍展开成输入/输出明细。 - 预估时用输入 token + 输出 token 预估值;最终结算用 provider usage 或 Gateway tokenizer 回填。
图像类:
- 基础单位为
image。 - 动态权重至少支持:分辨率、质量、张数、编辑/生成模式。
- 分辨率示例:
512x512、1024x1024、1K、2K、4K、8K。 - 质量示例:
standard、hd、high、ultra。 - 公式示例:
count * basePrice * resolutionWeight * qualityWeight * modeWeight。
视频类:
- 基础单位建议为
5s或second,与原 provider 配置保持可映射。 - 动态权重至少支持:时长、分辨率、是否包含音频、是否使用参考视频、是否指定声音/音色、生成数量。
- 分辨率示例:
480p、720p、1080p、2160p。 - 公式示例:
count * ceil(durationSeconds / unitSeconds) * basePrice * resolutionWeight * audioWeight * referenceWeight。
音频、音乐、数字人、3D 模型:
- TTS / audio 可按
character_1k或秒计费。 - 音乐可按首数、时长、模型权重计费。
- 数字人可按时长、分辨率、音频驱动方式计费。
- 3D / model 类按任务数、模型复杂度或 provider 返回的计费单位映射。
9.4 estimated billing 一致性
/integration-platform/models/estimatedBilling、/integration-platform/models/estimatedBilling/:workflowId 和真实任务结算必须使用同一个 resolver:
request -> normalize params -> resolve effective platform model
-> resolve effective capabilities -> resolve effective pricing
-> estimate billing -> route / submit -> final billing reconcile
要求:
- 预估扣费不能只按模型名查第一条,必须使用真实候选平台过滤、权限过滤和优先级排序后的有效平台模型。
- 同名模型跨平台时,平台权限和平台模型价格都不能串。
- 测试模式使用同样的预估逻辑,但 billings 标记
simulated=true。 - 基准模型价格变更时,平台模型可选择自动 follow 最新版本或 pin 到指定
pricing_version,避免历史价格被静默改变。
10. Base Client 与 Provider 实现架构
Gateway 需要保留类似原 BaseModelClient 的整体架构:公共基类定义通用生命周期,不同 provider/client 只实现平台差异。这样才能把参数预处理、限流、日志、进度、重试、计费和结果归一放在同一层处理。
10.1 Client 接口
Go 侧建议抽象为:
type ModelClient interface {
Provider() string
MethodName() string
BuildParams(ctx context.Context, task TaskContext) (VendorRequest, error)
SubmitTask(ctx context.Context, request VendorRequest) (RemoteTask, error)
PollResult(ctx context.Context, remote RemoteTask) (PollResult, error)
NormalizeResult(ctx context.Context, result PollResult) (NormalizedResult, error)
EstimateBilling(ctx context.Context, task TaskContext) ([]BillingItem, error)
Cancel(ctx context.Context, remote RemoteTask) error
}
10.2 Base Client 公共职责
BaseClient / BaseModelClient 负责:
- 调用参数处理链:尺寸、画幅、时长、多模态内容过滤、默认值填充。
- 校验模型能力:图片参考、视频参考、音频参考、文件输入、stream 支持等。
- 发出进度事件:构建参数、排队、提交、轮询、下载、上传、完成。
- 执行通用 timeout、backoff、错误分类、重试和 failover 策略。
- 统一记录
gateway_task_attempts、gateway_task_events、运行指标。 - 统一提取
billings与result,交给 outbox 做结算事件。 - 统一处理文件资产:已上传文件引用、远程 URL 转存、provider 临时 URL 拉取后调用
server-main/v1/files/upload。
10.3 Provider Client 实现职责
具体 client 只负责供应商协议:
- OpenAI-compatible:同步/stream chat、responses、image generation。
- Gemini:Chat、多模态图片输入、生图、图像编辑优先进入第一阶段迁移。
- RunningHub:模板类 app 提交、轮询、结果提取,队列 key 使用
${provider}-${methodName}。 - Jimeng / Vidu / Kling / Hunyuan Video:视频任务提交与轮询。
- Suno / Speech / Digital Human:各自的提交和结果归一。
- SimulationClient:用于测试模式和 dry-run,完整模拟提交、轮询、进度、失败、结果,不参与真实生产候选。
- MockTest:用于 provider contract 回归和 loopback,不能和生产路由混在同一个职责里;应由独立 registry / loopback service 管理。
每个 provider 至少需要:
- contract test:固定输入输出 DTO。
- retry classification test:哪些错误可重试、哪些不可重试。
- progress event snapshot:确保前端进度面板兼容。
- billing snapshot:确保预估扣费和最终 billings 语义一致。
11. 队列持久化、恢复与限流执行
11.1 持久化队列原则
队列必须以 PostgreSQL 为事实源,Redis / 内存只作为加速和通知层。服务异常重启后,任务状态必须可从 PG 恢复。
任务生命周期:
created -> queued -> leased -> running -> polling -> succeeded
| |
| -> failed_retryable -> queued
-> failed_final / cancelled
核心规则:
- 入队时写
gateway_tasks,状态为queued,并写初始gateway_task_events。 - worker 获取任务时使用事务、
FOR UPDATE SKIP LOCKED或 advisory lock,写入locked_by、locked_at、heartbeat_at。 - worker 运行中定期刷新
heartbeat_at。 - 服务启动时扫描
running/polling/leased且 heartbeat 超时的任务。 - 有
remote_task_id的任务优先进入 reconciliation poller,继续取回供应商结果。 - 没有
remote_task_id的任务按 retry 策略回到queued或进入failed_final。 - 结算 outbox 独立重试,任务成功不等于结算成功。
11.2 恢复策略
| 重启前状态 | 恢复动作 |
|---|---|
queued |
保持队列,等待 worker 重新租约 |
leased 但无 heartbeat |
释放锁,回到 queued |
running 且无 remote_task_id |
视为提交前失败,按 retry 策略重试 |
running/polling 且有 remote_task_id |
进入 poller 继续取回结果 |
succeeded 但 settlement pending |
outbox 继续补偿 |
failed_retryable 且未超最大次数 |
按 next_run_at 回队列 |
failed_final/cancelled |
不自动恢复 |
11.3 限流执行
worker 租约任务前必须先拿到限流令牌:
- TPM 令牌:按
gateway_rate_limit_counters的一分钟窗口预占 token,完成后 reconcile 实际 token。 - RPM 令牌:提交 provider 前扣减一分钟窗口请求数,失败切换的新 attempt 重新评估。
- 并发令牌:按
queue_key/client_id/method_name/platform_model_id维度写gateway_concurrency_leases。 - 排队令牌:超过
max_waiting后直接返回兼容错误。 - 冷却令牌:客户端错误达到阈值后设置
cooldown_until。
如果 worker 拿不到令牌,任务保持 queued 并更新 next_run_at,不能占用 worker 长时间空等。
12. 进度流与 RxJS-like 中间结果
原系统里很多 provider 使用 RxJS Observable 在中间过程返回进度。Go 侧需要提供等价语义:
type ProgressPublisher interface {
Publish(ctx context.Context, event ProgressEvent) error
Subscribe(ctx context.Context, taskID string, afterSeq int64) (<-chan ProgressEvent, error)
}
12.1 ProgressEvent 结构
{
"taskId": "uuid",
"seq": 12,
"event": "progress",
"status": "running",
"phase": "polling",
"progress": 0.42,
"message": "Generating video frames",
"payload": {},
"createdAt": "2026-05-09T12:00:00Z"
}
12.2 推送通道
- 业务实时进度主通道:Gateway 按
TASK_PROGRESS_CALLBACK_URL回调server-main,由server-main复用原 WebSocket 网关推送给业务前端。 - 控制台 SSE:
GET /api/v1/tasks/:taskId/events,支持Last-Event-ID回放,用于 Gateway 控制台、调试和故障诊断。 - Gateway WebSocket:用于网关控制台高频队列监控、运行态监控;不替代业务前端现有 WebSocket 通道。
- OpenAI stream:
/chat/completions、/v1/chat/completions、/responses需要保持原 stream chunk 格式。
任务进度回调示例:
POST ${TASK_PROGRESS_CALLBACK_URL}
Authorization: Bearer ${SERVER_MAIN_INTERNAL_TOKEN}
Content-Type: application/json
Idempotency-Key: ${taskId}:${seq}
X-EasyAI-Event-Type: task.progress
{
"eventId": "uuid",
"taskId": "uuid",
"externalTaskId": "server-main-task-id",
"userId": "user-id",
"tenantId": "tenant-id",
"apiKeyId": "optional",
"kind": "images.generations",
"model": "gpt-image-1",
"seq": 12,
"event": "progress",
"status": "running",
"phase": "polling",
"progress": 0.42,
"message": "Generating video frames",
"payload": {},
"createdAt": "2026-05-09T12:00:00Z"
}
server-main 收到后不重新执行任务,只做三件事:
- 按
Idempotency-Key或taskId + seq幂等落库/去重。 - 根据
externalTaskId、taskId、userId、tenantId找到原业务会话或任务频道。 - 通过原 WebSocket 网关推送给业务前端,保持前端订阅协议不大改。
12.3 进度持久化要求
- 所有对用户可见的状态变化都必须先写
gateway_task_events,再写gateway_task_callback_outbox,最后广播/投递。 - 客户端断线后能从
seq或Last-Event-ID补齐。 - 任务恢复后必须先重放最近状态,再继续发布新进度。
- 完成态事件必须包含足够的结果摘要,前端无需额外轮询才能更新卡片状态。
- 回调投递失败不能阻塞任务执行,但必须进入 outbox 重试,并在控制台显示滞留状态。
- 同一任务的回调按
seq保序投递;允许不同任务并发投递。
13. 测试模式与全链路模拟
AI Gateway 需要内置测试模式,目标是在不触达真实供应商、不真实扣费、不调用主服务生产上传接口的前提下,把请求完整跑过路由、候选客户端选择、队列、限流、失败切换、进度流、结果归一、任务恢复等核心链路。
13.1 启用方式
支持三种启用粒度:
| 粒度 | 配置 | 说明 |
|---|---|---|
| 全局 | `AI_GATEWAY_RUN_MODE=production | simulation` |
| 请求级 | X-EasyAI-Test-Mode: simulation 或 body test_mode=true |
仅对单次请求模拟,默认只允许 power/manager 或内部服务使用 |
| 平台/模型级 | platform.config.testMode、model.config.testMode |
指定平台、模型在测试环境永远走模拟 |
安全要求:
- 生产环境默认禁止普通用户请求级 test mode;需要显式开启
ALLOW_REQUEST_TEST_MODE=true。 - OpenAPI
sk-*默认不能打开 test mode,除非 API Key 或租户配置允许。 - 进入 simulation 后必须在 task、attempt、event、response 中标记
simulated=true,避免和真实任务混淆。
13.2 模拟链路
测试模式仍走完整执行链:
HTTP route -> auth -> route compatibility adapter -> model router
-> persistent queue -> rate limiter -> SimulationClient
-> progress events -> normalized result -> dry-run billing -> task completed
不会执行的动作:
- 不调用真实 provider 的 submit / poll / cancel API。
- 不生成真实供应商订单或任务。
- 不向
server-main发真实扣费结算事件。 - 不调用
server-main的生产文件上传,除非显式使用测试环境上传接口。 - 不消耗真实平台并发额度。
仍会执行的动作:
- 真实鉴权。
- 真实路由和候选客户端排序。
- 真实队列持久化、锁、heartbeat、恢复。
- 真实限流令牌计算。
- 真实进度事件持久化和推送。
- 真实任务记录、attempt 记录、dry-run outbox 记录。
13.3 Simulation Profile
SimulationClient 根据 simulation_profile 生成确定性行为:
{
"simulation_profile": {
"latencyMs": [300, 800, 1200],
"progressSteps": [0.1, 0.35, 0.7, 1],
"resultKind": "image",
"resultCount": 2,
"failures": [
{ "attempt": 1, "errorCode": "timeout", "retryable": true },
{ "attempt": 2, "errorCode": "rate_limit", "retryable": true }
],
"seed": "task-id-or-custom-seed"
}
}
用途:
- 模拟成功:返回固定格式图片、视频、音频、文本或 embedding 结果。
- 模拟失败:按 attempt 注入可重试 / 不可重试错误。
- 模拟慢任务:验证进度、断线重连、任务恢复。
- 模拟限流:验证排队、等待上限、cooldown。
- 模拟未知提交态:验证
retryOnSubmitUnknown和人工补偿路径。
13.4 失败切换测试
测试模式必须能验证“上一个客户端失败,下一个客户端重试”:
- router 仍按真实规则选出多个候选客户端。
- SimulationClient 为第一个候选返回 retryable error。
- runtime 写入 failed attempt。
- retry policy 判断可切换。
- router 排除已失败且不可复用的 client,选下一个候选。
- 第二个候选成功后,任务以
succeeded完成,并记录完整 attempts。
禁用重试时,同样的失败应直接进入 failed_final,不能切换客户端。
13.5 Dry-run 计费与结算
EstimateBilling仍使用真实计费配置计算,结果标记simulated=true。settlement_outbox可写入dry_run事件,但默认状态为skipped,不触发主服务扣费。- 若需要联调主服务结算接口,只能调用
server-main的 dry-run settlement endpoint,不能复用真实扣费 endpoint。
13.6 模拟结果资产
测试模式下的文件结果有三种来源:
- 内置静态占位 URL,例如
/static/simulation/image.png。 - data URL / inline metadata,仅用于接口契约测试。
- 测试文件上传接口:只有显式启用
AI_GATEWAY_SIMULATION_UPLOAD=true时才允许调用server-main测试环境上传接口;默认使用内置静态占位资产。
结果必须带:
{
"simulated": true,
"assetSource": "simulation",
"provider": "simulation"
}
13.7 观测与审计
控制台需要能筛选 run_mode=simulation 的任务,展示:
- 使用的 simulation profile。
- 每次 attempt 的模拟错误和重试决策。
- 生成的 progress events。
- dry-run billing。
- 是否触发了恢复流程。
14. server-main 对接方式
14.1 短期:server-main 仍保留入口
server-main 对外接口不变:
/chat/completions/images/generations/video/generations/v1/chat/completions/v1/images/generations/v1/video/generations
内部 OpenaiService 变成薄门面:
sequenceDiagram
participant FE as Frontend
participant MAIN as server-main
participant GW as AI Gateway
participant V as Vendor
FE->>MAIN: POST /images/generations
MAIN->>MAIN: create history / task id
MAIN->>GW: POST /api/v1/images/generations
GW->>V: submit task
GW-->>MAIN: POST task progress callback
MAIN-->>FE: WebSocket task progress
V-->>GW: result
GW-->>MAIN: POST task completed callback
MAIN-->>FE: WebSocket task completed
GW-->>MAIN: settlement event with billings
MAIN->>MAIN: billing + history update
GW-->>MAIN: task result
MAIN-->>FE: response
14.2 实时任务进度回调
AI Gateway 不直接替换原业务前端的 WebSocket 订阅方式。Gateway 在配置中指定任务进度回调地址:
TASK_PROGRESS_CALLBACK_URL=http://easyai-server-main:3000/internal/platform/task-progress-callbacks
TASK_PROGRESS_CALLBACK_ENABLED=true
TASK_PROGRESS_CALLBACK_TIMEOUT_MS=5000
TASK_PROGRESS_CALLBACK_MAX_ATTEMPTS=10
运行时要求:
BaseClient/ runtime 每产生一个进度事件,先写gateway_task_events,再写gateway_task_callback_outbox。- callback worker 使用
SERVER_MAIN_INTERNAL_TOKEN调用TASK_PROGRESS_CALLBACK_URL。 server-main收到事件后进入主服务内部推送流程,再由原 WebSocket 网关推送给业务前端。- Gateway 控制台仍可通过 SSE 读
gateway_task_events,用于运维诊断,不影响业务前端推送通道。 - 如果
server-main短暂不可用,Gateway 按 outbox 重试;超过最大次数后标记failed,控制台可手动 replay。
14.3 中期:前端直接打 Gateway
前端配置 VITE_GATEWAY_API_BASE_URL,生成类请求直接走网关路由到 AI Gateway。server-main 只处理登录、刷新、余额、历史、开放文件上传、扣费。
即使生成请求改为前端直连 Gateway,业务实时进度默认仍回调 server-main 内部任务进度接口,除非某个新业务明确迁移到 Gateway 自身的事件通道。
14.4 结算事件
Gateway 任务成功后向 server-main 发送结算事件:
{
"eventId": "uuid",
"taskId": "uuid",
"userId": "user-id",
"tenantId": "tenant-id",
"apiKeyId": "optional",
"kind": "images.generations",
"model": "gpt-image-1",
"billings": [
{
"resourceType": "image",
"amount": 1,
"unit": "item",
"cost": 100
}
],
"resultRef": {
"url": "https://..."
}
}
要求:
server-main以eventId/taskId做幂等。- Gateway 保留 outbox 或重试表,避免结算事件丢失。
- 扣费失败时任务结果仍可先保存,但前端展示需要明确异常状态。
15. 文件上传
文件上传策略收敛为:AI Gateway 不维护自己的 OSS/COS/S3 配置,不做预签名,不直接写对象存储;所有需要上传或转存的文件,统一调用 server-main 已开放的文件上传接口。这样可以复用主服务现有 OSS 配置、权限、文件记录、MIME 推断和后续审计。
15.1 ServerMainUploadClient 抽象
type ServerMainUploadClient interface {
UploadMultipart(ctx context.Context, input UploadMultipartInput) (UploadedAsset, error)
UploadFromURL(ctx context.Context, input UploadFromURLInput) (UploadedAsset, error)
ResolveAsset(ctx context.Context, ref AssetRef) (ResolvedAsset, error)
}
15.2 调用接口
默认调用 server-main 的开放上传接口:
| Method | Path | 说明 |
|---|---|---|
POST |
/v1/files/upload |
OpenAPI 兼容 multipart 文件上传 |
调用要求:
- 用户请求:透传用户 JWT 或 OpenAPI
sk-*,保持与原主服务上传权限一致。 - Gateway 内部上传:使用服务令牌加原始用户上下文头,例如
X-EasyAI-User-Id、X-EasyAI-Tenant-Id、X-EasyAI-Task-Id,具体以server-main内部约定为准。 - 上传返回值以主服务响应为准,Gateway 只做字段归一并写入
gateway_upload_assets。 - 测试模式默认不调用上传接口,除非显式启用测试上传。
15.3 上传场景
| 场景 | 处理方式 |
|---|---|
| 前端已经通过主服务上传 | 请求中携带主服务返回的 URL / file id,Gateway 只校验和记录 |
| Gateway 需要转存远程 URL | Gateway 下载远程内容,作为 multipart 调 /v1/files/upload 上传到主服务 |
| Provider 返回临时 URL | Gateway 拉取结果,再调 /v1/files/upload 转存,最终结果使用主服务 URL |
| OpenAPI multipart 上传 | 继续由 server-main 的 /v1/files/upload 承接;Gateway 不新增并行上传入口 |
| base64 小文件 | Gateway 解码为 multipart,再调用 /v1/files/upload |
15.4 安全与可靠性
- 限制最大文件大小、content-type、扩展名。
- 远程 URL 下载必须禁止内网地址、metadata 地址、超长重定向链。
- 上传到
server-main时带Idempotency-Key,避免重启或 retry 造成重复文件记录。 gateway_upload_assets记录主服务返回的 file id、URL、object key、checksum、响应快照。- 如果上传失败,任务按 provider retry 策略判断是否可重试;上传失败本身也要写 attempt/event。
- Gateway 不保存长期文件密钥,不暴露 OSS 配置页面。
16. 前端页面设计
前端使用 React + TypeScript + TSX + shadcn-ui,定位不是单纯后台控制台,而是“AI 能力门户 + 用户工作台 + 管理工作台 + API 文档中心”。页面根据 IDENTITY_MODE 切换身份来源:独立模式走 Gateway 本地登录 / API Key;接入 server-main 时走主服务 JWT / OpenAPI Key 授权。普通用户只能进入模型、用户工作台和 API 文档,管理员额外进入管理工作台。
16.1 一级导航
| 一级模块 | 路由 | 默认权限 | 定位 |
|---|---|---|---|
| 登录 | /login、/register |
public |
Gateway 本地账号登录注册、外部 token 入口 |
| 首页 | / |
public/basic |
服务入口、模型能力概览、用量摘要、最近任务、快捷入口 |
| 模型 | /models |
public/basic |
可用模型浏览、能力筛选、价格/限流展示、模型调用入口 |
| 用户工作台 | /workspace |
basic |
个人中心、余额充值、API Key、任务记录 |
| 管理工作台 | /admin |
power/manager |
用户与用户组、全局模型配置、平台管理、队列限流、结算与系统设置 |
| API 文档 | /docs |
public/basic |
开放接口文档、鉴权说明、示例代码、在线调用测试 |
16.2 登录与注册
首期登录页先提供普通账号能力和可选邀请码注册,后续再接 SSO、验证码、MFA:
| 页面 | 路由 | 权限 | 说明 |
|---|---|---|---|
| 登录 | /login |
public |
用户名/邮箱 + 密码登录 Gateway 本地账号 |
| 注册 | /register |
public |
创建 Gateway 本地账号;可填写租户 key / 租户名称 / 邀请码,邀请码不是必填 |
| 外部 Token 入口 | /login?mode=external |
public |
粘贴 server-main access token,用于接入模式和开发调试 |
登录成功后统一获得 Gateway 可校验的 access token;本地账号 token 中包含 source=gateway、gatewayUserId、gatewayTenantId、tenantKey。外部 token 保留 source=server-main 或主服务返回的 claim。
16.3 首页
首页不是营销页,首屏应直接提供可操作入口和运行状态。
| 页面 | 路由 | 权限 | 说明 |
|---|---|---|---|
| 首页总览 | / |
public/basic |
推荐模型、最近任务、余额摘要、API Key 状态、服务公告 |
| 能力概览 | /capabilities |
public/basic |
Chat、生图、生视频、音频、Embedding 等能力入口 |
| 服务状态 | /status |
public/basic |
网关健康、模型可用性、平台异常公告、限流提示 |
首页组件:
HeroStatusBand:显示 Gateway 健康、可用模型数、今日任务数、当前余额。QuickStartActions:Chat 调用、生图调用、生视频调用、创建 API Key、查看 API 文档。ModelCapabilityGrid:按文本、图像、视频、音频、Embedding 展示可用能力。RecentTasks:最近任务状态、进度、失败原因和重试入口。UsageSnapshot:本月调用量、token、图片/视频任务、费用趋势。
16.4 模型
模型页面面向普通用户和管理员,但展示内容按权限裁剪。
| 页面 | 路由 | 权限 | 说明 |
|---|---|---|---|
| 模型广场 | /models |
public/basic |
按能力、provider、价格、上下文、多模态筛选模型 |
| 模型详情 | /models/:modelKey |
public/basic |
能力、价格、限流、参数 schema、示例请求、在线试用 |
| 模型价格 | /models/:modelKey/pricing |
basic |
文本输入/输出、图像分辨率/质量、视频时长/分辨率等价格 |
| 模型调用测试 | /models/:modelKey/playground |
basic |
选择 API Key 或 JWT,在线测试 Chat/生图/生视频 |
模型页能力:
- 筛选:模型类型、provider、是否支持 stream、多模态、参考图/视频/音频、上下文窗口、价格区间。
- 展示:有效模型价、TPM/RPM/并发限制、是否支持测试模式、常见错误码。
- 管理员视角:展示平台模型来源、基准模型映射、平台折扣、能力覆盖和实际候选平台。
16.5 用户工作台
用户工作台关注“我能用什么、用了多少、怎么调用、历史任务在哪里”。
| 页面 | 路由 | 权限 | 说明 |
|---|---|---|---|
| 个人中心总览 | /workspace/overview |
basic |
账号信息、身份来源、角色、用户组、余额、API Key 数、最近任务、用量摘要 |
| 余额与充值 | /workspace/billing |
basic |
余额、资源包、充值入口、消费记录、用户组充值折扣、发票/订单状态 |
| API Key 管理 | /workspace/api-keys |
basic |
API Key 列表、创建、禁用、重置、权限范围、最近调用 |
| 任务记录 | /workspace/tasks |
basic |
Chat/生图/生视频等任务列表,按状态、模型、时间筛选 |
| 任务详情 | /workspace/tasks/:id |
basic |
请求、进度事件、结果、计费、上传资产、失败原因 |
边界:
- 独立模式下,用户资料、API Key、余额、充值订单和钱包流水由 Gateway 本地模块闭环;接入
server-main时,余额、充值、订单、API Key 生命周期仍归server-main,Gateway 前端可以通过server-mainAPI 或兼容代理展示。 - 用户组影响充值折扣、调用折扣和并发/限流;工作台需要展示当前用户命中的用户组、折扣说明、TPM/RPM/并发限制。
- 任务记录以 Gateway 任务为执行事实源,但业务历史最终仍可由
server-main汇总。 - API Key 创建时必须显示一次性 secret,后续只展示 key 名称、前缀、权限和最近使用时间。
16.6 管理工作台
管理工作台只对 power/manager 开放,负责全局模型、平台、限流、重试、运行态和系统集成。当前阶段平台凭证和 provider 凭证仅允许全局管理员配置,租户管理员暂不拥有自助配置入口。
| 页面 | 路由 | 权限 | 说明 |
|---|---|---|---|
| 管理总览 | /admin |
power |
服务健康、任务吞吐、成功率、队列积压、异常平台、回调 outbox |
| 租户管理 | /admin/tenants |
power |
本地租户、同步租户、租户策略、状态和用量隔离 |
| 用户管理 | /admin/users |
power |
本地用户、同步用户、角色、状态、同步差异和用户组命中 |
| 用户组管理 | /admin/user-groups |
power |
用户组、成员关系、充值折扣、调用折扣、并发/限流策略 |
| 全局模型配置 | /admin/models/global |
power |
基准模型库、能力 schema、基准定价、默认限流模板 |
| 基准 Provider | /admin/catalog/providers |
power |
provider 列表、协议类型、能力 schema、启停状态 |
| 基准模型详情 | /admin/catalog/base-models/:id |
power |
能力、价格版本、默认 TPM/RPM/并发、引用平台模型 |
| 定价规则 | /admin/pricing/rules |
power |
文本/图像/视频/音频价格、动态权重、版本生效时间 |
| 平台管理 | /admin/platforms |
power |
平台 CRUD、启停、优先级、凭证状态、复制、异常重置 |
| 平台详情 | /admin/platforms/:id |
power |
基础信息、凭证、模型、折扣、限流、重试、运行态、快照 |
| 平台模型 | /admin/platform-models |
power |
平台模型列表、基准映射、能力覆盖、价格覆盖、权限过滤 |
| API 配置 | /admin/platform-apis |
power |
兼容原 integration/platform-api 的 CRUD 与执行测试 |
| 队列监控 | /admin/runtime/queues |
power |
queue key、等待数、运行数、租约、恢复状态 |
| 客户端运行态 | /admin/runtime/clients |
power |
running、waiting、limiter ratio、cooldown、last error |
| 限流策略 | /admin/policies/rate-limit |
power |
TPM、RPM、并发、队列长度、冷却策略 |
| 重试策略 | /admin/policies/retry |
power |
global/provider/platform/model/method 重试策略 |
| 任务审计 | /admin/tasks |
power |
全量任务、attempt、失败切换、用户/模型/平台筛选 |
| 上传记录 | /admin/uploads |
power |
主服务上传记录映射、任务关联、失败重试 |
| 结算 outbox | /admin/settlements |
manager |
结算事件、重试、跳过、幂等 key |
| 回调 outbox | /admin/callbacks |
power |
任务进度回调状态、失败原因、手动 replay |
| 测试模式 | /admin/simulation |
power |
simulation profile、失败注入、慢任务、dry-run billing |
| 系统设置 | /admin/settings |
manager |
server-main 地址、内部令牌、运行模式、安全开关 |
关键管理页面:
- 全局模型配置:维护
model_catalog_providers、base_model_catalog、model_pricing_rules,支持导入旧项目配置。 - 租户管理:维护
gateway_tenants,支持本地租户 CRUD、从server-main增量同步、禁用状态同步、租户策略和用量隔离审计。 - 用户管理:维护
gateway_users,支持本地用户 CRUD、从server-main增量同步、禁用状态同步、角色同步和同步差异审计。 - 用户组管理:维护
gateway_user_groups、gateway_user_group_memberships,支持从server-main同步成员关系;独立模式执行全部策略,接入模式下充值折扣由server-main执行,Gateway 执行调用折扣和并发/限流。 - 平台管理:创建平台时选择基准 provider,配置默认折扣系数;平台模型可 follow 基准、按折扣继承或自定义覆盖。
- 队列与限流:展示 TPM/RPM 窗口、预占值、并发 lease、cooldown、next run。
- 回调 outbox:展示 Gateway 到进度回调目标的投递结果,支持按 task replay。
16.7 API 文档与在线调用测试
API 文档面向开发者,需要能完成从鉴权到真实调用的闭环。
| 页面 | 路由 | 权限 | 说明 |
|---|---|---|---|
| 文档首页 | /docs |
public/basic |
快速开始、鉴权方式、模型列表、错误码、限流说明 |
| 鉴权 | /docs/auth |
public/basic |
JWT、OpenAPI sk-*、Header、权限范围、API Key 安全 |
| Chat | /docs/api/chat |
public/basic |
/v1/chat/completions、stream、工具调用、示例代码 |
| Responses | /docs/api/responses |
public/basic |
/v1/responses 请求、取消、结构化输出 |
| 图片 | /docs/api/images |
public/basic |
/v1/images/generations、/v1/images/edits、文件输入 |
| 视频 | /docs/api/videos |
public/basic |
/v1/video/generations、进度查询、结果取回 |
| 音频/语音 | /docs/api/audio |
public/basic |
speech、music、digital human 等接口 |
| Embeddings | /docs/api/embeddings |
public/basic |
embedding 模型、批量输入、维度说明 |
| 文件上传 | /docs/api/files |
public/basic |
说明仍调用 server-main /v1/files/upload |
| 任务结果 | /docs/api/tasks |
public/basic |
/v1/ai/result/:taskId、取消、状态、回调语义 |
| 错误码 | /docs/errors |
public/basic |
兼容错误结构、retryable 分类、限流错误 |
| 在线调试 | /docs/playground |
basic |
选择模型、API Key、请求模板,执行在线调用测试 |
在线调试器:
- 左侧选择接口类型:Chat、Responses、生图、图像编辑、生视频、Embedding、语音。
- 中间为参数表单 + JSON editor 双模式,表单字段来自模型能力 schema。
- 右侧展示 curl、JavaScript、Python 示例代码和实时响应。
- 支持 stream 结果面板、任务进度时间线、结果预览、usage / billings。
- 支持测试模式开关;开启后标记
simulation,不触达真实 provider、不真实扣费。 - 调试前展示当前 API Key 的权限、余额、TPM/RPM/并发限制。
16.8 前端模块目录
apps/web/src/
app/
routes.tsx
providers.tsx
components/
ui/ # shadcn-ui generated components
layout/
data-table/
json-viewer/
status-badge/
features/
auth/
login/
register/
home/
models/
workspace/
overview/
billing/
api-keys/
tasks/
admin/
dashboard/
catalog/
tenants/
users/
user-groups/
pricing/
platforms/
platform-apis/
runtime/
policies/
tasks/
callbacks/
uploads/
settlements/
simulation/
settings/
docs/
api-reference/
playground/
lib/
api-client.ts
auth.ts
format.ts
query-keys.ts
contracts/
dto.ts # 或从 packages/contracts 引入
16.9 前端数据流
- 使用
@tanstack/react-query管理列表、详情、轮询和 mutation。 - 表格使用
@tanstack/react-table,配合 shadcnTable。 - 表单使用
react-hook-form+zod,校验策略与后端 DTO 对齐。 - 用户工作台在独立模式下调用 Gateway 本地身份/账务模块;接入
server-main时余额、充值、API Key 生命周期优先调用server-mainAPI,Gateway 展示执行侧任务、模型能力、用户同步状态和用户组执行策略。 - Gateway 控制台事件统一封装为
useTaskEvents(taskId);业务前端实时进度仍由原 WebSocket 网关推送。 - 所有 destructive action 使用 shadcn
Dialog二次确认。 - 页面级权限由路由 loader 或 wrapper 判断,不在每个按钮里重复散落判断。
17. 迁移计划
Phase 0:脚手架与契约
- 建立 monorepo、Go API、React 控制台、PG migration。
- 前端接入
shadcn-ui,建立基础组件目录和主题 token。 - PG 目标版本为 PostgreSQL 18;复用 Agent memory 的
easyai-pgvector实例,但使用独立数据库easyai_ai_gateway,避免与easyai_memory的记忆表混库。 - 容器网络内默认连接串为
postgresql://easyai:easyai2025@easyai-pgvector:5432/easyai_ai_gateway?schema=public;宿主机直跑时必须改成宿主机可访问的 host/port,例如localhost或实际映射端口。 - 如果只提供
MEMORY_DATABASE_URL,Go 侧只借用其中的 host/user/password,并按AI_GATEWAY_DATABASE_NAME替换数据库名;同时会把 Prisma 风格的schema=public转成 PostgreSQLsearch_path参数。 - 建立 JWT / API Key 授权中间件骨架和
standalone/server-main/hybrid身份模式配置,默认hybrid。 - 固化 API、事件、数据库设计。
- 建立 simulation / dry-run 模式配置与安全开关,默认禁止生产环境普通用户随意开启。
- 建立基准 provider、基准模型库、价格规则、用户、用户组策略、邀请码、本地 API Key、钱包、充值订单和 TPM/RPM/并发限流表结构。
Phase 1:模型库 + 首批生成能力
第一阶段只做可落地的核心闭环:模型库、大模型对话、生图、图像编辑。Client 只迁移 OpenAI 和 Gemini 两个,视频和其他 provider 后移。
- 导入 OpenAI、Gemini 的基准 provider、基准模型、能力 schema、默认限流模板,形成首批
base_model_catalog。 - 建立全局模型配置:模型类型、上下文、多模态能力、图片输入/输出能力、stream 支持、价格规则。
- 平台创建支持选择基准模型、设置默认折扣系数;平台模型支持 follow 基准模型、折扣继承和自定义覆盖。
- 建立
gateway_users同步副本和gateway_user_groups策略解析,支持本地用户与server-main用户在同一套策略链路里命中不同折扣和限流。 - 建立本地账号闭环:普通注册可选邀请码,独立模式支持本地 API Key、余额、充值订单和钱包流水。
- 迁移大模型对话路由:
/chat/completions、/v1/chat/completions,并为/responses//v1/responses保留兼容契约。 - 迁移生图和图像编辑路由:
/images/generations、/v1/images/generations、/images/edits、/v1/images/edits。 - 建立 OpenAI Chat / Image Client 与 Gemini Chat / Image Client 的 Base Client 实现和 contract test。
- 支持文本输入/输出 token 计费,图像按分辨率、质量、数量、生成/编辑模式计费。
- 实现调用
server-main开放上传接口的ServerMainUploadClient,覆盖参考图、mask、provider 临时 URL 转存、base64 小文件上传。 - 实现 SimulationClient,支持 Chat、生图、图像编辑的成功、失败、慢任务、限流、未知提交态等 profile。
Phase 2:路由、队列与稳定性补强
- 迁移
IntegrationModelFactory.assignClientsByModelName行为。 - 建立 Base Client 生命周期:构建参数、提交任务、轮询结果、归一化结果、估算计费、取消任务。
- 实现客户端失败切换策略:上一个客户端失败后按配置切到下一个候选客户端。
- 补队列持久化、TPM/RPM/并发限流、重启恢复、重试、超时、任务事件。
- 实现任务进度 callback outbox,将 Gateway 进度回调给实时推送通道;同时保留 Gateway 控制台 SSE / WebSocket 和 OpenAI stream。
- 为 OpenAI、Gemini 增加 retry classification test、billing snapshot、progress event snapshot。
Phase 3:server-main 切薄门面与灰度
OpenaiService内部先将 Chat、生图、图像编辑切到 Gateway HTTP SDK。- 结算事件接入
server-main扣费链路。 - 前端逐步改
Gateway API Base URL。 - 开启 shadow / dry-run,比对旧实现和 Gateway 的候选模型、预估扣费、参数预处理结果。
Phase 4:视频与更多 provider
- 在 Phase 1 稳定后再迁移生视频、音频、Embedding、音乐、数字人等能力。
- 迁移 RunningHub、Jimeng、Vidu、Kling、Hunyuan Video、Suno 等 provider。
- 对 app-style provider 补
assignClientsByProviderMethod和provider + methodName队列 key。 - 每个 provider 增加 contract test、retry classification test、billing snapshot。
Phase 5:server-main 清理旧实现
- 删除或冻结
server-main中重复的 runtime client。 - 保留必要 BFF、历史、账单、文件上传能力。
18. 验收标准
pnpm nx run api:migrate可在 Agent memory PostgreSQL 中初始化 AI Gateway 表。pnpm nx run api:test通过。pnpm nx run web:build通过。- 控制台使用
shadcn-ui组件,不出现散落的独立 UI 体系。 - 使用
server-main签发的 JWT 能访问/api/v1/me。 - OpenAPI
sk-*能委托server-main校验并获得用户 claim。 - 原
integration-platform、站内生成、/v1/*核心路由可用,DTO 与响应结构兼容。 - 基准 provider、基准模型库、基准价格和默认限流模板可在控制台维护。
- 用户、用户组、成员关系、充值折扣、调用折扣、TPM/RPM/并发策略可在管理工作台维护;独立模式可本地闭环,接入模式下充值执行仍以
server-main为事实源。 - 接入
server-main时,用户、角色、禁用状态和用户组成员关系支持增量同步;任务保存用户和用户组策略快照。 - 平台创建可设置默认折扣系数;平台模型未配置时 follow 基准模型,配置折扣时按基准价折扣计算,自定义覆盖时覆盖价格和能力。
- 文本定价支持输入/输出 token 分开计费;图像定价支持分辨率和质量权重;视频定价支持时长、分辨率、有无音频等权重。
- estimated billing 与真实任务 billings 使用同一个 effective pricing resolver,不能出现预估和真实路由价格来源不一致。
- 队列任务在服务异常重启后可恢复:已提交供应商的任务继续 poll,未提交任务重新排队。
- 限流策略覆盖 TPM、RPM、并发,并支持平台、客户端、基准模型、平台模型、方法、租户、用户组、用户、API Key 维度。
- TPM/RPM 一分钟窗口和并发 lease 在服务重启后可恢复或释放,不产生永久占用。
- 多客户端候选下,一个客户端可重试失败后按策略切换下一个客户端;禁用重试时必须直接失败。
- Base Client 架构覆盖构建参数、提交任务、取回结果、归一化结果、计费估算、取消任务。
- 任务中间进度可通过 SSE / WebSocket / stream 返回,并支持断线重放。
- 业务前端实时进度通过
TASK_PROGRESS_CALLBACK_URL回调到server-main内部任务进度接口,再由主服务复用原 WebSocket 网关推送;callback outbox 支持失败重试和手动 replay。 - 文件上传统一调用
server-main开放上传接口,Gateway 不维护自己的 OSS 配置。 - 测试模式下不会向真实平台提交任务、不会真实扣费、不会调用主服务生产上传接口,但会完整经过路由、队列、限流、重试、进度和结果归一。
- 测试模式支持 profile 注入可重试错误,能验证客户端失败切换;禁用重试时能验证直接失败。
- Phase 1 至少完成 Chat / 生图 / 图像编辑各一个 provider 的端到端任务;生视频放到 Phase 4 后续迁移。
- 结算事件在
server-main幂等扣费。 - 同名模型跨平台权限过滤与旧逻辑一致。
19. 本轮设计复核结论与已确认决策
本轮复核后已修正:
- 身份边界:不再写成“用户/租户只能留在
server-main”,改为standalone、server-main、hybrid三种模式。 - 多租户:补
gateway_tenants,并把用户、任务、平台可见性、限流和策略解析都接入租户上下文。 - 登录注册:补 Gateway 本地账号注册登录接口和前端登录页规划。
- 进度回调:统一表述为回调
server-main内部任务进度接口,不直接回调 ws-gateway。 - Phase 1 范围:继续限定为模型库、Chat、生图、图像编辑、OpenAI 和 Gemini;视频迁移放到后续阶段。
已确认决策:
- 默认身份模式就是
hybrid,用于同时支持本地账号和server-maintoken / API Key。 - 普通注册支持填写邀请码,但邀请码不是必填项;填写后按邀请码加入指定租户和用户组。
- 先做本地闭环:独立模式下本地 API Key、余额、充值订单和钱包流水由 Gateway 承接。
- 当前阶段仅允许全局管理员配置 provider / platform 凭证和全局模型,不开放租户管理员自助配置凭证。