OA0
OA0 是一个探索 AI 的社区
现在注册
已注册用户请  登录
OA0  ›  技能包  ›  smart-router:具有语义领域评分功能的专家感知型模型路由器

smart-router:具有语义领域评分功能的专家感知型模型路由器

 
  gateway ·  2026-02-08 00:12:19 · 16 次点击  · 0 条评论  

名称: smart-router
描述: >
具备语义领域评分、上下文溢出保护与安全信息脱敏能力的专家感知模型路由系统。通过加权专家评分(基于2026年2月基准测试)自动选择最优AI模型。支持Claude、GPT、Gemini、Grok,提供自动故障转移链、人工介入(HITL)关卡及成本优化功能。
作者: c0nSpIc0uS7uRk3r
版本: 2.1.0
许可证: MIT
元数据:
openclaw:
requires:
bins: ["python3"]
env: ["ANTHROPIC_API_KEY"]
optional_env: ["GOOGLE_API_KEY", "OPENAI_API_KEY", "XAI_API_KEY"]
features:
- 语义领域检测
- 加权专家评分(0-100分)
- 基于风险的强制路由
- 上下文溢出保护(>150K → Gemini)
- 安全凭证脱敏
- 带持久化状态的熔断器
- 低置信度路由的人工介入(HITL)关卡
benchmarks:
source: "2026年2月 MLOC 分析报告"
models:
- "Claude Opus 4.5: SWE-bench 80.9%"
- "GPT-5.2: AIME 100%, 控制流错误率 22 errors/MLOC"
- "Gemini 3 Pro: 并发问题 69 issues/MLOC"


AI 智能路由系统

通过分层分类、自动故障转移处理和成本优化,智能地将请求路由至最优的AI模型。

工作原理(默认静默)

路由系统透明运行——用户正常发送消息,即可获得最适合其任务的最佳模型响应。无需特殊指令。

可选可见性:在任何消息中包含 [show routing] 即可查看路由决策详情。

分层分类系统

路由系统采用三层决策流程:

┌─────────────────────────────────────────────────────────────────┐
│                    第一层:意图检测                              │
│  识别请求的主要目的                                              │
├─────────────────────────────────────────────────────────────────┤
│  代码        │ 分析      │ 创意      │ 实时      │ 通用        │
│  编写/调试   │ 研究      │ 写作      │ 新闻/直播 │ 问答/聊天   │
│  重构        │ 解释      │ 故事      │ X/Twitter │ 翻译        │
│  审查        │ 比较      │ 头脑风暴  │ 价格      │ 总结        │
└──────┬───────┴──────┬──────┴─────┬──────┴─────┬─────┴─────┬─────┘
       │              │            │            │           │
       ▼              ▼            ▼            ▼           ▼
┌─────────────────────────────────────────────────────────────────┐
│                    第二层:复杂度评估                            │
├─────────────────────────────────────────────────────────────────┤
│  简单($级)       │ 中等($$级)   │ 复杂($$$级)              │
│  • 单步任务        │ • 多步任务     │ • 深度推理                │
│  • 简短回复即可    │ • 有一定复杂性 │ • 大量输出                │
│  • 事实查询        │ • 中等上下文   │ • 关键任务                │
│  → Haiku/Flash     │ → Sonnet/Grok/GPT → Opus/GPT-5             │
└──────────────────────────┴─────────────────────┴───────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────┐
│                    第三层:特殊情形覆盖                          │
├─────────────────────────────────────────────────────────────────┤
│  条件                              │ 覆盖至                     │
│  ───────────────────────────────────┼───────────────────────────│
│  上下文 >100K tokens               │ → Gemini Pro (1M 上下文)   │
│  上下文 >500K tokens               │ → Gemini Pro 唯一          │
│  需要实时数据                      │ → Grok(无条件)           │
│  图像/视觉输入                     │ → Opus 或 Gemini Pro       │
│  用户显式覆盖                      │ → 请求的模型               │
└────────────────────────────────────┴────────────────────────────┘

