aidy-router V2 功能计划与数据库表设计
状态:草案 v0.1 目标:将当前以静态/动态配置驱动的
aidy-gateway,演进为具备独立控制面、PG 配置中心、多上游调度、消费者体系、配额与计费能力的aidy-router。
1. 背景与设计目标
V1 的核心价值是“轻量可配置的 AI Gateway”,重点在于:
- 按
route转发到单一 upstream; - 按
tenant做基础限速; - 在
route级做能力开关、Guard 与自定义限速; - 通过动态配置实现热更新。
V2 的目标不是在 V1 上继续堆字段,而是把产品形态从“配置驱动的网关”升级为“带控制面、发布面和运行面的完整路由系统”。
V2 的核心变化:
- 增加独立控制面
- 管理租户、路由、消费者、上游、模型计费、发布版本;
- 负责配置校验、编译、发布与审计;
- 不再把“编辑配置文件/Redis Hash”作为主要管理方式。
- 增加 PostgreSQL
- 控制面与数据面都直接连接 同一个 RW PG,作为事实数据源(source of truth);
- 数据面在主流程中既读取当前生效配置,也写入 quota / usage 等强一致性数据;
- V2 运行时不再兼容
file、redis-v1、redis-v2等旧 dynamic config source,只支持 PG; - 路由实例不直接依赖控制面 API,可在控制面故障时继续运行。
- 租户模型升级
- 从“tenant 只有限速”升级为“tenant 拥有 upstream、模型路由、消费者与配额”,计费由全局默认与 provider 覆盖决定。
- 路由模型升级
- 从“route 决定唯一 upstream”升级为“route 负责入口治理与访问约束,真正的模型选择和上游选择在 tenant 级完成”。
2. 现状梳理:V1 能力与 V2 目标
2.1 当前仓库里 V1 已有能力
结合当前 proto、文档与实现,V1 已落地的核心能力可以整理为:
租户级(tenant)
inbound_rate_limit_qpsdetect_rate_limit_cpm
路由级(route)
- 基础信息:
id、name、prefix、tenant_id - 单 upstream 配置:
typeurltokenextra_headersmax_idle_conns_per_host
- 路由访问认证:
auth.bearer.tokens passthrough_auth_token- 能力开关:
openai-modelsopenai-chatopenai-embeddingsforward-unknown
- 路由标签:
labels - 路由插件:
- Guard
- Inbound Rate Limit
- Upstream Rate Limit
动态配置形态
当前动态配置的主模型仍是“整份配置 / Redis Hash -> 构造运行态 route”:
route/<prefix>->entity.Routetenant/<tenant-id>->entity.Tenantplugin/log/plugin/guard-> 全局插件配置- 配置进入 manager 后构建为内存中的运行态
route.Route
说明:以上仅用于描述 V1 现状。 V2 不兼容 这些 dynamic config source,运行时只支持 PG。
2.2 V2 目标能力
全局
- 默认模型计费配置(Credit)
Provider 级
- Provider 覆盖的模型计费配置(Credit)
租户级(tenant)
- 基础限速(延续 V1)
- Provider 目录(全局默认 + tenant 自定义)
- 多 upstreams(每个 upstream 绑定一个 provider,并定义 model / alias)
- 协议转换
- 跨 provider 的 LB / fallback
- 消费者与配额
- 消费者 API Key
- 从 V1 的 route 级单 token 模式升级为 tenant 级消费者模型
- 一个 consumer 可绑定多个 API key
- API Key 独立限额
路由级(route)
- 自定义限速(延续 V1)
- Guard(延续 V1)
- 脱敏(预留,首版暂不做)
- 敏感信息检测 / 数据出网策略(预留,首版暂不做)
- 限制允许的消费者
- 限制可用模型
- [计划弃用]
passthrough_auth_token(当前仍保留)
2.3 V2 的核心产品定位
V2 可以抽象为一句话:
route决定“谁可以从哪个入口进来”,tenant决定“这些请求可以用哪些模型、走哪些上游、按什么策略路由、怎么配额”,而计费由“全局默认 + provider 覆盖”决定。
这个划分非常重要,因为它直接决定了控制面和数据库表设计的边界。
3. 总体架构设计
3.1 组件拆分
建议将 V2 拆为两类 aidy 自身组件:
A. Control Plane(独立控制面)
职责:
- 提供基于 Connect RPC 的 management API / Console API;
- 管理租户、路由、上游、消费者、计费与策略;
- 做字段级和跨实体校验;
- 将配置直接写入主业务表;
- 审计变更。
B. aidy-router(数据面)
职责:
- 直接通过同一个 RW PG 查询当前生效配置;
- 按主流程完成 route、tenant、consumer、api key、模型路由等查询;
- 请求路径上使用“PG 直查 + PG 强一致性 quota 扣减 + Redis rate limit”;
- 在主流程中完成请求鉴权、usage 记录、计量上下文生成与账单所需数据写入;
- 完成模型解析、上游选择、协议转换、策略执行、转发与埋点。
存储与基础设施边界
- RW PG:Control Plane 与 aidy-router 共同使用;Control Plane 负责配置管理与发布,aidy-router 负责配置读取、quota 扣减与 usage 写入;
- Redis:继续承担 QPS/CPS 等 rate limit 计数;
- Quota 强一致性:Consumer Quota 与 API Key Quota 均存于 PG,并在请求事务中扣减。
结论:V2 引入 PG,不意味着所有运行态写路径都要进 PG。 对数据面来说,PG 负责“当前生效配置 + 强一致性 quota + usage 写入”,Redis 负责“高频 rate limit 计数”。
3.2 配置发布链路
建议采用“PG 草稿编辑 + PG 原子发布”的简化模型:
- 控制面将编辑中的配置写入核心业务表中的草稿态记录;
- 控制面执行完整校验:
- 引用完整性;
- 路由冲突;
- tenant / route / consumer / upstream 之间的约束;
- 模型路由是否可达;
- 计费 / Credit 配置是否有缺口;
- 控制面发起
publish时,在单个事务中完成:- 将目标草稿集提升为
active; - 将被替换的旧生效记录置为
disabled/ 软删除; - 覆盖更新关联关系表;
- 更新
publish_state;
- 将目标草稿集提升为
- 事务提交前,
aidy-router继续读取旧的已发布数据; - 事务提交后,
aidy-router后续请求直接读取新的已发布数据。
首版 不保存历史配置,也 不提供配置回滚。 如果发布失败,直接事务回滚;如果发布成功,则以 PG 中的新发布态为准。
首版不再向file、redis-v1、redis-v2等旧 dynamic config source 做发布或同步。
3.3 为什么首版不做 runtime 表拆分
首版如果引入 runtime 派生表结构,会额外增加几类复杂性:
- 控制面发布链路会更长,调试成本更高;
- 需要维护双份表结构和额外的数据同步/编译逻辑;
- 还会和“暂不做历史版本、暂不做回滚”的目标形成重复设计;
- 对当前阶段来说,收益不足以覆盖复杂性。
因此首版建议:
- 控制面与数据面共用同一组主业务表;
- 数据面直接读取
status=active的当前生效数据; - 后续如果确认性能瓶颈,再引入缓存或
runtime拆表。
3.4 一致性模型
V2 建议采用“事务提交后直接生效 + quota 强一致写入”的语义:
- 控制面发布成功,只代表 PG 写路径上的配置事务提交成功;
- 若数据面直接读同一套 PG 数据,则后续请求可直接看到新配置;
- 若部署上存在读写分离,则以数据库读可见性为准;
- Consumer Quota 与 API Key Quota 通过 PG 事务保证强一致;
- 首版不引入缓存一致性问题;
- 首版不提供历史版本回滚能力。
4. 领域模型重构
4.1 V2 的核心实体
建议 V2 固化以下领域实体:
- Tenant
- 业务隔离与资源归属的顶层单位;
- 拥有 tenant 级 provider、自定义 upstream、消费者、模型路由策略。
- Route
- 入口匹配单元;
- 归属于 tenant;
- 承担入口访问限制与治理策略;
- 不再拥有唯一 upstream。
- Provider
- 可复用的上游模板,可来源于全局默认或 tenant 自定义;
- 代码中不内置 provider 类型,运行时只根据
protocol+compat决定怎么处理请求; - 其中
protocol对应aidy-models中Provider.api的语义; - 字段可分为元数据(如
name、description)和运行时默认值(如protocol、base_url、headers、compat)。
- Upstream
- 可实际出网调用的后端目标;
- 必须绑定一个 provider,并默认继承 provider 的运行时字段;
- 可覆盖
base_url、headers、compat、认证等默认值,但不可覆盖protocol; - 承载连接池、健康检查、权重/优先级等运行时配置;
- 归属于 tenant。
- Upstream Model
- 某个 upstream 上支持的具体模型定义;
- 把 tenant 对外暴露的
model_name/aliases映射到 upstream 实际模型名。
- Model Routing Policy
- 描述某个 tenant 下,一个
model_name应如何在多个 upstream model 之间进行选择; - 支持 single / weighted LB / priority fallback。
- 描述某个 tenant 下,一个
- Consumer
- tenant 下的调用方;
- 可绑定一个或多个 API Key;
- 可携带独立 quota。
- API Key
- Consumer 的认证凭证;
- 具备独立限额与状态;
- 首版数据库中直接保存明文,同时保留查找用 hash;
- 后续再演进到 KMS / 密文存储。
- Policy
- Guard Policy
- Desensitization Policy(预留)
- DLP / Egress Policy(预留)
- Rate Limit Policy
- Quota Policy
- Pricing Plan
- 模型计费规则;
- 支持全局默认与 provider override;
- 所有价格与计算结果都以整数 Credit 表示。
- Config Metadata
- 当前配置的元信息;
- 用于标记当前运行态是否发生变更;
- 不保存历史版本。
4.2 配置归属原则
建议在 V2 中明确以下归属:
属于 Global 的
- 默认模型计费配置;
- 全局默认 provider 模板;
- 用于测试或初始化的 provider seed 数据;
- (可选)全局默认 Guard 模版;
- 脱敏 / DLP 模版为后续能力,首版不实现。
属于 Provider 的
- provider 运行时默认值(
protocol/base_url/headers/compat); - provider 级模型计费覆盖;
- provider 元数据。
属于 Tenant 的
- 基础限速;
- tenant 自定义 providers;
- upstreams;
- upstream 支持的模型与别名;
- 模型路由策略(LB / fallback);
- consumers;
- consumer API keys;
- consumer / API key 配额。
属于 Route 的
- path prefix;
- 归属 tenant;
- 路由自定义限速;
- guard;
- 脱敏(后续能力,首版不实现);
- 数据出网策略(后续能力,首版不实现);
- allowed consumers;
- allowed models;
- 兼容期开关(例如 legacy passthrough 行为)。
明确不再推荐放在 Route 的
- 唯一 upstream;
- provider / protocol 选择;
- 上游 token 透传;
- 复杂模型选择逻辑;
- 计费定义;
- 消费者定义。
4.3 配置优先级与合并规则
建议把 V2 的优先级固定下来,避免后续实现分叉:
- 模型计费
- provider pricing > global default pricing
- 所有计费结果一律为整数 Credit
- 模型可用范围
- tenant model routing 定义“可供选择的全集”
- route allowed models 做进一步收缩
- 消费者可用范围
- tenant consumers 定义“可认证的全集”
- route allowed consumers 做进一步收缩
- 若 route 未配置 allowed consumers,则默认允许该 tenant 下所有 consumers
- 限速
- tenant 基础限速始终生效
- route 自定义限速为附加限制,不是覆盖
- API key 独立限额为附加限制,不是覆盖
- 治理策略
- 首版只迁移现有的 Guard 插件以适配 V2
- 不新增脱敏、敏感信息检测、数据出网策略等新的业务侧治理功能
- tenant/global 级策略如后续引入,应采用“默认 + route override”的模型
- 兼容行为
passthrough_auth_token当前仍保留,但已进入计划弃用范围- 当 route 开启该功能时,沿用当前行为:使用路由配置的 token 作为下游 token
4.4 工程重构约束
本期开始增加一个轻量但明确的重构项:all in protobuf。
记录原则即可,不额外展开复杂设计:
- 所有跨模块、跨进程、需长期维护的数据契约,优先以 protobuf 作为唯一 schema source;
- JSON / YAML / OpenAPI 等展示或交互格式,均视为 protobuf 的派生表示,而不是并列主定义;
- 新增或重构接口时,优先先定义 protobuf message / service,再生成或适配具体接入层。
本期明确落地两项:
- management API 也使用 Connect RPC
- 控制面的管理接口统一按 protobuf
service定义; - 服务暴露形式统一为 Connect RPC;
- 如仍需 OpenAPI 或 HTTP 文档,应从 protobuf / Connect 定义派生,而不是手写一套独立契约。
- 控制面的管理接口统一按 protobuf
- 请求日志记录插件使用 protobuf 定义格式
- 请求日志记录结构由 protobuf message 定义;
- 各类日志输出目标(如 HTTP / Kafka)基于同一份 protobuf 日志结构做序列化或投递;
- 日志字段的增删改应优先修改 protobuf 定义,再同步影响文档与下游消费方。
5. 请求执行链路设计
建议数据面按如下顺序处理请求:
- 路由匹配
- 根据 path prefix 命中 route;
- 取得 route 绑定的 tenant。
- 消费者认证
- 当前版本只从
Authorization: Bearer <token>提取 API key; - 计算
lookup_hash后直接查询 PG 中的consumer_api_keys; - 得到
consumer与api_key。
- 当前版本只从
- 入口访问限制
- 若 route 配置了 allowed consumers,则校验 consumer 是否在白名单中;
- 若 route 未配置 allowed consumers,则默认允许该 tenant 下所有 consumers;
- 若 route 处于禁用状态,直接拒绝。
- 限速与配额预检查
- tenant 基础限速;
- route 自定义限速;
- API key 独立限额;
- consumer / API key quota 在 PG 事务中做强一致性预检查与扣减。
- 模型解析
- 从请求中提取调用方传入的
model; - 先按
upstream_models.model_name/aliases解析出统一的model_name; - 若 route 配置了 allowed models,则按解析后的
model_name做白名单校验; - 在 tenant model routing policy 中找到该
model_name对应的候选 upstream model 集合。
- 从请求中提取调用方传入的
- 请求治理
- 首版仅迁移现有 Guard 插件到 V2 请求链路;
- 不新增脱敏、敏感信息检测、数据出网策略等业务侧治理功能;
- 若命中 Guard 的 block 策略,则直接拒绝。
- 上游选择
- 按 routing policy 选择 upstream target;
- 执行协议转换;
- 附加 upstream 的 auth/header/连接池参数;
- 进行实际转发。
- 失败处理
- 若策略是 weighted LB,则选定一个目标后执行;
- 若策略是 priority fallback,则按优先级依次尝试;
- 失败原因与 retry/fallback 过程需打点。
- 响应治理
- 如保留 Guard response 检测,则在返回前执行;
- 记录 usage、延迟、状态码、上游命中信息。
- 异步计费/统计
- 将 usage event 异步发送到 ingestion pipeline;
- 再写入 PG 聚合表与账单表。
关于 passthrough_auth_token
建议 V2 处理方式:
- 默认关闭;
- 当前仍保留在 route 配置模型中;
- 当 route 开启该功能时,沿用当前行为:使用路由配置的 token 作为下游 token;
- 该能力已进入计划弃用范围,但首版不移除;
- 长期仍建议用 “consumer API key -> upstream credential / routing policy” 替代。
6. 分阶段功能计划
为了控制复杂度,建议按能力依赖拆分为 6 个阶段。
Phase 0:统一领域模型与迁移 / 废弃策略
目标
- 明确 V2 的实体边界;
- 明确 V1 -> V2 的迁移映射;
- 固定字段命名和优先级规则;
- 明确哪些能力继续保留、哪些直接废弃,并明确 V2 不兼容旧 dynamic config source,
route.auth不进入 V2 主模型。
交付物
- V2 领域模型文档;
- API/resource 命名规范;
- 废弃字段清单与离线导入约定;
- 配置 CRUD / validate 的事务边界定义;
- protobuf / Connect 契约约束说明。
验收标准
- 路由不再绑定唯一 upstream;
- consumer / api key 成为主认证模型;
- API key 当前只支持
Authorization: Bearer <token>入口; - V2 运行时只支持 PG,不再兼容
file、redis-v1、redis-v2; - V1 的
route.auth不作为兼容层保留; passthrough_auth_token被标记为“计划弃用但当前保留”;- 所有 V1 能力都有明确迁移归宿。
Phase 1:独立控制面 + Control DB
目标
- 控制面具备独立部署能力;
- 所有业务配置都能通过 PG 写路径持久化,并支持草稿态编辑;
- 支持草稿态编辑和校验。
范围
- tenant CRUD
- route CRUD
- upstream CRUD
- consumer / api key CRUD
- pricing plan CRUD
- policy CRUD
- validate API
验收标准
- 控制面不再依赖直接编辑 Redis;
- 所有配置变更都有审计记录;
- 支持“只校验不发布”;
- 可以输出结构化 diff。
Phase 2:单表发布 + 数据面直查
目标
- 建立“控制面在 PG 内完成草稿 -> 已发布态切换,数据面直接读已发布态”的发布链路;
- router 直接通过 PG 读路径读取当前生效配置;
- 不做内存缓存,不做
runtime表拆分,也不做历史版本保存与回滚。
范围
- publish transaction
- active / draft / disabled 状态切换
- route / tenant / api key 直查路径
- 关键查询索引优化
- publish state metadata
验收标准
- 新配置发布后,后续请求可直接读取到新配置;
- 数据面不依赖任何进程内缓存;
- 不保存历史配置,也不提供配置回滚;
- 发布动作不再依赖
file/redis-v1/redis-v2; - 首版不做
runtime表拆分。
Phase 3:多 upstream + 模型路由
目标
- 建立 provider 模板(global + tenant custom);
- tenant 拥有多个 upstream;
- upstream 必须绑定一个 provider,并继承 provider 默认值;
- 一个对外
model_name(含其aliases)可映射多个 upstream model; - 支持跨 provider 的 LB / fallback;
- 支持基于
protocol+compat的协议处理。
范围
- provider catalog
- upstream inventory
- upstream model / alias 映射
- model routing policy
- weighted LB
- priority fallback
- protocol adapter
验收标准
- upstream 创建时必须选择 provider;
- upstream 可以覆盖 provider 的
base_url/compat/headers,但不能覆盖protocol; - 同一 tenant 下可为同一
model_name配置多个目标; - route 不再关心具体 upstream URL;
- 失败可自动 fallback;
- provider 仅提供默认运行时模板,连接池和健康策略归属于 upstream。
Phase 4:消费者、API Key、限额与配额
目标
- 用 tenant 级 consumer/API key 替代 V1 的 route 级 token 模式;
- 支持 per consumer / per API key 的独立控制。
范围
- consumer
- consumer api key
- key rotation / revoke
- API key rate limit
- consumer quota
- API key quota
- route allowed consumers
验收标准
- API key 可独立启停、过期、轮换;
- 一个 consumer 可绑定多个 API key;
- route 可以限制允许调用的 consumer,未配置时默认允许该 tenant 下所有 consumers;
- 配额超限和限速超限有清晰的错误码与 headers;
- Consumer Quota 与 API Key Quota 在 PG 中做强一致扣减;
- API key 首版以明文落库,并为后续 KMS 演进预留升级路径。
Phase 5:路由治理能力
目标
- route 成为入口治理中心;
- 首版聚焦 Guard、限速与访问约束;
- 脱敏、敏感信息检测、数据出网策略留到后续版本。
范围
- route custom rate limit
- guard
- route allowed models
- route allowed consumers
验收标准
- route 可限制可用 model;
- route 可限制可用 consumer;
- Guard 的执行顺序固定;
- 请求被 block 时具备明确审计原因。
Phase 6:计费、账单、审计与可观测性
目标
- 基于 usage event 形成统一计量;
- 支持 provider 级 override 的模型计费;
- 形成账单与审计基础设施。
范围
- usage ingestion
- pricing resolution
- credit aggregation
- billable ledger
- audit log
- publish diff summary
验收标准
- 能回放一次请求的计费来源;
- 能解释“为什么命中某个 upstream / 某个 fallback / 某个 Credit 价格”;
- provider 级 pricing override 可正确覆盖 global default;
- 能按 tenant / provider / consumer / api key / model 聚合账单。
7. V1 到 V2 的迁移建议
7.1 配置映射
| V1 能力 | V2 归宿 | 备注 |
|---|---|---|
tenant.inbound_rate_limit_qps | tenant 绑定的 rate_limit_policy | 直接迁移 |
tenant.detect_rate_limit_cpm | tenant 绑定的 detect/rate policy | 直接迁移 |
route.upstream | providers + tenant.upstreams + tenant.model_routing_policy | 从“路由唯一上游”改为“provider 模板 + upstream + 租户模型路由” |
route.auth.bearer.tokens | tenant.consumer + consumer_api_keys | 从 route 级单 token 升级为 tenant 级消费者模型;一个 consumer 可绑定多个 key |
route.passthrough_auth_token | route.passthrough_auth_token | 当前保留,后续计划弃用 |
route.capabilities | upstream.capabilities + route allowlist | route 不再扩大 upstream 能力 |
route.plugin_config.guard | route.guard_policy_id | 基本可平移 |
route.plugin_config.rate_limit | route.rate_limit_policy_id | 直接迁移 |
7.2 迁移策略
由于 V2 运行时只支持 PG,不再兼容 file、redis-v1、redis-v2,建议采用“离线导入 + 一次切换”的迁移方式,而不是长期兼容。
阶段 A:离线导出与导入
- 从 V1 的 file / redis source 导出一次性快照;
- 由迁移工具将其转换为 V2 PG 实体;
- 自动生成:
- tenant
- provider
- route
- 单个 upstream
- 默认 model routing policy
- consumer/api key(若原来有 route token,则导入为 tenant 下的 consumer 与 bearer API key)
- 自动生成:
- 在 PG 中完成 validate / preview / publish 前校验;
- 该阶段不要求 V2 运行时直接读取旧 dynamic config source。
阶段 B:切换到 PG-only
- 新增配置只允许写 V2 模型;
- 数据面只读取 PG 中的当前生效业务表;
- 不再保留 file / redis source 的双写、双读或兼容层;
- V1 的
route.auth不进入 V2 运行态; passthrough_auth_token当前继续保留,并沿用现有行为;- 如需回滚,应回滚到上一版 PG 数据快照,而不是回退到旧 dynamic config source。
8. 数据库表设计原则
8.1 表组织方式
首版不做数据库 schema 分层,所有表都使用 PostgreSQL 默认的 public schema。
为了便于理解,本文档仍按“核心业务表”和“计量 / 账单相关表”分章描述,但这只是文档分组,不代表真实的 PG schema 分层。
V2 不兼容也不复用现有
dynamic config source体系(包括file、redis-v1、redis-v2)。 PG 是 V2 唯一配置来源。
8.2 ID 规范与来源字段
首版不再区分“内部 UUID 主键”和“外部业务键”。
所有实体主键及其引用字段统一使用 text,格式固定为:
<prefix>_<ulid>
约束:
<prefix>尽量控制在 2-3 个字符,极端不超过 5 个字符;<prefix>必须全局唯一,避免不同表之间产生歧义;<prefix>应尽量与实体原始单词或词组对应;- 同一张表允许存在多种 prefix,只要这些 prefix 能表达来源差异。
- 程序侧所有 ID 字段统一使用完整字符串,不拆分保存裸 ULID。
推荐 prefix:
| 实体 / 表 | prefix | 说明 |
|---|---|---|
tenants | tn_ | tenant |
routes | rt_ | route |
providers | gp_ / tp_ | global provider / tenant provider |
upstreams | ups_ | upstream |
upstream_models | um_ | upstream model |
model_routing_policies | mrp_ | model routing policy |
model_routing_targets | mrt_ | model routing target |
pricing_plans | gpp_ / ppp_ | global pricing plan / provider pricing plan |
pricing_plan_items | gpi_ / ppi_ | global pricing item / provider pricing item |
consumers | cs_ | consumer |
consumer_api_keys | cak_ | consumer api key |
rate_limit_policies | rlp_ | rate limit policy |
quota_policies | qpl_ | quota policy |
quota_policy_items | qpi_ | quota policy item |
guard_policies | ggd_ / tgd_ | global guard / tenant guard |
desensitization_policies | gds_ / tds_ | global / tenant desensitization |
dlp_policies | gdl_ / tdl_ | global / tenant dlp |
audit_logs | aud_ | audit log |
usage_events | uev_ | usage event |
billing_ledger | bll_ | billing ledger |
说明:
publish_state、route_allowed_consumers、route_allowed_models、daily_usage_aggregates这类表可以继续使用自然主键或组合主键,不强制新增独立id;- 所有业务 ID 字段(主键、外键、
owner_id、source_ref_id、resource_id)都直接保存完整的 text id,例如tn_xxx、rt_xxx、gp_xxx。
对于同一张表中可能同时承载 global / tenant / route / provider 等多个来源的数据,除了在 id prefix 上体现来源外,还统一增加两个专门字段:
source_scope text not nullsource_ref_id text not null default ''
示例:
- 全局数据:
source_scope='global',source_ref_id='' - tenant 数据:
source_scope='tenant',source_ref_id='tn_xxx' - route 数据:
source_scope='route',source_ref_id='rt_xxx' - provider 数据:
source_scope='provider',source_ref_id='gp_xxx'或tp_xxx
8.3 复杂策略优先用 JSONB
以下内容不建议一开始强行拆成高度规范化小表:
- Guard 规则 DSL
- 脱敏规则 DSL
- DLP / egress 规则 DSL
- protocol adapter 的细节参数
- fallback 条件细节
这类结构变化快,建议:
- 元信息关系化;
- 规则内容用
jsonb; - 发布时直接写入主业务表中的当前生效结构,必要时做适度冗余。
8.4 Secret 处理
默认建议:
- 首版本为降低复杂性,数据库中先直接存放明文凭证;
- 适用范围包括 upstream 凭证,以及 consumer API key;
- 后续版本再引入 KMS / 加密存储;
- 因此首版表设计要为后续 KMS 改造预留字段演进空间。
8.5 高频计数不要直接写 PG
以下运行时高频数据不建议由 router 直接写 PG:
- QPS/CPS 限速计数
建议:
- 实时判定用 Redis;
- usage/event 在主流程写入 PG;
- Consumer Quota 与 API Key Quota 由于要求强一致性,直接存于 PG 并在事务中扣减;
- PG 负责 quota、usage、汇总、审计、账单;Redis 仅负责 rate limit。
8.6 Credit 记账单位
V2 对外暴露费用统一使用 Credit,内部所有计费、配额、余额、账单数据结构也统一使用 Credit,并且必须为整数。
设计约束:
- 所有价格、余额、quota、账单金额字段统一使用
bigint; - 程序中无论是配额还是计费,都只使用 Credit 作为单位,不涉及法币转换;
- router 和所有数据库表内部不保存浮点金额或法币币种;
- 法币与 Credit 的换算由业务侧负责,不属于 router 核心职责;
- 建议默认采用
1 USD = 1_000_000 Credit、1 CNY = 1_000_000 Credit; - 上述换算只是业务接入建议,router 内部只消费和产出整数 Credit。
9. 核心业务表设计
9.1 tenants
租户主表。
| 字段 | 类型 | 说明 |
|---|---|---|
id | text pk | 主键,格式 tn_<ulid> |
name | text not null | 租户名称 |
status | text not null | active / disabled / draft |
metadata | jsonb not null default '{}' | 扩展字段 |
created_at | timestamptz not null | 创建时间 |
updated_at | timestamptz not null | 更新时间 |
deleted_at | timestamptz null | 软删除 |
索引建议:
index (status)
9.2 routes
路由主表,只负责入口与治理,不承载唯一 upstream。
| 字段 | 类型 | 说明 |
|---|---|---|
id | text pk | 主键,格式 rt_<ulid> |
tenant_id | text not null fk -> tenants.id | 所属 tenant |
name | text not null | 路由名称 |
path_prefix | text not null | 路由前缀,如 /demo |
status | text not null | active / disabled / draft |
rate_limit_policy_id | text null fk -> rate_limit_policies.id | 路由自定义限速策略 |
guard_policy_id | text null fk -> guard_policies.id | Guard 策略 |
desensitization_policy_id | text null fk -> desensitization_policies.id | 脱敏策略(预留,首版不实现) |
dlp_policy_id | text null fk -> dlp_policies.id | 数据出网策略(预留,首版不实现) |
passthrough_auth_token | boolean not null default false | 当前仍保留;开启时使用路由配置的 token 作为下游 token;后续计划弃用 |
metadata | jsonb not null default '{}' | 扩展字段 |
created_at | timestamptz not null | 创建时间 |
updated_at | timestamptz not null | 更新时间 |
deleted_at | timestamptz null | 软删除 |
约束/索引建议:
unique (path_prefix) where deleted_at is nullindex (tenant_id, status)check (path_prefix ~ '^/[a-zA-Z0-9_-]+$')
9.3 providers
Provider 模板表。
Provider 可来源于全局默认或 tenant 自定义。代码本身不内置 provider 类型;运行时只根据
protocol+compat工作。
其中protocol的语义对应aidy-models中Provider.api。
| 字段 | 类型 | 说明 |
|---|---|---|
id | text pk | 主键,格式 gp_<ulid> 或 tp_<ulid> |
source_scope | text not null | global / tenant |
source_ref_id | text not null default '' | 全局为空字符串;tenant 级为 tn_xxx |
name | text not null | 展示名称 |
description | text null | 描述 |
protocol | text not null | 运行时协议,语义对应 aidy-models.Provider.api |
base_url | text null | 默认 base URL |
headers | jsonb not null default '{}' | 默认静态 headers |
compat | jsonb not null default '{}' | 默认兼容性配置 |
metadata | jsonb not null default '{}' | 其余元数据,如 doc/url/check model/source |
status | text not null | active / disabled / draft |
created_at | timestamptz not null | 创建时间 |
updated_at | timestamptz not null | 更新时间 |
deleted_at | timestamptz null | 软删除 |
约束/索引建议:
unique (source_scope, source_ref_id, name) where deleted_at is nullindex (source_scope, source_ref_id, status)
9.4 upstreams
tenant 下的上游目标。
创建 upstream 时必须选择一个 provider。
upstream 默认继承 provider 的protocol/base_url/headers/compat;其中只有base_url、headers、compat可覆盖,protocol不可覆盖。
| 字段 | 类型 | 说明 |
|---|---|---|
id | text pk | 主键,格式 ups_<ulid> |
tenant_id | text not null fk -> tenants.id | 所属 tenant |
provider_id | text not null fk -> providers.id | 绑定的 provider 模板 |
name | text not null | 上游名称 |
base_url_override | text null | 覆盖 provider 默认 base URL |
headers_override | jsonb not null default '{}' | 覆盖 / 追加 provider 默认 headers |
compat_override | jsonb not null default '{}' | 覆盖 provider 默认 compat |
secret_value | text null | 首版直接明文存储的上游凭证 |
capabilities | text[] not null default '{}' | 上游能力开关 |
max_idle_conns_per_host | int null | 连接池参数 |
lb_weight | int not null default 100 | 默认权重 |
priority | int not null default 100 | 默认优先级 |
health_policy | jsonb not null default '{}' | 健康探测 / 熔断参数 |
status | text not null | active / disabled / draft |
created_at | timestamptz not null | 创建时间 |
updated_at | timestamptz not null | 更新时间 |
deleted_at | timestamptz null | 软删除 |
约束/索引建议:
index (tenant_id, status)index (tenant_id, provider_id)
9.5 upstream_models
定义某个 upstream 上可用的模型、映射与别名。
| 字段 | 类型 | 说明 |
|---|---|---|
id | text pk | 主键,格式 um_<ulid> |
upstream_id | text not null fk -> upstreams.id | 所属 upstream |
model_name | text not null | 对外暴露并统一用于路由/计费的模型名 |
aliases | text[] not null default '{}' | 该模型的额外别名 |
upstream_model | text not null | upstream 实际调用的模型名 |
model_features | jsonb not null default '{}' | 模型能力补充信息 |
weight | int not null default 100 | 默认权重 |
priority | int not null default 100 | 默认优先级 |
status | text not null | active / disabled / draft |
created_at | timestamptz not null | 创建时间 |
updated_at | timestamptz not null | 更新时间 |
约束/索引建议:
unique (upstream_id, model_name, upstream_model)index (model_name)gin (aliases)
9.6 model_routing_policies
tenant 下统一 model_name 的路由策略主表。
| 字段 | 类型 | 说明 |
|---|---|---|
id | text pk | 主键,格式 mrp_<ulid> |
tenant_id | text not null fk -> tenants.id | 所属 tenant |
model_name | text not null | 统一使用的对外模型名 |
strategy | text not null | single / weighted / priority_fallback |
failure_policy | jsonb not null default '{}' | 超时、重试、熔断与失败处理 |
timeout_ms | int null | 默认超时时间 |
metadata | jsonb not null default '{}' | 扩展字段 |
created_at | timestamptz not null | 创建时间 |
updated_at | timestamptz not null | 更新时间 |
约束/索引建议:
unique (tenant_id, model_name)index (tenant_id)
9.7 model_routing_targets
路由策略的候选目标列表。
| 字段 | 类型 | 说明 |
|---|---|---|
id | text pk | 主键,格式 mrt_<ulid> |
policy_id | text not null fk -> model_routing_policies.id | 所属策略 |
upstream_model_id | text not null fk -> upstream_models.id | 指向候选目标 |
priority | int not null default 100 | fallback 优先级 |
weight | int not null default 100 | weighted LB 权重 |
max_retry_count | int not null default 0 | 单 target 最大重试次数 |
condition_expr | jsonb null | 预留:条件路由 |
enabled | boolean not null default true | 是否启用 |
约束/索引建议:
unique (policy_id, upstream_model_id)index (policy_id, enabled)
9.8 pricing_plans
全局默认与 provider override 的计费方案主表。
| 字段 | 类型 | 说明 |
|---|---|---|
id | text pk | 主键,格式 gpp_<ulid> 或 ppp_<ulid> |
source_scope | text not null | global / provider |
source_ref_id | text not null default '' | 全局为空字符串;provider 级为 gp_xxx 或 tp_xxx |
name | text not null | 名称 |
is_default | boolean not null default false | 是否默认 |
effective_from | timestamptz not null | 生效时间 |
effective_to | timestamptz null | 失效时间 |
status | text not null | active / disabled / draft |
created_at | timestamptz not null | 创建时间 |
updated_at | timestamptz not null | 更新时间 |
约束/索引建议:
unique (source_scope, source_ref_id, name, effective_from)index (source_scope, source_ref_id, is_default, status)
9.9 pricing_plan_items
计费方案明细。
所有计费统一按解析后的对外 model_name 匹配;aliases 在请求阶段先归一到对应的 model_name。
| 字段 | 类型 | 说明 |
|---|---|---|
id | text pk | 主键,格式 gpi_<ulid> 或 ppi_<ulid> |
plan_id | text not null fk -> pricing_plans.id | 所属 plan |
model_name | text not null | 统一用于计费的模型名 |
meter_type | text not null | input_tokens / output_tokens / requests |
unit_size | bigint not null | 计费单位,如 1000 tokens |
unit_credit | bigint not null | 整数 Credit 单价 |
metadata | jsonb not null default '{}' | 预留扩展 |
effective_from | timestamptz not null | 生效时间 |
effective_to | timestamptz null | 失效时间 |
约束/索引建议:
unique (plan_id, model_name, meter_type, effective_from)index (plan_id, model_name)
9.10 consumers
tenant 下的调用方实体。
| 字段 | 类型 | 说明 |
|---|---|---|
id | text pk | 主键,格式 cs_<ulid> |
tenant_id | text not null fk -> tenants.id | 所属 tenant |
name | text not null | 名称 |
status | text not null | active / disabled / draft |
quota_policy_id | text null fk -> quota_policies.id | 默认 Credit Quota 策略 |
remaining_credit | bigint not null default 0 | 当前剩余 Credit |
credit_version | bigint not null default 0 | 用于并发控制的版本号 |
metadata | jsonb not null default '{}' | 扩展字段 |
created_at | timestamptz not null | 创建时间 |
updated_at | timestamptz not null | 更新时间 |
deleted_at | timestamptz null | 软删除 |
约束/索引建议:
index (tenant_id, status)
9.11 consumer_api_keys
API Key 明细表。
| 字段 | 类型 | 说明 |
|---|---|---|
id | text pk | 主键,格式 cak_<ulid> |
consumer_id | text not null fk -> consumers.id | 所属 consumer |
key_prefix | text not null | 用于展示与排查 |
key_plaintext | text not null | 首版直接明文存储的 API key |
lookup_hash | bytea not null unique | 用于快速查找的稳定 hash |
status | text not null | active / disabled / revoked / expired |
expires_at | timestamptz null | 过期时间 |
rate_limit_policy_id | text null fk -> rate_limit_policies.id | API key 限速策略 |
quota_policy_id | text null fk -> quota_policies.id | API key Credit Quota 策略 |
remaining_credit | bigint not null default 0 | 当前剩余 Credit |
credit_version | bigint not null default 0 | 用于并发控制的版本号 |
last_used_at | timestamptz null | 最近使用时间 |
created_at | timestamptz not null | 创建时间 |
revoked_at | timestamptz null | 吊销时间 |
索引建议:
unique (lookup_hash)index (consumer_id, status)index (expires_at)
9.12 rate_limit_policies
通用限速策略表。
| 字段 | 类型 | 说明 |
|---|---|---|
id | text pk | 主键,格式 rlp_<ulid> |
name | text not null | 策略名称 |
algorithm | text not null | sliding_window / token_bucket |
metric | text not null | requests / characters / tokens |
limit_value | bigint not null | 限额值 |
window_seconds | int not null | 时间窗口 |
burst_value | bigint null | 突发值 |
backend | text not null default 'redis' | 实时判定 backend |
config | jsonb not null default '{}' | 扩展字段 |
created_at | timestamptz not null | 创建时间 |
updated_at | timestamptz not null | 更新时间 |
9.13 rate_limit_bindings
把限速策略绑定到 tenant / route / api_key。
| 字段 | 类型 | 说明 |
|---|---|---|
owner_type | text not null | tenant / route / api_key |
owner_id | text not null | 绑定对象 ID,如 tn_xxx / rt_xxx / cak_xxx |
stage | text not null | inbound / detect / upstream |
policy_id | text not null fk -> rate_limit_policies.id | 策略 ID |
priority | int not null default 100 | 执行顺序 |
约束/索引建议:
primary key (owner_type, owner_id, stage, policy_id)index (policy_id)
9.14 quota_policies
Credit 配额策略主表。
| 字段 | 类型 | 说明 |
|---|---|---|
id | text pk | 主键,格式 qpl_<ulid> |
name | text not null | 策略名称 |
period_type | text not null | daily / monthly / rolling_window |
period_value | int null | rolling window 秒数等 |
timezone | text null | 日/月结算时区 |
on_exhausted | text not null | block / soft_limit / log_only |
config | jsonb not null default '{}' | 扩展字段 |
created_at | timestamptz not null | 创建时间 |
updated_at | timestamptz not null | 更新时间 |
9.15 quota_policy_items
Credit 配额明细。
| 字段 | 类型 | 说明 |
|---|---|---|
id | text pk | 主键,格式 qpi_<ulid> |
policy_id | text not null fk -> quota_policies.id | 所属策略 |
metric | text not null | credit |
hard_limit_credit | bigint not null | 硬限制 Credit |
soft_limit_credit | bigint null | 软限制 Credit |
约束/索引建议:
unique (policy_id, metric)
9.16 guard_policies
Guard 策略表。
| 字段 | 类型 | 说明 |
|---|---|---|
id | text pk | 主键,格式 ggd_<ulid> 或 tgd_<ulid> |
source_scope | text not null | global / tenant |
source_ref_id | text not null default '' | 全局为空字符串;tenant 级为 tn_xxx |
name | text not null | 名称 |
mode | text not null | 默认处理方式 |
rule_set | jsonb not null | Guard 规则 DSL |
version | int not null default 1 | DSL 版本 |
status | text not null | active / disabled / draft |
created_at | timestamptz not null | 创建时间 |
updated_at | timestamptz not null | 更新时间 |
9.17 desensitization_policies
预留表:首版暂不实现脱敏能力,但建议预留表名与字段位置,避免后续命名漂移。
脱敏策略表。
| 字段 | 类型 | 说明 |
|---|---|---|
id | text pk | 主键,格式 gds_<ulid> 或 tds_<ulid> |
source_scope | text not null | global / tenant |
source_ref_id | text not null default '' | 全局为空字符串;tenant 级为 tn_xxx |
name | text not null | 名称 |
rule_set | jsonb not null | 脱敏规则 DSL |
version | int not null default 1 | DSL 版本 |
status | text not null | active / disabled / draft |
created_at | timestamptz not null | 创建时间 |
updated_at | timestamptz not null | 更新时间 |
9.18 dlp_policies
预留表:首版暂不实现 DLP 能力,但建议预留表名与字段位置,避免后续命名漂移。
数据出网策略表。
| 字段 | 类型 | 说明 |
|---|---|---|
id | text pk | 主键,格式 gdl_<ulid> 或 tdl_<ulid> |
source_scope | text not null | global / tenant |
source_ref_id | text not null default '' | 全局为空字符串;tenant 级为 tn_xxx |
name | text not null | 名称 |
rule_set | jsonb not null | DLP / egress 规则 DSL |
version | int not null default 1 | DSL 版本 |
status | text not null | active / disabled / draft |
created_at | timestamptz not null | 创建时间 |
updated_at | timestamptz not null | 更新时间 |
9.19 route_allowed_consumers
route 对 consumer 的白名单。
若某个 route 在该表中没有任何记录,表示 默认允许该 tenant 下所有 consumers。
| 字段 | 类型 | 说明 |
|---|---|---|
route_id | text not null fk -> routes.id | route |
consumer_id | text not null fk -> consumers.id | consumer |
约束建议:
primary key (route_id, consumer_id)
9.20 route_allowed_models
route 对 model 的白名单。
| 字段 | 类型 | 说明 |
|---|---|---|
route_id | text not null fk -> routes.id | route |
model_name | text not null | 允许的统一模型名 |
约束建议:
primary key (route_id, model_name)
9.21 publish_state
当前发布态主表。
该表只记录 当前 发布信息,不保存历史配置版本。
| 字段 | 类型 | 说明 |
|---|---|---|
slot | text pk | 预留多环境/多集群,默认 main |
config_hash | text not null | 当前配置整体 hash |
data_version | bigint not null | 当前发布版本号(单调递增,但不保留历史) |
validation_report | jsonb not null default '{}' | 最近一次校验摘要 |
published_by | text null | 发布人/系统 |
published_at | timestamptz not null | 发布时间 |
notes | text null | 备注 |
9.22 audit_logs
审计与追责基础表。
| 字段 | 类型 | 说明 |
|---|---|---|
id | text pk | 主键,格式 aud_<ulid> |
actor | text not null | 操作人/系统 |
action | text not null | create / update / delete / publish |
resource_type | text not null | tenant / route / upstream / ... |
resource_id | text null | 对应实体 ID |
change_summary | jsonb not null default '{}' | 变更摘要 |
created_at | timestamptz not null | 操作时间 |
10. 数据面直查设计
首版不引入额外的 runtime 表集,也不做业务表 / runtime 表双轨拆分。
aidy-router 直接读取当前生效业务表。
10.1 直查原则
- 不做进程内缓存;
- 不做
runtime bundle/current tables; - 所有数据面查询统一过滤
status='active'且deleted_at is null的记录; - 若发布事务尚未提交,数据面不可见;事务提交后,后续请求可直接读取到新配置。
10.2 关键查询路径
Route 查询
- 按请求路径查询
routes.path_prefix; - 取到 route 后读取其
tenant_id、guard_policy_id、rate_limit_policy_id、passthrough_auth_token等字段。
API Key 鉴权查询
- 从 Bearer token 计算
lookup_hash; - 查询
consumer_api_keys; - 首版可按需继续比对
key_plaintext,后续切到 KMS 时保留同一查找路径; - 关联
consumers与tenants,确认 key、consumer、tenant 均处于active。
Consumer 白名单查询
- 查询
route_allowed_consumers; - 若某 route 没有任何记录,则默认允许该 tenant 下所有 consumers;
- 若存在记录,则仅允许白名单中的 consumers。
Model 白名单查询
- 查询
route_allowed_models; - 若配置了 allowlist,则按解析后的
model_name做白名单收缩。
Tenant / Upstream / Model Routing 查询
- 查询
tenants; - 查询
providers; - 查询
upstreams; - 查询
upstream_models; - 查询
model_routing_policies; - 查询
model_routing_targets。
运行时解析建议:
- 先通过
upstreams.provider_id取到 provider 默认配置; - 再将 upstream 的
base_url_override/headers_override/compat_override合并到 provider 默认值; - 最终按 provider 的
protocol选择协议实现。
Pricing 查询
- 查询
pricing_plans; - 查询
pricing_plan_items; - 以解析后的
model_name作为计费模型名; - 基于命中的 upstream 解析其
provider_id; - 按 provider override 优先于 global default 的规则解析 Credit 单价。
10.3 数据面查询索引建议
建议优先保证以下查询索引:
routes(path_prefix);consumer_api_keys(lookup_hash);providers(source_scope, source_ref_id, status);route_allowed_consumers(route_id, consumer_id);route_allowed_models(route_id, model_name);upstreams(tenant_id, status);model_routing_policies(tenant_id, model_name);model_routing_targets(policy_id, enabled)。
10.4 发布语义
publish在单事务内更新当前生效业务数据,并更新publish_state;- 首版不保存历史
runtime snapshot; - 首版不提供配置回滚;
- 若后续确认性能瓶颈,再评估引入数据面缓存或额外的
runtime派生表。
11. 计量与账单相关表设计
虽然 usage 记录会在主流程完成写入,本节用于描述后续聚合、账单与审计所需的相关表结构。
11.1 usage_events
原始 usage 事件表。
| 字段 | 类型 | 说明 |
|---|---|---|
id | text pk | 事件 ID,格式 uev_<ulid> |
request_id | text not null | 请求 ID |
occurred_at | timestamptz not null | 发生时间 |
config_hash | text null | 请求命中的当前配置 hash(仅用于观测) |
tenant_id | text not null | tenant |
route_id | text not null | route |
consumer_id | text null | consumer |
api_key_id | text null | api key |
provider_id | text null | 命中的 provider |
upstream_id | text null | upstream |
model_name | text null | 对外模型名 |
upstream_model | text null | 实际模型名 |
input_tokens | bigint null | 输入 token |
output_tokens | bigint null | 输出 token |
input_characters | bigint null | 输入字符数 |
output_characters | bigint null | 输出字符数 |
estimated_credit | bigint null | 预估消耗 Credit |
status_code | int not null | HTTP 状态 |
error_type | text null | 错误分类 |
latency_ms | int null | 延迟 |
payload | jsonb not null default '{}' | 额外上下文 |
索引建议:
index (occurred_at desc)index (tenant_id, occurred_at desc)index (consumer_id, occurred_at desc)index (model_name, occurred_at desc)
11.2 daily_usage_aggregates
按天聚合的 usage。
| 字段 | 类型 | 说明 |
|---|---|---|
usage_date | date not null | 日期 |
tenant_id | text not null | tenant |
consumer_id | text null | consumer |
api_key_id | text null | api key |
model_name | text not null | model |
request_count | bigint not null | 请求数 |
input_tokens | bigint not null | 输入 token |
output_tokens | bigint not null | 输出 token |
total_credit | bigint not null | 聚合后的 Credit 消耗 |
约束建议:
primary key (usage_date, tenant_id, consumer_id, api_key_id, model_name)
11.3 billing_ledger
可用于账单追溯的记账表。
| 字段 | 类型 | 说明 |
|---|---|---|
id | text pk | 账目 ID,格式 bll_<ulid> |
usage_event_id | text not null | 对应 usage event |
pricing_plan_id | text not null | 命中的 pricing plan |
pricing_plan_item_id | text not null | 命中的明细价格 |
credit_amount | bigint not null | 入账 Credit |
created_at | timestamptz not null | 入账时间 |
用途:
- 解释某次请求为什么是这个 Credit 金额;
- 支撑重算与对账;
- 支撑 provider override 与 global default 的优先级审计。
12. 关键实现建议
12.1 API Key 校验建议
当前确认的入口规范:
- 只支持
Authorization: Bearer <token>; - 暂不支持
X-API-Key; - Bearer token 统一映射到
consumer_api_keys。
首版为了降低复杂性,建议同时保存明文与查找索引:
key_prefix:用于展示;key_plaintext:首版直接明文存储;lookup_hash:用于 O(1) 查找。
这样数据面可先用 lookup_hash 命中;后续引入 KMS 时,可把 key_plaintext 演进为密文或 secret ref,而不改变查找路径。
12.2 模型名与别名建议
首版统一使用正常的 model_name,不再单独引入额外的模型归一化层。
建议采用四层含义:
model:调用方在请求里传入的原始字符串;model_name:tenant 对外暴露的统一模型名;route allowlist、quota、计费、usage / billing 全部使用该值;aliases:同一个model_name的额外别名;upstream_model:实际发送给某个 upstream 的模型名。
推荐流程:
- 请求进入后,先用请求里的
model去匹配upstream_models.model_name或aliases,解析出统一的model_name; route_allowed_models、model_routing_policies、pricing_plan_items、usage / billing 全部按这个model_name工作;- 最终把命中的
upstream_model发给上游。
发布校验需保证:同一 tenant 下,model_name 与 aliases 组成的命名空间不能冲突。
这样首版可以先满足“模型别名 / 模型映射 / 多 upstream 路由”的需求,而不引入额外的模型归一化层复杂度。
12.3 Provider / Protocol 建议
建议将 provider、protocol、upstream 三者的职责拆清:
- route 只表达“能不能用”
- tenant model routing 表达“应该去哪”
- provider 表达“默认怎么连、默认怎么解释协议”
- upstream 表达“这次具体连哪个 endpoint,用哪些覆盖项”
- protocol implementation 表达“怎么转过去”
补充约束:
- 代码中不要硬编码 provider 类型列表;
- runtime 只按
protocol选择协议实现; protocol的语义与aidy-models中Provider.api对齐;- upstream 允许覆盖
base_url/headers/compat,但不允许覆盖protocol; upstream_models负责保存实际模型名,以及 tenant 侧暴露的model_name/aliases。
12.4 当前生效数据发布要原子化
建议发布流程中,以下动作必须放在同一事务边界中:
- 覆盖更新生效的
routes - 覆盖更新生效的
providers - 覆盖更新生效的
consumers/consumer_api_keys - 覆盖更新生效的
upstreams/upstream_models/model_routing_* - 覆盖更新关联关系表,如
route_allowed_consumers/route_allowed_models - 更新
publish_state
否则 router 可能直接查到“半成品配置”。
12.5 router 首版直接查库
V2 的正确姿势应该仍然是:
- 启动时只初始化必要连接;
- 请求时直接查询当前生效业务表;
- 不做进程内缓存,也不做 runtime 拆表;
- 热路径计数仍走 Redis;
- usage 记录与计量所需写入在主流程完成。
这点的目标是先降低实现复杂性,后续若出现性能瓶颈再优化。
13. 当前建议的最小可落地版本(MVP)
如果希望尽快启动 V2 开发,建议第一版只做以下最小闭环:
MVP 功能
- 控制面:
- tenant / route / upstream / consumer / api key CRUD
- validate
- publish
- 数据面:
- route / tenant / api key 的 PG 直查
- route -> tenant -> consumer auth
- route allowed consumers
- route allowed models
- tenant 多 upstream 的 single / priority fallback
- 运行时:
- Redis 限速
- Consumer Quota / API Key Quota 的 PG 强一致扣减
- 在主流程完成 usage 记录与计量所需写入
- 计费:
- global default credit pricing
- provider credit pricing override
MVP 可以暂缓的能力
- weighted LB 的复杂自适应调度
- 脱敏能力
- 丰富的 DLP DSL
- 精细化的 response-side 数据治理
- 法币充值、法币对账与 Credit 换算体系
- 高级 protocol adapter 模板化
14. 待确认问题
下面这些问题会影响表设计的最终定稿,建议你确认一下:
- alias 的匹配规则
- 是否大小写敏感?
- 是否要求同一 tenant 下全局唯一?
14.1 已确认决策
- API key 当前只支持
Authorization: Bearer <token>; - V2 不保留
route.auth兼容层; - V2 运行时只支持 PG,不兼容
file、redis-v1、redis-v2; - upstream 与 API key 凭证首版在 PG 中明文存储,后续再引入 KMS;
- 实时 rate limit 继续使用 Redis;Consumer Quota / API Key Quota 在 PG 中强一致扣减;
- 首版暂不做脱敏、敏感信息检测、数据出网策略;
- 首版计费粒度只做
tokens / requests,并统一记为整数 Credit; - 首版数据面不做内存缓存;
- 首版不做
runtime表拆分,所有表均使用默认 PG schema。 - 本期增加
all in protobuf重构约束; - management API 统一使用 Connect RPC;
- 请求日志记录插件统一使用 protobuf 定义日志格式。
如果你确认这些问题,我下一步可以继续把这份设计收敛成:
- 更接近实现的 SQL migration 草案;
- 控制面 Connect RPC / resource 设计;
- proto / Connect / OpenAPI 衍生草图;
- V1 -> V2 的导入迁移脚本方案。