先记住这个答案
可以把交互和批量任务分成独立队列或逻辑类别,由调度器为交互保留可用并发,并给批量分配最低服务份额;空闲容量再按规则借用。单纯提高交互优先级可能让批量长期得不到执行,而只调整队列顺序也不能抢回已被长任务占用的资源。应结合任务期限、可安全中断的边界、加权公平或等待时间提升机制,并分别监控两类任务的排队时延和完成率。调度策略最终需要对接真实模型与工具容量,而不是只在消息标签上写一个优先级。
- 等待顺序与已运行资源的隔离是两件事
- 为交互保留容量,同时保证批量有最低进展
- 按等待时长和实际资源成本验证公平性
为什么一个优先级字段还不够
如果所有消费者已经在运行几十秒的批量模型调用,新来的交互任务即使排到队首,也需要等某个调用结束。对于无法安全抢占的任务,需要限制批量最大在途数或预留交互容量,让用户请求有机会及时开始。
反过来,交互流量持续到来时,绝对优先级可能让批量队列永远不被选择。可以给批量最低份额,或随等待时间提高优先级。借用空闲容量时应说明回收发生在任务结束还是安全检查点,不能随意中断已经产生外部副作用的步骤。
用加权选择展示最低进展机会
下面用两个交互槽位加一个批量槽位形成选择周期,某类队列为空时借用另一类。只要两类都持续有任务,批量就能周期性获得一次选择;某类空了也不会浪费这次调度机会。
这种轮转按任务数量分配机会,但一个长报告和一次短查询成本不同。如果要公平分配 Token、计算时间或浏览器实例占用,应引入成本估计与实际用量校正。不能因为示例输出交替出现,就声称系统已经实现资源层面的公平。
const queues = { interactive: ['i1', 'i2', 'i3'], batch: ['b1', 'b2', 'b3'] };
const cycle = ['interactive', 'interactive', 'batch'];
let cursor = 0;
function pick() {
const preferred = cycle[cursor++ % cycle.length];
const fallback = preferred === 'interactive' ? 'batch' : 'interactive';
return queues[preferred].shift() ?? queues[fallback].shift();
}
const order = [];
while (queues.interactive.length || queues.batch.length) order.push(pick());
console.log(order.join(','));查看输出与解释
i1,i2,b1,i3,b2,b3批量在第三次选择时获得机会,交互耗尽后剩余批量继续执行。函数只处理单进程内的选择顺序,生产调度还需要租户公平、许可获取、失败恢复与真实容量上限。
把长任务切成可恢复的调度单位
批量 Agent 可以在完成一个文档、一个文件或一个安全步骤后保存状态,再等待下一次许可。切片边界应与可验证产物对齐,避免为了缩短运行时间而在不可重复的外部写入中间强行暂停。过细切片也会增加恢复和上下文成本。
指标至少分别记录两类任务的排队分位数、最老等待年龄、到期丢弃和吞吐。压测应包含持续交互加大批量积压,而不是只看平均负载;如果批量永远积压,应调整容量或准入策略,不能用高交互成功率掩盖另一类任务没有进展。
容易答错的地方
- 高优先级队列可以立即抢占正在执行的请求
- 多数队列只决定下一项任务,是否能中断已有执行取决于工作本身与执行器;需要为不可抢占的长任务单独规划资源。
- 批量业务不着急所以允许无限期等待
- 批量仍可能有完成期限和积压成本,应定义最低服务水平并监控最老任务年龄,避免低优先级变成永不执行。
面试官还会怎么问?
直接部署两套服务是否更简单?
物理隔离能清楚分配容量,但增加运维成本且可能闲置资源;逻辑队列和独立许可池可以先满足部分需求,按隔离强度选择。
权重是二比一就保证 Token 消耗二比一吗?
不会,示例权重只控制选择次数。任务成本不同,需要按估计成本扣除份额并用实际消耗校正,才能接近资源公平。
过期的交互请求还要继续执行吗?
如果结果已无使用价值且尚未产生必要副作用,可以取消或丢弃;若动作已经发生,需要保留结果与状态,不能仅因用户超时就当作未执行。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。