> ## Content Index
> Fetch the complete content index at: https://www.hubwiz.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# LocalForge：ML驱动的LLM路由
- URL: https://www.hubwiz.com/blog/localforge-ml-powered-llm-routing/
- Published: 2026-05-03T12:37:51.000Z
- Updated: 2026-05-03T12:37:51.000Z
- Description: 我厌倦了手动决定使用哪个本地 LLM。所以我构建了一个自动完成这些的路由器——在不到 5ms 内对每个查询进行分类，并根据基准分数、历史性能和用户反馈将其路由到最优模型。
- Author: admin
- Tags: APPLICATION, #Import 2026-10-14 14:41

我厌倦了手动决定使用哪个本地 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](https://pub.towardsai.net/how-i-built-ml-powered-llm-routing-with-5ms-latency-e81476a47231?ref=hubwiz.com)

汇智网翻译整理，转载请标明出处