LmStudio本地大模型无限续杯OpenClaw解决方案

0 阅读6分钟

本篇文章主要讲解通过LmStudio利用本地gpu资源实现对OpenClaw的无限续杯解决方案及实现方式。 作者:任聪聪 日期:2026年3月9日

必要条件

必须是独显且,显卡基本超过GTX4090。

准备材料

lmStudio

下载地址:lmstudio.ai/

配置脚本:

#!/usr/bin/env python3
"""
LM Studio OpenClaw 配置助手
自动探测本地 LM Studio (http://localhost:1234) 的模型,并生成 OpenClaw 配置。
"""

import json
import os
import sys
import urllib.request
import urllib.error
from datetime import datetime
from typing import Any, Dict, List, Optional, Tuple

# --- 配置常量 ---
DEFAULT_LMSTUDIO_URL = "http://localhost:1234/v1"
DEFAULT_CONTEXT_WINDOW = 32768  # 默认假设模型支持 32k
DEFAULT_MAX_TOKENS = 8192
OPENCLAW_MIN_CONTEXT = 16000    # OpenClaw 强制要求的最小上下文

def print_banner():
    print("=" * 70)
    print("🔧 LM Studio OpenClaw 配置助手")
    print("=" * 70)
    print()

def get_config_path() -> str:
    possible_paths = [
        os.path.expanduser("~/.openclaw/openclaw.json"),
        os.path.expanduser("~/.moltbot/moltbot.json"),
    ]
    env_path = os.environ.get("OPENCLAW_CONFIG_PATH")
    if env_path:
        return env_path
    for path in possible_paths:
        if os.path.exists(path):
            return path
    return possible_paths[0]

def load_config(path: str) -> Dict[str, Any]:
    if not os.path.exists(path):
        print(f"⚠️  配置文件不存在: {path},将创建新配置...")
        return {}
    try:
        with open(path, "r", encoding="utf-8") as f:
            return json.load(f)
    except Exception as e:
        print(f"❌ 读取失败: {e}")
        sys.exit(1)

def backup_config(path: str) -> str:
    if not os.path.exists(path):
        return ""
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    backup_path = f"{path}.backup.{timestamp}"
    import shutil
    shutil.copy2(path, backup_path)
    print(f"✅ 已创建备份: {backup_path}")
    return backup_path

def save_config(config: Dict, path: str) -> bool:
    try:
        os.makedirs(os.path.dirname(path), exist_ok=True)
        with open(path, "w", encoding="utf-8") as f:
            json.dump(config, f, indent=2, ensure_ascii=False)
        return True
    except Exception as e:
        print(f"❌ 保存失败: {e}")
        return False

def is_embedding_model(model_id: str) -> bool:
    model_id_lower = model_id.lower()
    keywords = ["embed", "bge", "m3e", "text-embedding", "gte", "e5", "nomic"]
    return any(k in model_id_lower for k in keywords)

def fetch_models(base_url: str) -> Tuple[List[Dict], Optional[str]]:
    """从 LM Studio 获取模型列表"""
    models_url = f"{base_url.rstrip('/')}/models"
    request = urllib.request.Request(models_url, method="GET")
    try:
        with urllib.request.urlopen(request, timeout=10) as response:
            data = json.loads(response.read().decode("utf-8"))
            models = data.get("data", []) if isinstance(data, dict) else data
            return [{"id": m["id"]} for m in models if isinstance(m, dict) and "id" in m], None
    except urllib.error.URLError as e:
        return [], f"无法连接到 LM Studio ({base_url}): {e.reason}"
    except Exception as e:
        return [], str(e)

