写了 10 个 Agent Skill 后,我把 300 行重复代码压到了 30 行
1. 引言
在最近的一个 AI Agent 项目中,我陆续编写了 10 个不同的 Agent Skill。每个 Skill 都负责一个独立的业务能力,比如查询天气、发送邮件、调用内部 API、解析文档等。写第一个 Skill 时很顺畅,写第二个也还行,但写到第五个时,我发现自己陷入了 Ctrl+C / Ctrl+V 的泥潭。
每个 Skill 都包含几乎相同的模板代码:参数校验、错误处理、日志记录、结果格式化……10 个 Skill 下来,光这些重复的“骨架”代码就堆了将近 300 行。这不仅让代码库变得臃肿,更可怕的是——每次要修改公共逻辑(比如统一日志格式),就得手动改 10 个文件。
于是,我决定重构。最终,通过抽象公共基类和装饰器模式,我把这 300 行重复代码压缩到了 30 行。这篇文章就来分享我是怎么做到的。
2. 问题重现:一个典型的 Agent Skill 长什么样
先看一个典型的 Skill 实现。假设我们有一个 WeatherSkill,用于查询天气:
class WeatherSkill:
def __init__(self, name: str = "weather"):
self.name = name
self.logger = logging.getLogger(self.name)
def can_handle(self, intent: str) -> bool:
return intent == "query_weather"
def execute(self, params: dict) -> dict:
# 参数校验
if "city" not in params:
return {"success": False, "error": "Missing city parameter"}
# 业务逻辑
try:
result = self._query_weather(params["city"])
# 结果格式化
return {"success": True, "data": result}
except Exception as e:
self.logger.error(f"Failed to query weather: {e}")
return {"success": False, "error": str(e)}
def _query_weather(self, city: str) -> dict:
# 实际的 API 调用
return {"temperature": 25, "condition": "sunny"}
看起来还不错?但当你写出第 2 个、第 3 个 Skill 时,你会发现 __init__、can_handle、execute 里的参数校验、异常捕获、日志记录、返回格式——这些代码几乎一模一样。
3. 重复代码的统计与分析
我整理了 10 个 Skill 的代码,统计了重复部分:
| 重复模块 | 每个 Skill 行数 | 10 个 Skill 总行数 |
|---|---|---|
__init__ 初始化 |
3 行 | 30 行 |
can_handle 意图匹配 |
3 行 | 30 行 |
execute 参数校验 |
5 行 | 50 行 |
execute try-except 包裹 |
6 行 | 60 行 |
| 日志记录 | 4 行 | 40 行 |
| 结果格式化 | 3 行 | 30 行 |
| 其他(类型注解、文档字符串等) | 6 行 | 60 行 |
| 合计 | 30 行 | 300 行 |
核心业务逻辑(真正的 API 调用、数据处理)只占每个 Skill 的 20% 左右。80% 的代码都是“基础设施”。
4. 重构方案:抽象基类 + 装饰器
我的目标是:让每个 Skill 只写业务逻辑,其余全部由框架自动完成。
4.1 定义抽象基类
首先,创建一个 BaseSkill,把公共逻辑全部收进来:
import logging
from abc import ABC, abstractmethod
from functools import wraps
class BaseSkill(ABC):
def __init__(self, name: str = None):
self.name = name or self.__class__.__name__.lower()
self.logger = logging.getLogger(self.name)
def can_handle(self, intent: str) -> bool:
# 默认通过类名匹配,子类可覆盖
return intent == self.name
def execute(self, params: dict) -> dict:
# 统一的执行入口,自动包裹校验和异常处理
try:
self._validate(params)
result = self._execute(params)
return self._format_result(result)
except Exception as e:
self.logger.error(f"Execution failed: {e}", exc_info=True)
return {"success": False, "error": str(e)}
def _validate(self, params: dict):
"""子类可重写,默认不做校验"""
pass
@abstractmethod
def _execute(self, params: dict) -> dict:
"""子类必须实现:真正的业务逻辑"""
pass
def _format_result(self, result: dict) -> dict:
"""子类可重写,默认包装为成功响应"""
return {"success": True, "data": result}
4.2 用装饰器处理参数校验
对于需要参数校验的 Skill,我写了一个装饰器,避免在每个 _execute 里写 if-else:
def require_params(*required_keys):
def decorator(func):
@wraps(func)
def wrapper(self, params: dict, *args, **kwargs):
missing = [k for k in required_keys if k not in params]
if missing:
raise ValueError(f"Missing required parameters: {missing}")
return func(self, params, *args, **kwargs)
return wrapper
return decorator
4.3 重构后的 Skill 长什么样
现在,一个 Skill 只需要 3 行核心代码:
class WeatherSkill(BaseSkill):
@require_params("city")
def _execute(self, params: dict) -> dict:
return {"temperature": 25, "condition": "sunny"}
另一个 Skill 也类似:
class EmailSkill(BaseSkill):
def __init__(self):
super().__init__(name="send_email")
@require_params("to", "subject", "body")
def _execute(self, params: dict) -> dict:
# 真正的发邮件逻辑
return {"message_id": "12345", "status": "sent"}
5. 效果对比
重构前后对比:
| 指标 | 重构前 | 重构后 |
|---|---|---|
| 每个 Skill 平均代码行数 | 30 行 | 3 行 |
| 10 个 Skill 总行数 | 300 行 | 30 行 |
| 公共逻辑修改 | 改 10 个文件 | 改 1 个基类 |
| 新增一个 Skill 成本 | 30 行模板 + 业务 | 3 行业务 |
| 单元测试覆盖 | 难(模板代码干扰) | 易(只测业务) |
更重要的是,可维护性得到了质的提升。后来我又加了 5 个 Skill,每个只花了 5 分钟写业务逻辑,再也不用复制粘贴了。
6. 总结与思考
这次重构让我深刻体会到:
- 重复代码是万恶之源。当你在写第 3 个相似的类/函数时,就该停下来思考抽象方案了。
- 抽象基类 + 装饰器是消除 Agent Skill 模板代码的绝佳组合。基类负责骨架,装饰器负责横切关注点(参数校验、权限检查等)。
- 不要过度抽象。我的原则是:只有出现 3 次以上重复时,才考虑抽象。过早抽象反而会增加理解成本。
- 代码行数不是唯一目标。虽然从 300 行压到了 30 行,但更重要的是代码的可读性和可维护性。30 行清晰的代码,远胜 300 行重复的代码。
如果你也在写 Agent 或类似的插件化系统,不妨试试这个模式。你会发现,写代码可以是一件很清爽的事。
更多推荐

所有评论(0)