跳到主要内容

aidy-router V2 功能计划与数据库表设计

状态:草案 v0.1 目标:将当前以静态/动态配置驱动的 aidy-gateway,演进为具备独立控制面、PG 配置中心、多上游调度、消费者体系、配额与计费能力的 aidy-router


1. 背景与设计目标

V1 的核心价值是“轻量可配置的 AI Gateway”,重点在于:

  • route 转发到单一 upstream;
  • tenant 做基础限速;
  • route 级做能力开关、Guard 与自定义限速;
  • 通过动态配置实现热更新。

V2 的目标不是在 V1 上继续堆字段,而是把产品形态从“配置驱动的网关”升级为“带控制面、发布面和运行面的完整路由系统”。

V2 的核心变化:

  1. 增加独立控制面
    • 管理租户、路由、消费者、上游、模型计费、发布版本;
    • 负责配置校验、编译、发布与审计;
    • 不再把“编辑配置文件/Redis Hash”作为主要管理方式。
  2. 增加 PostgreSQL
    • 控制面与数据面都直接连接 同一个 RW PG,作为事实数据源(source of truth);
    • 数据面在主流程中既读取当前生效配置,也写入 quota / usage 等强一致性数据;
    • V2 运行时不再兼容 fileredis-v1redis-v2 等旧 dynamic config source,只支持 PG;
    • 路由实例不直接依赖控制面 API,可在控制面故障时继续运行。
  3. 租户模型升级
    • 从“tenant 只有限速”升级为“tenant 拥有 upstream、模型路由、消费者与配额”,计费由全局默认与 provider 覆盖决定。
  4. 路由模型升级
    • 从“route 决定唯一 upstream”升级为“route 负责入口治理与访问约束,真正的模型选择和上游选择在 tenant 级完成”。

2. 现状梳理:V1 能力与 V2 目标

2.1 当前仓库里 V1 已有能力

结合当前 proto、文档与实现,V1 已落地的核心能力可以整理为:

