OpenClaw 构建指南:打造智能多工具编排运行时框架
OpenClaw 构建指南:打造智能多工具编排运行时框架
引言
在 AI 应用开发的浪潮中,如何让智能体具备连接多个外部工具、处理复杂工作流的能力?答案就是 OpenClaw——一个为智能体设计的多工具编排运行时框架。本文将详细介绍如何从零开始构建 OpenClaw,让你的 AI 智能体能够连接微信、Slack、邮件等各种工具,实现真正的自动化工作流。
什么是 OpenClaw?
核心定位
OpenClaw 是连接智能体与外部工具的运行时框架,负责工具注册、调用编排、消息路由等基础设施。
设计理念
OpenClaw 遵循以下核心原则:
- 关注点分离:运行时管理 vs 业务逻辑 vs 工具实现
- 可扩展性:轻松接入新工具,支持插件化架构
- 智能驱动:通过大模型的 Function Calling 机制自动选择和调用工具
- 灵活路由:支持多输入源监听和多输出目标路由
OpenClaw 的能力
- ✅ 多渠道输入监听(微信、钉钉、Slack、邮件、Webhook 等)
- ✅ 工具注册与发现(动态加载和管理工具)
- ✅ 智能编排(大模型驱动的工具调用链)
- ✅ 输出路由(根据策略决定响应发送位置)
- ✅ 会话状态管理(维护对话上下文)
- ✅ 错误恢复与重试机制
OpenClaw 不是什么
- ❌ 智能体本身(不负责理解、推理、生成)
- ❌ 工具库(不实现具体的工具功能)
- ❌ 独立应用(是运行时框架,为智能体提供基础设施)
整体架构
系统架构图
┌─────────────────────────────────────────────────────────────┐
│ OpenClaw 运行时框架 │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 输入源管理器(多渠道监听) │ │
│ │ • 微信 API • 钉钉 API • Slack API • 邮件 • Webhook │ │
│ └────────────────────┬─────────────────────────────────┘ │
│ ↓ 统一消息格式 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 路由与编排引擎 │ │
│ │ • 意图识别 • 工具选择 • 调用编排 • 结果聚合 │ │
│ └────────────────────┬─────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 工具注册表(Tool Registry) │ │
│ │ ┌──────────────┬──────────────┬──────────────┐ │ │
│ │ │ wechat-tool │ slack-tool │ email-tool │ ... │ │
│ │ └──────────────┴──────────────┴──────────────┘ │ │
│ └────────────────────┬─────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 大模型(智能引擎) │ │
│ │ • 理解意图 • 选择工具 • 生成参数 • 整合结果 │ │
│ └────────────────────┬─────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 输出路由器 │ │
│ │ • 返回原输入源 • 多路输出 • 条件路由 │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓ ↓
外部工具 外部系统
(API调用) (消息发送)
核心组件说明
| 组件 | 职责 | 状态 |
|---|---|---|
| 输入源管理器 | 监听多个输入源,统一消息格式 | 持久化 |
| 路由与编排引擎 | 协调工具调用,管理调用链 | 持久化 |
| 工具注册表 | 管理所有可用工具的能力和接口 | 运行时 |
| 大模型客户端 | 调用大模型 API,实现 Function Calling | 无状态 |
| 输出路由器 | 决定响应发送位置 | 运行时 |
| 会话管理器 | 维护对话历史和上下文 | 持久化 |
目录结构
OpenClaw 完整目录结构
openclaw/
├── openclaw/ # 核心框架代码
│ ├── __init__.py
│ ├── core/ # 核心组件
│ │ ├── __init__.py
│ │ ├── input_manager.py # 输入源管理器
│ │ ├── orchestration.py # 编排引擎
│ │ ├── tool_registry.py # 工具注册表
│ │ ├── llm_client.py # 大模型客户端
│ │ ├── output_router.py # 输出路由器
│ │ └── session_manager.py # 会话管理器
│ ├── tools/ # 内置工具实现
│ │ ├── __init__.py
│ │ ├── wechat/
│ │ ├── slack/
│ │ └── email/
│ ├── config/ # 配置文件
│ │ ├── settings.yaml
│ │ └── tools.yaml
│ ├── storage/ # 存储层(会话、状态)
│ │ ├── session_store.py
│ │ └── state_manager.py
│ └── utils/ # 工具函数
│ ├── __init__.py
│ └── logger.py
├── tools/ # 外部工具(插件式)
│ ├── wechat-tool/
│ ├── slack-tool/
│ └── email-tool/
├── config/ # 配置目录
│ ├── openclaw.yaml
│ └── tools/
│ ├── wechat.yaml
│ ├── slack.yaml
│ └── email.yaml
├── scripts/ # 脚本工具
│ ├── start.sh
│ └── install.sh
├── tests/ # 测试
│ ├── test_tool_registry.py
│ ├── test_orchestration.py
│ └── test_output_router.py
├── requirements.txt # Python 依赖
├── setup.py # 安装脚本
├── README.md # 项目说明
└── openclaw.py # 主入口
核心组件实现
1. 工具注册表(Tool Registry)
工具注册表负责管理所有可用工具的能力、接口和元数据。
工具定义格式
每个工具需要提供工具定义文件(YAML):
# config/tools/wechat.yaml
name: wechat-tool
version: "1.0.0"
description: "微信消息发送与用户信息查询"
capabilities:
- name: send_message
description: "发送微信消息给指定用户"
parameters:
- name: user_id
type: string
required: true
description: "微信用户ID"
- name: content
type: string
required: true
description: "消息内容"
returns:
type: object
description: "发送结果,包含 success 和 message_id 字段"
- name: get_user_info
description: "获取微信用户信息"
parameters:
- name: user_id
type: string
required: true
description: "微信用户ID"
returns:
type: object
description: "用户信息对象"
metadata:
author: "OpenClaw Team"
category: "messaging"
requires_auth: true
auth_type: "API_KEY"
工具注册表实现
# openclaw/core/tool_registry.py
from typing import Dict, List, Any
import yaml
import importlib
class ToolDefinition:
"""工具定义类"""
def __init__(self, name: str, definition: dict):
self.name = name
self.version = definition.get('version', '1.0.0')
self.description = definition.get('description', '')
self.capabilities = definition.get('capabilities', [])
self.metadata = definition.get('metadata', {})
self._functions = {}
def add_function(self, func_name: str, func_callable):
"""注册工具函数"""
self._functions[func_name] = func_callable
def get_function(self, func_name: str):
"""获取工具函数"""
return self._functions.get(func_name)
def to_llm_format(self) -> List[dict]:
"""转换为大模型 Function Calling 格式"""
tools = []
for capability in self.capabilities:
tools.append({
"type": "function",
"function": {
"name": f"{self.name}.{capability['name']}",
"description": capability['description'],
"parameters": {
"type": "object",
"properties": {
param['name']: {
"type": param['type'],
"description": param.get('description', '')
}
for param in capability['parameters']
},
"required": [
param['name'] for param in capability['parameters']
if param.get('required', False)
]
}
}
})
return tools
class ToolRegistry:
"""工具注册表"""
def __init__(self):
self.tools: Dict[str, ToolDefinition] = {}
def register_from_yaml(self, tool_name: str, yaml_path: str):
"""从 YAML 文件加载工具定义"""
with open(yaml_path, 'r', encoding='utf-8') as f:
definition = yaml.safe_load(f)
tool_def = ToolDefinition(tool_name, definition)
self.tools[tool_name] = tool_def
return tool_def
def register_functions(self, tool_name: str, module_path: str):
"""从 Python 模块加载工具函数"""
if tool_name not in self.tools:
raise ValueError(f"Tool {tool_name} not registered")
module = importlib.import_module(module_path)
tool_def = self.tools[tool_name]
for capability in tool_def.capabilities:
func_name = capability['name']
if hasattr(module, func_name):
tool_def.add_function(func_name, getattr(module, func_name))
def get_all_tools_llm_format(self) -> List[dict]:
"""获取所有工具的大模型格式定义"""
all_tools = []
for tool_def in self.tools.values():
all_tools.extend(tool_def.to_llm_format())
return all_tools
def call_tool(self, full_tool_name: str, parameters: dict) -> Any:
"""
调用工具函数
Args:
full_tool_name: 完整工具名,格式为 "tool_name.function_name"
parameters: 函数参数
Returns:
工具函数的返回值
"""
tool_name, func_name = full_tool_name.split('.')
if tool_name not in self.tools:
raise ValueError(f"Tool {tool_name} not found")
tool_def = self.tools[tool_name]
func = tool_def.get_function(func_name)
if not func:
raise ValueError(f"Function {func_name} not found in tool {tool_name}")
return func(**parameters)
def list_tools(self) -> List[str]:
"""列出所有注册的工具"""
return list(self.tools.keys())
2. 输入源管理器(Input Manager)
输入源管理器负责监听多个输入源,并将消息统一格式化。
统一消息格式
# openclaw/core/input_manager.py
from dataclasses import dataclass
from datetime import datetime
from typing import Any, Dict, Optional
@dataclass
class Message:
"""统一消息格式"""
source: str # 输入源类型(wechat/slack/email等)
source_id: str # 输入源实例ID(如频道ID、用户ID)
user_id: str # 用户ID
content: str # 消息内容
timestamp: datetime # 时间戳
metadata: Dict[str, Any] # 额外元数据
message_id: Optional[str] = None # 原始消息ID
def to_dict(self) -> dict:
"""转换为字典格式"""
return {
"source": self.source,
"source_id": self.source_id,
"user_id": self.user_id,
"content": self.content,
"timestamp": self.timestamp.isoformat(),
"metadata": self.metadata,
"message_id": self.message_id
}
class InputSource:
"""输入源基类"""
def __init__(self, source_id: str):
self.source_id = source_id
self.source_type = self.__class__.__name__.replace('Source', '').lower()
def listen(self, callback):
"""开始监听消息"""
raise NotImplementedError
def send_message(self, user_id: str, content: str) -> dict:
"""发送消息"""
raise NotImplementedError
def stop(self):
"""停止监听"""
raise NotImplementedError
class InputManager:
"""输入源管理器"""
def __init__(self):
self.sources: Dict[str, InputSource] = {}
def register_source(self, source_id: str, source: InputSource):
"""注册输入源"""
self.sources[source_id] = source
def start_all(self, message_callback):
"""启动所有输入源"""
for source in self.sources.values():
source.listen(message_callback)
def stop_all(self):
"""停止所有输入源"""
for source in self.sources.values():
source.stop()
def get_source(self, source_id: str) -> InputSource:
"""获取指定输入源"""
return self.sources.get(source_id)
微信输入源实现示例
# openclaw/tools/wechat/source.py
from openclaw.core.input_manager import InputSource, Message
from datetime import datetime
import requests
class WeChatSource(InputSource):
"""微信输入源"""
def __init__(self, source_id: str, app_id: str, app_secret: str):
super().__init__(source_id)
self.app_id = app_id
self.app_secret = app_secret
self.access_token = None
self._running = False
def _get_access_token(self):
"""获取访问令牌"""
url = f"https://api.weixin.qq.com/cgi-bin/token"
params = {
"grant_type": "client_credential",
"appid": self.app_id,
"secret": self.app_secret
}
response = requests.get(url, params=params)
data = response.json()
self.access_token = data['access_token']
return self.access_token
def listen(self, callback):
"""监听微信消息(通过回调接口)"""
# 这里需要实现微信的回调接口
# 实际场景中通常是启动一个 HTTP 服务器接收微信的推送
pass
def send_message(self, user_id: str, content: str) -> dict:
"""发送微信消息"""
url = f"https://api.weixin.qq.com/cgi-bin/message/custom/send"
url += f"?access_token={self.access_token}"
payload = {
"touser": user_id,
"msgtype": "text",
"text": {"content": content}
}
response = requests.post(url, json=payload)
return response.json()
def stop(self):
"""停止监听"""
self._running = False
3. 大模型客户端(LLM Client)
大模型客户端负责与大模型 API 交互,支持 Function Calling 机制。
# openclaw/core/llm_client.py
import requests
from typing import List, Dict, Any, Optional
import json
class LLMClient:
"""大模型客户端"""
def __init__(self, api_key: str, base_url: str, model: str = "gpt-4"):
self.api_key = api_key
self.base_url = base_url
self.model = model
def chat(
self,
messages: List[dict],
tools: Optional[List[dict]] = None,
tool_choice: str = "auto",
temperature: float = 0.7,
max_tokens: int = 2000
) -> dict:
"""
调用大模型聊天接口
Args:
messages: 消息列表
tools: 工具定义列表(Function Calling)
tool_choice: 工具选择策略("auto"/"none"/"required")
temperature: 温度参数
max_tokens: 最大token数
Returns:
大模型响应
"""
url = f"{self.base_url}/chat/completions"
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
payload = {
"model": self.model,
"messages": messages,
"temperature": temperature,
"max_tokens": max_tokens
}
if tools:
payload["tools"] = tools
payload["tool_choice"] = tool_choice
response = requests.post(url, headers=headers, json=payload, timeout=30)
response.raise_for_status()
return response.json()
def process_tool_calls(
self,
messages: List[dict],
tools: List[dict],
tool_registry
) -> str:
"""
处理工具调用并获取最终响应
Args:
messages: 消息历史
tools: 工具定义
tool_registry: 工具注册表
Returns:
最终文本响应
"""
while True:
response = self.chat(messages, tools=tools)
assistant_message = response["choices"][0]["message"]
messages.append(assistant_message)
# 检查是否需要调用工具
if "tool_calls" not in assistant_message:
# 没有工具调用,返回最终响应
return assistant_message["content"]
# 处理工具调用
for tool_call in assistant_message["tool_calls"]:
func_name = tool_call["function"]["name"]
func_args = json.loads(tool_call["function"]["arguments"])
# 调用工具
try:
result = tool_registry.call_tool(func_name, func_args)
except Exception as e:
result = f"工具调用错误: {str(e)}"
# 将工具结果添加到消息历史
messages.append({
"role": "tool",
"tool_call_id": tool_call["id"],
"content": json.dumps(result, ensure_ascii=False)
})
4. 编排引擎(Orchestration Engine)
编排引擎负责协调整个消息处理流程。
# openclaw/core/orchestration.py
from typing import Callable, Any
from openclaw.core.input_manager import Message
from openclaw.core.llm_client import LLMClient
from openclaw.core.tool_registry import ToolRegistry
class OrchestrationEngine:
"""编排引擎"""
def __init__(
self,
llm_client: LLMClient,
tool_registry: ToolRegistry,
system_prompt: str = "你是一个智能助手,可以使用各种工具帮助用户完成任务。"
):
self.llm_client = llm_client
self.tool_registry = tool_registry
self.system_prompt = system_prompt
def process_message(self, message: Message) -> str:
"""
处理消息并返回响应
Args:
message: 统一消息格式
Returns:
响应文本
"""
# 构建消息历史
messages = [
{"role": "system", "content": self.system_prompt},
{"role": "user", "content": message.content}
]
# 获取可用工具定义
tools = self.tool_registry.get_all_tools_llm_format()
# 处理工具调用并获取最终响应
response = self.llm_client.process_tool_calls(messages, tools, self.tool_registry)
return response
def set_system_prompt(self, prompt: str):
"""设置系统提示词"""
self.system_prompt = prompt
5. 输出路由器(Output Router)
输出路由器根据策略决定响应发送位置。
# openclaw/core/output_router.py
from typing import Dict, Any
from openclaw.core.input_manager import InputManager
class RoutingStrategy:
"""路由策略"""
RETURN_TO_SOURCE = "return_to_source" # 返回原输入源
BROADCAST = "broadcast" # 广播到多个渠道
CONDITIONAL = "conditional" # 条件路由
NONE = "none" # 不发送(静默处理)
class OutputRouter:
"""输出路由器"""
def __init__(self, input_manager: InputManager):
self.input_manager = input_manager
def route(
self,
content: str,
source: str,
user_id: str,
strategy: str = RoutingStrategy.RETURN_TO_SOURCE,
destinations: Dict[str, Any] = None
):
"""
路由输出
Args:
content: 响应内容
source: 原输入源
user_id: 用户ID
strategy: 路由策略
destinations: 目标配置(用于广播或条件路由)
"""
if strategy == RoutingStrategy.RETURN_TO_SOURCE:
# 返回原输入源
input_source = self.input_manager.get_source(source)
if input_source:
input_source.send_message(user_id, content)
elif strategy == RoutingStrategy.BROADCAST:
# 广播到多个渠道
for dest_id, dest_config in destinations.items():
input_source = self.input_manager.get_source(dest_id)
if input_source:
target_user = dest_config.get('user_id', user_id)
input_source.send_message(target_user, content)
elif strategy == RoutingStrategy.CONDITIONAL:
# 条件路由(根据内容或元数据决定)
for condition, dest_info in destinations.items():
if self._evaluate_condition(content, condition):
dest_id = dest_info['source']
dest_user = dest_info.get('user_id', user_id)
input_source = self.input_manager.get_source(dest_id)
if input_source:
input_source.send_message(dest_user, content)
# NONE 策略不发送任何消息
def _evaluate_condition(self, content: str, condition: str) -> bool:
"""评估路由条件"""
# 这里可以实现复杂的条件判断逻辑
# 例如:包含关键词、错误码、优先级等
if condition.startswith("contains:"):
keyword = condition.split(":", 1)[1]
return keyword in content
elif condition.startswith("error"):
return "error" in content.lower()
return False
6. 会话管理器(Session Manager)
会话管理器负责维护对话历史和上下文。
# openclaw/core/session_manager.py
from typing import Dict, List, Any
from datetime import datetime
import json
class Session:
"""会话类"""
def __init__(self, session_id: str):
self.session_id = session_id
self.messages: List[dict] = []
self.metadata: Dict[str, Any] = {}
self.created_at = datetime.now()
self.updated_at = datetime.now()
def add_message(self, role: str, content: str, **kwargs):
"""添加消息"""
message = {
"role": role,
"content": content,
"timestamp": datetime.now().isoformat(),
**kwargs
}
self.messages.append(message)
self.updated_at = datetime.now()
def get_messages(self, max_count: int = None) -> List[dict]:
"""获取消息历史"""
if max_count:
return self.messages[-max_count:]
return self.messages
def to_dict(self) -> dict:
"""转换为字典"""
return {
"session_id": self.session_id,
"messages": self.messages,
"metadata": self.metadata,
"created_at": self.created_at.isoformat(),
"updated_at": self.updated_at.isoformat()
}
class SessionManager:
"""会话管理器"""
def __init__(self):
self.sessions: Dict[str, Session] = {}
def get_or_create(self, session_id: str) -> Session:
"""获取或创建会话"""
if session_id not in self.sessions:
self.sessions[session_id] = Session(session_id)
return self.sessions[session_id]
def get_session(self, session_id: str) -> Session:
"""获取会话"""
return self.sessions.get(session_id)
def delete_session(self, session_id: str):
"""删除会话"""
if session_id in self.sessions:
del self.sessions[session_id]
def list_sessions(self) -> List[str]:
"""列出所有会话ID"""
return list(self.sessions.keys())
主入口实现
OpenClaw 主类
# openclaw/__init__.py
from openclaw.core.input_manager import InputManager
from openclaw.core.tool_registry import ToolRegistry
from openclaw.core.llm_client import LLMClient
from openclaw.core.orchestration import OrchestrationEngine
from openclaw.core.output_router import OutputRouter
from openclaw.core.session_manager import SessionManager
import yaml
import os
class OpenClaw:
"""OpenClaw 主类"""
def __init__(self, config_path: str = "config/openclaw.yaml"):
# 加载配置
self.config = self._load_config(config_path)
# 初始化核心组件
self.input_manager = InputManager()
self.tool_registry = ToolRegistry()
self.session_manager = SessionManager()
# 初始化大模型客户端
llm_config = self.config.get('llm', {})
self.llm_client = LLMClient(
api_key=llm_config.get('api_key'),
base_url=llm_config.get('base_url', 'https://api.openai.com/v1'),
model=llm_config.get('model', 'gpt-4')
)
# 初始化编排引擎
system_prompt = self.config.get('system_prompt', '你是一个智能助手。')
self.orchestration_engine = OrchestrationEngine(
llm_client=self.llm_client,
tool_registry=self.tool_registry,
system_prompt=system_prompt
)
# 初始化输出路由器
self.output_router = OutputRouter(self.input_manager)
# 注册工具
self._register_tools()
# 注册输入源
self._register_input_sources()
def _load_config(self, config_path: str) -> dict:
"""加载配置文件"""
with open(config_path, 'r', encoding='utf-8') as f:
return yaml.safe_load(f)
def _register_tools(self):
"""注册工具"""
tools_config = self.config.get('tools', {})
for tool_name, tool_config in tools_config.items():
# 从 YAML 加载工具定义
yaml_path = tool_config.get('definition')
if yaml_path and os.path.exists(yaml_path):
self.tool_registry.register_from_yaml(tool_name, yaml_path)
# 加载工具函数
module_path = tool_config.get('module')
if module_path:
self.tool_registry.register_functions(tool_name, module_path)
def _register_input_sources(self):
"""注册输入源"""
sources_config = self.config.get('input_sources', {})
for source_id, source_config in sources_config.items():
source_type = source_config.get('type')
params = source_config.get('params', {})
# 根据类型创建输入源
if source_type == 'wechat':
from openclaw.tools.wechat.source import WeChatSource
source = WeChatSource(
source_id=source_id,
app_id=params.get('app_id'),
app_secret=params.get('app_secret')
)
# 可以添加其他输入源类型
self.input_manager.register_source(source_id, source)
def handle_message(self, message):
"""
处理消息
Args:
message: 统一消息格式
"""
# 获取或创建会话
session_id = f"{message.source}:{message.user_id}"
session = self.session_manager.get_or_create(session_id)
# 添加用户消息到会话
session.add_message("user", message.content)
# 获取会话历史(用于上下文)
history = session.get_messages(max_count=20)
# 构建消息列表(排除系统消息)
messages = [msg for msg in history if msg['role'] != 'system']
# 处理消息
response = self.orchestration_engine.process_message(message)
# 添加助手响应到会话
session.add_message("assistant", response)
# 路由输出
routing_config = self.config.get('output_routing', {})
strategy = routing_config.get('strategy', 'return_to_source')
destinations = routing_config.get('destinations', {})
self.output_router.route(
content=response,
source=message.source,
user_id=message.user_id,
strategy=strategy,
destinations=destinations
)
return response
def start(self):
"""启动 OpenClaw"""
print("OpenClaw 启动中...")
# 定义消息处理回调
def message_callback(message):
try:
self.handle_message(message)
except Exception as e:
print(f"处理消息时出错: {e}")
# 启动所有输入源
self.input_manager.start_all(message_callback)
print("OpenClaw 已启动,等待消息...")
def stop(self):
"""停止 OpenClaw"""
print("OpenClaw 停止中...")
self.input_manager.stop_all()
print("OpenClaw 已停止")
主入口脚本
# openclaw.py
from openclaw import OpenClaw
import signal
import sys
def main():
# 创建 OpenClaw 实例
openclaw = OpenClaw(config_path="config/openclaw.yaml")
# 优雅退出处理
def signal_handler(sig, frame):
print("
收到退出信号...")
openclaw.stop()
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
# 启动
openclaw.start()
# 保持运行
import time
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
openclaw.stop()
if __name__ == "__main__":
main()
配置文件
主配置文件
# config/openclaw.yaml
# 大模型配置
llm:
api_key: "your-api-key-here"
base_url: "https://api.openai.com/v1"
model: "gpt-4"
temperature: 0.7
max_tokens: 2000
# 系统提示词
system_prompt: |
你是一个智能助手,可以使用各种工具帮助用户完成任务。
你能够连接微信、Slack、邮件等多个平台,为用户提供全方位的服务。
在使用工具时,请确保参数正确,并在出现错误时给出清晰的反馈。
# 工具配置
tools:
wechat-tool:
definition: config/tools/wechat.yaml
module: openclaw.tools.wechat.functions
slack-tool:
definition: config/tools/slack.yaml
module: openclaw.tools.slack.functions
email-tool:
definition: config/tools/email.yaml
module: openclaw.tools.email.functions
# 输入源配置
input_sources:
wechat-primary:
type: wechat
params:
app_id: "your-wechat-app-id"
app_secret: "your-wechat-app-secret"
slack-workspace:
type: slack
params:
bot_token: "xoxb-your-bot-token"
signing_secret: "your-signing-secret"
# 输出路由配置
output_routing:
strategy: return_to_source # 可选: return_to_source, broadcast, conditional
destinations:
# 广播配置示例
slack-alerts:
source: slack-workspace
user_id: "alerts-channel"
# 条件路由示例
"contains:error":
source: wechat-primary
user_id: "admin-user-id"
# 日志配置
logging:
level: INFO
file: logs/openclaw.log
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
# 存储配置
storage:
type: file # 可选: file, redis, database
path: data/sessions
完整工具实现示例
微信工具实现
# openclaw/tools/wechat/functions.py
import os
from coze_workload_identity import requests
def send_message(user_id: str, content: str) -> dict:
"""
发送微信消息
Args:
user_id: 微信用户ID
content: 消息内容
Returns:
发送结果
"""
# 获取凭证
skill_id = "7603711974731677702"
access_token = os.getenv("COZE_WECHAT_API")
# 构建请求
url = f"https://api.weixin.qq.com/cgi-bin/message/custom/send"
url += f"?access_token={access_token}"
payload = {
"touser": user_id,
"msgtype": "text",
"text": {"content": content}
}
response = requests.post(url, json=payload, timeout=30)
response.raise_for_status()
return response.json()
def get_user_info(user_id: str) -> dict:
"""
获取微信用户信息
Args:
user_id: 微信用户ID
Returns:
用户信息
"""
# 获取凭证
skill_id = "7603711974731677702"
access_token = os.getenv("COZE_WECHAT_API")
# 构建请求
url = f"https://api.weixin.qq.com/cgi-bin/user/info"
url += f"?access_token={access_token}"
url += f"&openid={user_id}&lang=zh_CN"
response = requests.get(url, timeout=30)
response.raise_for_status()
return response.json()
工具开发指南
开发新工具的步骤
1. 创建工具目录结构
tools/your-tool/
├── __init__.py
├── source.py # 输入源实现(如果需要监听)
├── functions.py # 工具函数实现
├── config.yaml # 工具配置
└── README.md # 工具说明
2. 定义工具接口
# config/tools/your-tool.yaml
name: your-tool
version: "1.0.0"
description: "工具描述"
capabilities:
- name: function_name
description: "函数描述"
parameters:
- name: param1
type: string
required: true
description: "参数描述"
returns:
type: object
description: "返回值描述"
3. 实现工具函数
# tools/your-tool/functions.py
def function_name(param1: str) -> dict:
"""
函数实现
Args:
param1: 参数描述
Returns:
返回值描述
"""
# 实现逻辑
result = {...}
return result
4. 注册工具
在 config/openclaw.yaml 中添加:
tools:
your-tool:
definition: config/tools/your-tool.yaml
module: tools.your_tool.functions
部署与运行
安装依赖
# 创建虚拟环境
python -m venv venv
source venv/bin/activate # Linux/Mac
# venvScriptsctivate # Windows
# 安装依赖
pip install -r requirements.txt
requirements.txt
# OpenClaw 核心依赖
pyyaml>=6.0
requests>=2.28.0
python-dotenv>=1.0.0
# 可选依赖(根据需要)
redis>=4.5.0 # 如果使用 Redis 存储
pymongo>=4.0.0 # 如果使用 MongoDB 存储
配置环境变量
# .env
OPENAI_API_KEY=your-api-key
WECHAT_APP_ID=your-app-id
WECHAT_APP_SECRET=your-app-secret
SLACK_BOT_TOKEN=xoxb-your-token
运行 OpenClaw
# 开发模式
python openclaw.py
# 生产模式(使用 Gunicorn)
gunicorn -w 4 -b 0.0.0.0:8000 openclaw:app
使用 Docker 部署
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "openclaw.py"]
# docker-compose.yml
version: '3.8'
services:
openclaw:
build: .
ports:
- "8000:8000"
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY}
- WECHAT_APP_ID=${WECHAT_APP_ID}
volumes:
- ./config:/app/config
- ./logs:/app/logs
- ./data:/app/data
测试
单元测试示例
# tests/test_tool_registry.py
import unittest
from openclaw.core.tool_registry import ToolRegistry
class TestToolRegistry(unittest.TestCase):
def setUp(self):
self.registry = ToolRegistry()
def test_register_tool(self):
"""测试工具注册"""
tool_def = self.registry.register_from_yaml(
"test-tool",
"config/tools/wechat.yaml"
)
self.assertIsNotNone(tool_def)
self.assertEqual(tool_def.name, "test-tool")
def test_call_tool(self):
"""测试工具调用"""
# 注册测试工具
# ...
result = self.registry.call_tool(
"test-tool.send_message",
{"user_id": "test", "content": "hello"}
)
self.assertIsNotNone(result)
if __name__ == "__main__":
unittest.main()
最佳实践
1. 错误处理
def safe_tool_call(func):
"""工具调用装饰器"""
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
return {
"success": False,
"error": str(e),
"error_type": type(e).__name__
}
return wrapper
@safe_tool_call
def send_message(user_id: str, content: str):
# 实现
pass
2. 日志记录
import logging
logger = logging.getLogger(__name__)
def send_message(user_id: str, content: str):
logger.info(f"发送消息给用户 {user_id}")
try:
# 实现逻辑
logger.info("消息发送成功")
except Exception as e:
logger.error(f"消息发送失败: {e}", exc_info=True)
raise
3. 性能优化
# 使用缓存
from functools import lru_cache
@lru_cache(maxsize=128)
def get_user_info_cached(user_id: str):
# 实现
pass
# 批量处理
def batch_send_messages(messages: list):
# 批量发送优化
pass
4. 安全性
# 输入验证
def validate_user_id(user_id: str) -> bool:
"""验证用户ID格式"""
import re
pattern = r'^[a-zA-Z0-9_-]+$'
return re.match(pattern, user_id) is not None
# 敏感信息保护
def mask_sensitive_data(data: dict) -> dict:
"""脱敏处理敏感数据"""
masked = data.copy()
if 'token' in masked:
masked['token'] = '***MASKED***'
return masked
故障排查
常见问题
问题1:工具调用失败
- 检查工具定义是否正确
- 验证参数名称和类型
- 查看日志获取详细错误信息
问题2:消息没有响应
- 检查输入源是否正常连接
- 验证输出路由配置
- 确认大模型 API 是否可用
问题3:性能问题
- 增加超时设置
- 使用异步处理
- 优化工具调用链
进阶功能
1. 异步支持
import asyncio
class AsyncLLMClient(LLMClient):
async def chat_async(self, messages, **kwargs):
# 异步实现
pass
2. 分布式部署
# 使用 Redis 共享会话状态
class RedisSessionManager(SessionManager):
def __init__(self, redis_url: str):
self.redis_client = redis.from_url(redis_url)
3. 工具编排优化
# 并行工具调用
async def parallel_tool_calls(tools):
tasks = [call_tool(tool) for tool in tools]
results = await asyncio.gather(*tasks)
return results
总结
OpenClaw 是一个强大的多工具编排运行时框架,它通过以下核心能力为智能体赋能:
- 统一的多渠道连接:轻松接入各种外部工具和平台
- 智能的工具编排:基于大模型 Function Calling 自动选择和调用工具
- 灵活的消息路由:支持多种路由策略,满足不同场景需求
- 可扩展的架构:插件式工具开发,持续扩展能力
通过本指南,你已经了解了如何从零开始构建 OpenClaw,包括:
- 核心组件的设计与实现
- 工具的开发与注册
- 配置管理
- 部署与运行
- 测试与优化
开始构建你自己的 OpenClaw 吧,让智能体连接世界!









