Token导航 LogoToken导航

AI 资讯 · 科技行者

TokenRouter:两个AI模型逐字接力写作的调度机制

更新 2026-11-11科技行者正文 6383字阅读约 20分钟

你有没有想过,一个AI大模型回答问题的时候,其实可能不是"一个"模型在干活?

2025年,一篇叫R2R的研究发现了一件挺颠覆认知的事:让一个32B的大模型和一个1.5B的小模型配合着写,只需要把5%的"难词"交给大模型处理,剩下95%全靠小模型搞定,最终答案的质量居然能跟全程用大模型一样好。这比"每次问题整体交给大模型还是小模型"的粗粒度分配方式聪明太多了,原来按整个问题分配,可能得把40%的问题都丢给大模型才能达到同等效果。

这个发现让人兴奋。如果真能做到"每个字都精挑细选该用哪个模型写",那无论是省钱还是提升质量,想象空间都很大。

但是,TokenRouter这篇论文的作者们很快发现了一个尴尬的现实:这个漂亮的想法,在真实服务器上跑起来,慢得让人绝望。

核心问题:理论上的美好,工程上的地狱

先说清楚,这里说的"按token路由"到底是什么意思。

AI生成文字是一个字一个字(准确说是一个token一个token)往外蹦的过程,这个过程叫解码

解码*:大模型根据已经生成的内容,预测并输出下一个字(token)的过程,一步一步循环进行,直到生成完整回答。

传统的路由方式是"session级"或"query级"的,就是说整个对话或者整个问题从头到尾交给同一个模型负责。ChatGPT、Cursor这些商业产品现在就是这么干的:先判断你这个问题难不难,然后分派给合适的模型,分派完之后这个模型就得把活儿干到底。

token级路由则激进得多:生成每一个字的时候,都重新判断一下,这个字该让哪个模型来写。简单的字用小模型写,省钱又快;碰到需要深度思考的难点,立刻切换到大模型。写完这一段,可能又切回小模型。

听起来很美,但问题来了。现有的服务系统,比如业界常用的SGLang和vLLM,从设计之初就是给"单个模型服务"用的。这些系统有个隐藏假设:同一批正在处理的请求,步调应该是一致的,大家一起往前走一步,再一起走下一步。

这个假设在token级路由面前彻底崩塌了,论文指出了三个具体的坑。

第一个坑是步调不一致。32B大模型算一步要用的时间,可能是1.5B小模型的好几倍。如果系统非要让这批请求在同一个节奏里走,那就相当于一群人跑步比赛,非要等最慢的人跑完这一圈,所有人才能开始下一圈。快的人大把时间在那儿干等着,显卡资源被活活浪费。

这就像你和朋友们一起吃自助餐,有人吃得快有人吃得慢。如果餐厅非要求所有人必须同时去同一个窗口取同一道菜,那吃得快的人只能眼巴巴看着慢的人,谁都没法按自己的节奏吃完整顿饭。如果不改变这个"统一节奏"的规则,快的人永远要为慢的人陪跑,饭吃到一半肚子早就饿得咕咕叫了。

第二个坑叫批次准入延迟。因为token级路由导致模型切换特别频繁,一个刚被路由过来的请求经常会撞上目标模型正忙着处理上一批任务的情况,只能排队等下一轮。这种排队会制造大量的"空隙",让原本可以攒成一大批一起处理的请求变得支离破碎。

第三个坑是实现复杂度太高。现有服务系统根本没给"每一步都要做路由决策"这件事留接口,开发者想实现token级路由,得对一个庞大的代码库做大量魔改,还得跟系统里原本耦合很紧的连续批处理、前缀缓存等功能小心翼翼地协调,一不小心就牵一发动全身。

TokenRouter的解法:让开发者只管写"一个请求的旅程"

面对这三个坑,TokenRouter团队提出了一个设计原则,叫"以请求为中心编程,以模型为中心执行"。

这句话听起来有点绕,拆开讲其实很直白。开发者在写路由逻辑的时候,完全不需要操心底层那些批处理、调度、异步执行的脏活累活,只需要站在"一个请求"的视角,描述这个请求该怎么在不同模型之间流转就行。而真正跑起来的时候,系统会在背后把这套逻辑拆解成多个独立运转的、以模型为单位的服务进程,自己去处理异步调度的复杂性。

