说说 LLMRouter,Agent 执行过程中怎么动态的选择 LLM

Cursor 在执行的过程中,是能够自己动态的选择合适的 LLLM 的。

我们在实际项目中,尤其是当使用量很大,并发很高的时候,也是面临一样的问题,如何动态的选择合适的 LLM,降低成本和延迟,保障效果。

LLMRouter 是一个开源的 LLM 智能路由库:对每个用户查询,在多个候选大模型中自动选一个最合适的来回答,目标是在效果、成本和延迟之间做权衡。

核心问题

你通常有一堆模型(小模型便宜快、大模型贵但强)。LLMRouter 做的事是:

给定 query → 路由模型决策 → 调用选中的 LLM → 返回结果

主要能力

  1. 智能路由(核心)

    根据任务难度、性能、成本等,动态选择最优模型,而不是固定用某一个。

  2. 16+ 种路由策略,分五类:

    • 单轮:KNN / SVM / MLP / 矩阵分解 / Elo / 图路由 / 对比学习等
    • 多轮:Router-R1(多轮对话)
    • 多模态:TSRouter(时序推理,选文本 LLM 或视觉 VLM)
    • 个性化:按用户偏好路由(GMTRouter 等)
    • Agentic:复杂任务多轮编排(KNN/LLM multiround)
  3. 统一 CLI(llmrouter

    • train:训练路由模型
    • infer:批量/单条推理
    • chat:Gradio 交互聊天
  4. 数据生成流水线

    从约 11 个 benchmark 生成训练数据:调 API → 评估质量/成本 → 得到路由训练样本。

  5. 可扩展

    • custom_routers/:自定义路由算法
    • custom_tasks/:自定义评测任务
    • 插件系统动态加载
  6. 部署与集成

    • OpenClaw Router:兼容 OpenAI 的 API 服务,可接 Slack 等
    • ComfyUI:可视化搭数据/训练/推理流水线

架构简图

js 复制代码
Query
  → Router(MetaRouter 基类 + 具体策略)
  → 选出 LLM candidate
  → API 调用(NVIDIA / OpenAI / 本地 Ollama 等)
  → Response

统一抽象在 MetaRouter:加载 YAML 配置与数据,子类实现 route_batch();训练逻辑在各 *Trainer 中。

一句话总结

这是一个研究 + 工程一体的 LLM 路由框架:帮你在模型池里按查询智能选模型,覆盖训练、推理、评测、自定义路由和线上服务。

接下来我们介绍里面两个比较有代表性的做法。

KNNrouter

KNNRouter 是典型的「懒学习」路由:不训练神经网络参数,而是把历史 query 的 embedding 和「当时表现最好的 LLM」存起来;新 query 来了,找最相似的 K 条历史样本,投票决定该用哪个模型。


1. 核心原理

js 复制代码
新 Query
→ Longformer 编码成向量
→ 在历史 embedding 中找 K 个最近邻
→ 邻居各自「投出」自己的最优 LLM
→ 多数票(或距离加权)选出目标模型
→(可选)调用该模型 API 返回答案

直觉:相似问题往往适合同一类模型------难推理题常走大模型,简单问答常走小模型。


2. 实现拆解

2.1 训练阶段:造标签 + fit 索引

关键逻辑在 KNNRouter.__init__KNNRouterTrainer.train

Step A --- 为每个 query 打「最优 LLM」标签

python 复制代码
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 分类器

python 复制代码
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)

这里的「训练」实质是:

  1. KNeighborsClassifier.fit(X, y):存下 (embedding → 最优模型名),并建好检索结构(Ball Tree / KD Tree 等)
  2. 序列化成 saved_models/knnrouter/knnrouter.pkl

没有梯度、没有 epoch。

2.2 推理阶段:编码 → 预测 →(可选)调 API

python 复制代码
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

流程:

  1. 加载 .pkl 里的 KNN
  2. 用 Longformer 对新 query 做 mean-pooling embedding(与训练侧同一套)
  3. predict → 得到 model_name
  4. route_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 对多个模型各有一条,字段含 performancemodel_nameembedding_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. 怎么使用

前置

js 复制代码
cd LLMRouter-main

pip install -e .

export API_KEYS='{"NVIDIA": "your-key"}' # 推理/调 API 时需要

方式一:CLI(推荐)

1)「训练」(建索引并保存 pkl)

js 复制代码
llmrouter train --router knnrouter --config configs/model_config_train/knnrouter.yaml

2)只路由(不调 API)

js 复制代码
llmrouter infer --router knnrouter --config configs/model_config_test/knnrouter.yaml \

--query "Explain neural networks" --route-only

