想象你是一家精品咖啡馆的店长:每位顾客的会员卡上记录着“今天最多能点几杯”,吧台的取餐牌控制着一秒钟能放多少杯出去,后厨的用量台账记下每杯咖啡消耗的豆子和牛奶,月底会计根据菜单价格生成结算单。AI 服务的配额与计费 API 设计,就是为每一类“顾客”(用户、租户、API 调用方)搭建这样一套经营基础设施——配额管上限、限流管节奏、计量管记录、计费管定价、账单管结算。本文带你从一张“会员卡”出发,看懂这套体系如何端到端运转,以及它在真实 AI 平台中如何落地。

岗位关联

配额与计费是 AI 服务商业化的“水电表”:掌握它,你才能设计出既扛得住高峰流量、又能精准算清每一分钱的大模型 API 平台。这是 AI 工程化从“能用”走向“可运营”的必备技能。

目录
  1. 可视化演示
  2. 配额管理维度
  3. 令牌桶限流
  4. 用量计量管道
  5. 计费模型设计
  6. 账单生成流程
  7. API设计规范
  8. 落地实践验证
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

通过令牌桶限流、用量计量管道与计费账单三模式,展示AI服务配额管理与计费系统的核心机制与工程实现。

本节配有交互动效(进入视口后自动加载,约 3KB)。

令牌桶限流 令牌桶以固定速率填充,请求消耗令牌,桶空时返回429 Allow iff Tokens≥Cost(req) 按用户/租户/API三维度限制

配额管理维度

咖啡馆里坐着三种人:拿着个人会员卡的散客、公司统一订的团餐客户、以及只喝特定单品豆的熟客。AI 平台也一样,我们需要用三把尺子来丈量“每个人能点多少”:用户维度(你是谁)、租户维度(你属于哪个组织)、API 维度(你调用的是哪个模型服务)。这就是配额管理(Quota)的三维坐标。

为什么要分三个维度?因为只看用户、不看看租户,一个公司开了 100 个小号就能绕过限制;只看租户、不看 API,重度调用 Embedding(向量化)接口的请求会和对话接口抢资源。三个维度各设一个上限,还要组合判定:单用户超限、或单租户超限、或单 API 超限,任一触发都拒绝放行。

实际系统中,配额类型通常分两类:速率配额(每秒请求数 QPS)和用量配额(每天 Token 总量)。两者要合成一个“综合配额占用率”来判断是否超限:

回到咖啡店:这张“会员卡”上同时印着个人杯数、公司月额度、指定单品限额三条规则,任一条见底,这杯就不卖了。三维配额的精髓,正是把“人、组织、服务”三个账本放在同一张卡上统一核算。

Q=wr⋅rusedrquota+wt⋅tusedtquota,超限判定: Q>1\displaystyle Q = w_{r} \cdot \frac{r_{used}}{r_{quota}} + w_{t} \cdot \frac{t_{used}}{t_{quota}}, \qquad \text{超限判定: } Q > 1
公式含义:把“当前请求速率占配额的比例”和“当前 Token 用量占配额的比例”按权重加权,得到一个综合占用率。综合占用率超过 100% 就拒绝新请求。
  1. 速率占用率计算当前每秒请求数 r_used 占速率配额上限 r_quota 的比例,反映“频率这条腿”踩了多深。
    rusedrquota\displaystyle \frac{r_{used}}{r_{quota}}
  2. 用量占用率计算已消耗的 Token 数 t_used 占 Token 配额上限 t_quota 的比例,反映“总量这条腿”踩了多深。
    tusedtquota\displaystyle \frac{t_{used}}{t_{quota}}
  3. 加权合成用权重 w_r 和 w_t 给两条腿加权(两者相加为 1),得到综合占用率 Q,权重取决于业务侧重速率还是侧重用量。
    Q=wr⋅rusedrquota+wt⋅tusedtquota\displaystyle Q = w_{r} \cdot \frac{r_{used}}{r_{quota}} + w_{t} \cdot \frac{t_{used}}{t_{quota}}
  4. 超限判定当 Q 大于 1 时,说明组合占用已超过“一条满额线”,系统对该请求返回配额超限错误。
    Q>1\displaystyle Q > 1

