OpenClaw 网关配置与插件开发
网关(Gateway)是 OpenClaw 的核心服务,负责调度、工具执行、会话管理等功能。本文详解网关配置、插件开发、服务管理等高级主题。
概述
OpenClaw 网关提供:
- 🔄 会话调度与管理
- 🛠️ 工具执行与编排
- 🔌 插件系统扩展
- 📡 远程节点连接
- ⏰ 定时任务调度
- 🔐 安全与权限控制
一、网关架构
1.1 核心组件
┌─────────────────────────────────────────────────────┐
│ OpenClaw Gateway │
├─────────────────────────────────────────────────────┤
│ Session Manager │ 会话创建、调度、状态管理 │
│ Tool Executor │ 工具调用、权限检查、结果返回 │
│ Plugin Loader │ 插件加载、注册、生命周期 │
│ Cron Scheduler │ 定时任务、唤醒事件 │
│ Node Manager │ 远程节点连接、设备管理 │
│ Config Manager │ 配置加载、热更新、验证 │
│ Security Module │ 认证、授权、审计 │
└─────────────────────────────────────────────────────┘1.2 配置文件结构
yaml
# ~/.openclaw/config.yaml
gateway:
bind: "0.0.0.0:8080" # 监听地址
remote:
enabled: true # 启用远程连接
url: "https://your-domain.com"
plugins:
entries:
- name: "feishu"
enabled: true
config:
appId: "cli_xxx"
- name: "device-pair"
enabled: true
config:
publicUrl: "https://your-domain.com"
models:
default: "modelstudio/qwen3.5-plus"
overrides:
coding: "claude-sonnet-4-5-20250929"
analysis: "modelstudio/qwen3.5-plus"
sessions:
maxConcurrent: 10
defaultTimeout: 300
security:
execPolicy: "allowlist" # deny | allowlist | full
toolRestrictions:
- tool: "exec"
requireApproval: true二、配置管理
2.1 查看配置
bash
# 查看当前配置
openclaw gateway config.get
# 查看配置 schema
openclaw gateway config.schema.lookup --path "plugins.entries"
# 验证配置
openclaw gateway config.validate2.2 修改配置
方法 1:使用 config.patch(推荐)
bash
# 部分更新配置
openclaw gateway config.patch \
--raw '{"plugins": {"entries": [{"name": "feishu", "enabled": true}]}}' \
--note "启用 Feishu 插件"方法 2:直接编辑配置文件
bash
# 编辑配置
vim ~/.openclaw/config.yaml
# 重启网关使配置生效
openclaw gateway restart方法 3:使用 gateway 工具
javascript
// 在会话中修改配置
await gateway({
action: 'config.patch',
raw: JSON.stringify({
models: { default: 'claude-sonnet-4-5-20250929' }
}),
note: '切换默认模型'
})2.3 配置热更新
javascript
async function updateConfigSafely(changes, note) {
// 1. 备份当前配置
const currentConfig = await read({
path: '~/.openclaw/config.yaml'
})
await write({
path: '~/.openclaw/config.yaml.bak',
content: currentConfig
})
// 2. 应用配置变更
try {
await gateway({
action: 'config.patch',
raw: JSON.stringify(changes),
note
})
console.log('✅ 配置更新成功')
} catch (error) {
console.error('❌ 配置更新失败:', error)
// 3. 回滚
await exec({
command: 'cp ~/.openclaw/config.yaml.bak ~/.openclaw/config.yaml'
})
throw error
}
}
// 使用
await updateConfigSafely(
{ models: { default: 'new-model' } },
'测试新模型'
)2.4 实战案例 1:模型切换器
javascript
class ModelSwitcher {
constructor() {
this.models = {
default: 'modelstudio/qwen3.5-plus',
coding: 'claude-sonnet-4-5-20250929',
analysis: 'modelstudio/qwen3.5-plus',
creative: 'claude-opus-4-5-20251101',
fast: 'modelstudio/qwen3.5-plus'
}
this.current = 'default'
}
async switch(modelType) {
if (!this.models[modelType]) {
throw new Error(`未知模型类型:${modelType}`)
}
const previous = this.current
this.current = modelType
await gateway({
action: 'config.patch',
raw: JSON.stringify({
models: { default: this.models[modelType] }
}),
note: `切换模型:${previous} → ${modelType}`
})
console.log(`模型已切换:${modelType} (${this.models[modelType]})`)
return previous
}
async forTask(modelType, task) {
const previous = await this.switch(modelType)
try {
return await task()
} finally {
await this.switch(previous)
}
}
getStatus() {
return {
current: this.current,
model: this.models[this.current],
available: Object.keys(this.models)
}
}
}
// 使用
const switcher = new ModelSwitcher()
// 临时切换模型执行任务
const result = await switcher.forTask('coding', async () => {
return await sessions_spawn({
task: '编写一个 Python 函数...',
mode: 'run'
})
})三、插件系统
3.1 插件类型
OpenClaw 支持多种插件:
| 类型 | 用途 | 示例 |
|---|---|---|
| 工具插件 | 提供新工具 | feishu, weather |
| 消息插件 | 消息通道 | discord, telegram |
| 设备插件 | 节点连接 | device-pair |
| 存储插件 | 数据存储 | s3, gcs |
| 认证插件 | 身份验证 | oauth, jwt |
3.2 插件结构
~/.openclaw/plugins/
└── my-plugin/
├── plugin.json # 插件元数据
├── index.js # 入口文件
├── tools/ # 工具定义
│ └── my-tool.js
├── config.schema.json # 配置 schema
└── README.md # 文档plugin.json 示例:
json
{
"name": "my-plugin",
"version": "1.0.0",
"description": "我的自定义插件",
"author": "你的名字",
"main": "index.js",
"tools": ["my-tool", "another-tool"],
"config": {
"apiKey": {
"type": "string",
"required": true,
"description": "API 密钥"
},
"timeout": {
"type": "number",
"default": 30,
"description": "超时时间(秒)"
}
},
"dependencies": {
"node-fetch": "^3.0.0"
}
}3.3 开发工具插件
示例:创建天气查询工具
javascript
// ~/.openclaw/plugins/weather-plugin/tools/weather.js
const fetch = require('node-fetch')
module.exports = {
name: 'weather',
description: '查询天气信息',
// 参数 schema
parameters: {
type: 'object',
properties: {
location: {
type: 'string',
description: '地点名称'
},
days: {
type: 'number',
default: 1,
description: '预报天数'
}
},
required: ['location']
},
// 执行函数
async execute(params, context) {
const { location, days = 1 } = params
try {
// 调用 wttr.in API
const response = await fetch(
`https://wttr.in/${encodeURIComponent(location)}?format=j${days}`
)
if (!response.ok) {
throw new Error(`天气查询失败:${response.statusText}`)
}
const data = await response.json()
// 格式化结果
return {
location: data.nearest_area[0].areaName[0].value,
current: {
temp: data.current_condition[0].temp_C,
condition: data.current_condition[0].weatherDesc[0].value,
humidity: data.current_condition[0].humidity,
wind: data.current_condition[0].windspeedKmph
},
forecast: data.weather?.slice(0, days).map(day => ({
date: day.date,
maxTemp: day.maxtempC,
minTemp: day.mintempC,
condition: day.hourly[12].weatherDesc[0].value
}))
}
} catch (error) {
throw new Error(`天气查询错误:${error.message}`)
}
}
}注册工具:
javascript
// ~/.openclaw/plugins/weather-plugin/index.js
const weatherTool = require('./tools/weather')
module.exports = {
name: 'weather-plugin',
// 插件初始化
async init(config) {
console.log('天气插件初始化')
this.config = config
},
// 注册工具
getTools() {
return [weatherTool]
},
// 插件清理
async cleanup() {
console.log('天气插件清理')
}
}3.4 配置 schema
json
// ~/.openclaw/plugins/weather-plugin/config.schema.json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"apiKey": {
"type": "string",
"description": "可选的 API 密钥(用于高级功能)"
},
"defaultLocation": {
"type": "string",
"description": "默认查询地点"
},
"cacheTimeout": {
"type": "number",
"default": 600,
"description": "缓存超时(秒)"
}
}
}3.5 实战案例 2:自定义通知插件
javascript
// ~/.openclaw/plugins/notify-plugin/index.js
const fetch = require('node-fetch')
class NotifyPlugin {
constructor() {
this.channels = new Map()
}
async init(config) {
this.config = config
// 注册通知渠道
if (config.channels) {
for (const channel of config.channels) {
this.channels.set(channel.name, channel)
}
}
}
getTools() {
return [
{
name: 'notify',
description: '发送通知到指定渠道',
parameters: {
type: 'object',
properties: {
channel: { type: 'string' },
title: { type: 'string' },
message: { type: 'string' },
priority: {
type: 'string',
enum: ['low', 'normal', 'high', 'urgent']
}
},
required: ['channel', 'message']
},
execute: async (params) => this.sendNotification(params)
},
{
name: 'notify_broadcast',
description: '广播通知到所有渠道',
parameters: {
type: 'object',
properties: {
title: { type: 'string' },
message: { type: 'string' }
},
required: ['message']
},
execute: async (params) => this.broadcast(params)
}
]
}
async sendNotification({ channel, title, message, priority = 'normal' }) {
const config = this.channels.get(channel)
if (!config) {
throw new Error(`未知渠道:${channel}`)
}
switch (config.type) {
case 'webhook':
return await this.sendWebhook(config.url, { title, message, priority })
case 'email':
return await this.sendEmail(config, { title, message })
case 'sms':
return await this.sendSms(config, message)
default:
throw new Error(`不支持的渠道类型:${config.type}`)
}
}
async sendWebhook(url, payload) {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
})
if (!response.ok) {
throw new Error(`Webhook 发送失败:${response.statusText}`)
}
return { success: true, channel: 'webhook' }
}
async sendEmail(config, { title, message }) {
// 使用 nodemailer 或其他邮件库
// 这里简化处理
console.log(`发送邮件到 ${config.to}: ${title}`)
return { success: true, channel: 'email' }
}
async sendSms(config, message) {
// 使用 SMS API
console.log(`发送短信到 ${config.phone}: ${message}`)
return { success: true, channel: 'sms' }
}
async broadcast({ title, message }) {
const results = []
for (const [name, config] of this.channels) {
try {
const result = await this.sendNotification({
channel: name,
title,
message,
priority: 'normal'
})
results.push({ channel: name, ...result })
} catch (error) {
results.push({ channel: name, success: false, error: error.message })
}
}
return results
}
}
module.exports = new NotifyPlugin()四、服务管理
4.1 启动/停止/重启
bash
# 查看网关状态
openclaw gateway status
# 启动网关
openclaw gateway start
# 停止网关
openclaw gateway stop
# 重启网关
openclaw gateway restart
# 重启并延迟
openclaw gateway restart --delay 50004.2 日志查看
bash
# 查看实时日志
openclaw gateway logs --follow
# 查看最近 100 行
openclaw gateway logs --lines 100
# 查看错误日志
openclaw gateway logs --level error
# 导出日志
openclaw gateway logs --output gateway.log4.3 健康检查
javascript
async function gatewayHealthCheck() {
const checks = [
{
name: '网关状态',
fn: async () => {
const status = await gateway({ action: 'status' })
return status.running ? 'ok' : 'fail'
}
},
{
name: '配置加载',
fn: async () => {
const config = await gateway({ action: 'config.get' })
return config ? 'ok' : 'fail'
}
},
{
name: '插件状态',
fn: async () => {
// 检查关键插件是否加载
return 'ok'
}
},
{
name: '模型连接',
fn: async () => {
// 测试模型连接
const result = await sessions_spawn({
task: 'ping',
mode: 'run',
timeoutSeconds: 10
})
return result ? 'ok' : 'fail'
}
}
]
const results = {}
for (const check of checks) {
try {
results[check.name] = await check.fn()
} catch (error) {
results[check.name] = `fail: ${error.message}`
}
}
return results
}
// 定期健康检查
setInterval(async () => {
const health = await gatewayHealthCheck()
console.log('网关健康检查:', health)
}, 5 * 60 * 1000) // 每 5 分钟4.4 实战案例 3:自动恢复服务
javascript
class GatewayAutoRecovery {
constructor() {
this.consecutiveFailures = 0
this.maxFailures = 3
this.checkInterval = 60000 // 1 分钟
}
async start() {
console.log('启动网关自动恢复监控')
setInterval(() => this.checkAndRecover(), this.checkInterval)
}
async checkAndRecover() {
try {
// 健康检查
const status = await gateway({ action: 'status' })
if (!status.running) {
throw new Error('网关未运行')
}
// 重置失败计数
this.consecutiveFailures = 0
} catch (error) {
this.consecutiveFailures++
console.error(`健康检查失败 (${this.consecutiveFailures}/${this.maxFailures}):`, error.message)
if (this.consecutiveFailures >= this.maxFailures) {
await this.recover()
}
}
}
async recover() {
console.log('⚠️ 连续失败,尝试恢复...')
try {
// 1. 尝试重启
console.log('重启网关...')
await gateway({
action: 'restart',
note: '自动恢复:连续健康检查失败'
})
// 2. 等待启动
await sleep(10000)
// 3. 验证恢复
const status = await gateway({ action: 'status' })
if (status.running) {
console.log('✅ 网关恢复成功')
this.consecutiveFailures = 0
// 通知用户
await this.notifyRecovery('success')
} else {
throw new Error('重启后仍未运行')
}
} catch (error) {
console.error('❌ 恢复失败:', error)
// 通知用户
await this.notifyRecovery('failed', error)
}
}
async notifyRecovery(status, error = null) {
const message = status === 'success'
? '✅ 网关已自动恢复'
: `❌ 网关恢复失败:${error?.message}`
try {
await message({
action: 'send',
target: '老大',
message: `🔧 网关自动恢复通知\n\n${message}\n时间:${new Date().toLocaleString('zh-CN')}`
})
} catch (e) {
console.error('发送通知失败:', e)
}
}
}
// 使用
const recovery = new GatewayAutoRecovery()
recovery.start()五、远程节点
5.1 节点连接配置
yaml
# ~/.openclaw/config.yaml
plugins:
entries:
- name: "device-pair"
enabled: true
config:
publicUrl: "https://your-domain.com" # 公网可访问地址
port: 80805.2 节点管理
javascript
// 列出节点
async function listNodes() {
return await nodes({ action: 'status' })
}
// 获取节点详情
async function getNodeInfo(deviceId) {
return await nodes({
action: 'device_info',
deviceId
})
}
// 发送通知到节点
async function notifyNode(deviceId, title, body) {
return await nodes({
action: 'notify',
deviceId,
title,
body,
priority: 'active'
})
}
// 获取节点位置
async function getNodeLocation(deviceId) {
return await nodes({
action: 'location_get',
deviceId,
desiredAccuracy: 'balanced'
})
}
// 截取屏幕
async function captureNodeScreen(deviceId) {
return await nodes({
action: 'screen_record',
deviceId,
durationMs: 5000
})
}5.3 实战案例 4:节点监控仪表板
javascript
class NodeMonitor {
constructor() {
this.nodes = new Map()
}
async refresh() {
const status = await nodes({ action: 'status' })
for (const node of status.nodes || []) {
const info = await nodes({
action: 'device_info',
deviceId: node.id
})
this.nodes.set(node.id, {
...node,
info,
lastSeen: Date.now()
})
}
return this.getDashboard()
}
getDashboard() {
const nodes = Array.from(this.nodes.values())
return {
total: nodes.length,
online: nodes.filter(n => n.connected).length,
offline: nodes.filter(n => !n.connected).length,
battery: nodes.map(n => ({
name: n.name,
level: n.info?.battery?.level,
charging: n.info?.battery?.charging
})),
locations: nodes.map(n => ({
name: n.name,
location: n.info?.location
}))
}
}
async generateReport() {
const dashboard = await this.refresh()
let report = `# 节点监控报告\n\n`
report += `## 概览\n`
report += `- 总节点数:${dashboard.total}\n`
report += `- 在线:${dashboard.online}\n`
report += `- 离线:${dashboard.offline}\n\n`
report += `## 电池状态\n`
for (const node of dashboard.battery) {
const icon = node.level > 50 ? '🔋' : node.level > 20 ? '🪫' : '⚠️'
report += `- ${node.name}: ${icon} ${node.level}%${node.charging ? ' (充电中)' : ''}\n`
}
report += `\n## 位置信息\n`
for (const node of dashboard.locations) {
if (node.location) {
report += `- ${node.name}: ${node.location.address}\n`
}
}
return report
}
async checkAlerts() {
const alerts = []
for (const [id, node] of this.nodes) {
// 低电量告警
if (node.info?.battery?.level < 20) {
alerts.push({
type: 'low_battery',
node: node.name,
level: node.info.battery.level
})
}
// 离线告警
if (!node.connected && Date.now() - node.lastSeen > 300000) {
alerts.push({
type: 'offline',
node: node.name,
since: node.lastSeen
})
}
}
return alerts
}
}
// 使用
const monitor = new NodeMonitor()
// 定期生成报告
async function nodeMonitoringJob() {
const report = await monitor.generateReport()
console.log(report)
// 检查告警
const alerts = await monitor.checkAlerts()
for (const alert of alerts) {
console.log('⚠️ 告警:', alert)
}
}六、安全配置
6.1 执行策略
yaml
# ~/.openclaw/config.yaml
security:
# 执行策略
execPolicy: "allowlist" # deny | allowlist | full
# 允许的命令列表
execAllowlist:
- "ls"
- "cat"
- "grep"
- "find"
- "git"
- "npm"
- "node"
# 需要审批的命令
execRequireApproval:
- "rm"
- "sudo"
- "curl"
- "wget"
# 禁止的命令
execDenylist:
- "mkfs"
- "dd"
- "shutdown"6.2 工具限制
yaml
security:
toolRestrictions:
- tool: "exec"
requireApproval: true
maxTimeout: 300
- tool: "browser"
allowedDomains:
- "*.github.com"
- "*.feishu.cn"
- tool: "message"
allowedChannels:
- "feishu"
- "discord"6.3 审计日志
javascript
async function enableAuditLogging() {
// 配置审计
await gateway({
action: 'config.patch',
raw: JSON.stringify({
security: {
auditLog: {
enabled: true,
path: '~/.openclaw/logs/audit.log',
events: ['exec', 'message', 'config.change']
}
}
}),
note: '启用审计日志'
})
}
// 查看审计日志
async function viewAuditLog(options = {}) {
const { lines = 100, event = null } = options
let command = `tail -${lines} ~/.openclaw/logs/audit.log`
if (event) {
command += ` | grep "${event}"`
}
return await exec({ command })
}七、性能优化
7.1 并发控制
yaml
sessions:
maxConcurrent: 10 # 最大并发会话数
maxConcurrentPerUser: 5 # 每用户最大并发
subagents:
maxConcurrent: 5 # 最大并发了代理
queueEnabled: true # 启用队列7.2 超时配置
yaml
timeouts:
default: 300 # 默认超时(秒)
exec: 60 # 命令执行超时
browser: 120 # 浏览器操作超时
api: 30 # API 调用超时
total: 1800 # 总任务超时(30 分钟)7.3 缓存配置
yaml
cache:
enabled: true
memory:
maxSize: 512 # MB
disk:
path: "~/.openclaw/cache"
maxSize: 2048 # MB
ttl:
default: 3600 # 秒
api: 600 # API 响应缓存
search: 1800 # 搜索结果缓存八、总结
核心要点
- 网关配置决定系统行为
- 插件系统提供无限扩展
- 安全配置保护系统安全
- 监控和恢复保障稳定性
- 性能优化提升用户体验
进阶方向
- 📖 阅读网关源码
- 🔧 开发自定义插件
- 🏗️ 构建企业级部署
- 📊 实现监控告警系统
🟢🐉 开始定制你的 OpenClaw 网关吧!