Cursor 在执行的过程中,是能够自己动态的选择合适的 LLLM 的。
我们在实际项目中,尤其是当使用量很大,并发很高的时候,也是面临一样的问题,如何动态的选择合适的 LLM,降低成本和延迟,保障效果。
LLMRouter 是一个开源的 LLM 智能路由库:对每个用户查询,在多个候选大模型中自动选一个最合适的来回答,目标是在效果、成本和延迟之间做权衡。
核心问题
你通常有一堆模型(小模型便宜快、大模型贵但强)。LLMRouter 做的事是:
给定 query → 路由模型决策 → 调用选中的 LLM → 返回结果
主要能力
-
智能路由(核心)
根据任务难度、性能、成本等,动态选择最优模型,而不是固定用某一个。 -
16+ 种路由策略,分五类:
- 单轮:KNN / SVM / MLP / 矩阵分解 / Elo / 图路由 / 对比学习等
- 多轮:Router-R1(多轮对话)
- 多模态:TSRouter(时序推理,选文本 LLM 或视觉 VLM)
- 个性化:按用户偏好路由(GMTRouter 等)
- Agentic:复杂任务多轮编排(KNN/LLM multiround)
-
统一 CLI(
llmrouter)train:训练路由模型infer:批量/单条推理chat:Gradio 交互聊天
-
数据生成流水线
从约 11 个 benchmark 生成训练数据:调 API → 评估质量/成本 → 得到路由训练样本。 -
可扩展
custom_routers/:自定义路由算法custom_tasks/:自定义评测任务- 插件系统动态加载
-
部署与集成
- OpenClaw Router:兼容 OpenAI 的 API 服务,可接 Slack 等
- ComfyUI:可视化搭数据/训练/推理流水线
架构简图
Query
→ Router(MetaRouter 基类 + 具体策略)
→ 选出 LLM candidate
→ API 调用(NVIDIA / OpenAI / 本地 Ollama 等)
→ Response
统一抽象在 MetaRouter:加载 YAML 配置与数据,子类实现 route_batch();训练逻辑在各 *Trainer 中。
一句话总结
这是一个研究 + 工程一体的 LLM 路由框架:帮你在模型池里按查询智能选模型,覆盖训练、推理、评测、自定义路由和线上服务。
接下来我们介绍里面两个比较有代表性的做法。
KNNrouter
KNNRouter 是典型的「懒学习」路由:不训练神经网络参数,而是把历史 query 的 embedding 和「当时表现最好的 LLM」存起来;新 query 来了,找最相似的 K 条历史样本,投票决定该用哪个模型。
1. 核心原理
新 Query
→ Longformer 编码成向量
→ 在历史 embedding 中找 K 个最近邻
→ 邻居各自「投出」自己的最优 LLM
→ 多数票(或距离加权)选出目标模型
→(可选)调用该模型 API 返回答案
直觉:相似问题往往适合同一类模型——难推理题常走大模型,简单问答常走小模型。
2. 实现拆解
2.1 训练阶段:造标签 + fit 索引
关键逻辑在 KNNRouter.__init__ 和 KNNRouterTrainer.train:
Step A — 为每个 query 打「最优 LLM」标签
routing_best = self.routing_data_train.loc[
self.routing_data_train.groupby("query")["performance"].idxmax()
].reset_index(drop=True)
# Prepare embedding and label arrays for KNN training
query_embedding_id = routing_best["embedding_id"].tolist()
self.query_embedding_list = [self.query_embedding_data[i].numpy() for i in query_embedding_id]
self.model_name_list = routing_best["model_name"].tolist()
含义:
routing_data_train里每条记录是「某 query × 某 LLM」的表现(performance等)- 对同一个
query,取performance最高的那条 → 该 query 的标签就是那个model_name - 用
embedding_id从预计算的 Longformer embedding(.pt)取出对应向量
Step B — 拟合 sklearn 的 KNN 分类器
def train(self):
if os.path.exists(self.ini_model_path) and self.ini_model_path.endswith(".pkl"):
self.model = load_model(self.ini_model_path)
self.model.fit(self.query_embedding_list, self.model_name_list)
save_model(self.model, self.save_model_path)
这里的「训练」实质是:
KNeighborsClassifier.fit(X, y):存下(embedding → 最优模型名),并建好检索结构(Ball Tree / KD Tree 等)- 序列化成
saved_models/knnrouter/knnrouter.pkl
没有梯度、没有 epoch。
2.2 推理阶段:编码 → 预测 →(可选)调 API
self.knn_model = load_model(load_model_path)
# Compute query embedding and predict model
query_embedding = [get_longformer_embedding(query["query"]).numpy()]
model_name = self.knn_model.predict(query_embedding)[0]
query_output = copy.copy(query)
query_output["model_name"] = model_name
return query_output
流程:
- 加载
.pkl里的 KNN - 用 Longformer 对新 query 做 mean-pooling embedding(与训练侧同一套)
predict→ 得到model_nameroute_batch还会:按任务拼 prompt →call_api→ 若有 ground truth 则算task_performance
2.3 Embedding 怎么来的
get_longformer_embedding:tokenize → Longformer → attention mask 加权 mean pooling → CPU 上的向量。训练用的是预计算的 query_embeddings_longformer.pt;推理时对线查询实时算。
3. 数据依赖
| 文件 | 作用 |
|---|---|
routing_data_train.jsonl | query × model 的历史表现,用来选最优标签 |
query_embeddings_longformer.pt | 预计算 query 向量 |
llm_data(如 default_llm.json) | 候选模型、API endpoint、价格等 |
query_data_* | 训练/测试 query 文本 |
路由训练样本大致长这样:同一 query 对多个模型各有一条,字段含 performance、model_name、embedding_id。
4. 关键超参(hparam)
| 参数 | 含义 | 建议 |
|---|---|---|
n_neighbors | K | 常用 3–10,奇数可减少平票 |
weights | uniform / distance | 文本路由常试 distance |
metric | minkowski / cosine 等 | 文本 embedding 可试 cosine |
p | Minkowski 的幂 | 2 = 欧氏距离 |
algorithm | auto / ball_tree / … | 一般 auto 即可 |
n_jobs | 并行 | -1 用满 CPU |
配置示例见 configs/model_config_train/knnrouter.yaml。
5. 怎么使用
前置
cd LLMRouter-main
pip install -e .
export API_KEYS='{"NVIDIA": "your-key"}' # 推理/调 API 时需要
方式一:CLI(推荐)
1)「训练」(建索引并保存 pkl)
llmrouter train --router knnrouter --config configs/model_config_train/knnrouter.yaml
2)只路由(不调 API)
llmrouter infer --router knnrouter --config configs/model_config_test/knnrouter.yaml \
--query "Explain neural networks" --route-only
3)路由 + 调 LLM
llmrouter infer --router knnrouter --config configs/model_config_test/knnrouter.yaml \
--query "What are the ethical implications of AI?"
4)批量
llmrouter infer --router knnrouter --config configs/model_config_test/knnrouter.yaml \
--input queries.jsonl --output results.json
5)Gradio 聊天
llmrouter chat --router knnrouter --config configs/model_config_test/knnrouter.yaml
注意:测试配置里的 load_model_path 必须指向已训好的 knnrouter.pkl。
方式二:Python API
from llmrouter.models import KNNRouter, KNNRouterTrainer
# 训练
router = KNNRouter(yaml_path="configs/model_config_train/knnrouter.yaml")
trainer = KNNRouterTrainer(router=router, device="cpu")
trainer.train() # → saved_models/knnrouter/knnrouter.pkl
# 推理:只选模型
router = KNNRouter(yaml_path="configs/model_config_test/knnrouter.yaml")
result = router.route_single({"query": "What are the ethical implications of AI?"})
print(result["model_name"])
# 推理:选模型 + 调 API + 评测
results = router.route_batch(
batch=[{"query": "Which element has atomic number 6?", "ground_truth": "B", ...}],
task_name="mmlu",
)
也可直接跑仓库自带脚本:
python tests/train_test/test_knnrouter.py --yaml_path configs/model_config_train/knnrouter.yaml
6. 适用与局限(实现层面)
| 优点 | 局限 |
|---|---|
| 无真正训练,上手快 | 要存全量样本,内存随数据涨 |
| 可解释(可看邻居是谁) | 推理复杂度大致 O(N) |
| 小数据往往还不错 | 强依赖 embedding 质量 |
加新样本后重新 fit 即可 | 高维时距离度量会变钝 |
7. 一句话对照代码
- 标签:每个 query 选
performance最高的model_name - 特征:Longformer embedding
- 模型:
sklearn.neighbors.KNeighborsClassifier - 产物:
.pkl;推理时embed(new_query)→predict→ 选中 LLM(再可选call_api)
Router-R1
Router-R1 是一种 Agentic 多轮路由:不是开局选一个模型就结束,而是让一个「基座策略模型」边推理边决定——何时向哪个外部 LLM 提问、问什么——再把返回信息吸收进推理,多轮后给出最终答案。
论文:Router-R1: Teaching LLMs Multi-Round Routing and Aggregation via Reinforcement Learning(NeurIPS 2025)。
本仓库里主要是 推理侧集成;策略模型一般用官方已训好的权重(如 ulab-ai/Router-R1-Qwen2.5-3B-Instruct),不在本库里做 RL 训练。
要解决的问题
| 传统单轮路由(如 KNN) | Router-R1 |
|---|---|
| 整题 → 选 1 个 LLM → 答一次 | 推理中途可 多次 调用不同 LLM |
| 一次决策,无法中途换模型 | 可按子问题换模型、再聚合 |
| 决策不可解释或只有「选了谁」 | 有完整推理轨迹(<think> / <search> / <answer>) |
适合:多步、跨能力(数学 + 代码 + 常识)的复杂题;不适合:要极低延迟/成本的简单问答。
核心思想
把路由做成 可学习的多轮工具调用:
- 基座模型(Router-R1)负责 规划与决策
- 候选 LLM 池负责 执行子查询
- 用 RL 教基座:何时搜、选谁、怎么整合(论文侧);本库加载训好的模型直接跑
特殊之处:<search> 里不是随意检索,而是固定格式:
<search> LLM-Name:Your-Query </search>
例如:<search> Qwen2.5-7B-Instruct:What is self-attention? </search>
→ 路由池解析模型名 → 调对应 API → 结果塞回 <information>...</information>。
运行时架构
用户问题
│
▼
┌─────────────────────────────────────────┐
│ Router-R1 基座(本地 vLLM,GPU) │
│ 生成:推理 / <search> / <answer> │
└───────────────┬─────────────────────────┘
│ 若输出 <search>Model:Query</search>
▼
┌─────────────────────────────────────────┐
│ Routing Pool(route_service.py) │
│ 解析模型名 → 调 OpenAI 兼容 API │
│ 返回子模型回答 │
└───────────────┬─────────────────────────┘
│ 拼进 prompt:
│ <information>...</information>
▼
继续下一轮生成(最多约 5 轮)
│
▼
输出 <answer>最终答案</answer>
本仓库主循环在 llmrouter/models/router_r1/router.py 的 route_single():
- 按模型族选 prompt(Qwen / Llama)
- vLLM 生成,stop 在
</search>/</answer> - 解析
<search>→access_routing_pool - 把结果 append 回 prompt,循环;出现
<answer>或超过 5 轮则停
交互协议(模型「说话」格式)
Prompt(prompt_pool.py)约束模型:
- 有新信息先在
<think>...</think>里推理 - 缺知识则调用:
<search> LLM-Name:具体问题 </search> - 候选池固定为类似:
Qwen2.5-7B、LLaMA-3.1-8B/70B、Mistral-7B、Mixtral-8x22B、Gemma-2-27B - 够用了就输出:
<answer> ... </answer>
示意轨迹:
[Generation 0]
<think>需要弄清 transformer 定义,Qwen 擅长技术解释</think>
<search>Qwen2.5-7B-Instruct:What are transformers in ML?</search>
<information>
Transformers use self-attention...
</information>
[Generation 1]
<think>信息够了</think>
<answer>
Transformers are neural networks that use self-attention...
</answer>
本仓库模块分工
| 文件 | 作用 |
|---|---|
router.py | 主类 RouterR1:vLLM 循环、解析 search、计 token |
route_service.py | Routing Pool:解析 Model:Query,多线程调外部 LLM API |
prompt_pool.py | Qwen/Llama 的系统提示与候选模型说明 |
| 官方 HF 权重 | 如 ulab-ai/Router-R1-Qwen2.5-3B-Instruct(YAML 里配置) |
成本会分开统计:
prompt_tokens/completion_tokens:本地 vLLMroute_tokens:外部路由池调用
和同库其他「多轮」的对比
| Router-R1 | KNN MultiRound | LLM MultiRound | |
|---|---|---|---|
| 谁决策路由 | 训好的策略 LLM(边想边选) | KNN(按 embedding) | 通用 LLM + prompt |
| 多轮形态 | 推理中可反复 <search> | 先拆题 → 各子题路由 → 聚合 | 同左,但路由也靠 LLM |
| 训练 | 论文用 RL;本库直接加载权重 | 只需 fit KNN | 零样本,基本不训 |
| 依赖 | GPU + vLLM + 路由池 API | 可无 API;拆题可用本地/API | 主要靠 API |
如何使用(本库)
依赖
pip install -e ".[router-r1]" # vllm==0.6.3, torch==2.4.0, openai
# 需要 CUDA
环境
export API_KEYS='your-key' # 路由池后端
export API_BASE='https://...' # OpenAI 兼容 base URL
配置(configs/model_config_test/router_r1.yaml)
hparam:
model_id: "ulab-ai/Router-R1-Qwen2.5-3B-Instruct"
api_base: "" # 或写死;空则用 API_BASE
api_key: "" # 空则用 API_KEYS
CLI
CUDA_VISIBLE_DEVICES=0 llmrouter infer --router router_r1 \
--config configs/model_config_test/router_r1.yaml \
--query "Explain how transformers work in machine learning"
Python
from llmrouter.models import RouterR1
router = RouterR1(yaml_path="configs/model_config_test/router_r1.yaml")
result = router.route_single(
{"query": "Explain how transformers work"},
return_details=True,
)
print(result["response"])
print(result["total_tokens"])
本库视角下:无需 llmrouter train;训练在论文/官方训练代码里完成。
优缺点与何时用
优点:多轮动态选模;轨迹可解释;可组合多个专家模型;加载权重即可推理。
缺点:必须 GPU;延迟和费用高;依赖外部路由池;行为不完全确定;调试复杂。
适合:复杂多步题、研究/演示、质量优先。
不适合:简单路由、无 GPU、成本敏感、要完全确定性输出 → 用 KNN/SVM/MLP 等单轮路由。
一句话总结
Router-R1 = 用 RL 训过的小策略模型当「调度员」:在推理循环里用 <search>模型:子问题</search> 动态调用路由池里的多个 LLM,再把信息聚合进 <answer>;本仓库用 vLLM 跑策略模型、用 route_service 对接外部候选 LLM,实现这套多轮 Agentic 路由。
当然,还有其他的好些方法,后面介绍