令牌桶限流

咖啡馆的咖啡豆不是一次倒满的,而是每隔一段时间往豆桶里补一把。如果突然来了十位客人,桶里攒下的豆子还能撑一轮;但如果豆子被舀空了,后到的客人只能等下一轮补货。令牌桶算法(Token Bucket)就是这个“豆桶”:它用两个参数控制节奏——生成速率 ρ(每秒补几个令牌)和桶容量 C(最多攒多少个令牌)。

每个请求到来时,需要从桶里取走一个令牌才放行;桶空则拒绝。数学上,令牌桶的运转分两步:先按时间补令牌,再判定是否放行:

令牌桶最迷人的地方在于突发流量(Burst)的处理:桶容量越大,允许的瞬时突发就越猛——相当于店里允许短时间的排队高峰,只要豆桶里有存货。这比“固定每秒 N 个请求”的计数器限流更符合真实业务:平时不抢资源,高峰时可以用平日攒下的小幅“额度”。

T←min⁡(C,  T+ρ⋅Δt),pass  ⟺  T≥1\displaystyle T \leftarrow \min\bigl(C,\; T + \rho \cdot \Delta t\bigr), \qquad \text{pass} \iff T \ge 1
公式含义:每隔 Δt 秒,桶里增加 ρ×Δt 个令牌,但总量不超过桶容量 C;请求只有当桶里至少还剩 1 个令牌时才被放行,放行后扣掉 1 个。
  1. 补入令牌每过 Δt 秒,就向桶里加入 ρ × Δt 个令牌——好比往豆桶里补一把豆子,补的量由生成速率 ρ 决定。
    T+ρ⋅Δt\displaystyle T + \rho \cdot \Delta t
  2. 容量裁剪补完后的令牌数不能超过桶容量,超出部分直接丢弃——豆桶满了就不会再往里塞,多余的豆子只能浪费掉。
    min⁡(C,  T+ρ⋅Δt)\displaystyle \min\bigl(C,\; T + \rho \cdot \Delta t\bigr)
  3. 放行判定当且仅当桶里令牌数 T 大于等于 1 时,请求获得放行资格。
    pass  ⟺  T≥1\displaystyle \text{pass} \iff T \ge 1
  4. 消费令牌放行后桶里令牌减少 1 个,为下一次请求做准备。
    T←T−1\displaystyle T \leftarrow T - 1

用量计量管道

后厨每做一杯咖啡,都要记一笔台账:用了多少克豆子、多少毫升奶、几只杯子。AI 平台的用量计量(Usage Metering)就是后厨的自动记账流水线——它回答一个核心问题:这次调用到底消耗了多少 Token。

计量管道通常分四段。第一段是请求预处理:解析请求头、校验 API Key、确认配额归属(是哪个用户、哪个租户、哪个 API),这一步相当于服务员确认“这单记在谁的会员卡下”。第二段是 Token 计数:把 prompt(输入文本)和 completion(模型输出)分别切成 Token 并统计数量。精确计数需要跑一遍模型的分词器(Tokenizer);在线请求要求低延迟,所以常用字符数近似估算:

第三段是计量事件流(Usage Event Stream):每次调用完成后,系统把 {user_id, tenant_id, api_id, prompt_tokens, completion_tokens, timestamp} 封装成一条不可变事件,写入消息队列。第四段是异步批处理:计费系统在后台批量消费事件流,定时落库——而不是在每个请求的返回路径上同步写数据库,避免拖慢主链路。这就像一个“缓存的账单抽屉”:先快速记账,月底再统一清点。

n^≈⌈c4⌉\displaystyle \hat{n} \approx \left\lceil \frac{c}{4} \right\rceil
公式含义:用字符总数 c 除以 4 后向上取整,粗略估算 Token 数量。英文场景下大约 4 个字符等于 1 个 Token,中文场景可调到更小的分母(如 1.5)。
  1. 统计字符数先把输入文本的字符总数 c 数出来。
    c=len(input)\displaystyle c = \text{len}(\text{input})
  2. 除以 4 估算用 4 个字符折算 1 个 Token,得到估算值。
    c4\displaystyle \frac{c}{4}
  3. 向上取整用取整符号保证估算值至少为 1,避免零 Token 的单子无法计费。
    ⌈c4⌉\displaystyle \left\lceil \frac{c}{4} \right\rceil