意图检测模式

代码意图

  • 关键词:write, code, debug, fix, refactor, implement, function, class, script, API, bug, error, compile, test, PR, commit
  • 提及的文件扩展名:.py, .js, .ts, .go, .rs, .java 等
  • 输入中包含代码块

分析意图

  • 关键词:analyze, explain, compare, research, understand, why, how does, evaluate, assess, review, investigate, examine
  • 长篇幅问题
  • "Help me understand..."(帮我理解...)

创意意图

  • 关键词:write (story/poem/essay), create, brainstorm, imagine, design, draft, compose
  • 小说/叙事类请求
  • 营销/文案类请求

实时意图

  • 关键词:now, today, current, latest, trending, news, happening, live, price, score, weather
  • 提及 X/Twitter
  • 股票/加密货币代码
  • 体育比分

通用意图(默认)

  • 简单问答
  • 翻译
  • 总结
  • 对话

混合意图(检测到多个意图)

当请求包含多个明确意图时(例如:"编写代码来分析这些数据并进行创意性解释"):
1. 识别主要意图——核心交付物是什么?
2. 路由至最高能力模型——混合任务需要多功能性
3. 默认设为复杂复杂度——多意图 = 多步骤

示例
- "编写代码并解释其工作原理" → 代码(主要)+ 分析 → 路由至 Opus
- "总结这个内容并告诉我关于它的最新消息" → 实时意图优先 → Grok
- "基于真实时事创作故事" → 实时 + 创意 → Grok(实时性优先)

语言处理

非英语请求正常处理——所有支持的模型都具备多语言能力:

模型 非英语支持能力
Opus/Sonnet/Haiku 优秀(100+ 种语言)
GPT-5 优秀(100+ 种语言)
Gemini Pro/Flash 优秀(100+ 种语言)
Grok 良好(主要语言)

意图检测仍然有效,因为:
- 关键词模式包含常见的非英语等效词
- 代码意图通过文件扩展名、代码块检测(与语言无关)
- 复杂度通过查询长度估算(跨语言有效)

边缘情况:如果因语言导致意图不明确,则默认设为通用意图,复杂度为中等。

复杂度信号

简单复杂度($)

  • 简短查询(<50 词)
  • 单个问号
  • "Quick question"(快速提问)、"Just tell me"(直接告诉我)、"Briefly"(简要地)
  • 是/否格式
  • 单位换算、定义

中等复杂度($$)

  • 中等长度查询(50-200 词)
  • 需要处理多个方面
  • "Explain"(解释)、"Describe"(描述)、"Compare"(比较)
  • 提供了一些上下文

复杂复杂度($$$)

  • 长篇幅查询(>200 词)或复杂任务
  • "Step by step"(逐步)、"Thoroughly"(彻底地)、"In detail"(详细地)
  • 多部分问题
  • 有关键/重要限定词
  • 研究、分析或创意性工作

路由矩阵

意图 简单 中等 复杂
代码 Sonnet Opus Opus
分析 Flash GPT-5 Opus
创意 Sonnet Opus Opus
实时 Grok Grok Grok-3
通用 Flash Sonnet Opus

令牌耗尽与自动模型切换

当模型在会话中变得不可用(令牌配额耗尽、达到速率限制、API错误)时,路由系统会自动切换到下一个最佳可用模型,并通知用户

通知格式

当因耗尽而发生模型切换时,用户会收到通知:

┌─────────────────────────────────────────────────────────────────┐
│  ⚠️ 模型切换通知                                                │
│                                                                  │
│  您的请求无法在 claude-opus-4-5 上完成                          │
│  (原因:令牌配额耗尽)。                                        │
│                                                                  │
│  ✅ 请求已使用以下模型完成:anthropic/claude-sonnet-4-5         │
│                                                                  │
│  下方的回复由故障转移模型生成。                                  │
└─────────────────────────────────────────────────────────────────┘

切换原因