这种设计思路,有点像你去银行办业务。你不需要知道银行后台是怎么调度柜员、怎么分配窗口的,你只需要知道"我先取号,然后等叫号,然后去窗口办事",剩下的调度问题全是银行内部系统的事。如果银行非要求你自己去学排班算法才能取号,那估计没人愿意去银行了。TokenRouter就是想让开发者像普通储户一样简单,而不是让每个人都变成排班专家。

具体来说,TokenRouter把路由算法抽象成三个函数:route(路由判断)、send(发送请求)、receive(接收请求)。

route函数在每次解码完一步之后被调用,根据刚刚生成的内容(比如logits、隐藏状态、采样出的token),判断这个请求接下来该留在本地模型继续写,还是该转交给另一个模型。

send函数在决定要转交的时候被调用,负责打包一个"请求包",里面装着请求ID、对方模型还没见过的那部分token、请求的状态(是还在进行还是已经结束),以及任何算法需要的额外信息。

receive函数在收到别的模型转交过来的请求包时被调用,负责把这个请求包转换成本地能识别的格式,让接收方模型可以继续往下写。

论文团队验证了这套接口的通用性,拿五种不同的token级路由算法试了一遍,包括CITER、R2R、R-Stitch、Co-LLM和ME,发现每一种都能被这三个函数优雅地表达出来,没有一个算法需要绕开这套框架另起炉灶。

系统架构:给每个模型配一个"独立小秘书"

接口设计解决了"开发者怎么写代码"的问题,但真正的硬骨头是"系统怎么跑得快"。

TokenRouter的架构是,每个参与路由的大模型都拥有一个专属的子服务器*(subserver):一个独立运行的服务进程,内部有自己的调度器、模型运行器和私有的KV缓存池,专门负责一个LLM的推理工作。

这些子服务器各自独立运转,谁也不等谁,彼此之间通过"对等请求"消息来通信协调。对外,整个系统只暴露一个统一的接口,客户端完全感知不到背后到底有几个模型在互相配合,用起来跟直接访问一个普通的单模型服务器没有任何区别。

这套设计解决步调不一致问题的具体手段,叫解耦三循环执行

解耦三循环执行*:在标准单模型服务原本就有的两个异步循环(客户端服务器循环负责接收请求和返回结果,解码循环负责调度和执行模型推理)基础上,额外增加第三个"跨模型循环",专门负责在不同模型的子服务器之间收发请求。

有了这第三条循环,一个被路由出去的请求可以先离开本地的处理队列,跑到另一个模型那边去处理,处理完了再自动回来接上。在它离开的这段时间里,本地剩下的请求完全不受影响,该怎么跑还怎么跑,谁也不用等谁。

论文里还专门设计了一个handoff-resume(交接-恢复)机制,用来解决请求"回家"时的效率问题。如果每次请求从别的模型转回来都当成全新请求处理,那意味着每次都要重新做前缀匹配、重新分配KV缓存,这些都是实打实的计算开销。为此TokenRouter引入了一个"挂起"状态,介于"运行中"和"已完成"之间。请求被转出去的时候标记为挂起,调度器会直接跳过它,但它的服务状态会被完整保留。等它从别的模型那边处理完回来,直接把新生成的token拼接上去,重新塞回本地队列继续跑,就像它从来没离开过一样。

这个机制很像你去另一个部门办事盖章,办完事回到自己工位,不需要重新打卡、重新登记工位信息,直接拿着盖好章的文件接着干活。如果每次办完事回来都得重新走一遍入职流程,那光是手续就能把人耗死,真正干活的时间反而被挤占了。

论文的实验数据印证了这个机制的价值。他们专门测量了一个没有这套交接机制的"标准服务基线"(Std. Serving)在每一步解码上的时间都花在哪儿了,结果发现,前缀匹配占了20.94%,缓存节点加锁占17.47%,释放锁占13.81%,更新基数缓存(RadixCache)占32.62%,真正用来做模型推理计算的时间只占4.2%。换句话说,九成以上的时间都耗在了"重新认领身份"这种行政手续上,真正干活的时间少得可怜。

