2439 字
12 分钟
打造你的全能私有 AI 助手:OpenClaw 部署与“本地+云端”混合配置指南

打造你的全能私有 AI 助手:OpenClaw 部署与“本地+云端”混合配置指南#

🌟 什么是 OpenClaw?#

OpenClaw 是一个开源的、自托管的个人 AI 助手/代理(AI Agent)项目。与普通的聊天机器人不同,它的核心目标是主动执行任务,而不仅仅是回答问题。

它就像是你私有服务器或电脑里的“数字化管家”:

  • 全平台连接:整合 WhatsApp、Telegram、Slack 等常用聊天工具。
  • 任务执行者:能够安排日程、管理邮件、执行脚本、自动化工作流。
  • 数据隐私:完全运行在你的本地设备上,拒绝数据上传云端。

小贴士:该项目曾用名 Moltbot,目前以 OpenClaw 的名字在 GitHub 上火速走红,拥有极高的社区热度。


🚀 快速安装#

OpenClaw 支持 macOS 和 Linux 系统。打开终端,执行以下一键安装命令:

Terminal window
curl -fsSL https://openclaw.ai/install.sh | bash

安装完成后,默认配置文件存放在:~/.openclaw/openclaw.json


🛠️ 痛点解决:OpenClaw 配置文件生成器#

20260220-132809-26aac413a65491d4.webp

在配置 OpenClaw 时,手动修改 openclaw.json 极其繁琐,尤其是涉及到 本地 Ollama 模型云端 API(如 DeepSeek) 的混合调用时。

为了解决这个问题,我开发了一个 HTML 配置文件生成器。它具备以下三大硬核功能:

  1. 🚀 实时显存预警(防爆神器): 自动计算 Ollama 本地模型的显存占用。例如:当你输入 ministral-3:14b 并设置高上下文时,系统会红字提示 “⚠️ 危险: ~29.1GB (爆显存)”,有效避免因显存溢出导致的系统卡死。
  2. 🔄 一键统筹 Fallbacks(后备方案): 只需选定一个“主模型”,生成器会自动将列表中的其他模型设为后备。一旦主模型响应失败,OpenClaw 会无缝切换。
  3. 🔐 安全数据继承: 支持导入现有配置,自动解析并保护你的 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>

第二步:提取原始配置#

  1. 在你的 Linux/macOS 主机上,复制配置内容:
    Terminal window
    cat /root/.openclaw/openclaw.json
  2. 双击打开 OpenClawConfig_V3.html,将内容粘贴到第一个编辑框,点击 “解析”

第三步:添加云端 DeepSeek#

  1. 点击页面右上角的 “+ 官方 DeepSeek” 按钮。
  2. 在新出的 DeepSeek 卡片中,填入你申请的 apiKey
  3. 在顶部的“主力模型”下拉框中,根据需求选择 ollama/xxxdeepseek/deepseek-chat

第四步:保存并重启#

  1. 点击 “生成并复制 JSON”
  2. 将生成的内容替换掉服务器上的 /root/.openclaw/openclaw.json
  3. 重启 OpenClaw 服务:
    Terminal window
    # 根据你的安装方式,通常是重启对应的服务或进程
    openclaw restart

💡 硬件与模型推荐#

针对常见的消费级显卡,建议配置如下:

  • 12G 显存(如 RTX 3060/4070)
    • 推荐模型:ministral-3:8b (极速)
    • 进阶模型:ministral-3:14b (逻辑更强,需注意上下文长度)
  • 混合模式建议
    • 主模型:Ollama 本地运行 ministral-3:8b(处理简单日常指令)。
    • 后备模型:DeepSeek-V3/R1(处理复杂代码或本地服务宕机时的兜底)。

🔗 相关资源#


打造你的全能私有 AI 助手:OpenClaw 部署与“本地+云端”混合配置指南
https://blog.wlens.top/posts/打造你的全能私有-ai-助手openclaw-部署与本地云端混合配置指南/
作者
Lao Wang
发布于
2026-02-20
许可协议
CC BY-NC-SA 4.0