LocalForge:ML驱动的LLM路由
我厌倦了手动决定使用哪个本地 LLM。所以我构建了一个自动完成这些的路由器——在不到 5ms 内对每个查询进行分类,并根据基准分数、历史性能和用户反馈将其路由到最优模型。
微信 ezpoda免费咨询:AI编程 | AI模型微调| AI私有化部署
AI模型价格对比 | AI工具导航 | ONNX模型库 | Tripo 3D | Meshy AI | ElevenLabs | KlingAI | ArtSpace | Phot.AI | InVideo
我厌倦了手动决定使用哪个本地 LLM。
编程问题?加载 Qwen。数学问题?切换到推理模型。一般问题?回到更小更快的模型。每次切换都意味着等待 30-60 秒让模型加载到 VRAM 中。这让人精疲力尽。
所以我构建了一个自动完成这些的路由器——在不到 5ms 内对每个查询进行分类,并根据基准分数、历史性能和用户反馈将其路由到最优模型。
这是关于它如何工作的故事,什么没起作用,以及最让我惊讶的是什么。
1、本地 LLM 的问题
在本地运行 LLM 很棒——零 API 成本、完全隐私、完全控制。但运营现实是痛苦的:
- 消费级 GPU(8-48GB VRAM)一次只能加载一个模型
- 没有任何现有的本地推理工具(Ollama、LM Studio、llama.cpp)具有查询感知路由
- 你要么对所有事情使用一个模型并接受平庸的结果,要么手动切换模型并接受这种摩擦
启动这个项目的洞察是:**不同模型在不同任务类型上表现得截然不同。**一个 7B 的编程优化模型在 HumanEval 上击败 32B 的通用模型。一个专注于推理、在数学竞赛上训练的模型在 GSM8K 上优于更大的指令调优模型。
如果你能自动检测查询是什么类型的任务并路由到正确的模型,你会得到比总是使用"最佳"模型更好的结果——而且无需手动切换。
2、路由架构
路由管道有四个阶段:
Query → Classify Task → Score All Models → Select Best → Load & Serve
让我逐一讲解。
3、阶段 1:不到 5ms 的任务分类
第一个问题是:你如何足够快地对查询进行分类,以至于不会增加明显的延迟?
我的第一直觉是使用一个小型神经分类器——微调的 BERT 或 sentence-transformers 模型。我尝试了。准确率不错(约 88%),但延迟是 40-120ms。对于一个在每次请求时都触发的路由层来说,这是不可接受的。
解决方案:TF-IDF + 逻辑回归。
我知道——听起来像 2015 年的技术。但请听我说完。
TF-IDF 向量化(使用 bigram,最多 5,000 个特征)+ 带有平衡类权重的逻辑回归分类器实现了:
- 在 6 个任务类别上达到约 85% 的准确率
- <5ms 推理时间(实际通常 1-3ms)
- 约 1MB 内存占用
- 启动时从 joblib pickle 在毫秒级加载
这 6 个任务类别直接映射到基准优势:
| 任务类型 | 基准信号 | 示例查询 |
|---|---|---|
| coding | HumanEval | "写一个 Python 函数来..." |
| math | GSM8K | "如果一列火车在下午3点出发..." |
| reasoning | MMLU-Pro | "解释为什么罗马帝国..." |
| instruction | MT-Bench | "写一封求职信..." |
| hard_reasoning | GPQA | "推导薛定谔方程..." |
| general | MMLU-Pro | 兜底回退 |
一个重要细节:如果分类器置信度低于 0.6,系统会回退到 general 路由,而不是做出低置信度的路由决策。宁可安全也不要自信地路由错误。
4、阶段 2:多信号评分
一旦我们知道任务类型,我们对每个注册模型进行评分:
final_score = 0.4 × benchmark_score
+ 0.3 × memory_score
+ 0.15 × latency_score
+ 0.15 × feedback_score
基准分数(40%): 分类任务类型的标准化基准结果。一个在 HumanEval 上得到 72% 的模型在编程查询上获得 0.72 分。这些分数来自 HuggingFace 模型卡片、Open LLM Leaderboard,或对于没有发布分数的模型进行本地迷你评估。
记忆分数(30%): 这个最让我惊讶——下面详细说。
延迟分数(15%): 历史平均延迟的标准化逆值。1 - (avg_latency_ms / 10000)。通常在 2 秒内响应的模型比需要 8 秒的得分更高。
反馈分数(15%): 用户的点赞/点踩。简单比率:thumbs_up / total_feedback。权重较低是因为早期反馈稀疏,但随着时间推移会有意义地累积。
5、阶段 3:记忆层(让我惊讶的部分)
这是我最自豪的部分,也是我没想到会如此重要的部分。
记忆层将每个已处理查询的嵌入存储在 Qdrant 中,同时存储结果:
{
"query_text": "implement binary search in Python",
"task_type": "coding",
"model_used": "Qwen2.5-7B-Coder:Q4_K_M",
"outcome": "success",
"latency_ms": 2340,
"timestamp": "2026-04-22T12:00:00Z"
}
当新查询到达时,我们用 nomic-embed-text-v1.5(768 维,余弦相似度)对其进行嵌入,并检索最相似的历史查询。这告诉我们:"对于与这个查询语义相似的查询,哪些模型历史上成功了?"
记忆分数使用指数衰减来给最近的交互更高的权重:
score = Σ(outcome_i × λ^days_since_i × similarity_i) / Σ(λ^days_since_i × similarity_i)
其中 λ = 0.95。14 天前的交互权重约为新交互的 49%。
让我惊讶的是: 一旦系统处理了 50-100 个查询,记忆信号就开始以有意义的方式主导路由决策。基准分数告诉你哪个模型通常更擅长编程。记忆层告诉你哪个模型更擅长你特定类型的编程查询——结果证明这确实是不同的。
一个主要询问异步 Python 模式的用户会看到记忆层学到模型 A 对他们的特定查询持续更好,即使模型 B 的整体 HumanEval 分数更高。
一个重要优化: 我不是为每个候选模型查询一次 Qdrant(N 个模型就是 N 次查询),而是做一次搜索请求 top_k × num_models 结果并在客户端分组。这使得无论注册了多少模型,记忆查找都只有一次向量搜索。
6、阶段 4:回退证据
存在冷启动问题:新模型没有记忆数据。还有置信度问题:有时分类器不确定。
当路由置信度低于 0.3 时,系统检查回退证据——任何成功处理了 ≥2 个相似查询且成功率超过 60% 的模型。这防止路由器对不熟悉的查询类型做出盲目决策。
7、什么没起作用
尝试 1 — 基于嵌入的分类 我尝试使用与每种任务类型的"原型"嵌入的余弦相似度。快速,但准确率很差(约 70%),因为嵌入空间在查询长度上不能干净地分离任务类型。
尝试 2 — 总是路由到最高基准模型 在构建记忆层之前,我测试了一个更简单的路由器,总是选择任务类型中基准分数最高的模型。这对明确的查询效果很好,但在模糊的查询上严重失败,而且忽略了频繁加载大模型的延迟成本。
尝试 3 — 所有信号等权重 早期版本在所有四个信号上使用 0.25 的权重。基准分数在早期(当记忆稀疏时)值得更高的权重,所以经过测试后我确定了 0.4/0.3/0.15/0.15。这些权重可以通过环境变量配置。
8、完整系统
路由器是 LocalForge 的一个组件——我构建的一个自托管 AI 控制平面,处理整个本地 LLM 生命周期:
- 模型管理 — 从 HuggingFace 浏览和下载 GGUF 模型,支持 VRAM 感知过滤
- 推理服务 — 兼容 OpenAI 的
/v1/chat/completions端点(只需更改你的 base URL) - 基准测试 — 从 HF 模型卡片自动获取分数 + 本地迷你评估
- LoRA 微调 — QLoRA 训练管道,通过 SSE 实时流式传输损失
- RAG 知识库 — 使用 LlamaIndex + Qdrant 进行文档摄入和检索
- 仪表板 — 实时硬件监控、路由追踪、记忆统计
一切都在本地运行。无需 Docker。无云依赖。SQLite 用于关系数据,Qdrant 以磁盘持久化模式用于向量。
技术栈:FastAPI · Next.js 16 · llama.cpp · Qdrant · LlamaIndex · PEFT · TRL · scikit-learn
9、结果
在我的 Ada 6000(48GB VRAM)上运行 Qwen2.5-7B、Mistral-7B 和编程优化模型的组合:
- 分类器延迟:1-3ms(第 99 百分位 <5ms)
- 路由开销(完整管道):额外 <10ms 延迟
- 任务分类准确率:在留出测试集上约 85%
- 在 200 个查询后,记忆增强路由在大约 30% 的情况下选择了与仅基准路由不同的模型——而且用户对这些响应评分更高
10、我会做的不同之处
从第一天开始收集标注的路由数据。 我在合成数据上构建了分类器。带有人工标注任务类型的真实查询日志会显著提高准确性,特别是在像"解释这段代码"(编程还是推理?)这样的边缘情况上。
添加模型级置信度。 有些模型在大多数方面都很好但在某一方面特别出色。当前的评分将基准分数视为固定的,但模型的有效分数应该取决于查询难度,而不仅仅是任务类型。
实现自适应权重调整。 0.4/0.3/0.15/0.15 的权重是手动调整的。一个基于反馈结果调整权重的简单赌博算法会更加合理。
11、试一试
LocalForge 在 MIT 许可下开源。
git clone https://github.com/al1-nasir/LocalForge.git
cd LocalForge/backend
pip install -r requirements.txt
uvicorn app.main:app --port 8010
如果你正在本地 LLM 领域构建任何东西,或者对路由方法有想法——我真心希望能听到你的意见。你在使用什么路由策略?
原文链接:How I Built ML-Powered LLM Routing with <5ms Latency
汇智网翻译整理,转载请标明出处