延迟批处理:用一点点等待换来更大的效率

步调不一致的问题解决了,但批次准入延迟这个坑还没填。

论文里给出了一个很直观的对比实验,画了四种调度方式的时间线图,用"谁先来谁先处理"这种急性子策略(论文里叫Eager-Async,即时异步),会出现请求零零散散到达、每次都得单独开一批处理的情况,平均延迟是10.83个时间单位。而TokenRouter采用的延迟批处理策略,平均延迟降到了8.67,比急性子策略还快了将近21%。

延迟批处理的核心思路是,让子服务器不要一有请求到达就立刻开始处理,而是先攒一攒,等攒够了一个阈值数量的同模型请求,再一起打包处理。

这听上去有点反直觉:明明是要降低延迟,为什么反而要故意多等一会儿?

答案藏在"攒批"带来的规模效应里。想象你开了一家煎饼摊,每来一个顾客就现摊一张,看起来最快响应,但实际上每张饼之间你都得重新预热锅、重新调面糊,效率其实很低。如果你愿意稍微等一等,等凑够三四个顾客的订单再统一开火连续摊,虽然第一个顾客要多等几十秒,但整体出餐速度反而更快,因为预热和调配的成本被摊薄了。如果坚持来一个做一个,摊主累死也供应不了多少顾客,排队只会越来越长。

当然,这里有个关键的权衡:攒批的阈值该定多大?

太小的话,批次准入延迟的问题压根没解决。太大的话,又会出现另一个问题:请求被困在队列里迟迟凑不够数,导致小模型那边没活干,反而白白浪费了时间。论文管这个阈值叫B,并且专门搭了一套数学模型来算出让吞吐量最大化的最优阈值B*。

这套数学模型用的是离散时间马尔可夫链*(DTMC):一种用来描述系统状态如何随时间随机演变的数学工具,系统下一个状态只取决于当前状态,跟更早的历史无关。

简单说,论文把整个系统每一次"有模型处理完一批请求"的时刻定义成一个状态,状态里记录了每个模型排队等待的请求数、正在执行的批次大小、以及这批还要多久执行完。给定路由算法的转移概率、每个模型单步解码所需的时间,这套模型就能推导出吞吐量作为阈值B的函数,然后搜索出能让吞吐量最大化的最优阈值。

论文在R2R算法上专门做了验证实验,固定小模型的阈值为1,调整大模型的阈值B1,对比理论预测吞吐量和实际跑在硬件上测出来的吞吐量。结果显示,在并发数为2、3、4的各种情况下,数学模型预测出来的最优阈值,和实际测量出来的最优阈值高度吻合。这说明这套数学建模不是纸上谈兵,是真能指导实际参数调优的。

实际效果:吞吐量涨了多少

说了这么多设计思路,最终还是要看数字说话。

论文在8张A100-80G GPU的服务器上,拿五种不同的token级路由算法(CITER、R2R、R-Stitch、Co-LLM、ME),跑了三类不同的工作负载(低难度推理、高难度推理、代理型多轮任务),对比了TokenRouter、官方原始实现(Official Code)、以及作者自己搭建的标准SGLang基线(Std. Serving)。

结果是,在全部15种"算法加工作负载"的组合测试中,TokenRouter的解码吞吐量比表现最好的那个基线还要高出2.01到64.15倍。端到端的请求完成延迟,相比标准服务基线也缩短了2.03到63.64倍。

一个很有意思的发现是,TokenRouter面对输出变长这件事表现得特别稳。论文对比了"低难度推理"(最大输出2048个token)和"高难度推理"(最大输出8192个token)两种场景,标准服务基线的吞吐量在输出变长之后直接掉了58.1%到85.2%,而TokenRouter的吞吐量基本保持平稳,几乎不受影响。

这背后的原因不难理解:输出越长,意味着模型之间切换的次数越多,如果每次切换都要付出"重新认领身份"的代价,输出越长代价累积得越可怕。TokenRouter靠挂起恢复机制把这个代价降到了最低,自然就不怕输出变长了。

