这道题表面问的是资源调度,本质其实是在异构任务和共享状态之间做权衡。[[Agent]]系统里,LLM推理和工具调用是两种完全不同的活,你不能用同一套策略去对付它们。LLM是计算密集的,对GPU依赖大,延迟敏感,你得给它独占批次调度,按token预估耗时来排队。工具调用是IO密集的,网络请求、文件读写,延迟长但CPU消耗小,用协程池加异步IO处理就行,超时直接熔断。所以第一步就是分层调度,把这两个队列分开,别让一次慢的工具调用把整个LLM推理线程都卡住。
具体说一下资源隔离。我会给每个Agent会话设硬上限,比如一个Agent最大能占多少显存、多少CPU时间,防止一个异常Agent把整个系统拖垮。配额按优先级分三档,高优先级的任务可以抢占低优先级的CPU资源,但GPU任务不抢占,因为上下文切换成本太高。你可以把它理解成酒店房间,普通标间和高层套房分开管理,但高级套房能优先用公共设施,不过不会把标间客人直接赶出去。
共享状态冲突是另一个大坑。多个Agent同时读写同一个用户会话或同一个知识库,很容易出问题。我的做法是分场景处理:同用户的多Agent用乐观锁,版本号CAS,冲突了就合并或重试,不做悲观锁死等。共享工具调用用分布式信号量,比如Redis RedLock,控制并发数。知识库写入走单Partition顺序消费,串行化处理,保证一致性。这里有个前提,就是你的业务场景允许最终一致性,如果要求强一致,那就要上分布式事务,性能会打折。
弹性扩展方面,我会把调度器和执行器分离,调度器是无状态的,可以水平扩展,执行器池根据负载动态扩缩。注册发现用etcd,故障转移靠心跳。稳定性三板斧:熔断、限流、背压。工具调用失败率超过50%就自动熔断,走缓存或默认值降级。全局LLM调用用令牌桶限流,保护下游模型服务。执行器队列积压到一定水位,调度器就不接新任务了。上线前我会特别关注压测,看熔断阈值和限流参数是不是合理,不然线上一个小故障可能演变成雪崩。
其实还有一个容易被忽略的点,就是Agent的上下文切换开销。如果Agent频繁在不同任务类型间切换,比如刚做完LLM推理又立刻做工具调用,上下文切换和缓存失效的成本会很高。我倾向于让同一Agent的连续任务尽量在同一执行器上执行,减少迁移。但这个设计会和负载均衡策略冲突,得在调度器层面做亲和性调度。
所以说,我会把资源调度看成「隔离加配额加弹性」的组合拳,没有银弹。核心是分清任务类型,给不同的资源池和调度策略,再通过配额和锁机制控制冲突,最后用熔断限流兜底。最怕的是上来就搞一个全局统一调度器,什么任务都往里丢,那样迟早出事。