✨ OpenClaw 最佳实践
来自真实项目的经验总结
适用版本: OpenClaw 2026.2+
最后更新: 2026-03-11
📋 目录
🔧 配置优化
模型选择策略
不同的任务适合不同的模型,合理选择可以平衡效果和成本。
场景推荐表
| 场景 | 推荐模型 | 原因 | 成本 |
|---|---|---|---|
| 日常对话 | qwen2.5:7b | 响应快、成本低 | 💰 |
| 复杂推理 | qwen3.5-plus | 推理能力强 | 💰💰💰 |
| 代码生成 | qwen3-coder-plus | 代码专项优化 | 💰💰💰 |
| 长文档分析 | qwen3-max | 256K 上下文 | 💰💰💰💰 |
| 批量处理 | ollama 本地 | 无 API 成本 | 💰 |
| 多模态 | qwen3.5-plus | 支持图像输入 | 💰💰💰 |
动态模型选择
在技能中实现智能路由:
javascript
// skills/smart-router.js
export async function route(task) {
const config = {
simple: 'ollama/qwen2.5:7b', // 简单任务
normal: 'bailian/qwen3.5-plus', // 一般任务
complex: 'bailian/qwen3-max', // 复杂任务
code: 'bailian/qwen3-coder-plus', // 代码任务
};
// 根据任务特征选择
if (task.type === 'code') return config.code;
if (task.tokens > 50000) return config.complex;
if (task.priority === 'low') return config.simple;
return config.normal;
}记忆管理
记忆分类
memory/
├── MEMORY.md # 长期记忆( curated )
├── memory/
│ ├── 2026-03-11.md # 每日日志(raw)
│ ├── 2026-03-10.md
│ └── ...
└── tools.md # 工具配置记忆维护策略
每日:
- 自动创建当日记忆文件
- 记录重要对话和决策
每周:
- 回顾本周记忆文件
- 提炼重要内容到 MEMORY.md
- 删除过期的临时记忆
每月:
- 清理旧的记忆文件(保留最近 30 天)
- 更新用户偏好和上下文
- 归档项目相关记忆
记忆优化技巧
markdown
## ✅ 好的记忆条目
- 用户偏好: "喜欢简洁的回答,不需要过多解释"
- 项目上下文: "正在开发 AI 知识库网站,使用 VitePress"
- 重要决策: "决定使用飞书 API 直接发送文件"
- 技能配置: "TTS 默认使用 Nova 声音"
## ❌ 避免的记忆内容
- 临时信息: "用户问今天天气如何"(无需记录)
- 敏感数据:密码、密钥、个人信息
- 冗余内容:已经存在于配置文件的信息工具调用优化
超时设置
json
{
"tools": {
"web_search": {
"timeout": 10000,
"retry": 2
},
"browser": {
"timeout": 30000,
"retry": 1
},
"exec": {
"timeout": 60000,
"yieldMs": 5000
}
}
}错误重试策略
javascript
async function callWithRetry(tool, params, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await tool(params);
} catch (error) {
if (i === maxRetries - 1) throw error;
// 指数退避
const delay = Math.pow(2, i) * 1000;
console.log(`重试 ${i+1}/${maxRetries}, 等待 ${delay}ms`);
await sleep(delay);
}
}
}结果缓存
javascript
// 简单缓存实现
const cache = new Map();
const CACHE_TTL = 5 * 60 * 1000; // 5 分钟
async function cachedSearch(query) {
const key = `search:${query}`;
const cached = cache.get(key);
if (cached && Date.now() - cached.time < CACHE_TTL) {
console.log('缓存命中');
return cached.data;
}
const result = await web_search({ query });
cache.set(key, { data: result, time: Date.now() });
return result;
}⚡ 性能优化
响应速度优化
并行调用
当任务可以拆分时,并行执行:
javascript
// ❌ 串行执行(慢)
const research = await web_search({ query: 'AI 趋势' });
const news = await web_search({ query: 'AI 新闻' });
const papers = await web_search({ query: 'AI 论文' });
// ✅ 并行执行(快)
const [research, news, papers] = await Promise.all([
web_search({ query: 'AI 趋势' }),
web_search({ query: 'AI 新闻' }),
web_search({ query: 'AI 论文' }),
]);流式输出
对于长内容,使用流式输出提升用户体验:
javascript
// 配置中启用流式
{
"model": {
"stream": true,
"streamOptions": {
"chunkSize": 100,
"delayMs": 50
}
}
}结果预加载
预测用户可能的下一步操作,提前加载:
javascript
// 用户询问天气后,可能想知道:
// 1. 未来几天预报
// 2. 穿衣建议
// 3. 出行建议
// 提前并行获取
const [current, forecast, suggestions] = await Promise.all([
getWeather(location),
getForecast(location, 7),
generateSuggestions(weather),
]);成本控制
Token 统计
javascript
// 技能中添加 token 统计
let tokenUsage = { input: 0, output: 0 };
export async function run(params) {
const start = Date.now();
const result = await callModel(params);
tokenUsage.input += result.usage.input_tokens;
tokenUsage.output += result.usage.output_tokens;
console.log(`本次消耗:${result.usage.total_tokens} tokens`);
console.log(`累计消耗:${tokenUsage.input + tokenUsage.output} tokens`);
return result;
}使用限额
javascript
// 每日限额检查
const DAILY_LIMIT = 100000; // tokens
let dailyUsage = 0;
function checkLimit(tokens) {
if (dailyUsage + tokens > DAILY_LIMIT) {
throw new Error('已达到每日 token 限额');
}
dailyUsage += tokens;
}
// 重置逻辑(每天零点)
function resetDaily() {
const now = new Date();
if (now.getHours() === 0 && now.getMinutes() === 0) {
dailyUsage = 0;
}
}优化提示词
减少不必要的 token 消耗:
markdown
## ❌ 冗长的提示词
你好,AI 助手。我是一位软件工程师,目前正在开发一个网站项目。
我遇到了一个问题,就是不知道如何优化网站的加载速度。
我知道这个问题可能有很多方面,包括图片优化、代码压缩、CDN 使用等等。
但是我不太确定具体应该从哪里入手,也不知道哪些方法最有效。
你能不能帮我分析一下,给我一些建议和最佳实践?
最好是能够按照优先级排序,让我知道应该先做什么后做什么。
谢谢!
## ✅ 简洁的提示词
网站性能优化建议:
- 场景:VitePress 静态网站
- 目标:首屏加载 < 2s
- 要求:按优先级排序,给出具体方案稳定性保障
异常处理
javascript
export async function run(params) {
try {
// 参数验证
if (!params.query) {
throw new Error('缺少必要参数:query');
}
// 主逻辑
const result = await execute(params);
// 结果验证
if (!result || result.length === 0) {
return { success: false, message: '未找到结果' };
}
return { success: true, data: result };
} catch (error) {
console.error('执行失败:', error);
// 友好错误信息
return {
success: false,
message: `处理失败:${error.message}`,
suggestion: '请稍后重试或检查输入参数'
};
}
}降级方案
javascript
// 多级降级策略
async function searchWithFallback(query) {
const strategies = [
() => web_search({ query, freshness: 'pd' }), // 优先:最新结果
() => web_search({ query }), // 备选:普通搜索
() => memory_search({ query }), // 降级:本地记忆
() => ({ results: [], message: '暂无相关信息' }) // 保底:友好提示
];
for (const strategy of strategies) {
try {
const result = await strategy();
if (result.results && result.results.length > 0) {
return result;
}
} catch (error) {
console.log('策略失败,尝试下一级');
}
}
}监控告警
javascript
// 简单监控
const metrics = {
calls: 0,
errors: 0,
latency: [],
};
function recordCall(latency, success) {
metrics.calls++;
metrics.latency.push(latency);
if (!success) metrics.errors++;
// 错误率告警
const errorRate = metrics.errors / metrics.calls;
if (errorRate > 0.1) {
console.warn(`⚠️ 错误率过高:${(errorRate * 100).toFixed(1)}%`);
}
// 延迟告警
const avgLatency = metrics.latency.reduce((a, b) => a + b) / metrics.latency.length;
if (avgLatency > 5000) {
console.warn(`⚠️ 平均延迟过高:${avgLatency.toFixed(0)}ms`);
}
}🔒 安全实践
数据安全
敏感信息脱敏
javascript
// 日志脱敏
function sanitizeLog(data) {
const sensitive = ['password', 'secret', 'token', 'key', 'auth'];
const sanitized = { ...data };
for (const key of Object.keys(sanitized)) {
if (sensitive.some(s => key.toLowerCase().includes(s))) {
sanitized[key] = '***REDACTED***';
}
}
return sanitized;
}
// 使用
console.log('处理数据:', sanitizeLog(userData));本地存储加密
javascript
const crypto = require('crypto');
function encrypt(text, key) {
const cipher = crypto.createCipher('aes-256-cbc', key);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
return encrypted;
}
function decrypt(encrypted, key) {
const decipher = crypto.createDecipher('aes-256-cbc', key);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
// 存储敏感配置
const encryptedConfig = encrypt(JSON.stringify(config), process.env.ENCRYPTION_KEY);
fs.writeFileSync('config.enc', encryptedConfig);访问权限控制
javascript
// 命令白名单
const ALLOWED_COMMANDS = [
'ls', 'cat', 'grep', 'find',
'npm', 'node', 'git',
// ... 其他安全命令
];
const DENIED_COMMANDS = [
'rm -rf', 'sudo', 'chmod', 'chown',
'dd', 'mkfs', 'fdisk',
// ... 危险命令
];
function validateCommand(cmd) {
// 检查黑名单
for (const denied of DENIED_COMMANDS) {
if (cmd.includes(denied)) {
throw new Error(`禁止执行命令:${denied}`);
}
}
// 检查白名单(如果启用)
const baseCmd = cmd.split(' ')[0];
if (!ALLOWED_COMMANDS.includes(baseCmd)) {
throw new Error(`命令未在白名单中:${baseCmd}`);
}
return true;
}使用安全
操作确认
javascript
// 危险操作需要确认
const DANGEROUS_ACTIONS = ['delete', 'remove', 'drop', 'truncate'];
async function executeAction(action, params) {
const isDangerous = DANGEROUS_ACTIONS.some(d => action.includes(d));
if (isDangerous) {
const confirmed = await askUser(
`⚠️ 确认执行危险操作:${action}\n` +
`影响范围:${params.scope}\n` +
`确定要继续吗?(yes/no)`
);
if (confirmed !== 'yes') {
throw new Error('用户取消操作');
}
}
return await performAction(action, params);
}审计日志
javascript
// 记录所有操作
function logAudit(action, params, result, user) {
const entry = {
timestamp: new Date().toISOString(),
user: user.id,
action,
params: sanitizeLog(params),
result: result.success ? 'success' : 'failure',
duration: result.duration,
};
// 追加到审计日志文件
fs.appendFileSync(
'audit.log',
JSON.stringify(entry) + '\n'
);
}
// 定期归档
function archiveLogs() {
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
// 移动旧日志到归档目录
// ...
}🚀 部署建议
开发环境
本地运行配置
json
{
"dev": {
"debug": true,
"hotReload": true,
"logLevel": "debug",
"mockExternal": false
}
}调试技巧
javascript
// 启用详细日志
process.env.DEBUG = 'openclaw:*';
// 使用调试模式启动
openclaw start --debug
// 查看实时日志
openclaw logs --follow生产环境
容器部署
dockerfile
FROM node:20-alpine
WORKDIR /app
# 安装依赖
COPY package*.json ./
RUN npm ci --only=production
# 复制代码
COPY . .
# 创建非 root 用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
USER nodejs
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget -q --spider http://localhost:18789/health || exit 1
CMD ["node", "server.js"]负载均衡
yaml
# docker-compose.yml
version: '3'
services:
openclaw-1:
image: openclaw:latest
ports:
- "18789:18789"
environment:
- INSTANCE_ID=1
openclaw-2:
image: openclaw:latest
ports:
- "18790:18789"
environment:
- INSTANCE_ID=2
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf自动扩展
yaml
# Kubernetes HPA
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: openclaw-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: openclaw
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70监控运维
日志收集
javascript
// 结构化日志
const logger = {
info: (msg, data) => {
console.log(JSON.stringify({
level: 'info',
timestamp: new Date().toISOString(),
message: msg,
...data
}));
},
error: (msg, data) => {
console.error(JSON.stringify({
level: 'error',
timestamp: new Date().toISOString(),
message: msg,
...data
}));
},
warn: (msg, data) => {
console.warn(JSON.stringify({
level: 'warn',
timestamp: new Date().toISOString(),
message: msg,
...data
}));
},
};指标监控
javascript
// Prometheus 指标
const promClient = require('prom-client');
const httpRequestDuration = new promClient.Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route', 'status_code'],
buckets: [0.1, 0.5, 1, 2, 5, 10]
});
const activeSessions = new promClient.Gauge({
name: 'active_sessions',
help: 'Number of active sessions'
});
// 暴露指标端点
app.get('/metrics', async (req, res) => {
res.set('Content-Type', promClient.register.contentType);
res.end(await promClient.register.metrics());
});告警通知
javascript
// 告警配置
const alerts = {
errorRate: {
threshold: 0.1,
window: '5m',
action: 'notify'
},
latency: {
threshold: 5000,
window: '1m',
action: 'notify'
},
diskUsage: {
threshold: 0.8,
window: '1h',
action: 'notify'
}
};
// 告警通知
async function sendAlert(type, value, threshold) {
const message = `⚠️ 告警:${type} 超出阈值\n` +
`当前值:${value}\n` +
`阈值:${threshold}\n` +
`时间:${new Date().toISOString()}`;
// 发送到飞书/钉钉/Slack
await notify(message);
}❓ 常见问题
Q1: 如何选择合适的模型?
A: 根据任务复杂度选择:
- 简单任务(问候、查询)→ 轻量模型
- 中等任务(写作、分析)→ 标准模型
- 复杂任务(推理、代码)→ 强模型
Q2: 记忆文件太大怎么办?
A:
- 定期清理旧文件(保留 30 天)
- 提炼重要内容到 MEMORY.md
- 压缩归档历史文件
Q3: 如何降低 API 成本?
A:
- 使用本地模型处理简单任务
- 优化提示词减少 token
- 缓存重复查询结果
- 设置使用限额
Q4: 技能开发的最佳实践?
A:
- 单一职责:每个技能只做一件事
- 错误处理:优雅处理异常
- 文档完整:清晰的使用说明
- 测试覆盖:单元测试 + 集成测试
Q5: 如何调试技能问题?
A:
- 启用 debug 日志
- 查看技能执行记录
- 使用单步调试
- 检查输入输出格式