OpenClaw+CC Switch工具实现国内外模型任意切换

前言

目前OpenClaw官方已经出了桌面应用, 再也不用像之前那般苦苦折腾命令行了

image-20260902190345482

CC Switch

以前我们切换模型, 需要手动修改配置文件, 假如有十来个模型, 管理上也是个大问题, CC Switch这个工具的出现就是解决这些痛点的, 它不仅可以自动帮我们修改配置文件切换模型,、可视化管理模型key, 还可以实时监控token额度, 很是方便

最主要的是开源免费, 不用担心隐私泄露

image-20260902185734424

image-20260902190158869

官网: 点击进入

免费模型

关于第三方的模型API, 大家可以自行B站或者抖音搜索, 有一些羊毛可以薅

如果是本地部署的话可以使用qwen2.5模型:

模型 ollama 名称 显存需求 特点(对你的剧本解析)
Qwen2.5‑3B qwen2.5:3b 4‑5G ⭐优先小模型,原生强中文;输出 yaml 稳定性远好 llama3.2‑3B;适合显卡配置不高机器,做测试原型
Qwen2.5‑7B qwen2.5:7b 7‑9G ⭐⭐⭐最推荐正式生产;中文剧本理解、结构化 yaml 输出能力强,Apache2.0 完全开源,无厂商附加约束
Qwen2.5‑14B qwen2.5:14b 12‑14G 长剧本、多角色大文本解析,效果最好,吃显存

Qwen(通义千问开源版),阿里开源,权重 Apache‑2.0 真正开源,没有 Llama 那一套社区协议约束,没有月活上限,个人、商用、二次分发都可以自由使用

可以使用Ollama或者vLLM本地部署, 两种方式对比如下:

