Skill: freepengyang/hermes-model-router (version v1.0.1)

# Model Router · OpenClaw > 本地优先的智能模型选择器 — 根据任务复杂度自动路由到本地或服务商模型 ## 核心原则 1. **模型选择只在任务入口做一次** 2. **同一任务中途不切换模型**（保护上下文连续性） 3. **简单任务用本地，复杂任务用服务商** --- ## 功能 ### 任务复杂度判断 - **简单任务** → 本地模型（Ollama / llama.cpp） - 单次回答即可完成 - 不需要多步推理或迭代 - 类型：翻译、格式转换、查定义、简单写作、文本润色 - **复杂任务** → 服务商模型（MiniMax / OpenRouter / 其他） - 需要深度推理、多次迭代 - 长上下文理解 - 类型：代码调试、架构设计、战略分析、长文撰写 ### 模型配置 ```yaml providers: local: ollama: endpoint: "http://localhost:11434" default_model: "qwen2.5-coder:7b" llama.cpp: endpoint: "http://localhost:8080" default_model: "qwen2.5-coder-7b-q4" cloud: minmax: endpoint: "https://api.minimax.chat" default_model: "MiniMax-Text-01" openrouter: endpoint: "https://openrouter.ai/api/v1" default_model: "anthropic/claude-sonnet-4-5" ``` --- ## 使用方式 ### 交互式分类 ```bash python3 scripts/classify_task.py "帮我把这段中文翻译成英文" ``` 输出示例： ``` 复杂度：简单 路由：本地模型（Ollama/qwen2.5-coder:7b） 理由：单次完成，无需迭代 ``` ### 批量分类 ```bash python3 scripts/classify_task.py "分析这个产品的市场策略" --verbose ``` --- ## 上下文连续性保护 | 情况 | 处理方式 | |---|---| | 任务内切换 | ❌ 不允许 | | 新任务开始 | ✅ 重新判断 | | 模型不可用 | 自动降级到同级别模型 | --- ## 扩展服务商 新增服务商只需在配置中添加： ```yaml providers: cloud: 新服务商: endpoint: "API地址" default_model: "默认模型" ``` --- ## 决策示例 | 任务描述 | 判断 | 路由 | |---|---|---| | "翻译成英文" | 简单 | 本地模型 | | "写一个快速排序" | 简单 | 本地模型 | | "调试内存泄漏" | 复杂 | 服务商模型 | | "分析市场策略" | 复杂 | 服务商模型 | | "润色邮件" | 简单 | 本地模型 | | "设计后端架构" | 复杂 | 服务商模型 | --- ## 文件结构 ``` openclaw-model-router/ ├── SKILL.md # 本文件 ├── package.json # OpenClaw 清单 ├── install.sh # 安装脚本 └── scripts/ ├── classify_task.py # 任务分类器 └── setup_wizard.py # 配置向导 ``` --- **版本**：v1.0.1 **许可证**：MIT **作者**：freepengyang

path: _meta.json
content: ownerId: kn786az04ebxjs45db9bx4fydx84z33v
slug: hermes-model-router
version: v1.0.0
publishedAt: 1776934947138

path: install.sh
content: #!/bin/bash
#
# Model Router Skill Installer (OpenClaw Version)
# 本地优先的智能模型选择器安装脚本
#

set -e

echo "🧠 Model Router Skill Installer (OpenClaw)"
echo "============================================"
echo ""

# Colors
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

# Configuration
SKILL_DIR="$HOME/.clawdbot/skills/model-router"
WORKSPACE_SKILL_DIR="$HOME/clawd/skills/model-router"

# Determine where to install
if [ -d "$HOME/clawd/skills" ]; then
    TARGET_DIR="$WORKSPACE_SKILL_DIR"
    echo -e "${GREEN}📁${NC} Installing to workspace skills: $TARGET_DIR"
elif [ -d "$HOME/.clawdbot/skills" ]; then
    TARGET_DIR="$SKILL_DIR"
    echo -e "${GREEN}📁${NC} Installing to global skills: $TARGET_DIR"
else
    mkdir -p "$SKILL_DIR"
    TARGET_DIR="$SKILL_DIR"
    echo -e "${GREEN}📁${NC} Creating global skills directory: $TARGET_DIR"
