共计 2734 个字符,预计需要花费 7 分钟才能阅读完成。
背景痛点:为什么需要好的封装
在 OpenClaw 平台上开发技能时,新手常遇到这些问题:

- 接口混乱:不同技能之间的调用方式不统一,有的用函数调用,有的用消息队列,维护成本高
- 状态管理困难:技能运行时状态分散在各个变量中,难以追踪和调试
- 复用性差:相似功能的代码在多个技能中重复出现,修改时需要到处同步
我曾封装过一个天气预报技能,最初版本把所有逻辑堆在一个 300 行的函数里,结果每次新增查询城市都要改核心逻辑,测试时发现 bug 也无从下手。这促使我重新思考封装的艺术。
技术方案:两种封装范式对比
函数式封装
适合简单、无状态的技能,比如:
def get_weather(city):
"""纯函数式天气查询"""
api_url = f"https://api.weather.com/{city}"
return requests.get(api_url).json()
优点:
– 代码简洁
– 天然线程安全
缺点:
– 难以维护复杂状态
– 不支持生命周期管理
面向对象封装
适合需要状态管理的技能,典型结构:
class WeatherSkill:
def __init__(self, api_key):
self.api_key = api_key # 初始化配置
self.cache = {} # 状态维护
def execute(self, city):
"""核心业务逻辑"""
if city in self.cache:
return self.cache[city]
data = self._call_api(city)
self.cache[city] = data
return data
def _call_api(self, city):
"""私有方法封装实现细节"""
# 实际 API 调用代码...
实战:天气技能完整封装示例
import logging
from datetime import datetime, timedelta
class WeatherSkill:
"""生产级天气技能封装示例"""
def __init__(self, api_key, cache_ttl=300):
self.logger = logging.getLogger(__name__)
self.api_key = api_key
self.cache = {}
self.cache_ttl = timedelta(seconds=cache_ttl)
def execute(self, city):
"""
对外暴露的统一接口
:param city: 城市名称
:return: 天气数据字典
:raises: WeatherAPIError 当 API 调用失败时
"""
try:
# 检查缓存
if self._is_cache_valid(city):
self.logger.debug(f"命中缓存: {city}")
return self.cache[city]['data']
# 调用 API
data = self._fetch_from_api(city)
# 更新缓存
self.cache[city] = {
'data': data,
'timestamp': datetime.now()}
return data
except Exception as e:
self.logger.error(f"查询 {city} 天气失败: {str(e)}")
raise WeatherAPIError(str(e))
def _is_cache_valid(self, city):
"""检查缓存是否有效"""
if city not in self.cache:
return False
cache_time = self.cache[city]['timestamp']
return datetime.now() - cache_time < self.cache_ttl
def _fetch_from_api(self, city):
"""实际 API 调用实现"""
# 这里应该是真实的 API 调用代码
# 示例伪代码:
# response = requests.get(f"...{city}&key={self.api_key}")
# return self._parse_response(response)
return {"temp": 25, "condition": "sunny"} # 模拟数据
class WeatherAPIError(Exception):
"""自定义异常类型"""
pass
性能优化关键点
冷启动优化
- 延迟加载:将非核心依赖(如大数据模型)放在首次调用时加载
- 预加载机制:平台启动时并行初始化常用技能
def __init__(self):
self._essential = load_essential() # 立即加载
self._heavy_model = None # 延迟加载
def _get_model(self):
if self._heavy_model is None:
self._heavy_model = load_heavy_model()
return self._heavy_model
内存泄漏预防
- 定期清理缓存(建议实现
cleanup方法) - 使用 WeakReference 管理外部资源引用
- 在
__del__中显式释放资源
常见陷阱及解决方案
异步处理坑
错误示例:
async def execute(self):
# 错误!多个并发调用会共享状态
self.temp_data = await fetch_data()
process(self.temp_data)
正确做法:
async def execute(self):
# 每个调用维护独立上下文
temp_data = await fetch_data()
return process(temp_data)
权限管理建议
- 在
__init__中验证必要权限 - 敏感操作前二次确认
def __init__(self):
if not check_permission('network'):
raise PermissionError('需要网络访问权限')
def execute(self, cmd):
if cmd == 'delete' and not confirm_permission('admin'):
raise PermissionError('需要管理员权限')
进阶思考
如何设计上下文共享系统?
考虑以下要素:
1. 上下文键的命名空间规划(建议 skill_name:var_name 格式)
2. 版本兼容性管理
3. 访问权限控制
动手实践建议
尝试封装一个计时器技能,要求:
– 支持开始 / 暂停 / 重置
– 提供耗时统计功能
– 实现持久化(如中断后恢复计时)
结语
好的封装就像给代码建造房间——每个功能有自己的空间,门窗(接口)标准统一,管线(数据流)走向清晰。当需求变更时,你只需改造特定房间,而不会牵一发而动全身。希望本文的实践经验能帮助你建造更 ” 宜居 ” 的技能代码。
正文完