租户级(tenant

  • inbound_rate_limit_qps
  • detect_rate_limit_cpm

路由级(route

  • 基础信息:idnameprefixtenant_id
  • 单 upstream 配置:
    • type
    • url
    • token
    • extra_headers
    • max_idle_conns_per_host
  • 路由访问认证:auth.bearer.tokens
  • passthrough_auth_token
  • 能力开关:
    • openai-models
    • openai-chat
    • openai-embeddings
    • forward-unknown
  • 路由标签:labels
  • 路由插件:
    • Guard
    • Inbound Rate Limit
    • Upstream Rate Limit

动态配置形态

当前动态配置的主模型仍是“整份配置 / Redis Hash -> 构造运行态 route”:

  • route/<prefix> -> entity.Route
  • tenant/<tenant-id> -> entity.Tenant
  • plugin/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 原子发布”的简化模型:

  1. 控制面将编辑中的配置写入核心业务表中的草稿态记录;
  2. 控制面执行完整校验:
    • 引用完整性;
    • 路由冲突;
    • tenant / route / consumer / upstream 之间的约束;
    • 模型路由是否可达;
    • 计费 / Credit 配置是否有缺口;
  3. 控制面发起 publish 时,在单个事务中完成:
    • 将目标草稿集提升为 active
    • 将被替换的旧生效记录置为 disabled / 软删除;
    • 覆盖更新关联关系表;
    • 更新 publish_state
  4. 事务提交前,aidy-router 继续读取旧的已发布数据;
  5. 事务提交后,aidy-router 后续请求直接读取新的已发布数据。

首版 不保存历史配置,也 不提供配置回滚。 如果发布失败,直接事务回滚;如果发布成功,则以 PG 中的新发布态为准。
首版不再向 fileredis-v1redis-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-modelsProvider.api 的语义;
    • 字段可分为元数据(如 namedescription)和运行时默认值(如 protocolbase_urlheaderscompat)。
  • Upstream
    • 可实际出网调用的后端目标;
    • 必须绑定一个 provider,并默认继承 provider 的运行时字段;
    • 可覆盖 base_urlheaderscompat、认证等默认值,但不可覆盖 protocol
    • 承载连接池、健康检查、权重/优先级等运行时配置;
    • 归属于 tenant。
  • Upstream Model
    • 某个 upstream 上支持的具体模型定义;
    • 把 tenant 对外暴露的 model_name / aliases 映射到 upstream 实际模型名。
  • Model Routing Policy
    • 描述某个 tenant 下,一个 model_name 应如何在多个 upstream model 之间进行选择;
    • 支持 single / weighted LB / priority fallback。
  • 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 的优先级固定下来,避免后续实现分叉:

  1. 模型计费
    • provider pricing > global default pricing
    • 所有计费结果一律为整数 Credit
  2. 模型可用范围
    • tenant model routing 定义“可供选择的全集”
    • route allowed models 做进一步收缩
  3. 消费者可用范围
    • tenant consumers 定义“可认证的全集”
    • route allowed consumers 做进一步收缩
    • 若 route 未配置 allowed consumers,则默认允许该 tenant 下所有 consumers
  4. 限速
    • tenant 基础限速始终生效
    • route 自定义限速为附加限制,不是覆盖
    • API key 独立限额为附加限制,不是覆盖
  5. 治理策略
    • 首版只迁移现有的 Guard 插件以适配 V2
    • 不新增脱敏、敏感信息检测、数据出网策略等新的业务侧治理功能
    • tenant/global 级策略如后续引入,应采用“默认 + route override”的模型
  6. 兼容行为
    • passthrough_auth_token 当前仍保留,但已进入计划弃用范围
    • 当 route 开启该功能时,沿用当前行为:使用路由配置的 token 作为下游 token

4.4 工程重构约束

本期开始增加一个轻量但明确的重构项:all in protobuf

记录原则即可,不额外展开复杂设计:

  • 所有跨模块、跨进程、需长期维护的数据契约,优先以 protobuf 作为唯一 schema source;
  • JSON / YAML / OpenAPI 等展示或交互格式,均视为 protobuf 的派生表示,而不是并列主定义;
  • 新增或重构接口时,优先先定义 protobuf message / service,再生成或适配具体接入层。

本期明确落地两项:

  1. management API 也使用 Connect RPC
    • 控制面的管理接口统一按 protobuf service 定义;
    • 服务暴露形式统一为 Connect RPC;
    • 如仍需 OpenAPI 或 HTTP 文档,应从 protobuf / Connect 定义派生,而不是手写一套独立契约。
  2. 请求日志记录插件使用 protobuf 定义格式
    • 请求日志记录结构由 protobuf message 定义;
    • 各类日志输出目标(如 HTTP / Kafka)基于同一份 protobuf 日志结构做序列化或投递;
    • 日志字段的增删改应优先修改 protobuf 定义,再同步影响文档与下游消费方。

5. 请求执行链路设计

建议数据面按如下顺序处理请求:

  1. 路由匹配
    • 根据 path prefix 命中 route;
    • 取得 route 绑定的 tenant。
  2. 消费者认证
    • 当前版本只从 Authorization: Bearer <token> 提取 API key;
    • 计算 lookup_hash 后直接查询 PG 中的 consumer_api_keys
    • 得到 consumerapi_key
  3. 入口访问限制
    • 若 route 配置了 allowed consumers,则校验 consumer 是否在白名单中;
    • 若 route 未配置 allowed consumers,则默认允许该 tenant 下所有 consumers;
    • 若 route 处于禁用状态,直接拒绝。
  4. 限速与配额预检查
    • tenant 基础限速;
    • route 自定义限速;
    • API key 独立限额;
    • consumer / API key quota 在 PG 事务中做强一致性预检查与扣减。
  5. 模型解析
    • 从请求中提取调用方传入的 model
    • 先按 upstream_models.model_name / aliases 解析出统一的 model_name
    • 若 route 配置了 allowed models,则按解析后的 model_name 做白名单校验;
    • 在 tenant model routing policy 中找到该 model_name 对应的候选 upstream model 集合。
  6. 请求治理
    • 首版仅迁移现有 Guard 插件到 V2 请求链路;
    • 不新增脱敏、敏感信息检测、数据出网策略等业务侧治理功能;
    • 若命中 Guard 的 block 策略,则直接拒绝。
  7. 上游选择
    • 按 routing policy 选择 upstream target;
    • 执行协议转换;
    • 附加 upstream 的 auth/header/连接池参数;
    • 进行实际转发。
  8. 失败处理
    • 若策略是 weighted LB,则选定一个目标后执行;
    • 若策略是 priority fallback,则按优先级依次尝试;
    • 失败原因与 retry/fallback 过程需打点。
  9. 响应治理
    • 如保留 Guard response 检测,则在返回前执行;
    • 记录 usage、延迟、状态码、上游命中信息。
  10. 异步计费/统计
  • 将 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,不再兼容 fileredis-v1redis-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_qpstenant 绑定的 rate_limit_policy直接迁移
tenant.detect_rate_limit_cpmtenant 绑定的 detect/rate policy直接迁移
route.upstreamproviders + tenant.upstreams + tenant.model_routing_policy从“路由唯一上游”改为“provider 模板 + upstream + 租户模型路由”
route.auth.bearer.tokenstenant.consumer + consumer_api_keys从 route 级单 token 升级为 tenant 级消费者模型;一个 consumer 可绑定多个 key
route.passthrough_auth_tokenroute.passthrough_auth_token当前保留,后续计划弃用
route.capabilitiesupstream.capabilities + route allowlistroute 不再扩大 upstream 能力
route.plugin_config.guardroute.guard_policy_id基本可平移
route.plugin_config.rate_limitroute.rate_limit_policy_id直接迁移

7.2 迁移策略

由于 V2 运行时只支持 PG,不再兼容 fileredis-v1redis-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 体系(包括 fileredis-v1redis-v2)。 PG 是 V2 唯一配置来源。

8.2 ID 规范与来源字段

首版不再区分“内部 UUID 主键”和“外部业务键”。
所有实体主键及其引用字段统一使用 text,格式固定为:

  • <prefix>_<ulid>

约束:

  • <prefix> 尽量控制在 2-3 个字符,极端不超过 5 个字符;
  • <prefix> 必须全局唯一,避免不同表之间产生歧义;
  • <prefix> 应尽量与实体原始单词或词组对应;
  • 同一张表允许存在多种 prefix,只要这些 prefix 能表达来源差异。
  • 程序侧所有 ID 字段统一使用完整字符串,不拆分保存裸 ULID。

推荐 prefix:

实体 / 表prefix说明
tenantstn_tenant
routesrt_route
providersgp_ / tp_global provider / tenant provider
upstreamsups_upstream
upstream_modelsum_upstream model
model_routing_policiesmrp_model routing policy
model_routing_targetsmrt_model routing target
pricing_plansgpp_ / ppp_global pricing plan / provider pricing plan
pricing_plan_itemsgpi_ / ppi_global pricing item / provider pricing item
consumerscs_consumer
consumer_api_keyscak_consumer api key
rate_limit_policiesrlp_rate limit policy
quota_policiesqpl_quota policy
quota_policy_itemsqpi_quota policy item
guard_policiesggd_ / tgd_global guard / tenant guard
desensitization_policiesgds_ / tds_global / tenant desensitization
dlp_policiesgdl_ / tdl_global / tenant dlp
audit_logsaud_audit log
usage_eventsuev_usage event
billing_ledgerbll_billing ledger

说明:

  • publish_stateroute_allowed_consumersroute_allowed_modelsdaily_usage_aggregates 这类表可以继续使用自然主键或组合主键,不强制新增独立 id
  • 所有业务 ID 字段(主键、外键、owner_idsource_ref_idresource_id)都直接保存完整的 text id,例如 tn_xxxrt_xxxgp_xxx

对于同一张表中可能同时承载 global / tenant / route / provider 等多个来源的数据,除了在 id prefix 上体现来源外,还统一增加两个专门字段:

  • source_scope text not null
  • source_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 Credit1 CNY = 1_000_000 Credit
  • 上述换算只是业务接入建议,router 内部只消费和产出整数 Credit。

9. 核心业务表设计

9.1 tenants

租户主表。

字段类型说明
idtext pk主键,格式 tn_<ulid>
nametext not null租户名称
statustext not nullactive / disabled / draft
metadatajsonb not null default '{}'扩展字段
created_attimestamptz not null创建时间
updated_attimestamptz not null更新时间
deleted_attimestamptz null软删除

索引建议:

  • index (status)

9.2 routes

路由主表,只负责入口与治理,不承载唯一 upstream。

字段类型说明
idtext pk主键,格式 rt_<ulid>
tenant_idtext not null fk -> tenants.id所属 tenant
nametext not null路由名称
path_prefixtext not null路由前缀,如 /demo
statustext not nullactive / disabled / draft
rate_limit_policy_idtext null fk -> rate_limit_policies.id路由自定义限速策略
guard_policy_idtext null fk -> guard_policies.idGuard 策略
desensitization_policy_idtext null fk -> desensitization_policies.id脱敏策略(预留,首版不实现)
dlp_policy_idtext null fk -> dlp_policies.id数据出网策略(预留,首版不实现)
passthrough_auth_tokenboolean not null default false当前仍保留;开启时使用路由配置的 token 作为下游 token;后续计划弃用
metadatajsonb not null default '{}'扩展字段
created_attimestamptz not null创建时间
updated_attimestamptz not null更新时间
deleted_attimestamptz null软删除

约束/索引建议:

  • unique (path_prefix) where deleted_at is null
  • index (tenant_id, status)
  • check (path_prefix ~ '^/[a-zA-Z0-9_-]+$')

9.3 providers

Provider 模板表。

Provider 可来源于全局默认或 tenant 自定义。代码本身不内置 provider 类型;运行时只根据 protocol + compat 工作。
其中 protocol 的语义对应 aidy-modelsProvider.api

字段类型说明
idtext pk主键,格式 gp_<ulid>tp_<ulid>
source_scopetext not nullglobal / tenant
source_ref_idtext not null default ''全局为空字符串;tenant 级为 tn_xxx
nametext not null展示名称
descriptiontext null描述
protocoltext not null运行时协议,语义对应 aidy-models.Provider.api
base_urltext null默认 base URL
headersjsonb not null default '{}'默认静态 headers
compatjsonb not null default '{}'默认兼容性配置
metadatajsonb not null default '{}'其余元数据,如 doc/url/check model/source
statustext not nullactive / disabled / draft
created_attimestamptz not null创建时间
updated_attimestamptz not null更新时间
deleted_attimestamptz null软删除

约束/索引建议:

  • unique (source_scope, source_ref_id, name) where deleted_at is null
  • index (source_scope, source_ref_id, status)

9.4 upstreams

tenant 下的上游目标。

创建 upstream 时必须选择一个 provider。
upstream 默认继承 provider 的 protocol / base_url / headers / compat;其中只有 base_urlheaderscompat 可覆盖,protocol 不可覆盖。

字段类型说明
idtext pk主键,格式 ups_<ulid>
tenant_idtext not null fk -> tenants.id所属 tenant
provider_idtext not null fk -> providers.id绑定的 provider 模板
nametext not null上游名称
base_url_overridetext null覆盖 provider 默认 base URL
headers_overridejsonb not null default '{}'覆盖 / 追加 provider 默认 headers
compat_overridejsonb not null default '{}'覆盖 provider 默认 compat
secret_valuetext null首版直接明文存储的上游凭证
capabilitiestext[] not null default '{}'上游能力开关
max_idle_conns_per_hostint null连接池参数
lb_weightint not null default 100默认权重
priorityint not null default 100默认优先级
health_policyjsonb not null default '{}'健康探测 / 熔断参数
statustext not nullactive / disabled / draft
created_attimestamptz not null创建时间
updated_attimestamptz not null更新时间
deleted_attimestamptz null软删除

约束/索引建议:

  • index (tenant_id, status)
  • index (tenant_id, provider_id)

9.5 upstream_models

定义某个 upstream 上可用的模型、映射与别名。

字段类型说明
idtext pk主键,格式 um_<ulid>
upstream_idtext not null fk -> upstreams.id所属 upstream
model_nametext not null对外暴露并统一用于路由/计费的模型名
aliasestext[] not null default '{}'该模型的额外别名
upstream_modeltext not nullupstream 实际调用的模型名
model_featuresjsonb not null default '{}'模型能力补充信息
weightint not null default 100默认权重
priorityint not null default 100默认优先级
statustext not nullactive / disabled / draft
created_attimestamptz not null创建时间
updated_attimestamptz not null更新时间

约束/索引建议:

  • unique (upstream_id, model_name, upstream_model)
  • index (model_name)
  • gin (aliases)

9.6 model_routing_policies

tenant 下统一 model_name 的路由策略主表。

字段类型说明
idtext pk主键,格式 mrp_<ulid>
tenant_idtext not null fk -> tenants.id所属 tenant
model_nametext not null统一使用的对外模型名
strategytext not nullsingle / weighted / priority_fallback
failure_policyjsonb not null default '{}'超时、重试、熔断与失败处理
timeout_msint null默认超时时间
metadatajsonb not null default '{}'扩展字段
created_attimestamptz not null创建时间
updated_attimestamptz not null更新时间

约束/索引建议:

  • unique (tenant_id, model_name)
  • index (tenant_id)

9.7 model_routing_targets

路由策略的候选目标列表。

字段类型说明
idtext pk主键,格式 mrt_<ulid>
policy_idtext not null fk -> model_routing_policies.id所属策略
upstream_model_idtext not null fk -> upstream_models.id指向候选目标
priorityint not null default 100fallback 优先级
weightint not null default 100weighted LB 权重
max_retry_countint not null default 0单 target 最大重试次数
condition_exprjsonb null预留:条件路由
enabledboolean not null default true是否启用

约束/索引建议:

  • unique (policy_id, upstream_model_id)
  • index (policy_id, enabled)

9.8 pricing_plans

全局默认与 provider override 的计费方案主表。

字段类型说明
idtext pk主键,格式 gpp_<ulid>ppp_<ulid>
source_scopetext not nullglobal / provider
source_ref_idtext not null default ''全局为空字符串;provider 级为 gp_xxxtp_xxx
nametext not null名称
is_defaultboolean not null default false是否默认
effective_fromtimestamptz not null生效时间
effective_totimestamptz null失效时间
statustext not nullactive / disabled / draft
created_attimestamptz not null创建时间
updated_attimestamptz 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

字段类型说明
idtext pk主键,格式 gpi_<ulid>ppi_<ulid>
plan_idtext not null fk -> pricing_plans.id所属 plan
model_nametext not null统一用于计费的模型名
meter_typetext not nullinput_tokens / output_tokens / requests
unit_sizebigint not null计费单位,如 1000 tokens
unit_creditbigint not null整数 Credit 单价
metadatajsonb not null default '{}'预留扩展
effective_fromtimestamptz not null生效时间
effective_totimestamptz null失效时间

约束/索引建议:

  • unique (plan_id, model_name, meter_type, effective_from)
  • index (plan_id, model_name)

9.10 consumers

tenant 下的调用方实体。

字段类型说明
idtext pk主键,格式 cs_<ulid>
tenant_idtext not null fk -> tenants.id所属 tenant
nametext not null名称
statustext not nullactive / disabled / draft
quota_policy_idtext null fk -> quota_policies.id默认 Credit Quota 策略
remaining_creditbigint not null default 0当前剩余 Credit
credit_versionbigint not null default 0用于并发控制的版本号
metadatajsonb not null default '{}'扩展字段
created_attimestamptz not null创建时间
updated_attimestamptz not null更新时间
deleted_attimestamptz null软删除

约束/索引建议:

  • index (tenant_id, status)

9.11 consumer_api_keys

API Key 明细表。

字段类型说明
idtext pk主键,格式 cak_<ulid>
consumer_idtext not null fk -> consumers.id所属 consumer
key_prefixtext not null用于展示与排查
key_plaintexttext not null首版直接明文存储的 API key
lookup_hashbytea not null unique用于快速查找的稳定 hash
statustext not nullactive / disabled / revoked / expired
expires_attimestamptz null过期时间
rate_limit_policy_idtext null fk -> rate_limit_policies.idAPI key 限速策略
quota_policy_idtext null fk -> quota_policies.idAPI key Credit Quota 策略
remaining_creditbigint not null default 0当前剩余 Credit
credit_versionbigint not null default 0用于并发控制的版本号
last_used_attimestamptz null最近使用时间
created_attimestamptz not null创建时间
revoked_attimestamptz null吊销时间

索引建议:

  • unique (lookup_hash)
  • index (consumer_id, status)
  • index (expires_at)

9.12 rate_limit_policies

通用限速策略表。

字段类型说明
idtext pk主键,格式 rlp_<ulid>
nametext not null策略名称
algorithmtext not nullsliding_window / token_bucket
metrictext not nullrequests / characters / tokens
limit_valuebigint not null限额值
window_secondsint not null时间窗口
burst_valuebigint null突发值
backendtext not null default 'redis'实时判定 backend
configjsonb not null default '{}'扩展字段
created_attimestamptz not null创建时间
updated_attimestamptz not null更新时间

9.13 rate_limit_bindings

把限速策略绑定到 tenant / route / api_key。

字段类型说明
owner_typetext not nulltenant / route / api_key
owner_idtext not null绑定对象 ID,如 tn_xxx / rt_xxx / cak_xxx
stagetext not nullinbound / detect / upstream
policy_idtext not null fk -> rate_limit_policies.id策略 ID
priorityint not null default 100执行顺序

约束/索引建议:

  • primary key (owner_type, owner_id, stage, policy_id)
  • index (policy_id)

9.14 quota_policies

Credit 配额策略主表。

字段类型说明
idtext pk主键,格式 qpl_<ulid>
nametext not null策略名称
period_typetext not nulldaily / monthly / rolling_window
period_valueint nullrolling window 秒数等
timezonetext null日/月结算时区
on_exhaustedtext not nullblock / soft_limit / log_only
configjsonb not null default '{}'扩展字段
created_attimestamptz not null创建时间
updated_attimestamptz not null更新时间

9.15 quota_policy_items

Credit 配额明细。

字段类型说明
idtext pk主键,格式 qpi_<ulid>
policy_idtext not null fk -> quota_policies.id所属策略
metrictext not nullcredit
hard_limit_creditbigint not null硬限制 Credit
soft_limit_creditbigint null软限制 Credit

约束/索引建议:

  • unique (policy_id, metric)

9.16 guard_policies

Guard 策略表。

字段类型说明
idtext pk主键,格式 ggd_<ulid>tgd_<ulid>
source_scopetext not nullglobal / tenant
source_ref_idtext not null default ''全局为空字符串;tenant 级为 tn_xxx
nametext not null名称
modetext not null默认处理方式
rule_setjsonb not nullGuard 规则 DSL
versionint not null default 1DSL 版本
statustext not nullactive / disabled / draft
created_attimestamptz not null创建时间
updated_attimestamptz not null更新时间

9.17 desensitization_policies

预留表:首版暂不实现脱敏能力,但建议预留表名与字段位置,避免后续命名漂移。

脱敏策略表。

字段类型说明
idtext pk主键,格式 gds_<ulid>tds_<ulid>
source_scopetext not nullglobal / tenant
source_ref_idtext not null default ''全局为空字符串;tenant 级为 tn_xxx
nametext not null名称
rule_setjsonb not null脱敏规则 DSL
versionint not null default 1DSL 版本
statustext not nullactive / disabled / draft
created_attimestamptz not null创建时间
updated_attimestamptz not null更新时间

9.18 dlp_policies

预留表:首版暂不实现 DLP 能力,但建议预留表名与字段位置,避免后续命名漂移。

数据出网策略表。

字段类型说明
idtext pk主键,格式 gdl_<ulid>tdl_<ulid>
source_scopetext not nullglobal / tenant
source_ref_idtext not null default ''全局为空字符串;tenant 级为 tn_xxx
nametext not null名称
rule_setjsonb not nullDLP / egress 规则 DSL
versionint not null default 1DSL 版本
statustext not nullactive / disabled / draft
created_attimestamptz not null创建时间
updated_attimestamptz not null更新时间

9.19 route_allowed_consumers

route 对 consumer 的白名单。

若某个 route 在该表中没有任何记录,表示 默认允许该 tenant 下所有 consumers

字段类型说明
route_idtext not null fk -> routes.idroute
consumer_idtext not null fk -> consumers.idconsumer

约束建议:

  • primary key (route_id, consumer_id)

9.20 route_allowed_models

route 对 model 的白名单。

字段类型说明
route_idtext not null fk -> routes.idroute
model_nametext not null允许的统一模型名

约束建议:

  • primary key (route_id, model_name)

9.21 publish_state

当前发布态主表。

该表只记录 当前 发布信息,不保存历史配置版本。

字段类型说明
slottext pk预留多环境/多集群,默认 main
config_hashtext not null当前配置整体 hash
data_versionbigint not null当前发布版本号(单调递增,但不保留历史)
validation_reportjsonb not null default '{}'最近一次校验摘要
published_bytext null发布人/系统
published_attimestamptz not null发布时间
notestext null备注

9.22 audit_logs

审计与追责基础表。

字段类型说明
idtext pk主键,格式 aud_<ulid>
actortext not null操作人/系统
actiontext not nullcreate / update / delete / publish
resource_typetext not nulltenant / route / upstream / ...
resource_idtext null对应实体 ID
change_summaryjsonb not null default '{}'变更摘要
created_attimestamptz 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_idguard_policy_idrate_limit_policy_idpassthrough_auth_token 等字段。

API Key 鉴权查询

  • 从 Bearer token 计算 lookup_hash
  • 查询 consumer_api_keys
  • 首版可按需继续比对 key_plaintext,后续切到 KMS 时保留同一查找路径;
  • 关联 consumerstenants,确认 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 事件表。

字段类型说明
idtext pk事件 ID,格式 uev_<ulid>
request_idtext not null请求 ID
occurred_attimestamptz not null发生时间
config_hashtext null请求命中的当前配置 hash(仅用于观测)
tenant_idtext not nulltenant
route_idtext not nullroute
consumer_idtext nullconsumer
api_key_idtext nullapi key
provider_idtext null命中的 provider
upstream_idtext nullupstream
model_nametext null对外模型名
upstream_modeltext null实际模型名
input_tokensbigint null输入 token
output_tokensbigint null输出 token
input_charactersbigint null输入字符数
output_charactersbigint null输出字符数
estimated_creditbigint null预估消耗 Credit
status_codeint not nullHTTP 状态
error_typetext null错误分类
latency_msint null延迟
payloadjsonb 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_datedate not null日期
tenant_idtext not nulltenant
consumer_idtext nullconsumer
api_key_idtext nullapi key
model_nametext not nullmodel
request_countbigint not null请求数
input_tokensbigint not null输入 token
output_tokensbigint not null输出 token
total_creditbigint not null聚合后的 Credit 消耗

约束建议:

  • primary key (usage_date, tenant_id, consumer_id, api_key_id, model_name)

11.3 billing_ledger

可用于账单追溯的记账表。

字段类型说明
idtext pk账目 ID,格式 bll_<ulid>
usage_event_idtext not null对应 usage event
pricing_plan_idtext not null命中的 pricing plan
pricing_plan_item_idtext not null命中的明细价格
credit_amountbigint not null入账 Credit
created_attimestamptz 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 的模型名。

推荐流程:

  1. 请求进入后,先用请求里的 model 去匹配 upstream_models.model_namealiases,解析出统一的 model_name
  2. route_allowed_modelsmodel_routing_policiespricing_plan_items、usage / billing 全部按这个 model_name 工作;
  3. 最终把命中的 upstream_model 发给上游。

发布校验需保证:同一 tenant 下,model_namealiases 组成的命名空间不能冲突。

这样首版可以先满足“模型别名 / 模型映射 / 多 upstream 路由”的需求,而不引入额外的模型归一化层复杂度。

12.3 Provider / Protocol 建议

建议将 provider、protocol、upstream 三者的职责拆清:

  • route 只表达“能不能用”
  • tenant model routing 表达“应该去哪”
  • provider 表达“默认怎么连、默认怎么解释协议”
  • upstream 表达“这次具体连哪个 endpoint,用哪些覆盖项”
  • protocol implementation 表达“怎么转过去”

补充约束:

  • 代码中不要硬编码 provider 类型列表;
  • runtime 只按 protocol 选择协议实现;
  • protocol 的语义与 aidy-modelsProvider.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. 待确认问题

下面这些问题会影响表设计的最终定稿,建议你确认一下:

  1. alias 的匹配规则
    • 是否大小写敏感?
    • 是否要求同一 tenant 下全局唯一?

14.1 已确认决策

  • API key 当前只支持 Authorization: Bearer <token>
  • V2 不保留 route.auth 兼容层;
  • V2 运行时只支持 PG,不兼容 fileredis-v1redis-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 的导入迁移脚本方案。