共计 2813 个字符,预计需要花费 8 分钟才能阅读完成。
问题背景
传统 Agent 系统中,技能(skill)通常以硬编码方式实现。这种方式存在明显缺陷:

- 新增 skill 需要修改主程序代码
- 更新现有 skill 必须重启整个 Agent 服务
- 无法实现不同 skill 的独立部署和版本管理
架构设计
采用插件化架构是解决上述问题的理想方案。核心分为三层:
- 发现层:扫描指定目录下的 skill 模块
- 加载层:动态导入符合接口规范的 skill 类
- 执行层:统一调度 skill 实例的运行
类图示意:
classDiagram
class Skill {
<<interface>>
+execute() Any
+version() str}
class SkillLoader {+discover_skills() List[Skill]
+load_skill(name:str) Skill
}
class SkillExecutor {
-_lock: Lock
+run(skill:Skill) Any
}
Skill <|-- ConcreteSkill
SkillLoader o-- Skill
SkillExecutor o-- Skill
核心代码实现
动态加载基础实现
import importlib
from pathlib import Path
from typing import Type, Dict
class SkillBase:
"""所有 skill 必须继承的基类"""
@classmethod
def version(cls) -> str:
raise NotImplementedError
def execute(self, *args, **kwargs):
raise NotImplementedError
class SkillLoader:
def __init__(self, skill_dir: str):
self.skill_dir = Path(skill_dir)
self._skills: Dict[str, Type[SkillBase]] = {}
def discover(self) -> None:
"""扫描目录并加载所有合法 skill 模块"""
for py_file in self.skill_dir.glob('*.py'):
if py_file.name.startswith('_'):
continue
module_name = py_file.stem
try:
module = importlib.import_module(f'skills.{module_name}')
for attr in dir(module):
cls = getattr(module, attr)
if (isinstance(cls, type) and
issubclass(cls, SkillBase) and
cls != SkillBase
):
self._skills[module_name] = cls
except ImportError as e:
print(f'加载失败 {module_name}: {e}')
线程安全执行器
from threading import Lock
from concurrent.futures import ThreadPoolExecutor
class SkillExecutor:
def __init__(self, max_workers=4):
self._lock = Lock()
self._pool = ThreadPoolExecutor(max_workers)
def run(self, skill: SkillBase, *args, **kwargs):
"""带锁保护的执行方法"""
with self._lock:
future = self._pool.submit(skill.execute, *args, **kwargs)
return future.result() # 同步等待结果
async def run_async(self, skill: SkillBase, *args, **kwargs):
"""协程版本"""
import asyncio
loop = asyncio.get_event_loop()
with self._lock:
return await loop.run_in_executor(
None,
lambda: skill.execute(*args, **kwargs)
)
安全防护机制
代码签名验证
import hashlib
from functools import wraps
def verify_signature(signature: str):
"""装饰器验证 skill 文件签名"""
def decorator(cls):
@wraps(cls)
def wrapper(*args, **kwargs):
with open(cls.__module__.__file__, 'rb') as f:
file_hash = hashlib.sha256(f.read()).hexdigest()
if file_hash != signature:
raise RuntimeError('skill 签名验证失败')
return cls(*args, **kwargs)
return wrapper
return decorator
# 使用示例
@verify_signature('a1b2c3...')
class SafeSkill(SkillBase):
...
性能优化技巧
LRU 缓存实现
from functools import lru_cache
class CachedSkillLoader(SkillLoader):
@lru_cache(maxsize=10)
def get_skill(self, name: str) -> SkillBase:
"""缓存最近使用的 skill 实例"""
if name not in self._skills:
raise KeyError(f'未找到 skill: {name}')
return self._skills[name]()
常见问题解决方案
循环依赖处理
当 skill 之间存在相互引用时,建议:
- 提取公共部分到独立模块
- 使用
import xxx而非from xxx import yyy - 在方法内部进行延迟导入
# 错误示例:直接交叉引用
# skill_a.py
from skill_b import Helper
# skill_b.py
from skill_a import Util
# 正确做法:# common.py
class Shared:
...
# skill_a.py
import common
# skill_b.py
import common
延伸思考
实现热更新需要解决:
- 文件监控(watchdog 库)
- 重新加载模块(importlib.reload)
- 版本平滑过渡
建议读者尝试以下扩展功能:
- 基于 WebSocket 的远程 skill 部署
- 自动生成 skill 的 API 文档
- 技能市场动态下载机制
通过本文介绍的技术方案,开发者可以构建出高扩展性的 Agent 技能系统。实际应用中还需根据具体场景调整线程模型和隔离策略。
正文完