def build_model_config(model_id: str, context_window: int) -> Dict:
    """为单个模型构建 OpenClaw 配置片段"""
    input_types = ["text"]
    # 简单判断是否为视觉模型 (可以根据需要扩展)
    if "vl" in model_id.lower() or "vision" in model_id.lower():
        input_types.append("image")

    config = {
        "id": model_id,
        "name": f"{model_id} (Local)",
        "reasoning": False,
        "input": input_types,
        "cost": {"input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0},
        "contextWindow": context_window,
        "maxTokens": min(DEFAULT_MAX_TOKENS, context_window // 4),
        "compat": {"supportsDeveloperRole": True}
    }
    return config

def main():
    print_banner()
    
    # 1. 探测 LM Studio
    lmstudio_url = DEFAULT_LMSTUDIO_URL
    print(f"🔄 正在探测本地 LM Studio ({lmstudio_url})...")
    available_models, error = fetch_models(lmstudio_url)
    
    if error:
        print(f"❌ 错误: {error}")
        print("\n请确保:")
        print("  1. LM Studio 已启动")
        print("  2. 'Local Server' 功能已开启 (端口 1234)")
        print("  3. 已加载至少一个模型")
        sys.exit(1)
    
    if not available_models:
        print("❌ 错误: LM Studio 未返回任何模型。")
        print("   请在 LM Studio 中加载一个模型后再试。")
        sys.exit(1)
    
    print(f"✅ 成功获取 {len(available_models)} 个模型\n")
    
    # 2. 分离 LLM 和 Embedding
    llm_models = [m for m in available_models if not is_embedding_model(m["id"])]
    embedding_models = [m for m in available_models if is_embedding_model(m["id"])]

    if not llm_models:
        print("❌ 错误: 未找到任何语言模型 (LLM)。")
        print("   请在 LM Studio 中加载一个 LLM (如 Qwen, Llama 等)。")
        sys.exit(1)

    # 3. 让用户选择主模型
    default_model = llm_models[0]["id"]
    print("📋 请选择主模型 (直接回车使用第一个):")
    for i, m in enumerate(llm_models):
        marker = " [默认]" if i == 0 else ""
        print(f"   [{i}] {m['id']}{marker}")
    
    choice = input("\n您的选择: ").strip()
    if not choice:
        selected_llm = llm_models[0]
    else:
        try:
            idx = int(choice)
            if 0 <= idx < len(llm_models):
                selected_llm = llm_models[idx]
            else:
                raise ValueError
        except ValueError:
            print("⚠️  无效输入,使用默认模型。")
            selected_llm = llm_models[0]
    
    print(f"✅ 已选择主模型: {selected_llm['id']}")

    # 4. 处理上下文窗口
    print(f"\n🔍 检查上下文窗口...")
    # 这里我们无法从 API 直接获取 LM Studio 的 Context Length 设置
    # 所以我们让用户自己确认,并提供一个合理的默认值
    print(f"   注意: OpenClaw 要求上下文窗口 >= {OPENCLAW_MIN_CONTEXT} tokens.")
    print(f"   请确保您在 LM Studio 的 'Local Server' 设置中将 'Context Length' 设为 >= {OPENCLAW_MIN_CONTEXT} (推荐 32768).")
    
    user_ctx = input(f"\n请输入您在 LM Studio 中设置的上下文长度 (直接回车默认 {DEFAULT_CONTEXT_WINDOW}): ").strip()
    try:
        context_window = int(user_ctx) if user_ctx else DEFAULT_CONTEXT_WINDOW
    except ValueError:
        print(f"⚠️  无效输入,使用默认值 {DEFAULT_CONTEXT_WINDOW}.")
        context_window = DEFAULT_CONTEXT_WINDOW
    
    if context_window < OPENCLAW_MIN_CONTEXT:
        print(f"\n⚠️  警告: 您输入的上下文 ({context_window}) 小于 OpenClaw 要求的最小值 ({OPENCLAW_MIN_CONTEXT})!")
        confirm = input("   是否继续? (y/N): ").strip().lower()
        if confirm != 'y':
            print("操作已取消。")
            sys.exit(0)
    
    # 5. 构建配置
    provider_id = "local-lmstudio"
    config_path = get_config_path()
    print(f"\n⚙️  正在生成配置 (Provider: {provider_id})...")
    
    # 加载现有配置并备份
    config = load_config(config_path)
    backup_config(config_path)
    
    # 构建模型配置
    all_models = llm_models + embedding_models
    model_configs = [build_model_config(m["id"], context_window) for m in all_models]
    
    # 更新主配置
    config.setdefault("models", {}).setdefault("providers", {})[provider_id] = {
        "baseUrl": lmstudio_url,
        "apiKey": "lm-studio",  # 任意值,LM Studio 不验证
        "api": "openai-completions",
        "models": model_configs
    }
    
    # 设置默认 Agent
    agents_defaults = config.setdefault("agents", {}).setdefault("defaults", {})
    agents_defaults["model"] = {"primary": f"{provider_id}/{selected_llm['id']}"}
    
    # 清理旧的模型映射
    models_map = agents_defaults.setdefault("models", {})
    prefix = f"{provider_id}/"
    keys_to_del = [k for k in models_map.keys() if k.startswith(prefix)]
    for k in keys_to_del:
        del models_map[k]
    # 添加新的映射
    for m in model_configs:
        key = f"{provider_id}/{m['id']}"
        models_map[key] = {"alias": provider_id} if m['id'] == selected_llm['id'] else {}
    
    # 配置 Embedding
    emb_id = embedding_models[0]["id"] if embedding_models else "text-embedding-ada-002"
    agents_defaults["memorySearch"] = {
        "enabled": len(embedding_models) > 0,
        "provider": "openai",
        "model": emb_id,
        "remote": {
            "baseUrl": lmstudio_url,
            "apiKey": "lm-studio",
            "batch": {"enabled": False}
        }
    }
    
    # 6. 保存配置
    if save_config(config, config_path):
        print(f"\n✅ 配置已成功保存至: {config_path}")
    else:
        print("\n❌ 保存失败!")
        return

    # 7. 总结
    print("\n" + "=" * 70)
    print("📋 配置摘要")
    print("=" * 70)
    print(f"✓ Provider: {provider_id}")
    print(f"✓ 主模型: {provider_id}/{selected_llm['id']}")
    print(f"✓ 上下文窗口: {context_window}")
    print(f"✓ Embedding: {'已配置' if embedding_models else '未检测到'}")
    print(f"\n🚀 下一步操作:")
    print(f"   1. 确保 LM Studio 的 'Context Length' 设置正确 (>= {OPENCLAW_MIN_CONTEXT})")
    print(f"   2. 启动网关: openclaw gateway")
    print(f"   3. 测试对话: openclaw agent --agent main -m \"你好\" --new")
    print("=" * 70)

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("\n\n❌ 用户取消操作")
        sys.exit(1)
    except Exception as e:
        print(f"\n❌ 发生未捕获错误: {e}")
        import traceback
        traceback.print_exc()
        sys.exit(1)

本地模型配置

步骤一、打开lmstudio下载模型选择如下模型:

file 如果报错:

🥲 Failed to load the model

Failed to load model

error loading model: error loading model architecture: unknown model architecture: 'qwen35'

则本机显卡不适合,需要下载其他模型,或者更新现有的驱动固件(在lmstudio工具中有,设置菜单页中可以更新)。

我的配置信息如下,长文本模型记得配置最大长度支持。

file

步骤二、点击左侧绿色图标,开启本地模型服务

file

步骤三、将上述的配置安装脚本python执行

file 这里就不介绍了,傻瓜式的选择,选择完毕后,重启openclaw即可使用。

实际效果

file