计费模型设计

菜单上,美式和拿铁价格不同;AI 计费也一样,不同模型单价不同,而且输入和输出的价格往往不同——输入相当于把豆子磨成粉(相对便宜),输出相当于精心拉花打奶泡(成本更高)。主流大模型平台都采用“输入 Token 单价 + 输出 Token 单价”的按量计费模型:

在多模型权重上,计费系统会维护一张模型价格表(如 gpt-4: 输入 $0.03/千Token、输出 $0.06/千Token),按实际调用的模型查表计价;不同模型配置不同的 (p_in, p_out)。对于高频大客户,还会引入阶梯计价:月用量超过阈值后,超出部分享受折扣单价——就像咖啡馆推出“办卡满 100 杯打九折”,既留住大客户,又平滑波峰。

fee=pin⋅nin+pout⋅nout\displaystyle fee = p_{in} \cdot n_{in} + p_{out} \cdot n_{out}
公式含义:一次调用的费用 = 输入 Token 数 × 输入单价 + 输出 Token 数 × 输出单价。输入输出分开计价,精确反映推理成本结构。
  1. 拆解用量把一次调用的 Token 数拆成输入部分 n_in 和输出部分 n_out。
    nin,nout\displaystyle n_{in}, \quad n_{out}
  2. 分别计价输入乘以输入单价 p_in,输出乘以输出单价 p_out——两块价格可能差好几倍。
    pin⋅nin+pout⋅nout\displaystyle p_{in} \cdot n_{in} + p_{out} \cdot n_{out}
  3. 求和得费用两部分相加,得到该次调用的费用 fee,作为后续记账和账单聚合的最小单位。
    fee=pin⋅nin+pout⋅nout\displaystyle fee = p_{in} \cdot n_{in} + p_{out} \cdot n_{out}

账单生成流程

月底到了,会计要干三件事:把散落在各处的消费小票按会员卡号归类、逐项加总、然后和收银系统核对一遍有没有漏单。AI 平台的账单生成就是把成千上万条计量事件按用户/租户聚合成一张张月度账单:

工程上,账单系统通常采用批次调度:每小时/每天跑一次聚合任务,而不是实时逐条出账,降低数据库压力。粒度聚合则强调“最小记账单位是事件、最大展示单位是账单”:明细可查、汇总可加、对账可拆。最后是对账闭环:账单金额必须和计量管道里原始事件的合计相等,若有偏差(比如消息重复消费导致多计费),则触发补偿任务自动纠正,确保每一分钱都有据可查。

Billu=∑e∈Sufeee,Su={e∣user(e)=u}\displaystyle Bill_{u} = \sum_{e \in S_{u}} fee_{e}, \qquad S_{u} = \bigl\{ e \mid \text{user}(e) = u \bigr\}
公式含义:用户 u 的月度账单,等于该用户在本月产生的所有计量事件 S_u 的费用之和。账单是按“事件的归属用户”筛选后聚合的结果。
  1. 筛选事件从全部计量事件中挑出归属用户 u 的那一批,记作集合 S_u。
    Su={e∣user(e)=u}\displaystyle S_{u} = \bigl\{ e \mid \text{user}(e) = u \bigr\}
  2. 逐项累加把集合中每个事件已算好的 fee_e 加总。
    ∑e∈Sufeee\displaystyle \sum_{e \in S_{u}} fee_{e}
  3. 输出账单加总结果就是该用户当月的账单金额。
    Billu=∑e∈Sufeee\displaystyle Bill_{u} = \sum_{e \in S_{u}} fee_{e}

API设计规范

顾客点单走固定窗口,店家回“已收款”或“卖完了”;AI 平台的配额与计费接口同样需要清晰的路由与状态码语义。设计规范可以分为四块。

