Blog.wlens.top
2439 字
12 分钟
打造你的全能私有 AI 助手:OpenClaw 部署与“本地+云端”混合配置指南
打造你的全能私有 AI 助手:OpenClaw 部署与“本地+云端”混合配置指南
🌟 什么是 OpenClaw?
OpenClaw 是一个开源的、自托管的个人 AI 助手/代理(AI Agent)项目。与普通的聊天机器人不同,它的核心目标是主动执行任务,而不仅仅是回答问题。
它就像是你私有服务器或电脑里的“数字化管家”:
- 全平台连接:整合 WhatsApp、Telegram、Slack 等常用聊天工具。
- 任务执行者:能够安排日程、管理邮件、执行脚本、自动化工作流。
- 数据隐私:完全运行在你的本地设备上,拒绝数据上传云端。
小贴士:该项目曾用名 Moltbot,目前以 OpenClaw 的名字在 GitHub 上火速走红,拥有极高的社区热度。
🚀 快速安装
OpenClaw 支持 macOS 和 Linux 系统。打开终端,执行以下一键安装命令:
curl -fsSL https://openclaw.ai/install.sh | bash安装完成后,默认配置文件存放在:~/.openclaw/openclaw.json。
🛠️ 痛点解决:OpenClaw 配置文件生成器