fi

# Get current directory
CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

echo ""
echo -e "${GREEN}📦${NC} Installing from: $CURRENT_DIR"

# Copy files
echo -e "${GREEN}📋${NC} Copying skill files..."
cp -r "$CURRENT_DIR"/* "$TARGET_DIR/"

# Make scripts executable
echo -e "${GREEN}🔧${NC} Making scripts executable..."
chmod +x "$TARGET_DIR/scripts/"*.py 2>/dev/null || true

# Create config directory
mkdir -p "$HOME/.model-router"
chmod 700 "$HOME/.model-router"

echo ""
echo -e "${GREEN}✅${NC} Skill installed to: $TARGET_DIR"
echo ""
echo -e "${GREEN}🧠${NC} Model Router v1.0.0 (OpenClaw)"
echo ""
echo -e "${GREEN}📖${NC} Next steps:"
echo "   1. Run the setup wizard:"
echo "      cd $TARGET_DIR"
echo "      python3 scripts/setup_wizard.py"
echo ""
echo "   2. Configure your providers (local / cloud)"
echo ""
echo "   3. Start routing tasks optimally!"
echo ""
echo -e "${GREEN}📚${NC} Documentation: $TARGET_DIR/SKILL.md"
echo ""
echo -e "${GREEN}🎉${NC} Installation complete!"


path: package.json
content: {
  "name": "openclaw-model-router",
  "version": "1.0.1",
  "description": "本地优先的智能模型选择器 — 简单任务路由到本地模型，复杂任务路由到服务商模型",
  "main": "scripts/classify_task.py",
  "scripts": {
    "classify": "python3 scripts/classify_task.py",
    "setup": "python3 scripts/setup_wizard.py",
    "install": "bash install.sh"
  },
  "keywords": [
    "model",
    "router",
    "ai",
    "local",
    "ollama",
    "openrouter",
    "minimax",
    "cost-optimization",
    "routing",
    "model-selector"
  ],
  "author": "freepengyang",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/freepengyang/openclaw-model-router/issues"
  },
  "homepage": "https://github.com/freepengyang/openclaw-model-router",
  "clawdbot": {
    "category": "ai",
    "skills": {
      "model-router": {
        "name": "Model Router",
        "description": "智能路由任务到本地或服务商模型 — 简单任务用本地模型省成本，复杂任务用服务商模型保质量"
      }
    }
  }
}


path: scripts/classify_task.py
content: #!/usr/bin/env python3
"""
Model Router - 任务分类器
根据任务描述判断复杂度并推荐模型
"""

import json
import sys
import os

# 简单任务关键词
SIMPLE_KEYWORDS = [
    "翻译", "润色", "格式", "转换", "查", "定义",
    "写一个简单的", "quick", "simple", "translate", "polish",
    "总结", "摘要", "提取"
]

# 复杂任务关键词
COMPLEX_KEYWORDS = [
    "调试", "分析", "架构", "设计", "战略", "报告",
    "debug", "analyze", "architecture", "design", "strategy",
    "3000字", "长文", "复杂", "推理", "证明", "为什么"
]

# 开发/全栈任务关键词（这类任务通常是复杂的）
DEV_KEYWORDS = [
    "写一个", "写个", "开发", "前端", "后端", "全栈",
    "SPA", "App", "应用", "网站", "系统", "平台",
    "接口", "API", "数据库", "登录", "注册", "管理后台",
    "组件", "页面", "路由", "切图", "UI", "UE",
    "classify", "classifier", "script", "脚本", "工具",
    "自动化", "机器人", "bot", "爬虫", "抓取",
    "部署", "docker", "k8s", "kubernetes", "CI/CD",
]


def classify_task(task_description: str, verbose: bool = False) -> dict:
    """判断任务复杂度并返回推荐模型"""
    
    task_lower = task_description.lower()
    
    # 推理类任务默认复杂
    has_reasoning = any(kw in task_lower for kw in ["推理", "证明", "为什么", "reason", "prove", "why"])
    
    # 复杂任务判断
    has_complex = any(kw in task_lower for kw in COMPLEX_KEYWORDS)

    # 开发/全栈任务判断（这类任务通常是复杂的）
    has_dev = any(kw in task_lower for kw in DEV_KEYWORDS)

    # 简单任务判断
    has_simple = any(kw in task_lower for kw in SIMPLE_KEYWORDS)

    if has_complex or has_reasoning or has_dev:
        complexity = "复杂"
        route = "服务商模型 (MiniMax / OpenRouter)"
        if has_dev:
            reason = "开发/全栈任务，需要多文件/多模块实现"
        else:
            reason = "需要深度推理或多步分析"
        local_model = None
        cloud_model = "MiniMax-Text-01"
    elif has_simple:
        complexity = "简单"
        route = "本地模型 (Ollama)"
        reason = "单次完成，无需迭代"
        local_model = "qwen2.5-coder:7b"
        cloud_model = None
    else:
        complexity = "简单"
        route = "本地模型 (Ollama)"
        reason = "默认简单任务"
        local_model = "qwen2.5-coder:7b"
        cloud_model = None
    
    result = {
        "task": task_description,
        "complexity": complexity,
        "route": route,
        "reason": reason
    }
    
    if verbose:
        result["local_model"] = local_model
        result["cloud_model"] = cloud_model
        result["matched_keywords"] = [
            kw for kw in COMPLEX_KEYWORDS + SIMPLE_KEYWORDS + DEV_KEYWORDS
            if kw.lower() in task_lower
        ]
    
    return result


def main():
    verbose = "--verbose" in sys.argv or "-v" in sys.argv
    
    if len(sys.argv) < 2:
        print("用法: python3 classify_task.py <任务描述> [--verbose]")
        print("")
        print("示例:")
        print('  python3 classify_task.py "帮我翻译成英文"')
        print('  python3 classify_task.py "分析市场策略" --verbose')
        sys.exit(1)
    
    # 获取任务描述（过滤掉 --verbose）
    task_description = " ".join([arg for arg in sys.argv[1:] if not arg.startswith("--")])
    
    result = classify_task(task_description, verbose)
    
    print(f"\n📋 任务: {result['task']}")
    print(f"⚡ 复杂度: {result['complexity']}")
    print(f"🎯 路由: {result['route']}")
    print(f"💡 理由: {result['reason']}")
    
    if verbose and result.get("matched_keywords"):
        print(f"\n🔍 匹配关键词: {', '.join(result['matched_keywords'])}")
    
    if verbose:
        if result["local_model"]:
            print(f"\n📦 本地模型: {result['local_model']}")
        if result["cloud_model"]:
            print(f"☁️  服务商模型: {result['cloud_model']}")
    
    print()
    
    # 返回退出码：0=简单，1=复杂
    sys.exit(0 if result["complexity"] == "简单" else 1)


if __name__ == "__main__":
    main()


path: scripts/setup_wizard.py
content: #!/usr/bin/env python3
"""
Model Router - 配置向导
引导用户配置本地模型和服务商模型
"""

import json
import os
import sys

CONFIG_DIR = os.path.expanduser("~/.model-router")
CONFIG_FILE = os.path.join(CONFIG_DIR, "config.json")


def load_config() -> dict:
    """加载现有配置"""
    if os.path.exists(CONFIG_FILE):
        with open(CONFIG_FILE, "r", encoding="utf-8") as f:
            return json.load(f)
    return {
        "local": {
            "provider": "ollama",
            "endpoint": "http://localhost:11434",
            "model": "qwen2.5-coder:7b"
        },
        "cloud": {
            "provider": "minmax",
            "endpoint": "https://api.minimax.chat",
            "model": "MiniMax-Text-01"
        }
    }


def save_config(config: dict):
    """保存配置"""
    os.makedirs(CONFIG_DIR, exist_ok=True)
    with open(CONFIG_FILE, "w", encoding="utf-8") as f:
        json.dump(config, f, indent=2, ensure_ascii=False)
    print(f"\n✅ 配置已保存到: {CONFIG_FILE}")


def setup_local_model(config: dict):
    """配置本地模型"""
    print("\n" + "=" * 50)
    print("🔧 本地模型配置")
    print("=" * 50)
    
    print("\n支持的本地模型提供商:")
    print("  1. Ollama (默认)")
    print("  2. llama.cpp")
    
    provider = input("\n选择提供商 [1]: ").strip() or "1"
    
    if provider == "2":
        config["local"]["provider"] = "llama.cpp"
        config["local"]["endpoint"] = input("endpoint [http://localhost:8080]: ").strip() or "http://localhost:8080"
        config["local"]["model"] = input("模型名 [qwen2.5-coder-7b-q4]: ").strip() or "qwen2.5-coder-7b-q4"
    else:
        config["local"]["provider"] = "ollama"
        config["local"]["endpoint"] = input("endpoint [http://localhost:11434]: ").strip() or "http://localhost:11434"
        config["local"]["model"] = input("模型名 [qwen2.5-coder:7b]: ").strip() or "qwen2.5-coder:7b"
    
    print(f"\n✅ 本地模型配置完成:")
    print(f"   提供商: {config['local']['provider']}")
    print(f"   endpoint: {config['local']['endpoint']}")
    print(f"   模型: {config['local']['model']}")


def setup_cloud_model(config: dict):
    """配置服务商模型"""
    print("\n" + "=" * 50)
    print("☁️ 服务商模型配置")
    print("=" * 50)
    
    print("\n支持的服务商:")
    print("  1. MiniMax (默认)")
    print("  2. OpenRouter")
    print("  3. 其他")
    
    provider = input("\n选择服务商 [1]: ").strip() or "1"
    
    if provider == "2":
        config["cloud"]["provider"] = "openrouter"
        config["cloud"]["endpoint"] = "https://openrouter.ai/api/v1"
        config["cloud"]["model"] = input("模型 (如 anthropic/claude-sonnet-4-5) [anthropic/claude-sonnet-4-5]: ").strip() or "anthropic/claude-sonnet-4-5"
    elif provider == "3":
        config["cloud"]["provider"] = input("服务商名称: ").strip()
        config["cloud"]["endpoint"] = input("API endpoint: ").strip()
        config["cloud"]["model"] = input("默认模型: ").strip()
    else:
        config["cloud"]["provider"] = "minmax"
        config["cloud"]["endpoint"] = "https://api.minimax.chat"
        config["cloud"]["model"] = input("模型 [MiniMax-Text-01]: ").strip() or "MiniMax-Text-01"
    
    print(f"\n✅ 服务商模型配置完成:")
    print(f"   提供商: {config['cloud']['provider']}")
    print(f"   endpoint: {config['cloud']['endpoint']}")
    print(f"   模型: {config['cloud']['model']}")


def main():
    print("=" * 50)
    print("🧠 Model Router 配置向导")
    print("=" * 50)
    print("\n本向导将帮助你配置:")
    print("  1. 本地模型 (用于简单任务)")
    print("  2. 服务商模型 (用于复杂任务)")
    
    config = load_config()
    print("\n当前配置:")
    print(json.dumps(config, indent=2, ensure_ascii=False))
    
    print("\n" + "-" * 50)
    print("选择要配置的项目:")
    print("  1. 配置本地模型")
    print("  2. 配置服务商模型")
    print("  3. 全部配置")
    print("  4. 保存当前配置并退出")
    print("  5. 仅测试连接")
    
    choice = input("\n选择 [3]: ").strip() or "3"
    
    if choice == "1":
        setup_local_model(config)
    elif choice == "2":
        setup_cloud_model(config)
    elif choice == "3":
        setup_local_model(config)
        setup_cloud_model(config)
    elif choice == "4":
        save_config(config)
        sys.exit(0)
    elif choice == "5":
        print("\n🔍 测试连接...")
        # TODO: 实现连接测试
        print("连接测试功能待实现")
        sys.exit(0)
    else:
        print("无效选择")
        sys.exit(1)
    
    save_config(config)
    
    print("\n" + "=" * 50)
    print("🎉 配置完成！")
    print("=" * 50)
    print("\n使用示例:")
    print("  python3 scripts/classify_task.py \"翻译成英文\"")
    print("  python3 scripts/classify_task.py \"分析市场策略\" --verbose")
    print()


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)