第一块是资源路由设计:用 RESTful 风格将配额、计费视为资源。例如 POST /v1/quotas/{tenant_id} 为某租户设置配额,GET /v1/usage/{user_id}/events 查询用户的计量事件,POST /v1/bills/{user_id}/settle 触发账单结算。第二块是状态码语义:创建成功返回 201 Created,鉴权失败返回 401 Unauthorized,权限不足返回 403 Forbidden,配额超限返回 429 Too Many Requests 并附带 Retry-After 头告诉调用方多久后重试,服务端内部故障才返回 500。

第三块是幂等设计:计费和扣费接口必须支持重试不重复扣款。客户端每次提交扣费请求时带一个幂等键(Idempotency-Key),服务端用哈希标识“这笔请求是不是重复的”:

第四块是错误响应体:所有异常统一返回 JSON 结构 {"code": "QUOTA_EXCEEDED", "message": "...", "request_id": "req_123"}——机器可读的错误码、人类可读的提示、以及用于排查的请求追踪 ID。这套规范就像咖啡馆的“取餐号”:无论客人重试几次、服务员换了几班,都不会把同一杯咖啡重复做两遍。

k=hash(m,  p,  λ)\displaystyle k = \mathrm{hash}\bigl(m,\; p,\; \lambda\bigr)
公式含义:幂等键由请求方法 m、请求路径 p 和客户端生成的唯一标识 λ 组合哈希而成。相同请求重试时 k 相同,服务端识别为重复并返回第一次的结果,而不是再次扣费。
  1. 收集输入取请求方法 m(如 POST)、请求路径 p(如 /v1/bills)和客户端生成的唯一标识 λ。
    m,p,λ\displaystyle m, \quad p, \quad \lambda
  2. 组合哈希把三者拼接后做哈希运算,得到稳定的幂等键 k。
    k=hash(m,  p,  λ)\displaystyle k = \mathrm{hash}\bigl(m,\; p,\; \lambda\bigr)
  3. 存储比对首次请求将 k 存入缓存,后续相同请求查表命中,直接返回历史结果。
    find(k)→result\displaystyle \text{find}(k) \rightarrow \text{result}

落地实践验证

试营业一周后,店长要复盘三件事:高峰期客人排队多久、出杯速度够不够、有没有客人等不及走了。AI 平台的配额与计费系统上线前,也需要通过压测来验证三个核心指标。

第一是性能指标:重点关注网关处理请求的延迟分布,尤其是 P99 延迟——99% 的请求都能在这个时间内完成;同时观察限流器的有效请求率(放行比例)。第二是压测验证:用压测工具模拟比预期高 2-3 倍的流量,观察令牌桶是否按设定限流、计量管道是否丢事件、账单聚合是否按时产出。有效请求率的公式是:

第三是优雅降级:当配额超限时,不要直接粗暴拒绝,而是优先返回 429 + Retry-After 让客户端错峰重试;对可降级的请求(如实时对话降级为离线问答)可走备选模型。最后是案例复盘:每轮压测后,把“配额配置 → 限流表现 → 计量准确率 → 账单偏差”四个环节的日志串起来复盘,持续调优参数。店长靠复盘把排队从 10 分钟压到 2 分钟,AI 平台靠复盘把吞吐率从 80% 提到 99%——这就是配额与计费体系的闭环价值。

---

η=NpassNtotal\displaystyle \eta = \frac{N_{\text{pass}}}{N_{\text{total}}}
公式含义:有效请求率 η = 放行的请求数 ÷ 总请求数。它反映限流策略在多大比例上“接住了”业务流量,过高说明配额太松甚至形同虚设,过低说明配额太紧伤害正常业务。
  1. 统计总数统计一段压测时间内到达网关的总请求数 N_total。
    Ntotal\displaystyle N_{\text{total}}
  2. 统计放行数统计其中被令牌桶放行进入后端的请求数 N_pass。
    Npass\displaystyle N_{\text{pass}}
  3. 计算比例两者相除得到有效请求率,用于和业务目标对比。
    η=NpassNtotal\displaystyle \eta = \frac{N_{\text{pass}}}{N_{\text{total}}}

AI 应用场景