对比项 vLLM(当前方案) Ollama(备选方案)
标准 modelscope 模型 ✅直接加载,无需转换 ❌必须转为 GGUF 格式
长文本分镜生成速度 ⭐⭐⭐⭐⭐(PagedAttention 长上下文加速) ⭐⭐⭐
空闲自动释放显存 原生无,需要外部脚本 kill 进程 ✅自带 keep‑alive N 秒自动卸载模型
环境部署复杂度 高(CUDA、vLLM 版本、显存参数) 极低
量化方案 FP16、AWQ、FP8,工业级 GGUF(Q4_K_M/Q5_K_M)
ComfyUI 节点 HTTP 调用 支持(OpenAI 接口 http://127.0.0.1:8000/v1)

ollama 一键拉取模型命令:

1
2
3
4
5
# 3B小模型,测试原型,Apache‑2.0真正开源
ollama pull qwen2.5:3b

# 7B正式生产,强烈推荐
ollama pull qwen2.5:7b

如果使用vLLM部署, 那么推荐国内的modelscope源速度快, 一键下载指令如下:

1
modelscope download --model qwen/Qwen2.5-7B-Instruct --local_dir /models/Qwen2.5-7B-Instruct

vLLM部署+启动脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
vLLM 一条龙后端服务:自动下载Qwen2.5‑7B‑Instruct + 启动OpenAI兼容API
启动后地址:http://127.0.0.1:8000/v1
"""
import os
import sys
import subprocess
import time
import requests

# ======================== 配置区,按需修改 ========================
MODEL_ID = "qwen/Qwen2.5-7B-Instruct"
LOCAL_MODEL_ROOT = "./models"
MODEL_FOLDER_NAME = "Qwen2.5-7B-Instruct"
# 拼接本地完整路径
LOCAL_MODEL_PATH = os.path.abspath(os.path.join(LOCAL_MODEL_ROOT, MODEL_FOLDER_NAME))
API_HOST = "127.0.0.1"
API_PORT = 8000
SERVED_MODEL_NAME = "qwen2.5-7b"
GPU_MEM_UTIL = 0.85
WAIT_SERVICE_TIMEOUT = 180
# ================================================================

def check_model_ready(model_dir: str) -> bool:
"""校验分片模型完整性:检查索引文件是否存在"""
index_file = os.path.join(model_dir, "model.safetensors.index.json")
config_file = os.path.join(model_dir, "config.json")
if os.path.isfile(index_file) and os.path.isfile(config_file):
return True
return False

def download_model():
"""调用 modelscope cli 下载模型"""
print(f"\n📥 模型不存在,开始下载 {MODEL_ID}")
print(f"目标目录: {LOCAL_MODEL_PATH}")
cmd = [
"modelscope", "download",
"--model", MODEL_ID,
"--local_dir", LOCAL_MODEL_PATH
]
proc = subprocess.run(cmd)
if proc.returncode != 0:
print("❌ modelscope 下载失败!")
sys.exit(1)
print("✅ 模型下载完成")

def start_vllm_server():
cmd = [
sys.executable, "-m", "vllm.entrypoints.openai.api_server",
"--model", LOCAL_MODEL_PATH,
"--served-model-name", SERVED_MODEL_NAME,
"--host", API_HOST,
"--port", str(API_PORT),
"--gpu-memory-utilization", str(GPU_MEM_UTIL)
]
print("\n🚀 正在启动 vLLM API 后端,加载模型,请耐心等待...")
print(f"命令: {' '.join(cmd)}")
# 后台拉起服务进程
subprocess.Popen(cmd)

def wait_for_service_ready():
base_url = f"http://{API_HOST}:{API_PORT}/v1/models"
print(f"\n⏳ 等待vLLM服务就绪,最长等待 {WAIT_SERVICE_TIMEOUT}s ...")
for t in range(1, WAIT_SERVICE_TIMEOUT + 1):
time.sleep(1)
try:
resp = requests.get(base_url, timeout=3)
if resp.status_code == 200:
print(f"✅ LLM后端服务启动成功!耗时 {t}s")
print(f"API Endpoint: http://{API_HOST}:{API_PORT}/v1")
return True
except Exception:
pass
print("❌ 等待超时,vLLM启动失败,请手动查看控制台报错")
return False

def main():
print("===== vLLM‑Qwen2.5 一条龙服务启动器 =====")
# 1.检测模型
if not check_model_ready(LOCAL_MODEL_PATH):
download_model()
else:
print(f"✅ 本地模型已就绪: {LOCAL_MODEL_PATH}")

# 2.启动vllm后台
start_vllm_server()
# 3.轮询等待端口就绪
ok = wait_for_service_ready()
if not ok:
sys.exit(1)

# 简单连通性测试
test_payload = {
"model": SERVED_MODEL_NAME,
"messages": [{"role": "user", "content": "只用两个字回复OK"}],
"temperature": 0.1,
"max_tokens": 16,
"stream": False
}
resp = requests.post(f"http://{API_HOST}:{API_PORT}/v1/chat/completions", json=test_payload, timeout=120)
if resp.status_code == 200:
res_json = resp.json()
print("🧪 推理测试返回:", res_json["choices"][0]["message"]["content"])
print("\n🎉 LLM后端完全就绪,可以开始运行你的视频流水线脚本!")
else:
print("⚠️ 连通测试失败,HTTP", resp.status_code)

if __name__ == "__main__":
main()

ollama一键部署脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Ollama + Qwen2.5 一键部署脚本(工程优化版)
支持 Windows / Linux;Linux国内镜像加速;交互式选择模型;显存释放配置;环境校验
"""
import os
import sys
import time
import platform
import subprocess
import requests
from typing import Optional, Dict, List

# ======================== 配置常量区 ========================
OLLAMA_HOST = "http://127.0.0.1:11434"
SERVICE_WAIT_SECONDS = 40
KEEP_ALIVE = "30s" # 模型空闲多久释放显存,串行视频流水线建议30s

MODEL_OPTIONS: List[Dict] = [
{
"id": 1,
"tag": "qwen2.5:3b",
"vram": "4‑5GB",
"desc": "轻量版,适合测试、显存紧张机器"
},
{
"id": 2,
"tag": "qwen2.5:7b",
"vram": "7‑9GB",
"desc": "⭐推荐,短剧剧本解析生产首选,综合平衡"
},
{
"id": 3,
"tag": "qwen2.5:14b",
"vram": "12‑14GB",
"desc": "高性能版,长剧本、多角色复杂剧本解析"
}
]

# Linux国内镜像安装地址
INSTALL_SCRIPT_CN = "https://ollama.ac.cn/install.sh"
# ==========================================================


def run_subprocess(args: List[str], shell: bool = False) -> subprocess.CompletedProcess:
"""封装subprocess,统一捕获输出"""
return subprocess.run(
args,
shell=shell,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)


def get_ollama_version() -> Optional[str]:
"""
多层校验:判断ollama是否可用
返回版本字符串;二进制损坏/残留文件则返回None
"""
try:
proc = run_subprocess(["ollama", "--version"])
if proc.returncode == 0 and proc.stdout.strip():
return proc.stdout.strip()
return None
except (FileNotFoundError, OSError):
return None


def install_ollama_linux():
"""Linux平台:国内镜像一键安装Ollama"""
print("\n📦 Linux 使用国内社区镜像安装 Ollama")
print(f"镜像源: {INSTALL_SCRIPT_CN}")
cmd = f'curl -fsSL {INSTALL_SCRIPT_CN} | sudo sh'
ret = run_subprocess(cmd, shell=True)
if ret.returncode != 0:
print("❌ Ollama国内镜像安装失败!")
print("stderr:", ret.stderr)
print("备选方案:手动下载tar包进行安装")
sys.exit(1)
print("✅ Ollama 安装脚本执行完毕")


def install_ollama_windows():
"""Windows平台:winget安装"""
print("\n📦 Windows 通过 winget 安装 Ollama")
ret = run_subprocess(["winget", "install", "Ollama.Ollama"], shell=True)
if ret.returncode != 0:
print("❌ winget安装失败,请手动官网下载安装包")
print("官网地址:https://ollama.com/download/windows")
sys.exit(1)
print("✅ Windows Ollama安装完成,请**重启终端**之后重新运行本脚本!")
sys.exit(0)


def install_flow():
os_name = platform.system()
if os_name == "Linux":
install_ollama_linux()
elif os_name == "Windows":
install_ollama_windows()
elif os_name == "Darwin":
print("\n⚠️ macOS暂不支持自动安装,请手动前往官网下载DMG安装包")
print("https://ollama.com/download/mac")
sys.exit(0)
else:
print(f"❌ 当前操作系统 {os_name} 暂不支持自动安装")
sys.exit(1)


def select_model() -> str:
"""交互式菜单选择模型"""
print("\n====== Qwen2.5模型选择(Apache‑2.0完全开源) ======")
for item in MODEL_OPTIONS:
print(f"[{item['id']}] {item['tag']:12} | 显存:{item['vram']:8} | {item['desc']}")
while True:
user_input = input("\n请输入模型序号(1/2/3): ").strip()
if user_input.isdigit():
sel = int(user_input)
for m in MODEL_OPTIONS:
if m["id"] == sel:
return m["tag"]
print("输入无效!请输入数字:1、2或者3")


def set_ollama_env_linux():
"""Linux设置环境变量OLLAMA_KEEP_ALIVE,控制显存释放"""
# systemd方式:修改ollama服务环境变量(永久生效)
service_path = "/etc/systemd/system/ollama.service"
if os.path.exists(service_path):
print(f"\n⚙️ 配置 ollama.service 显存释放策略 keep‑alive = {KEEP_ALIVE}")
try:
# 简单方案:环境变量写入/etc/environment(ollama进程可以读取)
env_line = f'OLLAMA_KEEP_ALIVE={KEEP_ALIVE}'
with open("/tmp/ollama_env.conf", "w", encoding="utf‑8") as f:
f.write(env_line + "\n")
subprocess.run(["sudo", "tee", "-a", "/etc/environment"],
input=env_line, text=True, capture_output=True)
subprocess.run(["sudo", "systemctl", "daemon‑reload"], capture_output=True)
subprocess.run(["sudo", "systemctl", "restart", "ollama"], capture_output=True)
print("✅ systemd Ollama服务已重启,显存释放策略永久生效")
except Exception as e:
print(f"⚠️ 无法修改systemd服务配置:{e},本次会话临时设置环境变量")
# 当前进程环境变量(临时生效,用于当前启动ollama serve)
os.environ["OLLAMA_KEEP_ALIVE"] = KEEP_ALIVE


def start_ollama_service() -> bool:
"""启动ollama服务,兼容systemd(Linux) / nohup(手动) / Windows后台进程"""
# 先检测端口是否已经就绪
try:
resp = requests.get(f"{OLLAMA_HOST}/api/tags", timeout=2)
if resp.status_code == 200:
print("✅ Ollama服务已经正在运行,跳过启动")
return True
except Exception:
pass

os_name = platform.system()
print("\n🚀 尝试启动 Ollama 后台服务")

if os_name == "Linux":
# 优先尝试systemd启动(安装版自带systemd)
res = run_subprocess(["systemctl", "is‑active", "--quiet", "ollama"], shell=False)
if res.returncode == 0:
subprocess.run(["sudo", "systemctl", "start", "ollama"], capture_output=True)
else:
# systemd不存在,手动nohup后台拉起
set_ollama_env_linux()
subprocess.Popen(
["ollama", "serve"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
start_new_session=True
)
elif os_name == "Windows":
os.environ["OLLAMA_KEEP_ALIVE"] = KEEP_ALIVE
subprocess.Popen(
["ollama", "serve"],
creationflags=subprocess.CREATE_NO_WINDOW
)

# 轮询等待端口就绪
print(f"⏳ 等待Ollama服务启动(最大等待 {SERVICE_WAIT_SECONDS}s)")
for sec in range(1, SERVICE_WAIT_SECONDS + 1):
time.sleep(1)
try:
r = requests.get(f"{OLLAMA_HOST}/api/tags", timeout=2)
if r.status_code == 200:
print(f"✅ Ollama服务启动成功,耗时 {sec}s")
return True
except Exception:
continue

print(f"❌ 等待 {SERVICE_WAIT_SECONDS}s,服务仍然未就绪,启动失败")
print("排查建议:终端手动执行 ollama serve 查看报错日志")
return False


def is_model_installed(model_tag: str) -> bool:
"""结构化检测模型是否已经本地存在,避免grep脆弱字符串匹配"""
try:
resp = requests.get(f"{OLLAMA_HOST}/api/tags", timeout=10)
data = resp.json()
models = data.get("models", [])
for m in models:
if m.get("name") == model_tag:
return True
return False
except Exception as e:
print(f"模型列表接口查询异常:{e}")
return False


def pull_model(model_tag: str):
if is_model_installed(model_tag):
print(f"\n✅ 模型 {model_tag} 本地已存在,跳过下载")
return

print(f"\n📥 开始拉取模型:{model_tag}")
print("提示:ollama原生支持断点续传,下载中断之后重新运行脚本即可恢复")
proc = run_subprocess(["ollama", "pull", model_tag])
if proc.returncode != 0:
raise RuntimeError(f"模型下载失败,返回码:{proc.returncode}, stderr:{proc.stderr}")
print(f"✅ {model_tag} 下载完成")


def model_inference_test(model_tag: str) -> bool:
"""模型推理连通性验证测试"""
print("\n🔍 模型推理功能验证")
payload = {
"model": model_tag,
"prompt": "只用两个字回复:OK",
"stream": False
}
try:
resp = requests.post(f"{OLLAMA_HOST}/api/generate", json=payload, timeout=120)
if resp.status_code != 200:
print(f"接口异常,HTTP状态码:{resp.status_code}")
return False
result_json = resp.json()
reply = result_json.get("response", "")
print(f"模型返回:{reply.strip()}")
if "ok" in reply.lower():
print("✅ 模型推理验证通过!")
return True
else:
print(f"⚠️ 验证返回结果不符合预期,返回内容:{reply}")
return False
except Exception as err:
print(f"推理测试失败,异常信息:{err}")
return False


def print_summary(ollama_version: str, selected_model: str):
print("\n" + "=" * 70)
print("📋 Ollama + Qwen2.5 部署环境汇总")
print(f"操作系统 : {platform.system()}")
print(f"Ollama版本 : {ollama_version}")
print(f"当前选中模型 : {selected_model}")
print(f"API服务地址 : {OLLAMA_HOST}")
print(f"显存释放策略 : keep‑alive = {KEEP_ALIVE}")
print("-" * 70)
print("Python调用示例(集成进你的script_parser.py):")
code_snippet = f'''
import requests

url = "http://127.0.0.1:11434/api/generate"
body = {{
"model": "{selected_model}",
"prompt": "你的短剧分镜解析提示词",
"stream": False
}}
resp = requests.post(url, json=body, timeout=180)
data = resp.json()
result_text = data["response"]
'''
print(code_snippet)
print("=" * 70)


def main():
print("==== Ollama & Qwen2.5 一键部署工具(工程优化版) ====")
ollama_ver = get_ollama_version()
if ollama_ver is not None:
print(f"✅ Ollama已检测到,版本信息:{ollama_ver}")
else:
print("⚠️ Ollama未安装或者二进制文件损坏,进入安装流程")
install_flow()
# 安装完成后二次校验版本
ollama_ver = get_ollama_version()
if ollama_ver is None:
print("❌ 安装完成,但是当前环境依然无法找到ollama命令!")
print("Linux:新开终端,刷新环境变量;Windows:必须重启终端!")
sys.exit(1)

# 启动服务
service_ok = start_ollama_service()
if not service_ok:
sys.exit(1)

# 模型选择
selected_model = select_model()

# 下载模型
pull_model(selected_model)

# 推理验证
test_ok = model_inference_test(selected_model)
if not test_ok:
print("⚠️ 警告:模型下载成功,但是推理验证失败,建议检查显卡显存大小!")

# 打印环境汇总
print_summary(ollama_ver, selected_model)


if __name__ == "__main__":
main()

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

0%