在配置 OpenClaw 时,手动修改 openclaw.json 极其繁琐,尤其是涉及到 本地 Ollama 模型 与 云端 API(如 DeepSeek) 的混合调用时。
为了解决这个问题,我开发了一个 HTML 配置文件生成器。它具备以下三大硬核功能:
- 🚀 实时显存预警(防爆神器):
自动计算 Ollama 本地模型的显存占用。例如:当你输入
ministral-3:14b并设置高上下文时,系统会红字提示 “⚠️ 危险: ~29.1GB (爆显存)”,有效避免因显存溢出导致的系统卡死。 - 🔄 一键统筹 Fallbacks(后备方案): 只需选定一个“主模型”,生成器会自动将列表中的其他模型设为后备。一旦主模型响应失败,OpenClaw 会无缝切换。
- 🔐 安全数据继承: 支持导入现有配置,自动解析并保护你的 Bot Token、鉴权码等敏感信息。
📖 进阶教程:如何把 DeepSeek 与本地 Ollama “缝合”起来?
本教程教你如何实现:优先使用本地模型(省钱/隐私),本地挂掉或显存爆了自动切到云端 DeepSeek(保命/强力)。
第一步:准备配置生成器
新建一个 OpenClawConfig_V3.html 文件,并将以下代码(或我提供的终极代码)粘贴进去并保存。
<!DOCTYPE html><html lang="zh-CN"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>OpenClaw 配置生成器 V3 (混合云版)</title> <style> body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #f0f2f5; color: #333; max-width: 1000px; margin: 20px auto; padding: 20px; } .card { background: white; padding: 25px; border-radius: 10px; box-shadow: 0 4px 12px rgba(0,0,0,0.05); margin-bottom: 25px; } h1, h2, h3 { color: #1a202c; margin-top: 0; } label { display: block; font-weight: bold; margin-bottom: 6px; font-size: 14px; color: #4a5568; } input[type="text"], input[type="number"], select, textarea { width: 100%; padding: 10px; margin-bottom: 15px; border: 1px solid #cbd5e0; border-radius: 6px; box-sizing: border-box; background-color: #f8fafc; } textarea { height: 120px; font-family: monospace; } .provider-block { border: 2px solid #e2e8f0; padding: 20px; border-radius: 8px; margin-bottom: 20px; background-color: #ffffff; position: relative; } .provider-header { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #e2e8f0; padding-bottom: 10px; margin-bottom: 15px; } .model-item { border: 1px dashed #cbd5e0; padding: 15px; border-radius: 6px; margin-bottom: 15px; background-color: #f8fafc; position: relative; } .row { display: flex; gap: 20px; margin-bottom: 5px; } .col { flex: 1; } .checkbox-group { display: flex; gap: 20px; align-items: center; margin-bottom: 10px; margin-top: 10px; } .checkbox-group label { display: inline; font-weight: normal; margin: 0; font-size: 14px; cursor: pointer; } button { background-color: #3182ce; color: white; border: none; padding: 10px 18px; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: bold; transition: all 0.2s; } button:hover { background-color: #2b6cb0; } .btn-danger { background-color: #e53e3e; } .btn-danger:hover { background-color: #c53030; } .btn-success { background-color: #38a169; } .btn-success:hover { background-color: #2f855a; } .btn-outline { background-color: transparent; border: 1px solid #cbd5e0; color: #4a5568; } .btn-outline:hover { background-color: #edf2f7; } .vram-badge { position: absolute; top: 15px; right: 15px; padding: 4px 10px; border-radius: 12px; font-size: 12px; font-weight: bold; color: white; } .vram-safe { background-color: #38a169; } .vram-warn { background-color: #d69e2e; } .vram-danger { background-color: #e53e3e; } </style></head><body>
<h1>⚙️ OpenClaw V3 (混合云配置中心)</h1>
<div class="card"> <h2>1. 导入现有配置 (无损继承)</h2> <p style="font-size: 13px; color: #718096;">粘贴现有的 openclaw.json,一键继承所有网关和 Bot Token 安全信息。</p> <textarea id="importJson" placeholder="粘贴完整的 openclaw.json..."></textarea> <button onclick="parseConfig()">⬇️ 解析配置</button></div>
<div id="mainWorkspace" style="display: none;"> <div class="card"> <h2>2. 全局路由控制</h2> <label>选择主模型 (Primary Model):</label> <select id="primaryModelSelect"></select> <p style="font-size: 12px; color: #718096; margin-top: -5px;">未被选为主模型的其他模型,将自动被设置为 Fallbacks (后备模型)。</p> </div>
<div class="card"> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;"> <h2 style="margin: 0;">3. 厂商与模型矩阵</h2> <div> <button onclick="addProvider('ollama')" class="btn-success">+ 本地 Ollama</button> <button onclick="addProvider('deepseek')" class="btn-success">+ 官方 DeepSeek</button> <button onclick="addProvider('openai')" class="btn-outline">+ 其他通用 API</button> </div> </div> <div id="providersContainer"></div> </div>
<div class="card"> <h2>4. 导出新配置</h2> <button onclick="generateConfig()">🚀 生成最终 openclaw.json</button> <button onclick="copyToClipboard()" class="btn-success" style="margin-left: 10px;">📋 一键复制</button> <textarea id="outputJson" readonly style="margin-top: 15px; height: 350px; background-color: #2d3748; color: #e2e8f0;"></textarea> </div></div>
<script> let baseConfig = {}; let providersData = {}; // 数据结构示例: { "ollama": { api: "ollama", baseUrl: "...", apiKey: "...", models: [...] }, "deepseek": {...} }
function parseConfig() { try { const rawText = document.getElementById('importJson').value; baseConfig = JSON.parse(rawText);
providersData = {}; if (baseConfig.models && baseConfig.models.providers) { // 深拷贝防止引用污染 providersData = JSON.parse(JSON.stringify(baseConfig.models.providers)); }
// 如果是空配置,默认给个 ollama 壳子 if (Object.keys(providersData).length === 0) { providersData["ollama"] = { api: "ollama", baseUrl: "http://192.168.5.235:11434", apiKey: "ollama-local", models: [] }; }
document.getElementById('mainWorkspace').style.display = 'block'; renderWorkspace(); alert("✅ 解析成功!安全令牌已锁定。"); } catch (e) { alert("❌ 解析失败,请检查 JSON 格式!\n" + e.message); } }
function addProvider(type) { let key = type + "_" + Date.now().toString().slice(-4); if(type === 'ollama') key = 'ollama'; // 本地通常只有一个 if(type === 'deepseek') key = 'deepseek';
if (!providersData[key]) { if (type === 'ollama') { providersData[key] = { api: "ollama", baseUrl: "http://192.168.5.235:11434", apiKey: "ollama-local", models: [] }; } else if (type === 'deepseek') { providersData[key] = { api: "openai-completions", baseUrl: "https://api.deepseek.com/v1", apiKey: "sk-填入你的DeepSeek密钥", models: [] }; } else { providersData[key] = { api: "openai", baseUrl: "https://api.openai.com/v1", apiKey: "sk-...", models: [] }; } } else { alert("该厂商已存在,请直接在下方添加模型。"); } renderWorkspace(); }
function removeProvider(providerKey) { if(confirm(`确定要删除整个 [${providerKey}] 厂商及其所有模型吗?`)) { delete providersData[providerKey]; renderWorkspace(); } }
function addModel(providerKey) { let defaultCtx = providerKey === 'ollama' ? 16384 : 65536; let defaultId = providerKey === 'deepseek' ? 'deepseek-chat' : ''; providersData[providerKey].models.push({ id: defaultId, name: defaultId.toUpperCase(), reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: defaultCtx, maxTokens: defaultCtx * 10 }); renderWorkspace(); }
function removeModel(providerKey, modelIndex) { providersData[providerKey].models.splice(modelIndex, 1); renderWorkspace(); }
function updateProvider(providerKey, field, value) { providersData[providerKey][field] = value; }
function updateModel(providerKey, modelIndex, field, value) { if (field === 'vision') { providersData[providerKey].models[modelIndex].input = value ? ["text", "image"] : ["text"]; } else { providersData[providerKey].models[modelIndex][field] = value; } if (field === 'id' || field === 'contextWindow') renderWorkspace(); // 触发下拉框和 VRAM 刷新 }
function calculateVRAM(id, ctx) { if (!id) return 0; let params = 8; const match = id.match(/(\d+(?:\.\d+)?)b/i); if (match) params = parseFloat(match[1]); return (params * 0.65) + ((ctx / 1024) * 0.08); }
function renderWorkspace() { const container = document.getElementById('providersContainer'); container.innerHTML = ''; const select = document.getElementById('primaryModelSelect');
// 记录之前选中的主模型 let currentSelection = select.value; select.innerHTML = ''; let allModels = [];
Object.keys(providersData).forEach(pKey => { const provider = providersData[pKey]; const isLocal = pKey.includes('ollama');
const pDiv = document.createElement('div'); pDiv.className = 'provider-block'; pDiv.innerHTML = ` <div class="provider-header"> <h3 style="margin: 0; color: #2b6cb0;">🌐 厂商配置: [ ${pKey.toUpperCase()} ]</h3> <button class="btn-danger" style="padding: 5px 10px; font-size: 12px;" onclick="removeProvider('${pKey}')">删除厂商</button> </div> <div class="row"> <div class="col"><label>API 格式 (api)</label><input type="text" value="${provider.api || ''}" oninput="updateProvider('${pKey}', 'api', this.value)"></div> <div class="col"><label>接口地址 (baseUrl)</label><input type="text" value="${provider.baseUrl || ''}" oninput="updateProvider('${pKey}', 'baseUrl', this.value)"></div> <div class="col"><label>密钥 (apiKey)</label><input type="text" value="${provider.apiKey || ''}" oninput="updateProvider('${pKey}', 'apiKey', this.value)"></div> </div> <hr style="border-top: 1px dashed #e2e8f0; margin: 15px 0;"> <div id="models_${pKey}"></div> <button class="btn-outline" style="width: 100%; margin-top: 10px;" onclick="addModel('${pKey}')">+ 添加模型到该厂商</button> `; container.appendChild(pDiv);
const mContainer = document.getElementById(`models_${pKey}`); if(provider.models) { provider.models.forEach((model, index) => { const fullModelPath = `${pKey}/${model.id}`; allModels.push(fullModelPath);
// 只有本地 Ollama 计算显存 let vramHtml = ''; if (isLocal) { const vramEstimate = calculateVRAM(model.id, model.contextWindow); let bClass = 'vram-safe', bText = `显存: ~${vramEstimate.toFixed(1)}GB`; if (vramEstimate > 11.5) { bClass = 'vram-danger'; bText = `⚠️ 爆显存风险: ~${vramEstimate.toFixed(1)}GB`; } else if (vramEstimate > 9) { bClass = 'vram-warn'; bText = `⚠️ 警告: ~${vramEstimate.toFixed(1)}GB`; } vramHtml = `<div class="vram-badge ${bClass}">${bText}</div>`; } else { vramHtml = `<div class="vram-badge" style="background-color: #4a5568;">☁️ 云端模型 (不耗本地显卡)</div>`; }
const hasImage = model.input && model.input.includes('image'); const mDiv = document.createElement('div'); mDiv.className = 'model-item'; mDiv.innerHTML = ` ${vramHtml} <div class="row" style="margin-top: 15px;"> <div class="col"><label>模型 ID</label><input type="text" value="${model.id || ''}" oninput="updateModel('${pKey}', ${index}, 'id', this.value)"></div> <div class="col"><label>显示名称</label><input type="text" value="${model.name || ''}" oninput="updateModel('${pKey}', ${index}, 'name', this.value)"></div> <div class="col"><label>Context Window</label><input type="number" value="${model.contextWindow || 8192}" oninput="updateModel('${pKey}', ${index}, 'contextWindow', parseInt(this.value)||0)"></div> </div> <div class="checkbox-group"> <label><input type="checkbox" ${model.reasoning ? 'checked' : ''} onchange="updateModel('${pKey}', ${index}, 'reasoning', this.checked)"> 🧠 启用深度思考 (Reasoning)</label> <label><input type="checkbox" ${hasImage ? 'checked' : ''} onchange="updateModel('${pKey}', ${index}, 'vision', this.checked)"> 👁️ 支持图片识别 (Vision)</label> <button class="btn-danger" style="padding: 4px 8px; margin-left: auto;" onclick="removeModel('${pKey}', ${index})">删除模型</button> </div> `; mContainer.appendChild(mDiv); }); } });
// 填充下拉框 allModels.forEach(modelPath => { const option = document.createElement('option'); option.value = modelPath; option.textContent = modelPath; select.appendChild(option); });
// 尝试恢复之前的选择,如果没有则看 baseConfig 里的是不是还在 if (allModels.includes(currentSelection)) { select.value = currentSelection; } else if (baseConfig.agents?.defaults?.model?.primary && allModels.includes(baseConfig.agents.defaults.model.primary)) { select.value = baseConfig.agents.defaults.model.primary; } }
function generateConfig() { let finalConfig = JSON.parse(JSON.stringify(baseConfig));
// 初始化基础节点 if (!finalConfig.models) finalConfig.models = {}; finalConfig.models.providers = providersData;
if (!finalConfig.agents) finalConfig.agents = {}; if (!finalConfig.agents.defaults) finalConfig.agents.defaults = {}; if (!finalConfig.agents.defaults.model) finalConfig.agents.defaults.model = {}; if (!finalConfig.agents.defaults.models) finalConfig.agents.defaults.models = {};
// 处理路由策略 const primaryFull = document.getElementById('primaryModelSelect').value; if (primaryFull) { finalConfig.agents.defaults.model.primary = primaryFull;
// 将主模型注册到 agents.defaults.models 占位 finalConfig.agents.defaults.models[primaryFull] = {};
// 收集所有的模型路径,排除主模型,放入 fallbacks let fallbacks = []; Object.keys(providersData).forEach(pKey => { providersData[pKey].models.forEach(m => { const fullPath = `${pKey}/${m.id}`; if (fullPath !== primaryFull) { fallbacks.push(fullPath); } }); });
if (fallbacks.length > 0) { finalConfig.agents.defaults.model.fallbacks = fallbacks; } else { delete finalConfig.agents.defaults.model.fallbacks; } }
document.getElementById('outputJson').value = JSON.stringify(finalConfig, null, 2); }
function copyToClipboard() { const copyText = document.getElementById("outputJson"); if (!copyText.value) { alert("请先点击生成!"); return; } copyText.select(); document.execCommand("copy"); alert("已复制!安全且完美的混合云配置已准备就绪。"); }</script>
</body></html>第二步:提取原始配置
- 在你的 Linux/macOS 主机上,复制配置内容:
Terminal window cat /root/.openclaw/openclaw.json - 双击打开
OpenClawConfig_V3.html,将内容粘贴到第一个编辑框,点击 “解析”。
第三步:添加云端 DeepSeek
- 点击页面右上角的 “+ 官方 DeepSeek” 按钮。
- 在新出的 DeepSeek 卡片中,填入你申请的
apiKey。 - 在顶部的“主力模型”下拉框中,根据需求选择
ollama/xxx或deepseek/deepseek-chat。
第四步:保存并重启
- 点击 “生成并复制 JSON”。
- 将生成的内容替换掉服务器上的
/root/.openclaw/openclaw.json。 - 重启 OpenClaw 服务:
Terminal window # 根据你的安装方式,通常是重启对应的服务或进程openclaw restart
💡 硬件与模型推荐
针对常见的消费级显卡,建议配置如下:
- 12G 显存(如 RTX 3060/4070):
- 推荐模型:
ministral-3:8b(极速) - 进阶模型:
ministral-3:14b(逻辑更强,需注意上下文长度)
- 推荐模型:
- 混合模式建议:
- 主模型:Ollama 本地运行
ministral-3:8b(处理简单日常指令)。 - 后备模型:DeepSeek-V3/R1(处理复杂代码或本地服务宕机时的兜底)。
- 主模型:Ollama 本地运行
🔗 相关资源
- OpenClaw 官网: https://openclaw.ai/
- GitHub 仓库: https://github.com/openclaw/openclaw
打造你的全能私有 AI 助手:OpenClaw 部署与“本地+云端”混合配置指南
https://blog.wlens.top/posts/打造你的全能私有-ai-助手openclaw-部署与本地云端混合配置指南/