大模型 API 平台的配额售卖与按 Token 计费

阶段:API 网关的配额管理 + 计费结算环节

解决的问题:面向 B 端客户售卖大模型推理能力时,传统方式要么按 QPS 包月粗放计价(客户用不满、平台赚不足),要么根本无法核算每个客户的实际资源消耗。AI 方案通过三维配额(租户+用户+API)精细控制调用上限,用计量管道统计每次调用的输入/输出 Token,按模型单价自动计算费用,月终生成账单。

具体案例:某大模型 SaaS 平台为每个企业租户配置默认 100 QPS、每日 5000 万 Token 的配额;某客户在活动期间突增流量,令牌桶自动限流到 120 QPS 并返回 429;月底账单系统按租户聚合成账单,客户在控制台看到“调用次数 + Token 明细 + 费用”的三段式报表。

企业内部多部门共用大模型网关的成本分摊

阶段:租户配额隔离 + 账单分摊

解决的问题:企业内部多个业务线(搜索、客服、推荐)共用同一套大模型 API,传统方式按部门出钱“吃大锅饭”,无法定位是谁把预算烧完了。通过租户维度配额,每个部门独立配额、独立账单,财务可按月自动分摊成本到部门预算中心。

具体案例:某电商公司三个事业部共用企业大模型网关,客服部每天调用 80 万次、搜索部 50 万次、推荐部 200 万次;配额系统按部门设置独立上限,推荐部大促期间触发限流保护,账单系统自动生成三张独立账单供财务结算。

Python 演示

python85 行
# -*- coding: utf-8 -*-"""令牌桶限流与按量计费演示"""import random  class TokenBucket:    """令牌桶限流器"""     def __init__(self, capacity, rate):        self.capacity = capacity        self.rate = rate        self.tokens = float(capacity)     def update(self, dt):        self.tokens = min(self.capacity,                          self.tokens + self.rate * dt)     def try_acquire(self, cost=1.0):        if self.tokens >= cost:            self.tokens -= cost            return True        return False  class UsageEvent:    """一次推理请求的用量记录"""     def __init__(self, user_id, model, prompt_chars):        self.user_id = user_id        self.model = model        self.prompt_chars = prompt_chars        self.completion_chars = random.randint(50, 300)  class Biller:    """按 Token 计价器:输入输出单价不同"""     PRICES = {        "gpt-3.5":   (0.0015, 0.0020),        "gpt-4":     (0.0300, 0.0600),        "embedding": (0.0001, 0.0000),    }     def __init__(self):        self.total_fee = 0.0        self.bill_map = {}     def bill_event(self, event):        p_in, p_out = self.PRICES[event.model]        n_in = (event.prompt_chars + 3) // 4        n_out = (event.completion_chars + 3) // 4        fee = p_in * n_in + p_out * n_out        self.total_fee += fee        self.bill_map[event.user_id] = \            self.bill_map.get(event.user_id, 0.0) + fee        return fee  def simulate():    bucket = TokenBucket(capacity=8, rate=2.0)    biller = Biller()    passed = rejected = 0     for _ in range(60):        bucket.update(1.0)        for _ in range(random.randint(0, 3)):            event = UsageEvent(                user_id=random.choice(["alice", "bob", "carol"]),                model=random.choice(list(biller.PRICES.keys())),                prompt_chars=random.randint(200, 800),            )            if bucket.try_acquire():                passed += 1                biller.bill_event(event)            else:                rejected += 1     print("通过请求:", passed, "被拒请求:", rejected)    print("总费用: $", round(biller.total_fee, 4))    for uid, fee in sorted(biller.bill_map.items()):        print("账单 %s: $%s" % (uid, round(fee, 4)))  if __name__ == "__main__":    simulate()
目的:用 60 秒模拟数据串联令牌桶限流、Token 估算计量、按模型单价计费、按用户聚合账单的完整闭环。
  1. self.tokens = min(self.capacity, self.tokens + self.rate * dt)每过 dt 秒补入 rate×dt 个令牌,但不超过桶容量
  2. if self.tokens >= cost:有令牌才放行,对应正文的"pass ⇔ T ≥ 1"
  3. n_in = (event.prompt_chars + 3) // 4字符数除以 4 估算输入 Token 数
  4. fee = p_in * n_in + p_out * n_out核心计费公式,输入输出分开计价
  5. self.bill_map[event.user_id] = self.bill_map.get(event.user_id, 0.0) + fee按用户聚合事件费用,生成账单
  6. bucket.update(1.0)模拟时间推进 1 秒,令牌按速率补充
  7. if bucket.try_acquire():限流判定,通过则记账计费,否则拒绝