原因 描述
token quota exhausted 达到每日/每月令牌限制
rate limit exceeded 每分钟请求过多
context window exceeded 输入对于模型过大
API timeout 模型响应超时
API error 提供商返回错误
model unavailable 模型暂时离线

实现

def execute_with_fallback(primary_model: str, fallback_chain: list[str], request: str) -> Response:
    """
    执行请求,包含自动故障转移和用户通知。
    """
    attempted_models = []
    switch_reason = None

    # 首先尝试主模型
    models_to_try = [primary_model] + fallback_chain

    for model in models_to_try:
        try:
            response = call_model(model, request)

            # 如果切换了模型,在回复前添加通知
            if attempted_models:
                notification = build_switch_notification(
                    failed_model=attempted_models[0],
                    reason=switch_reason,
                    success_model=model
                )
                return Response(
                    content=notification + "\n\n---\n\n" + response.content,
                    model_used=model,
                    switched=True
                )

            return Response(content=response.content, model_used=model, switched=False)

        except TokenQuotaExhausted:
            attempted_models.append(model)
            switch_reason = "token quota exhausted"
            log_fallback(model, switch_reason)
            continue

        except RateLimitExceeded:
            attempted_models.append(model)
            switch_reason = "rate limit exceeded"
            log_fallback(model, switch_reason)
            continue

        except ContextWindowExceeded:
            attempted_models.append(model)
            switch_reason = "context window exceeded"
            log_fallback(model, switch_reason)
            continue

        except APITimeout:
            attempted_models.append(model)
            switch_reason = "API timeout"
            log_fallback(model, switch_reason)
            continue

        except APIError as e:
            attempted_models.append(model)
            switch_reason = f"API error: {e.code}"
            log_fallback(model, switch_reason)
            continue

    # 所有模型均已耗尽
    return build_exhaustion_error(attempted_models)


def build_switch_notification(failed_model: str, reason: str, success_model: str) -> str:
    """构建模型切换时的用户通知。"""
    return f"""⚠️ **模型切换通知**

您的请求无法在 `{failed_model}` 上完成(原因:{reason})。

✅ **请求已使用以下模型完成:** `{success_model}`

下方的回复由故障转移模型生成。"""


def build_exhaustion_error(attempted_models: list[str]) -> Response:
    """构建所有模型耗尽时的错误信息。"""
    models_tried = ", ".join(attempted_models)
    return Response(
        content=f"""❌ **请求失败**

无法完成您的请求。所有可用模型均已耗尽。

**尝试过的模型:** {models_tried}

**您可以:**
1.  **等待** —— 令牌配额通常每小时或每日重置
2.  **简化请求** —— 尝试更短或更简单的请求
3.  **检查状态** —— 运行 `/router status` 查看模型可用性

如果问题持续存在,您的人类管理员可能需要检查API配额或添加其他提供商。""",
        model_used=None,
        switched=False,
        failed=True
    )

令牌耗尽时的故障转移优先级

当模型耗尽时,路由系统会为相同任务类型选择下一个最佳模型:

原始模型 故障转移优先级(相同能力)
Opus Sonnet → GPT-5 → Grok-3 → Gemini Pro
Sonnet GPT-5 → Grok-3 → Opus → Haiku
GPT-5 Sonnet → Opus → Grok-3 → Gemini Pro
Gemini Pro Flash → GPT-5 → Opus → Sonnet
Grok-2/3 (警告:无实时故障转移可用)

用户确认

模型切换后,代理应在回复中注明:
1. 原始模型不可用
2. 实际完成请求的模型
3. 回复质量可能与原始模型的典型输出有所不同

这确保了透明度并设定了适当的期望。

流式响应与故障转移

使用流式响应时,故障转移处理需要特殊考虑:

