feat: 多专业小模型+路由模型系统 MVP(mock 全链路 + FastAPI 网关 + 论文调研)

This commit is contained in:
tzt
2026-08-12 10:40:04 +08:00
commit 1e51167ea5
50 changed files with 49382 additions and 0 deletions
@@ -0,0 +1,681 @@
# 多专业小模型 + 路由模型 —— 实现方案
> 本方案从零开始,逐步构建可运行的原型系统。以代码生成为首个验证领域。
---
## 一、总体架构
```
┌───────────────────────────────────────────────────────────────┐
│ API Gateway │
└─────────────────────┬─────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────┐
│ Router (路由层) │
│ ┌─────────────┐ ┌─────────────┐ ┌───────────────────────┐ │
│ │Classifier │ │Difficulty │ │Router Cache │ │
│ │(意图识别) │ │Estimator │ │(频繁查询→直达路径) │ │
│ └──────┬──────┘ └──────┬──────┘ └───────────────────────┘ │
└─────────┼────────────────┼────────────────────────────────────┘
│ │
▼ ▼
┌───────────────────────────────────────────────────────────────┐
│ Expert Pool (专家模型池) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────┐ │
│ │Code │ │Math │ │Legal │ │Medical │ │... │ │
│ │Expert │ │Expert │ │Expert │ │Expert │ │ │ │
│ │(1.5-7B) │ │(1.5-4B) │ │(3-7B) │ │(3-7B) │ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └─────┘ │
└──────────────────────┬────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────┐
│ Quality Controller (质量层) │
│ ┌─────────────────┐ ┌──────────────────────────────────┐ │
│ │Judge Model │ │Fallback Trigger │ │
│ │(验证输出质量) │ │(不合格时升级到大模型) │ │
│ └─────────────────┘ └──────────────────────────────────┘ │
└──────────────────────┬────────────────────────────────────────┘
│ (必要时)
┌───────────────────────────────────────────────────────────────┐
│ Large Model Fallback (大模型回退) │
│ GPT-4 / Claude / DeepSeek 等 │
└───────────────────────────────────────────────────────────────┘
```
---
## 二、分阶段实现路线
### 第一阶段:MVP(最小可行原型)— 4–6 周
**目标:** 构建一个端到端原型,聚焦单个领域(代码生成),验证路由 + 小模型的可行性。
#### 2.1 环境准备
```bash
# 项目结构
project_root/
├── models/ # 模型权重(Git LFS 或 symlink
├── router/ # 路由模块
│ ├── classifier.py # 分类器
│ ├── config.yaml # 路由配置
│ └── router.py # 主路由逻辑
├── experts/ # 专家模型封装
│ ├── code_expert.py # 代码专家
│ └── base_expert.py # 专家基类
├── judge/ # 质量控制器
│ └── judge.py
├── gateway/ # API 网关
│ └── api.py
├── data/ # 训练/评估数据
├── scripts/ # 训练和评估脚本
├── requirements.txt
└── README.md
```
#### 2.2 构建分类路由器
**推荐方案:使用小模型做意图识别**
```python
# router/classifier.py — 核心分类器
# 方案 A:用 Qwen3-0.5B / Llama-3.2-1B 做 few-shot 分类
# 方案 B:训练一个轻量 BERT 分类器(更小、更快)
from transformers import AutoModelForSequenceClassification, AutoTokenizer
class IntentClassifier:
"""意图分类器:识别查询属于哪个领域和难度"""
def __init__(self, model_name="Qwen/Qwen3-0.5B"):
self.model = AutoModelForSequenceClassification.from_pretrained(
model_name,
num_labels=6 # code, math, legal, medical, general, unknown
)
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
def classify(self, query: str) -> dict:
"""返回 {domain: str, confidence: float, difficulty: str}"""
# 实现分类逻辑 + 置信度评估
pass
def should_fallback(self, query: str) -> bool:
"""判断是否直接走大模型(低置信度查询)"""
result = self.classify(query)
return result["confidence"] < 0.6
```
**关键设计指标:**
- 分类准确率 ≥ 95%
- 推理延迟 < 50ms
- 参数量建议 ≤ 1B
#### 2.3 构建代码专家模型
```python
# experts/code_expert.py
from transformers import AutoModelForCausalLM, AutoTokenizer
class CodeExpert:
"""代码生成专业模型"""
def __init__(self, model_name="Qwen/Qwen2.5-Coder-7B-Instruct"):
self.model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map="auto",
torch_dtype="bfloat16"
)
self.tokenizer = AutoTokenizer.from_pretrained(model_name)
def generate(self, prompt: str, **kwargs) -> str:
"""生成代码回复"""
messages = [{"role": "user", "content": prompt}]
text = self.tokenizer.apply_chat_template(
messages, tokenize=False
)
inputs = self.tokenizer(text, return_tensors="pt").to(self.model.device)
outputs = self.model.generate(
**inputs,
max_new_tokens=kwargs.get("max_tokens", 2048),
temperature=kwargs.get("temperature", 0.2), # 代码生成用低温度
)
return self.tokenizer.decode(outputs[0], skip_special_tokens=True)
```
**模型选择依据:**
| 模型 | 参数量 | 代码能力 | 推理成本 | 推荐场景 |
|------|--------|---------|---------|---------|
| Qwen2.5-Coder-7B | 7B | ★★★★★ | 中 | 主要代码专家 |
| DeepSeek-Coder-1.3B | 1.3B | ★★★★ | 低 | 轻量代码任务 |
| BokantLM-0.5B | 0.5B | ★★★ | 极低 | 简单代码补全 |
#### 2.4 构建质量控制器(Judge
```python
# judge/judge.py
class QualityJudge:
"""输出质量评估器——判断是否需要升级到大模型"""
def __init__(self, model_name="Qwen3-1B-Instruct"):
# 使用小模型作为 Judge,避免引入新的瓶颈
pass
def evaluate(self, query: str, response: str, domain: str) -> dict:
"""评估输出质量,返回评分和是否建议升级"""
# 评估维度:
# - 相关性:是否回答了问题
# - 正确性:领域知识是否正确
# - 完整性:是否遗漏关键信息
# - 安全性:是否包含有害内容
pass
def needs_fallback(self, evaluation: dict) -> bool:
"""判断是否需要升级到大模型"""
score = evaluation["overall_score"]
return score < 0.7 # 低于阈值→升级
```
#### 2.5 主路由逻辑
```python
# router/router.py
class Router:
"""主路由器——协调整个系统"""
def __init__(self, classifier, experts: dict, judge, fallback_model):
self.classifier = classifier
self.experts = experts
self.judge = judge
self.fallback = fallback_model
self.cache = {} # 简单缓存:高频查询跳过路由
async def route(self, query: str) -> dict:
"""路由一个查询"""
# Step 1: 检查缓存
cache_key = self._cache_key(query)
if cache_key in self.cache:
return self.cache[cache_key]
# Step 2: 评估是否需要跳过路由直接走大模型
if self.classifier.should_fallback(query):
return await self._call_fallback(query)
# Step 3: 分类
classification = self.classifier.classify(query)
domain = classification["domain"]
# Step 4: 选择专家模型
expert = self.experts.get(domain) or self.experts["general"]
# Step 5: 生成回复
response = expert.generate(query)
# Step 6: 质量评估
evaluation = self.judge.evaluate(query, response, domain)
# Step 7: 决定是否升级
if self.judge.needs_fallback(evaluation):
response = await self._call_fallback(query)
evaluation["upgraded"] = True
result = {
"response": response,
"domain": domain,
"upgraded": evaluation.get("upgraded", False),
"quality_score": evaluation.get("overall_score"),
"model_used": domain if not evaluation.get("upgraded") else "fallback"
}
# 高频查询写入缓存
self.cache[cache_key] = result
return result
async def _call_fallback(self, query: str) -> str:
"""回退到大模型"""
return await self.fallback.generate(query)
```
#### 2.6 API 网关
```python
# gateway/api.py
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI(title="Expert Router API")
class QueryRequest(BaseModel):
query: str
stream: bool = False
class QueryResponse(BaseModel):
response: str
domain: str
upgraded: bool
model_used: str
@app.post("/chat", response_model=QueryResponse)
async def chat(request: QueryRequest):
return await router.route(request.query)
@app.get("/health")
async def health():
return {"status": "ok", "experts": list(router.experts.keys())}
@app.get("/metrics")
async def metrics():
"""暴露路由统计信息"""
return {
"total_requests": stats.requests,
"fallback_rate": stats.fallback_rate,
"avg_latency": stats.avg_latency,
"domain_distribution": stats.domain_distribution
}
```
#### 2.7 运行与测试
```bash
# 安装依赖
pip install torch transformers fastapi uvicorn pyyaml
# 启动服务
python gateway/api.py
# 测试
curl -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{"query": "用Python写一个快速排序"}'
```
---
### 第二阶段:多领域扩展 — 4–6 周
#### 3.1 领域专家微调流水线
```python
# scripts/train_expert.py
"""
微调流水线:使用 LoRA 高效微调领域专家模型
使用方法:
python train_expert.py --domain math --base_model Qwen/Qwen3-4B-Instruct
"""
from datasets import load_dataset
from transformers import (
AutoModelForCausalLM, AutoTokenizer, TrainingArguments
)
from peft import LoraConfig, get_peft_model
def train_domain_expert(domain: str, base_model: str):
"""使用 LoRA 微调领域专家模型"""
print(f"开始微调 {domain} 专家模型,基础模型: {base_model}")
# 1. 加载领域数据集
# 数学:GSM8K, MATH
# 代码:CodeAlpaca, DomainCodeBench
# 法律:LAiW, UCL-Bench
# 医学:MedQA, PubMedQA
dataset = load_dataset(f"domain_data/{domain}_train")
# 2. 加载基础模型
model = AutoModelForCausalLM.from_pretrained(base_model)
tokenizer = AutoTokenizer.from_pretrained(base_model)
# 3. 配置 LoRA
lora_config = LoraConfig(
r=16, # 秩
lora_alpha=32,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.1,
bias="none",
task_type="CAUSAL_LM"
)
model = get_peft_model(model, lora_config)
# 4. 训练
training_args = TrainingArguments(
output_dir=f"./models/{domain}_expert_lora",
num_train_epochs=3,
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
learning_rate=2e-4,
logging_steps=10,
save_steps=500,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=dataset,
)
trainer.train()
# 5. 保存
model.save_pretrained(f"./models/{domain}_expert_lora")
print(f"{domain} 专家模型微调完成")
```
**推荐领域扩展优先级:**
```
阶段一 阶段二 阶段三
┌────────┐ ┌────────┐ ┌────────┐
│ 代码 │ ──── │ 数学 │ ──── │ 法律 │
│ (7B) │ │ (4B) │ │ (7B) │
└────────┘ └────────┘ └────────┘
│ │
▼ ▼
┌────────┐ ┌────────┐
│ 通用 │ │ 医学 │
│ (1.5B) │ │ (7B) │
└────────┘ └────────┘
```
#### 3.2 缓存层设计
```python
# router/cache.py
class RouterCache:
"""
两阶段缓存:
L1: 精确匹配缓存(完全相同查询)
L2: 语义相似缓存(相似查询命中,使用 embedding 检索)
"""
def __init__(self, embedding_model="BAAI/bge-small-zh-v1.5"):
self.embedder = self._load_embedder(embedding_model)
self.exact_cache = {} # {query_hash: result}
self.semantic_store = [] # [(embedding, query, result)]
self.similarity_threshold = 0.92
def get(self, query: str):
"""尝试从缓存获取"""
# L1: 精确匹配
if query in self.exact_cache:
return self.exact_cache[query]
# L2: 语义匹配
q_emb = self._embed(query)
for emb, cached_query, result in self.semantic_store:
if self._cosine_sim(q_emb, emb) > self.similarity_threshold:
return result
return None
def put(self, query: str, result: dict, frequency: int = 1):
"""写入缓存"""
if frequency > 5: # 高频查询→精确缓存
self.exact_cache[query] = result
else:
self.semantic_store.append((self._embed(query), query, result))
```
---
### 第三阶段:生产化 — 4–6 周
#### 4.1 监控与评估
```python
# scripts/evaluate_router.py
"""
路由系统持续评估脚本
定期在 benchmark 上评估路由系统性能
"""
DOMAIN_BENCHMARKS = {
"code": "humaneval", # 代码生成
"math": "gsm8k", # 数学推理
"legal": "laiw", # 法律推理
"medical": "medqa", # 医学问答
"general": "mmlu", # 通用知识
}
METRICS = {
"accuracy": None, # 路由决策准确率
"fallback_rate": 0.15, # 目标:≤15% 升级率
"avg_latency": 500, # 目标:≤500ms
"cost_ratio": 0.15, # 目标:≤大模型的 15%
"quality_delta": 0.0, # 目标:质量不下降(Δ≥0
}
def evaluate_routing_system():
"""
评估流水线:
1. 加载 benchmark 数据集
2. 对每条数据:路由决策 → 专家生成 → 评分
3. 与大模型 baseline 对比
4. 输出报告
"""
results = {}
for domain, benchmark in DOMAIN_BENCHMARKS.items():
accuracy = run_benchmark(domain, benchmark)
results[domain] = accuracy
# 计算总体指标
report = {
"overall_accuracy": sum(results.values()) / len(results),
"per_domain": results,
"fallback_rate": calculate_fallback_rate(),
"cost_savings": calculate_cost_savings(),
"latency_p50": calculate_latency_percentile(50),
"latency_p99": calculate_latency_percentile(99),
}
return report
```
#### 4.2 自动模型替换机制
```python
# scripts/model_updater.py
"""
利用 Densing Law:每 3.5 个月能力密度翻倍
定期检查是否有更小的新模型可以达到同等性能
"""
class ModelUpdater:
"""自动追踪新模型,评估是否替换旧专家"""
def __init__(self):
self.models_registry = {} # {domain: current_model_info}
def check_for_updates(self):
"""定期检查是否有更优模型可用"""
for domain, current in self.models_registry.items():
# 搜索该领域的新 SOTA 小模型
candidates = self._search_new_models(domain)
for candidate in candidates:
# 如果新模型参数量更小且性能不降
if (candidate["parameters"] < current["parameters"] and
candidate["benchmark_score"] >= current["benchmark_score"] * 0.98):
print(f"发现更优模型: {candidate['name']} "
f"({candidate['parameters']}B vs {current['parameters']}B)")
self._propose_replacement(domain, candidate)
```
#### 4.3 部署架构
```
┌──────────────┐
│ Load │
│ Balancer │
└──────┬───────┘
┌───────────────┼───────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Router │ │ Router │ │ Router │
│ Instance 1 │ │ Instance 2 │ │ Instance 3 │
└─────┬──────┘ └─────┬──────┘ └─────┬──────┘
│ │ │
└───────────────┼───────────────┘
┌───────────┴───────────┐
│ │
┌─────┴──────┐ ┌──────┴─────┐
│ Expert Pool│ │ Judge/Pool │
│ (GPU Nodes)│ │ (GPU Nodes)│
└────────────┘ └────────────┘
```
---
## 三、关键技术决策说明
### 3.1 为什么选择 Qwen 系列作为起点?
| 因素 | Qwen3/Qwen2.5 | Llama 3 | DeepSeek |
|------|--------------|---------|----------|
| 中文能力 | ★★★★★ | ★★★ | ★★★★ |
| 小模型效果 | ★★★★ (0.5B起) | ★★★★★ (1B起) | ★★★★ (1.3B起) |
| 代码专项 | ★★★★★ (有Coder版) | ★★★★ | ★★★★★ |
| 开源协议 | ★★★★ (Apache 2.0) | ★★★★ | ★★★ |
| LoRA 生态 | 成熟 | 成熟 | 成熟 |
**建议:** 中文场景优先选 Qwen 系列;英文/代码场景 Qwen Coder 或 DeepSeek 均可。
### 3.2 路由器选型对比
| 方案 | 延迟 | 准确率 | 成本 | 推荐场景 |
|------|------|--------|------|---------|
| BERT 分类器 (110M) | <10ms | 90-93% | 极低 | 初期/快速原型 |
| 小 LLM 分类 (0.5-1B) | 30-80ms | 94-97% | 低 | 正式环境 |
| 嵌入向量路由 (如 RouterRetriever) | 15-30ms | 93-96% | 低 | 需要动态增减专家 |
| LLM-as-Router (3-7B) | 100-300ms | 96-98% | 中 | 复杂路由逻辑 |
### 3.3 质量控制器方案
**方案 A:小模型 Judge(推荐)**
- 使用 1-3B 模型评估输出质量
- 延迟低(50-150ms
- 足够判断明显质量问题
**方案 BLLM-as-Judge**
- 使用 7-13B 模型
- 评估更全面(可覆盖事实性、合规性等)
- 成本较高,仅在 P99 场景使用
**方案 C:规则 + 模型混合**
- 规则快速过滤(格式、长度、关键词)
- 模型处理复杂评估
- 最佳性价比
---
## 四、预算估算
### 4.1 开发阶段硬件
| 项目 | 规格 | 预估成本(月) |
|------|------|--------------|
| GPU 节点 1 | 1× A100 80GB | ¥15,00020,000 |
| GPU 节点 2 | 1× RTX 4090 24GB | ¥5,0008,000 |
| CPU 节点 | 8核 32GB | ¥1,0002,000 |
### 4.2 运行阶段推理成本
假设日均 10 万次请求:
| 方案 | 估算成本(月) | 说明 |
|------|--------------|------|
| 全量大模型 (70B) | ¥50,00080,000 | 全部请求走 API |
| 路由系统 | ¥5,00012,000 | 80% 小模型,20% 升级 |
| **节省** | **¥45,00068,000** | **节省 8090%** |
### 4.3 开源替代方案(零成本起步)
- 模型:Qwen2.5-Coder / Qwen3 (开源)
- 路由器:BERT 分类器 (自训练)
- Judge:小模型 (开源)
- 推理框架:vLLM / llama.cpp (开源)
- 部署:Docker + 单 GPU (成本可控)
---
## 五、验证指标与成功标准
### 5.1 第一阶段验收标准
- [ ] 路由系统能正确分类 ≥5 种意图
- [ ] 代码专家在 HumanEval 上 pass@1 ≥ 大模型 baseline 的 95%
- [ ] 端到端延迟 < 大模型推理的 1.5×
- [ ] 成本 ≤ 大模型方案的 20%
### 5.2 第二阶段验收标准
- [ ] 3 个以上领域专家均达到或接近 SOTA
- [ ] 质量控制器准确率 ≥90%(与大模型评估的一致性)
- [ ] 缓存命中率 ≥30%
- [ ] 升级率(fallback rate)≤ 20%
### 5.3 第三阶段验收标准
- [ ] 系统可灰度发布、A/B 测试
- [ ] 模型热替换不影响在线服务
- [ ] 整体成本降低 ≥80%
---
## 六、开源工具与参考实现
| 组件 | 推荐工具 | 用途 |
|------|---------|------|
| 模型推理 | vLLM, llama.cpp, TGI | 高性能推理引擎 |
| 微调 | unsloth, LLaMA-Factory, Axolotl | LoRA/QLoRA 高效微调 |
| 路由 | RouterArena (评估), 自建 | 路由决策 |
| 监控 | Prometheus + Grafana | 延迟、升级率、准确率 |
| 嵌入 | BGE, text-embedding-3-small | 语义缓存 |
| 评估 | LM Evaluation Harness | 标准化 benchmark |
---
## 七、时间线总览
```
第 1-2 周 第 3-4 周 第 5-6 周 第 7-10 周 第 11-14 周
┌────────┐ ┌────────┐ ┌────────┐ ┌─────────┐ ┌──────────┐
│ 环境 │→ │ 路由 │→ │ 代码 │→ │ 多领域 │→ │ 生产化 │
│ 搭建 │ │ 原型 │ │ 专家 │ │ 扩展 │ │ 部署 │
└────────┘ └────────┘ └────────┘ └─────────┘ └──────────┘
· 选型 · 分类器 · 微调 · 数学专家 · 缓存
· 环境 · Router · 评估 · 法律专家 · 监控
· 数据 · Judge · 迭代 · 医学专家 · CI/CD
· RouterArena · 热替换
```
---
## 八、风险清单
| 风险 | 概率 | 影响 | 应对 |
|------|------|------|------|
| 路由误分类导致回答质量下降 | 中 | 高 | 引入置信度阈值,低置信走大模型 |
| 小模型推理天花板(复杂任务) | 高 | 中 | 级联升级机制兜底 |
| 多模型管理复杂度超预期 | 中 | 中 | 使用统一推理框架 + LoRA 统一管理 |
| 缓存击穿导致大模型负载飙升 | 低 | 高 | 限流 + 降级 + 预缓存热门 query |
| 领域数据不足微调效果差 | 中 | 中 | 使用合成数据 + few-shot 先验证 |
| 模型能力密度快速变化导致架构重选 | 低 | 低 | 接口抽象化,模型替换不影响路由层 |
---
*文档生成日期:2026-07-30*
*基于 2025 年前沿研究与开源生态*