共计 2970 个字符,预计需要花费 8 分钟才能阅读完成。
一、AI 技能管理的现实痛点
在开发 AI 助手类项目时,我经常遇到这样的困扰:随着代码量增长,不同开发者提交的技能函数开始出现功能重叠。更麻烦的是,当需要回答 ”Claude 现在支持哪些能力 ” 时,要么靠人工维护的过时文档,要么得全局搜索代码注释——这显然不是可持续的方案。

- 技能重复开发:团队中多人分别实现了天气查询功能,但参数规范不一致
- 版本迭代失控:v1.2 移除的『股票查询』技能在文档中仍被标记为可用
- 能力边界模糊:无法快速确认当前版本是否支持『多轮对话上下文记忆』这类复合功能
二、技术方案选型对比
传统方案是用 wiki 或 README 手工记录技能列表,但实践发现三个致命缺陷:
- 更新延迟:代码变更后文档常忘记同步
- 维度单一:难以自动提取输入 / 输出类型等结构化信息
- 缺乏验证:无法通过 CI 检查文档与实现的一致性
相比之下,基于 AST(抽象语法树)的自动化分析方案优势明显:
- 实时性:直接分析运行时代码,与实现强一致
- 可扩展:能从函数签名、装饰器、类型注解等多维度提取元数据
- 可编程:支持自定义规则验证(如检测敏感 API 调用)
三、核心实现详解
3.1 基础架构设计
整个系统分为三个核心组件:
- AST 解析引擎:将源代码转换为可遍历的语法树
- 技能提取器 :识别
@skill装饰的函数并分析其元数据 - 矩阵生成器:输出结构化技能描述(支持 Markdown/JSON)
3.2 关键代码实现
首先定义标准技能装饰器,这是后续分析的锚点:
from typing import Any, Callable
def skill(
category: str,
desc: str,
input_model: type = None,
output_model: type = None
) -> Callable[[Callable], Any]:
""" 标记一个函数作为 Claude 的技能单元
Args:
category: 技能分类标签,如 "weather"
desc: 人类可读的功能描述
input_model: 输入参数的类型注解类
output_model: 返回值的类型注解类
"""
def decorator(func: Callable) -> Any:
func.__skill_meta__ = {
'category': category,
'description': desc,
'input': input_model,
'output': output_model,
'source_file': inspect.getsourcefile(func)
}
return func
return decorator
接着是 AST 解析器的核心遍历逻辑(Python 版):
import ast
class SkillVisitor(ast.NodeVisitor):
def __init__(self):
self.skills = []
def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
# 检查是否存在 @skill 装饰器
for decorator in node.decorator_list:
if isinstance(decorator, ast.Call) and \
isinstance(decorator.func, ast.Name) and \
decorator.func.id == 'skill':
# 提取装饰器参数
meta = {
'name': node.name,
'args': [arg.arg for arg in node.args.args],
'doc': ast.get_docstring(node)
}
# 解析装饰器调用参数
for keyword in decorator.keywords:
if keyword.arg == 'category':
meta['category'] = ast.literal_eval(keyword.value)
elif keyword.arg == 'desc':
meta['description'] = ast.literal_eval(keyword.value)
self.skills.append(meta)
3.3 技能分类算法
结合规则引擎和 NLP 实现智能分类:
- 关键词提取:从 docstring 中提取 TF-IDF 权重最高的名词短语
- 意图识别:使用预定义的分类规则(如包含『查询』『获取』等动词归入查询类)
- 类型推导:通过函数参数的 Type Annotation 自动生成接口规范
from sklearn.feature_extraction.text import TfidfVectorizer
class SkillClassifier:
def __init__(self):
self.vectorizer = TfidfVectorizer(ngram_range=(1, 2))
def fit(self, docs: List[str]):
"""训练分类模型"""
self.vectorizer.fit(docs)
def predict(self, doc: str) -> str:
"""预测技能类别"""
tfidf = self.vectorizer.transform([doc])
# ... 实现分类逻辑
return predicted_category
四、生产环境实践要点
4.1 动态技能处理
对于通过 exec() 动态生成的技能,需要特殊处理:
- 在装饰器层添加
@dynamic_skill标记 - 运行时通过 inspect 模块获取函数对象
- 显式调用 AST 解析器分析动态代码字符串
def load_dynamic_skill(code_str: str):
"""加载动态生成的技能"""
# 1. 执行代码获取函数对象
local_vars = {}
exec(code_str, globals(), local_vars)
# 2. 找出被 @skill 装饰的函数
for obj in local_vars.values():
if callable(obj) and hasattr(obj, '__skill_meta__'):
# 3. 反编译源代码进行 AST 分析
src = inspect.getsource(obj)
node = ast.parse(src)
visitor = SkillVisitor()
visitor.visit(node)
return visitor.skills[0]
4.2 安全防护机制
必须内置以下安全检查:
- 敏感信息扫描:检测 docstring 中是否包含 API 密钥模式
- 危险调用检测 :阻止
os.system等高风险调用被标记为技能 - 权限分级 :对涉及数据修改的技能添加
@requires_auth装饰器
五、避坑指南
- 循环引用问题
- 避免在技能函数内导入同模块的其他技能
-
使用字符串类型注解(
'ModuleType')延迟加载 -
多语言混合场景
- 对 TypeScript 代码需要切换为 ts-morph 解析器
-
统一不同语言的技能 ID 命名规范
-
性能优化
- 对未修改的文件使用缓存的分析结果
- 增量更新技能矩阵(通过 git hook 触发)
六、延伸思考
这套技能矩阵的价值不仅在于文档生成,更可以:
- 在 CI 流水线中自动验证技能测试覆盖率
- 根据调用日志自动标记低使用率技能建议下线
- 生成技能依赖图用于架构优化
一个开放问题:如何设计自动化测试策略,使得当新增技能与现有技能参数规范冲突时,能立即在 PR 检查阶段被发现?这需要将技能矩阵与契约测试(Pact)框架深度集成。
正文完