常见误区

误区“配额和限流不是一回事吗?都是限制调用次数,加个计数器就行了吧。”

正解配额(Quota)回答“能花多少”——它是用户/租户/API 三个维度上的用量额度管理,包括速率配额和 Token 总量配额两类账本;限流(Rate Limiting)回答“放多快”——它是执行配额的具体机制(令牌桶、滑动窗口等)。两者是“目标与手段”的关系:配额定义上限,限流保证不超上限,而计量和计费则负责记录与结算。

为何常见:日常只看到“每秒最多 N 次”的限流器,误以为这就是全部,忽略了配额维度设计与用量核算。

误区“模型的价值在生成的回答上,输入只是背景信息,计费应该只算输出。”

正解主流大模型平台(OpenAI、Anthropic 等)都是输入输出分开计价:输入相对便宜、输出相对昂贵,但输入绝不免费——长上下文输入的 Prompt 累积起来可能占账单的大头。正确的计费模型是 fee = p_in × n_in + p_out × n_out,两者都要精确计量。

为何常见:直觉上“回答”才是产品价值,于是忽略了“处理长 prompt 也要消耗算力”这一事实。

误区“反正请求打到网关了,就算被 429 拒绝了,也应该收点处理费。”

正解被限流拒绝的请求没有进入模型推理,没有产生输出 Token,因此坚决不能计费。计费事件必须来自“完成计算并返回成功结果”的调用。被拒请求只应出现在限流日志里,作为压测和容量规划的输入,而不是出现在账单里。

为何常见:把“网关处理过请求”误认为“模型消耗了资源”,混淆了网络层的开销与应用层的推理成本。

误区“配额超限说明系统忙不过来了,返回 500 比 429 更有警示作用。”

正解500 表示服务端内部错误,调用方会把它当成故障重试,反而放大压力;正确的语义是返回 429 Too Many Requests,它明确告诉调用方“你的请求没问题,是额度/频率超了”,并配合 Retry-After 头指导稍后重试。状态码语义混乱会让客户端无法做出正确的重试策略。

为何常见:初学者只记得“错误就是 500”,忽略了 4xx 与 5xx 的本质区别在于“责任在谁”。

面试题

Q1. 配额管理通常从哪几个维度控制 AI API 的调用上限?

单选题 · easy

  • A.用户、租户与API维度← 正确答案
  • B.仅按用户维度统计
  • C.仅按租户维度统计
  • D.仅按地域维度统计

答案:A。配额管理需要三把尺子同时起作用:用户维度防止个人绕过、租户维度防止组织拆号绕过、API 维度防止特定接口资源被独占。只按单一维度统计都会留下漏洞,地域维度和配额无关。

Q2. 令牌桶算法中,桶容量参数主要决定了什么?

单选题 · mid

  • A.持续稳定通过的速率上限
  • B.允许的突发流量大小← 正确答案
  • C.令牌的过期时延
  • D.请求的失败重试次数

答案:B。令牌生成速率决定长期平均速率,而桶容量决定瞬时突发能力——桶越大,攒下的令牌越多,短时间能放行的突发请求就越多。因此“容量”不是持续速率上限,而是突发容量的“蓄水池”。

Q3. 关于配额与计费 API 设计,以下说法正确的有哪些?

多选题 · mid

  • A.配额超限应返回 429 状态码← 正确答案
  • B.计费必须以输出token单价计价
  • C.计量事件可采用异步批处理← 正确答案
  • D.幂等键用于防止重复扣费← 正确答案

