切换主题
应用配额与费用预算
本页阅读位置
完成第一轮对话后,按当前业务需求深入。Spring 常规声明优先使用 RegistrationManifest 与注解;本页的底层 register / publish 或直连方法供协议细化与兼容集成参考。
在控制台选择应用,打开 配额与预算,添加策略即可对新任务和模型发送生效。所有匹配策略同时约束额度,支持叠加分钟速率、日/月 token 与金额预算;无需修改 Agent 或业务 SDK 聊天请求。
配置顺序
- 给模型声明
capabilities.contextWindow、maxOutputTokens,发布固定版本。金额策略需要同币种的pricing.currency/inputPerMillion/outputPerMillion。 - 添加应用级策略,设置并发任务、周期任务请求、模型请求、token 或金额上限。留空表示不限制,零表示禁止;金额输入十进制字符串,如
"10.00"。 - 按租户、用户或模型增加策略,选择 UTC MINUTE/HOUR/DAY/MONTH 窗口。比如每用户每分钟 5 次任务,加全应用每天 USD 10 的预算。
- 保存原因并检查 周期使用量 与 模型预留与结算。旧配置未设规则时保持全局执行槽和 Agent 根预算;已有任务也计入新并发规则。
规则范围和计数
| 字段 | 用途 |
|---|---|
scope=APP, value=* | 当前应用独立额度 |
scope=TENANT | 使用服务端可信 tenantId;* 为每租户独立分桶 |
scope=USER | 使用可信 tenantId + subjectId;同名用户跨租户独立;* 为每用户独立额度 |
scope=MODEL | 精确 scope/models/id@version;* 为各固定模型版本独立额度 |
maxConcurrentRuns | 未终结业务 Run 数;等待审批也占用。MODEL 不接受此字段 |
maxRunRequests | 原周期成功接受的新 Run;幂等返回旧 Run 不再计数。MODEL 不接受此字段 |
maxConcurrentModels | 活动模型发送预留数,跨周期计算;UNKNOWN 不继续占活动并发 |
maxModelRequests | 进入发送边界的调用尝试;失败、备用尝试、合成探测都计入适用规则 |
maxTokens | 供应商实际 token 加未确认预留;不是 Agent 的字符 budgetUnits |
maxAmount + currency | 已结算金额加保留的上界金额;固定版本价目,供应商核对金额可修正同币种结算 |
unknownPriceMode | REJECT 禁止未知价格;SEPARATE 使用显式 maxUnpricedRequests,禁止同时配置金额上限 |
enabled=false | 停止新请求约束,保留既有预留、周期与结算事实 |
用户 Context 内的 tenantId/userId 不改变额度身份。合成探测没有业务用户身份,只匹配 APP/MODEL;它不能借用用户规则绕开应用预算。
已有策略标识的范围、选择值、周期、币种不可修改;修改上限或启停可以原位保存。变更维度请创建新标识,预算窗口和旧预留独立保留。移除规则是管理员明确放宽预算的操作,应记录原因。
HTTP 配置与读取
先 GET /v1/apps/{app}/quotas 取得 revision;未配置返回 revision 0。APP_ADMIN 或 PLATFORM_ADMIN 可写;业务 USER 无权读取应用预算明细。
http
PUT /v1/apps/support/quotas
Authorization: Bearer <应用管理员凭据>
If-Match: 0
Content-Type: application/jsonjson
{
"policies": [
{"id":"requests-minute","scope":"USER","period":"MINUTE","maxRunRequests":5,"maxConcurrentRuns":2},
{"id":"daily-budget","scope":"APP","period":"DAY","maxModelRequests":1000,"maxTokens":1000000,"maxAmount":"10.00","currency":"USD","unknownPriceMode":"REJECT"}
],
"reason":"设置客服应用的调用与日预算"
}GET /quotas/usage?limit=20&offset=0 读取历史周期桶;GET /quota-reservations?limit=20&offset=0 读取模型预留,路径均在 /v1/apps/{app}。分页 1–100,offset 0–1000000;返回 items/total/limit/offset。配置冲突 412,请刷新后重新确认,SDK 不自动重试写请求。
Java 与 Kotlin SDK
java
var current = client.quotaPolicies().toCompletableFuture().get();
var policy = new QuotaPolicy(Map.of(
"id", "daily-budget", "scope", "APP", "period", "DAY",
"maxAmount", "10.00", "currency", "USD"));
client.configureQuotaPolicies(List.of(policy), "配置日预算",
((Number) current.get("revision")).longValue()).toCompletableFuture().get();
var usage = client.quotaUsage(20, 0).toCompletableFuture().get();
var reservations = client.quotaReservations(20, 0).toCompletableFuture().get();kotlin
import dev.sparktide.sdk.QuotaPolicy
import dev.sparktide.sdk.kotlin.*
val current = client.getQuotaPolicies()
client.configureQuotas(
listOf(QuotaPolicy(mapOf("id" to "daily-budget", "scope" to "APP",
"period" to "DAY", "maxAmount" to "10.00", "currency" to "USD"))),
"配置日预算", (current.getValue("revision") as Number).toLong())
val usage = client.getQuotaUsage(limit = 20)
val reservations = client.getQuotaReservations(limit = 20)QuotaPolicy 接受严格字段、非负整数限额、十进制金额,构造后不可变;客户端凭据须管理身份。业务聊天仍使用自己的可信业务凭据,429 错误码向调用方返回,不能通过换幂等键绕过预算。
预留、未知与核对
预留输入上界为发布模型的 contextWindow,输出上界为本次 max_completion_tokens,token 预留取两者之和。金额按输入/输出价目以十进制计算并向上保留 12 位精度。这个保守上界通常大于实际请求,可能在余额仍大于实际平均费用时拒绝请求;应按模型上界规划余额,避免缩小声明伪造费用容量。
只有响应按协议完整终结且有完整 prompt/completion/total 整数回执才能自动结算。取消、超时、断流、不完整回执进入 UNKNOWN 并保留额度;流中途的用量即使三个数字齐全,也不作为最终费用释放依据。供应商账本仍显示收到的原始回执,两者口径不同。
业务调用在 运行记录 → 模型调用账本 → 核对供应商计量 对账。使用现有 reconcileModelInvocation / Kotlin recordModelReconciliation,携带当前 revision、最终供应商用量、证据与可选账单;配额自动按绝对新事实调整,不重复收费,不改变任务失败/取消状态,也不重放请求。
合成探测在 配额与预算 → 模型预留与结算 → 探测配额对账 操作;接口为 POST /v1/apps/{app}/quota-reservations/{id}/reconcile,If-Match 为预留 revision,正文与模型账本核对一致。只允许已结束探测,保留原探测回执和健康结论,仅修正额度事实;同一入口拒绝业务调用,避免绕过业务账本。
java
var evidence = ModelUsageReconciliation.builder(10, 5, 15,
"供应商最终计量", "invoice:20260930:probe-request")
.billedCost("USD", "0.00003").build();
client.reconcileQuotaProbe(reservationId, evidence, revision);Kotlin 使用 reconcileSyntheticProbeQuota(id, evidence, revision)。证据不得填真实密钥或原始对话;未知计量不能以零替代。探测原结果不会因人工费用核对变成成功。
失败处理和预算边界
| 错误码 | 处理 |
|---|---|
| QUOTA_EXCEEDED | 等待原周期结束,核对 UNKNOWN 或经管理员调整限额;不要自动重试发送 |
| QUOTA_PRICE_UNKNOWN | 发布已知价目版本,或明示采用无金额上限的 SEPARATE 未知价额度 |
| QUOTA_TOKEN_BOUND_UNKNOWN | 为固定模型版本声明真实 contextWindow |
| QUOTA_CURRENCY_MISMATCH | 配额币种与模型价目一致,不隐式汇率换算 |
| REVISION_MISMATCH | 重新读取并确认配置或预留,避免覆盖他人核对 |
跨周期晚回执结算原桶,不把旧费用扣到当天。UNKNOWN 的旧周期金额不凭空消失,但新 UTC 周期有新窗口;并发模型数跨周期计算。供应商超出声明上界的用量或后到真实账单照实记录,后续发送会受限;已经发生的外部费用无法撤销。本地硬预算保护的是已知价目下的预留与结算,不构成对不可信供应商账单的绝对上限保证。
探测配额拒绝不作为供应商传输故障,不清空原故障窗口或已验证能力。业务范围规则、发布价目与健康探测身份隔离保持有效。
升级与回滚
V12 新增 st_quota_config/st_quota_bucket/st_quota_reservation,保留旧历史;单活和显式 PostgreSQL 集群模式共用 Store.atomic 锁保证预留一致,网络不进事务。上线前备份数据库,先验证规则和 UNKNOWN 核对流程。回滚先停止新业务/探测、导出预算事实并完成核对,新增表保留;旧服务不执行配额拦截,恢复流量前须有外部预算控制或明确接受旧限制。
文档来源与验证记录
状态:已确认;日期:2026-09-30;责任人:SparkTide 维护者;关联任务:M06;来源:Quotas、Runs、ModelInvocations、ModelHealth、OpenAPI、Java/Kotlin SDK 与配额行为测试。