在论文里给出的吞吐量和单用户响应速度权衡的图上,有个数字特别打眼:当并发数从1涨到16的时候,TokenRouter的吞吐量涨了8.61倍,同时还能保住单用户速度的51.7%。而官方的R2R实现只能涨到5.14倍,单用户速度只剩31.3%。更夸张的是,TokenRouter在并发16的时候,吞吐量是R2R在并发1时候的18.58倍,而且单用户速度反而还要高出1.13倍。这意味着,即便你对响应速度的要求更苛刻,TokenRouter依然能在这个更严格的约束下跑出远超对手的吞吐量。

工程优化的拆解也很清楚。在并发数为8的设置下,光是扩展CUDA图这一项优化,就能把基础实现的132.8 token/s提升到230.79 token/s,相当于1.71倍于官方R2R实现(134.9 token/s)。再叠加异步执行和延迟批处理两项优化,最终跑到372.48 token/s,达到了2.76倍的整体提升。

更换模型组合之后,这套优化思路依然稳健。论文测试了从0.6B配8B到4B配8B等四种不同规模的小模型大模型搭配,相比R2R,TokenRouter的吞吐量提升幅度在1.99到3.21倍之间,证明这套系统优化不是针对某一种特定模型大小的取巧,而是具备普适性的。

论文还在吞吐量与准确率的权衡图上,把TokenRouter、R2R以及几种query级路由方法(来自RouteLLM的几个变体)放在一起比较,结果显示TokenRouter能把token级路由推到一条全新的帕累托前沿上,也就是说,在同样的准确率水平下,吞吐量更高,在同样的吞吐量水平下,准确率也更高,整体上比粗粒度的query级路由更有竞争力。

写在后面

读这篇论文最大的感触是,很多算法层面已经证明"更好"的想法,卡在工程落地这一步的原因,往往不是算法本身不行,而是整个服务系统的底层假设和新想法天然冲突。

R2R那篇论文证明了token级路由在理论上确实比query级路由聪明,但如果没有TokenRouter这篇论文把"怎么把这事跑快"的问题解决掉,那个漂亮的5%路由比例大概率只能停留在实验室的离线评测里,没法真正服务用户。这让我想起很多"学术上可行、工程上没人做"的案例,往往差的不是一个新想法,而是一整套配套的基础设施。

论文里那个标准服务基线的延迟拆解表格值得多看两眼:九成以上的时间耗在前缀匹配和缓存维护这些"行政手续"上,真正的模型推理只占4.2%。这个数字说明一个挺反直觉的事实,很多时候系统慢不是因为"算得慢",而是因为"管理账本太麻烦"。这跟很多企业里"开会讨论怎么干活的时间比真正干活的时间还长"是一个道理。

论文最后提到一个限制:他们的数学模型假设两次路由之间的token数服从几何分布,这个假设对大多数token级路由算法成立,但碰到不满足这个假设的边界情况,这套优化就不一定准了。这算是一个留给后来者的开放问题,如果路由模式变得更复杂、更不符合简单的概率分布,延迟批处理的最优阈值该怎么重新推导?

Q&A

Q1:TokenRouter是什么?

A:TokenRouter是一个专门为token级LLM路由设计的高效服务系统,通过"请求为中心编程,模型为中心执行"的架构,解决了多模型协作推理时的步调不一致和批次延迟问题,吞吐量比现有实现最高提升64.15倍。

Q2:token级路由和传统的query级路由有什么区别?

A:query级路由是整个问题交给一个模型从头写到尾,而token级路由是每生成一个字都重新判断该用哪个模型,粒度更细,像R2R算法只需把5%的token交给大模型就能达到跟全程用大模型相当的质量。

Q3:TokenRouter为什么要用延迟批处理而不是即时处理请求?

A:即时处理会导致请求到达时目标模型正忙,只能排队等待,造成批次碎片化。延迟批处理让系统攒够一定数量的同模型请求再一起处理,用小幅等待换来更大批次的处理效率,整体延迟反而更低。

文章标签AI资讯

资讯来源:由AI资讯编辑整理自互联网公开内容,版权归原作者所有,未经许可,不得转载。