在共享模型配额下区分准入、排队和执行,避免一个租户的长任务耗尽其他用户的服务能力。
有全局限流,仍可能没有租户公平性
模型网关同时服务三个团队:A 持续提交文档分析长任务,B 偶尔查一个工单状态,C 批量整理历史工单。即使总并发严格限制在供应商配额内,A 仍可能长期占满许可,让 B 的轻量请求一直等待。
全局限流保护下游,公平调度决定谁先得到有限资源。两者是不同层次的问题。只给每个租户设置相同请求次数,也无法处理单次任务从几百到几万 token 的成本差异。
可以把链路拆成准入、排队、资源预留和执行四个阶段,分别定义失败与恢复语义。
准入先限制积压,再谈优先级
请求进入系统时,先检查身份、任务类型、输入大小和租户未完成任务数。不符合约束的请求在入口明确拒绝,或者转为用户知晓的离线任务,不能无限堆进消息队列。
建议区分交互问答和批处理队列。交互任务设置较短的最大排队时间,批任务允许更长等待但不无限延续;等待人工审批的任务暂时释放模型执行许可。
租户优先级来自服务配置和业务策略,不能从模型生成的“紧急”标签直接决定。用户输入“最高优先级”也不应自动获得管理员级资源份额。
用成本单位而不是任务个数分配机会
一种候选方法是按租户维护调度积分,每轮按权重补充额度,并用估计成本扣减。下面是调度逻辑示意,不是可直接使用的完整算法:
每个调度轮次:
对有积压的租户补充 quantum × weight
对当前候选任务估算资源成本
若租户积分足够且全局许可可用:
原子预留许可和预算
扣减积分并开始执行
否则保留任务,继续考虑其他租户
成本可以用归一化 token 或其他业务定义单位,但必须在同一队列中可比较。输入 token 可提前估计,输出和工具步骤则需要设置上界,并在结束后结算。
假设 A、B 每轮各获得 100 单位,A 队首任务估计需要 300,B 的任务只需 50。A 在积分积累期间,B 可以继续处理;A 获得足够积分后也应有机会执行。这是机制示例,不代表某个固定公平率。
积分上限要允许最大合法任务最终获得资格,否则大任务会永久饥饿。也可以为超大任务设置独立队列或拆分执行,避免它占住租户队首。
预算预留需要跨实例一致
如果两个 Worker 同时看到“剩余预算 100”,各自启动一项成本 80 的任务,最终会突破预算。检查和扣减必须在同一原子操作中完成,可以使用数据库条件更新或经过验证的配额服务。
预留记录至少包含 taskId、租户、额度、状态和租约。模型调用尚未开始便失败时可以释放;已经开始但未拿到完整 usage 时,不能直接按零消耗回收,应采用保守估计或等待对账。
子 Agent 继承父任务的预算范围,不能各自向全局重新申请一份不受父任务限制的额度。否则主任务表面上只有一次请求,实际支出却随分支数量增长。
Java 的 Semaphore适合控制单进程内同时占用的许可;即便启用公平模式,也不等于实现跨实例的租户加权公平,更不自动处理 token 预算。
模型长调用无法轻易抢占
任务开始后,通常不能像 CPU 时间片一样随意暂停并恢复远端生成。公平性主要发生在新步骤获得执行资格时,而不是每秒强行切走正在生成的请求。
因此长任务需要限制单步输出、设置截止时间,并在步骤之间重新排队。对可以分块的文档分析,分块既提供恢复检查点,也提供其他租户获得执行机会的边界。
取消请求应沿执行链传播,但只有确认资源不再使用或进入明确结算规则后,才回收相应许可。过早释放会造成实际并发超限;永久不释放则会让系统逐渐失去容量。
公平和利用率之间需要明确取舍
为每个租户永久保留独占槽位可以降低干扰,却可能在租户空闲时浪费资源。共享剩余额度能提高利用率,但借用需要在后续步骤可回收,不能承诺已开始的不可抢占调用立刻让出容量。
还需要处理低优先级任务的等待上限。可以采用等待时间加权或定期保障份额,但要避免用户反复取消、重建任务来刷新排序优势。
如果供应商突然降低配额,应减少新步骤启动速度,并对超出排队时限的任务给出明确结果。继续按旧吞吐率接收请求,只会把压力积累到未来。
实验应该观察每个租户
在同一总配额下设置三组负载:一个租户持续高流量、一个租户偶发短任务、一个租户只提交大任务。记录各租户吞吐、最长等待、完成率、实际成本和全局利用率。
再让 Worker 在预算预留后崩溃,确认额度会在合理条件下恢复;模拟迟到的结算事件,确保不会重复退款或重复扣费。用操作 ID 去重,预算账本和任务状态保持可追溯关系。
可以延续此前的容量规划方法与成本预算设计。公平调度的结论应说明哪些租户得到保障、代价是什么,而不是只展示全站平均延迟下降。