3)路由 + 调 LLM

js 复制代码
llmrouter infer --router knnrouter --config configs/model_config_test/knnrouter.yaml \

--query "What are the ethical implications of AI?"

4)批量

js 复制代码
llmrouter infer --router knnrouter --config configs/model_config_test/knnrouter.yaml \

--input queries.jsonl --output results.json

5)Gradio 聊天

js 复制代码
llmrouter chat --router knnrouter --config configs/model_config_test/knnrouter.yaml

注意:测试配置里的 load_model_path 必须指向已训好的 knnrouter.pkl

方式二:Python API

python 复制代码
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",
)

也可直接跑仓库自带脚本:

js 复制代码
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>

适合:多步、跨能力(数学 + 代码 + 常识)的复杂题;不适合:要极低延迟/成本的简单问答。


核心思想

把路由做成 可学习的多轮工具调用:

  1. 基座模型(Router-R1)负责 规划与决策
  2. 候选 LLM 池负责 执行子查询
  3. 用 RL 教基座:何时搜、选谁、怎么整合(论文侧);本库加载训好的模型直接跑

特殊之处:<search> 里不是随意检索,而是固定格式:

js 复制代码
<search> LLM-Name:Your-Query </search>

例如:<search> Qwen2.5-7B-Instruct:What is self-attention? </search>

→ 路由池解析模型名 → 调对应 API → 结果塞回 <information>...</information>


运行时架构

js 复制代码
用户问题
    │
    ▼
┌─────────────────────────────────────────┐
│  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.pyroute_single()

  1. 按模型族选 prompt(Qwen / Llama)
  2. vLLM 生成,stop 在 </search> / </answer>
  3. 解析 <search>access_routing_pool
  4. 把结果 append 回 prompt,循环;出现 <answer> 或超过 5 轮则停

交互协议(模型「说话」格式)

Prompt(prompt_pool.py)约束模型:

  1. 有新信息先在 <think>...</think> 里推理
  2. 缺知识则调用:<search> LLM-Name:具体问题 </search>
  3. 候选池固定为类似:
    Qwen2.5-7BLLaMA-3.1-8B/70BMistral-7BMixtral-8x22BGemma-2-27B
  4. 够用了就输出:<answer> ... </answer>

示意轨迹:

js 复制代码
[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:本地 vLLM
  • route_tokens:外部路由池调用

和同库其他「多轮」的对比

Router-R1 KNN MultiRound LLM MultiRound
谁决策路由 训好的策略 LLM(边想边选) KNN(按 embedding) 通用 LLM + prompt
多轮形态 推理中可反复 <search> 先拆题 → 各子题路由 → 聚合 同左,但路由也靠 LLM
训练 论文用 RL;本库直接加载权重 只需 fit KNN 零样本,基本不训
依赖 GPU + vLLM + 路由池 API 可无 API;拆题可用本地/API 主要靠 API

如何使用(本库)

依赖

js 复制代码
pip install -e ".[router-r1]" # vllm==0.6.3, torch==2.4.0, openai

# 需要 CUDA

环境

js 复制代码
export API_KEYS='your-key'          # 路由池后端
export API_BASE='https://...'       # OpenAI 兼容 base URL

配置(configs/model_config_test/router_r1.yaml

js 复制代码
hparam:

model_id: "ulab-ai/Router-R1-Qwen2.5-3B-Instruct"

api_base: "" # 或写死;空则用 API_BASE

api_key: "" # 空则用 API_KEYS

CLI

js 复制代码
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

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 路由。

当然,还有其他的好些方法,后面介绍

相关推荐
gis开发之家2 小时前
《Vue3 从入门到大神40篇》Vue3 源码详解(十):diff 算法全解析 —— 为什么 Vue3 比 Vue2 更快?
javascript·算法·typescript·前端框架·vue3·vue3源码
进击的丸子2 小时前
虹软人脸服务器SDK-C++语言Demo实操指南
后端·算法
兰令水2 小时前
hot100【acm版】【2026.7.25/26打卡-java版本】
java·算法·排序算法
音视频工程实战3 小时前
PromptQL 新手入门与实战指南
数据库·sql·算法
wabs6663 小时前
关于图论【卡码网104.建造最大岛屿的思考】
数据结构·算法·图论
玖玥拾4 小时前
LeetCode 27 移除元素
算法·leetcode
冻柠檬飞冰走茶4 小时前
PTA基础编程题目集 7-15 计算圆周率(C语言实现)
c语言·开发语言·数据结构·算法
问商十三载4 小时前
AI 引擎生成式优化两种思路怎么选?2026 对比分析附选型方法
人工智能·算法