async def execute_with_streaming_fallback(primary_model: str, fallback_chain: list[str], request: str):
    """
    处理流式响应中的故障转移。

    如果模型在流式传输过程中(而非之前)失败,部分响应将丢失。
    策略:在成功收到第一个数据块之前,不开始流式传输。
    """
    models_to_try = [primary_model] + fallback_chain

    for model in models_to_try:
        try:
            # 首先使用非流式ping测试(可选,会增加延迟)
            # await test_model_availability(model)

            # 开始流式传输
            stream = await call_model_streaming(model, request)
            first_chunk = await stream.get_first_chunk(timeout=10_000)  # 第一个数据块10秒超时

            # 如果执行到这里,说明模型正在响应——继续流式传输
            yield first_chunk
            async for chunk in stream:
                yield chunk
            return  # 成功

        except (FirstChunkTimeout, StreamError) as e:
            log_fallback(model, str(e))
            continue  # 尝试下一个模型

    # 所有模型都失败
    yield build_exhaustion_error(models_to_try)

关键点:在收到第一个数据块后再确认使用该模型。如果第一个数据块超时,则在向用户显示任何部分响应之前进行故障转移。

重试时间配置

RETRY_CONFIG = {
    "initial_timeout_ms": 30_000,     # 首次尝试30秒超时
    "fallback_timeout_ms": 20_000,    # 故障转移尝试20秒超时(更快失败)
    "max_retries_per_model": 1,       # 不重试同一模型
    "backoff_multiplier": 1.5,        # 未使用(无同一模型重试)
    "circuit_breaker_threshold": 3,   # 跳过模型前的失败次数阈值
    "circuit_breaker_reset_ms": 300_000  # 5分钟后重试已失败的模型
}

熔断器:如果一个模型在5分钟内失败3次,则在接下来的5分钟内完全跳过它。这可以防止反复访问已宕机的服务。

故障转移链

当首选模型失败(速率限制、API宕机、错误)时,按顺序降级到下一个选项:

代码任务

Opus → Sonnet → GPT-5 → Gemini Pro

分析任务

Opus → GPT-5 → Gemini Pro → Sonnet

创意任务

Opus → GPT-5 → Sonnet → Gemini Pro

实时任务

Grok-2 → Grok-3 → (警告:无实时故障转移)

通用任务

Flash → Haiku → Sonnet → GPT-5

长上下文(按大小分级)

┌─────────────────────────────────────────────────────────────────┐
│                    长上下文故障转移链                            │
├─────────────────────────────────────────────────────────────────┤
│  令牌数量            │ 故障转移链                                │
│  ───────────────────┼───────────────────────────────────────────│
│  128K - 200K        │ Opus (200K) → Sonnet (200K) → Gemini Pro  │
│  200K - 1M          │ Gemini Pro → Flash (1M) → ERROR_MESSAGE   │
│  > 1M               │ ERROR_MESSAGE(无模型支持)               │
└─────────────────────┴───────────────────────────────────────────┘

实现

```python
def handle_long_context(token_count: int, available_models: dict) -> str | ErrorMessage:
"""处理长上下文请求,提供优雅降级。"""

# 第1级:128K - 200K tokens(Opus/Sonnet可以处理)
if token_count <= 200_000:
    for model in ["opus", "sonnet", "haiku", "gemini-pro", "flash"]:
        if model in available_models and get_context_limit(model) >= token_count:
            return model

# 第2级:200K - 1M tokens(仅Gemini)
elif token_count <= 1_000_000:
    for model in ["gemini-pro", "flash"]:
        if model in available_models:
            return model

# 第3级:> 1M tokens(无可用模型)
# 降级到错误处理

# 未找到合适的模型——返回有用的错误信息
return build_context_error(token_count, available_models)

def build_context_error(token_count: int, available_models: dict) -> ErrorMessage:
"""当没有模型能处理输入时,构建有用的错误信息。"""

# 查找最大的可用上下文窗口
max_available = max(
    (get_context_limit(m) for m in available_models),
    default=0
)

# 确定缺少什么
missing
16 次点击  ∙  0 人收藏  
登录后收藏  
0 条回复
关于 ·  帮助 ·  PING ·  隐私 ·  条款   
OA0 - Omni AI 0 一个探索 AI 的社区
沪ICP备2024103595号-2
耗时 19 ms
Developed with Cursor