答案:ACD。配额超限语义上属于“客户端频率问题”,应返回 429;计费是输入+输出分开计价而非只看输出;计量事件走消息队列异步批处理是标准做法;幂等键保证重试不重复扣费。B 错在漏掉了输入计费。

Q4. 一次推理调用的账单金额应如何计算?

单选题 · easy

  • A.输入量乘单价加输出量乘单价← 正确答案
  • B.只按输入token量计价
  • C.只按输出token量计价
  • D.按总token量取平均价

答案:A。正确答案对应核心计费公式 fee = p_in×n_in + p_out×n_out。输入和输出使用各自的单价,不能只算一边,也不能用简单平均价——不同模型和不同方向的 Token 成本差异很大。

Q5. 令牌桶处理突发流量的正确方式是?

单选题 · easy

  • A.桶满时丢弃多余令牌← 正确答案
  • B.突然来的请求一定被拒绝
  • C.突发请求等价于匀速请求
  • D.令牌桶无法应对突发

答案:A。令牌桶允许空闲时积累令牌,突发时一次性消耗,所以它能吸收突发流量。当桶满时,继续生成的令牌被丢弃,保证长期平均速率可控。突发请求并不一定被拒,关键是桶里有没有攒下令牌。

Q6. 一套可靠的计费系统必须保证哪些数据质量?

多选题 · hard

  • A.计量事件可重放且不重复扣费← 正确答案
  • B.幂等键只用于安全认证
  • C.账单与原始用量对账偏差为零← 正确答案
  • D.被拒绝的请求也计入费用

答案:AC。计量管道可能重复消费消息,所以事件必须可重放且通过幂等去重保证不重复扣费;账单金额必须与原始计量事件合计一致,对账闭环是必须项。B 错:幂等键是去重标识而非认证凭据;D 错:被拒请求不产生推理,不应计费。

Q7. 计量管道在在线请求路径上选用 Token 估算公式而不是精确分词,主要原因是?

单选题 · hard

  • A.精确计数开销高且耗时较长← 正确答案
  • B.估算公式比精确计数更准确
  • C.估算可以绕过配额检查
  • D.精确计数无法在离线实现

答案:A。精确 Token 计数需要完整跑一遍分词器,延迟开销大,不适合放在在线请求路径上;字符数除以 4 的估算能在微秒级完成。精确计数可以在异步批处理中补做,估算不代表“更准确”,更不是为了绕过配额检查。

Q8. 对于一个每秒产生 2000 条计量事件、单事件约 1KB 的 AI 网关,最合理的计量管道设计是?

单选题 · extreme

  • A.同步入库到关系数据库
  • B.事件流缓冲再批量落库← 正确答案
  • C.直接写入本地文件忽略
  • D.每请求实时调用计费接口

答案:B。每秒 2000 条×1KB 约 2MB/s 的写入量,同步入库会让请求路径变慢,实时计费接口同样拖慢主链路;写入本地文件则不可靠且无法分布式处理。正确方案是事件先进消息队列(如 Kafka)做缓冲削峰,后台批处理批量落库,兼顾可靠性与性能。

Q9. API 错误响应体中应当包含哪些字段?

多选题 · mid

  • A.机器可读错误码 code← 正确答案
  • B.人类可读信息 message← 正确答案
  • C.请求追踪标识 request_id← 正确答案
  • D.私有密钥 secret_key

答案:ABC。标准错误体包含三段:机器可读的 code 供程序分支处理、人类可读的 message 供开发者排障、request_id 便于日志追踪全链路。响应体绝不能携带私有密钥,那是鉴权用的凭据,暴露等于把钥匙交给外人。

Q10. 为什么配额超限要返回 429 而不是 500?

单选题 · mid

  • A.429 表明客户端需减少请求量← 正确答案
  • B.500 表示客户端请求数据太大
  • C.429 表示服务器发生了故障
  • D.500 表示配额已经被用尽

答案:A。429 属于 4xx 客户端错误,语义是“客户端发送请求太频繁,需要降低速率”,配合 Retry-After 头指导重试时机;500 是服务端内部故障,两者归属完全不同。配额超限是调用方触达了额度的边界,责任在调用方,所以必须用 4xx 而不是 5xx。