作者视角:资深架构师
目标读者:技术负责人、架构师、高级开发者
文档定位:从工程化、架构设计、最佳实践角度,提供 Agent Skills 落地的深度指南
传统的 Tool Calling(工具调用) 模式存在以下问题:
Agent Skills(智能体技能) 代表了从"工具调用"到"技能工程"的范式转变:
Agent Skills 的核心价值在于将系统提示词(System Prompt)、工具集(Tools)与领域知识(Context)封装成一个独立的专家模块,使得大模型能够从单纯的"响应者"转变为具备特定职业能力的"专家"。
关键指标:
Agent Skills 的组织应参考 DDD(领域驱动设计) 模式,按业务边界进行划分。
┌─────────────────────────────────────────┐
│ 触发器层 (Trigger Layer) │
│ - 协议转换(MCP / HTTP / WebSocket) │
│ - 意图识别与路由 │
│ - 权限校验 │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 应用层 (Application Layer) │
│ - 技能编排与组合 │
│ - 上下文管理 │
│ - 工作流引擎 │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 领域层 (Domain Layer) │
│ - 技能核心逻辑 │
│ - 业务模型定义 │
│ - 领域规则 │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ 基础设施层 (Infrastructure Layer) │
│ - 工具执行引擎 │
│ - 数据持久化 │
│ - 外部服务集成 │
└─────────────────────────────────────────┘
按技能类别划分限界上下文,例如:
每个限界上下文内部:
每个技能必须包含完整的定义,指导大模型何时以及如何调用。
---
name: maven-search # 唯一标识符(kebab-case)
description: | # 触发机制(关键!)
Provides comprehensive guidance for searching and retrieving
Maven components from Maven Central Repository.
Use when the user needs to find, verify, or retrieve Maven
dependencies, check component versions, analyze dependency
trees, or work with Maven coordinates.
license: Complete terms in LICENSE.txt
---
关键设计原则:
描述即触发器:description 字段是技能的主要触发机制,必须包含:
渐进式披露:采用三层加载机制管理上下文:
标准化的目录结构确保技能的可维护性和可扩展性:
skill-name/
├── SKILL.md # 必需:技能定义和说明
├── LICENSE.txt # 必需:许可证文件
├── examples/ # 可选:使用示例
│ ├── example-1.md
│ └── example-2.md
├── api/ # 可选:API 参考文档
│ └── api-reference.md
├── reference/ # 可选:领域知识库
│ ├── standards.md
│ └── best-practices.md
├── templates/ # 可选:输出模板
│ └── template-1.md
├── scripts/ # 可选:可执行脚本
│ └── script.py
└── assets/ # 可选:资源文件(不加载到上下文)
└── logo.png
设计原则:
reference/ 或 examples/ 中用户请求
↓
意图识别(基于 description)
↓
技能选择与加载
↓
参数校验(JSON Schema)
↓
技能执行(Scripts / Tools)
↓
结果格式化
↓
流式返回(SSE)
关键特性:
实时进度推送:通过 progress 事件反馈当前执行步骤
{
"type": "progress",
"step": "正在连接 Maven 仓库",
"progress": 30
}
思考过程展示(Reasoning):在技能执行前,允许 Agent 输出其选择该技能的推理路径
{
"type": "reasoning",
"skill": "maven-search",
"reason": "用户需要查找 Maven 依赖,maven-search 技能专门处理此类请求"
}
通过具体用例明确技能的功能边界:
示例:Maven 搜索技能
用例 1:用户说"查找 Spring Boot 的最新版本"
用例 2:用户说"帮我添加 Guava 依赖到 pom.xml"
分析每个用例,识别可复用的资源:
| 资源类型 | 何时包含 | 示例 |
|---|---|---|
| Scripts | 需要确定性执行或重复编写相同代码 | scripts/search_maven.py |
| References | 需要领域知识或 API 文档 | reference/maven-coordinates.md |
| Assets | 需要模板或资源文件 | assets/pom-template.xml |
| Examples | 需要展示使用模式 | examples/search-by-name.md |
使用标准化工具初始化技能结构:
python scripts/init_skill.py maven-search --path ./skills/
生成的内容:
原则:
示例:
#!/usr/bin/env python3
"""
Maven Central Repository 搜索脚本
用法:
python search_maven.py <query> [--limit N]
参数:
query: 搜索关键词(groupId 或 artifactId)
--limit: 返回结果数量限制(默认 10)
"""
import sys
import requests
import json
def search_maven(query, limit=10):
"""搜索 Maven Central Repository"""
url = "https://search.maven.org/solrsearch/select"
params = {
"q": query,
"rows": limit,
"wt": "json"
}
response = requests.get(url, params=params)
return response.json()
if __name__ == "__main__":
query = sys.argv[1]
limit = int(sys.argv[2]) if len(sys.argv) > 2 else 10
results = search_maven(query, limit)
print(json.dumps(results, indent=2))
原则:
示例结构:
# Maven 坐标参考
## 坐标格式
Maven 坐标由三部分组成:
- `groupId`:组织标识(如 `com.google.guava`)
- `artifactId`:项目标识(如 `guava`)
- `version`:版本号(如 `33.0.0`)
## 版本规范
- **发布版本**:遵循语义化版本(SemVer)
- **快照版本**:以 `-SNAPSHOT` 结尾
- **最新版本**:通过 `maven-metadata.xml` 查询
## 常见问题
### Q: 如何查找最新版本?
A: 查询 `{groupId}/{artifactId}/maven-metadata.xml`,解析 `<latest>` 标签
原则:
示例结构:
# 按名称搜索 Maven 组件
## 使用场景
当用户提供组件名称或关键词时,使用此方法搜索。
## 执行步骤
1. 解析用户输入,提取搜索关键词
2. 调用 Maven Central Search API
3. 解析返回结果,提取关键信息
4. 格式化输出,包含坐标和最新版本
## 示例
**输入**:查找 Spring Boot
**输出**:
xml
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>3.2.0</version>
## 参考
- [Maven 坐标参考](reference/maven-coordinates.md)
- [Maven Central API](api/maven-central-api.md)
关键要点:
description 是核心:这是技能的唯一触发机制
简洁而全面:在 100 词左右描述清楚技能的核心功能和触发条件
好的示例:
description: |
Provides comprehensive guidance for searching and retrieving Maven
components from Maven Central Repository. Use when the user needs
to find, verify, or retrieve Maven dependencies, check component
versions, analyze dependency trees, or work with Maven coordinates.
不好的示例:
description: Maven search tool # 太简单,缺乏触发条件
结构建议:
# Skill Title
## When to use this skill
**ALWAYS use this skill when the user mentions:**
- [明确的触发场景 1]
- [明确的触发场景 2]
**Trigger phrases include:**
- [关键词列表]
## How to use this skill
**CRITICAL: [关键触发条件]**
1. **Identify the task type** from the user's request
2. **Load the appropriate example** from `examples/` directory
3. **Follow the specific instructions** in that example
4. **Execute the workflow** using scripts or tools
5. **Present the results** in a clear format
## API Endpoints and Usage
[API 文档链接和说明]
## Best Practices
[最佳实践和注意事项]
## Keywords
**English keywords:**
[关键词列表]
**Chinese keywords:**
[中文关键词列表]
编写原则:
python scripts/package_skill.py ./skills/maven-search
验证内容:
当多个技能(Multi-Expert Collaboration)被同时激活时,Token 消耗会急剧增加:
100 个技能 × 5k tokens = 500k tokens(超出大多数模型的上下文窗口)
1. 动态注入(Dynamic Injection)
仅在检测到特定关键词或意图时才注入完整技能 Prompt:
def should_load_skill(user_input, skill_metadata):
"""判断是否需要加载完整技能"""
keywords = extract_keywords(user_input)
skill_keywords = skill_metadata.get('keywords', [])
# 简单匹配:关键词重叠度
overlap = len(set(keywords) & set(skill_keywords))
threshold = skill_metadata.get('trigger_threshold', 2)
return overlap >= threshold
2. 摘要机制(Summarization)
对长工具输出进行自动摘要:
def summarize_tool_output(output, max_tokens=500):
"""摘要工具输出,避免上下文溢出"""
if estimate_tokens(output) <= max_tokens:
return output
# 提取关键信息
summary = extract_key_points(output)
return f"{summary}\n\n[完整输出已截断,共 {len(output)} 字符]"
3. 分层加载(Progressive Loading)
class SkillLoader:
def load_skill(self, skill_name, level='metadata'):
"""按需加载技能内容"""
if level == 'metadata':
return self.load_metadata(skill_name)
elif level == 'body':
return self.load_body(skill_name)
elif level == 'resources':
return self.load_resources(skill_name, resource_type)
用户请求:"帮我创建一个 Spring Boot 项目,并添加 Guava 依赖"
这需要多个技能协作:
spring-boot-project-creator:创建项目结构maven-search:查找 Guava 依赖pom-xml-editor:编辑 pom.xml1. 技能编排(Skill Orchestration)
class SkillOrchestrator:
def execute_workflow(self, user_request):
"""编排多个技能完成复杂任务"""
# 1. 意图识别
intents = self.intent_recognizer.recognize(user_request)
# 2. 技能选择
skills = [self.skill_registry.get(skill_name)
for skill_name in intents.required_skills]
# 3. 执行顺序规划
execution_plan = self.planner.plan(skills, intents)
# 4. 顺序执行
context = {}
for step in execution_plan:
result = step.skill.execute(step.input, context)
context.update(result)
return context
2. 标准化接口(Standardized Interface)
所有技能必须遵循统一的输入输出格式:
class SkillInterface:
"""技能标准接口"""
def execute(self, input: dict, context: dict) -> dict:
"""
执行技能
Args:
input: 用户输入(JSON Schema 验证)
context: 上下文信息(来自其他技能)
Returns:
{
"status": "success" | "error",
"data": {...},
"next_skills": [...], # 建议的下一个技能
"context_updates": {...} # 更新上下文
}
"""
pass
3. 上下文传递(Context Passing)
class ContextManager:
def __init__(self):
self.context = {}
def update(self, skill_name, output):
"""更新上下文"""
self.context[f"{skill_name}_output"] = output
self.context["last_skill"] = skill_name
def get_relevant_context(self, skill_name):
"""获取相关上下文"""
# 只返回与当前技能相关的上下文
relevant = {}
for key, value in self.context.items():
if self.is_relevant(key, skill_name):
relevant[key] = value
return relevant
如何从 100+ 技能中快速找到最相关的技能?
1. 基于描述的语义匹配
class SkillRouter:
def __init__(self, skill_registry):
self.skill_registry = skill_registry
self.embeddings = self.load_embeddings()
def find_relevant_skills(self, user_input, top_k=3):
"""找到最相关的技能"""
# 1. 生成用户输入的嵌入向量
input_embedding = self.embeddings.encode(user_input)
# 2. 计算与所有技能描述的相似度
similarities = []
for skill in self.skill_registry.skills:
skill_embedding = self.embeddings.encode(skill.description)
similarity = cosine_similarity(input_embedding, skill_embedding)
similarities.append((skill, similarity))
# 3. 返回 Top-K
similarities.sort(key=lambda x: x[1], reverse=True)
return [skill for skill, _ in similarities[:top_k]]
2. 关键词匹配(快速路径)
def quick_match(user_input, skill):
"""快速关键词匹配"""
user_keywords = extract_keywords(user_input.lower())
skill_keywords = skill.metadata.get('keywords', [])
# 计算关键词重叠度
overlap = len(set(user_keywords) & set(skill_keywords))
total_keywords = len(set(user_keywords) | set(skill_keywords))
return overlap / total_keywords if total_keywords > 0 else 0
3. 混合策略
def route_to_skill(user_input):
"""混合路由策略"""
# 1. 快速路径:关键词匹配
quick_matches = [s for s in skills if quick_match(user_input, s) > 0.5]
if len(quick_matches) == 1:
return quick_matches[0]
# 2. 慢速路径:语义匹配
semantic_matches = find_relevant_skills(user_input, top_k=3)
# 3. 合并结果
candidates = merge_and_rank(quick_matches, semantic_matches)
# 4. 如果多个候选,询问用户或选择置信度最高的
if len(candidates) > 1:
return ask_user_or_select_best(candidates)
return candidates[0]
方法:
输出:
步骤:
定义技能边界
设计工作流
确定资源需求
开发:
测试:
按技能种类组织,而非按岗位:
marketplace.json
├── development-skills(开发技能)
├── document-skills(文档技能)
├── architecture-skills(架构技能)
├── testing-skills(测试技能)
└── ...
优势:
{
"name": "full-stack-skills",
"metadata": {
"version": "0.0.1",
"description": "...",
"skills_count": 171
},
"plugins": [
{
"name": "development-skills",
"version": "1.0.0",
"skills": [...]
}
]
}
策略:
技能打包为 .skill 文件(实际是 ZIP 文件):
maven-search.skill
├── SKILL.md
├── LICENSE.txt
├── examples/
├── api/
└── reference/
# Claude Code / Cursor
/plugin install development-skills@full-stack-skills
# 或指定版本
/plugin install development-skills@full-stack-skills@1.0.0
当技能库达到上百个时,加载所有技能的元数据也会消耗大量 Token。
按需加载技能元数据:
class LazySkillRegistry:
def __init__(self, marketplace_path):
self.marketplace_path = marketplace_path
self._skills_cache = {}
self._metadata_cache = {}
def get_skill_metadata(self, skill_name):
"""延迟加载技能元数据"""
if skill_name not in self._metadata_cache:
skill_path = self._resolve_skill_path(skill_name)
metadata = self._load_metadata(skill_path)
self._metadata_cache[skill_name] = metadata
return self._metadata_cache[skill_name]
def get_skill(self, skill_name):
"""延迟加载完整技能"""
if skill_name not in self._skills_cache:
skill_path = self._resolve_skill_path(skill_name)
skill = self._load_skill(skill_path)
self._skills_cache[skill_name] = skill
return self._skills_cache[skill_name]
对于幂等性的查询技能(如检索 Maven 坐标),建立结果缓存可以:
from functools import lru_cache
import hashlib
import json
class SkillCache:
def __init__(self, ttl=3600):
self.cache = {}
self.ttl = ttl
def get_cache_key(self, skill_name, input_params):
"""生成缓存键"""
key_data = {
"skill": skill_name,
"input": input_params
}
key_str = json.dumps(key_data, sort_keys=True)
return hashlib.md5(key_str.encode()).hexdigest()
def get(self, skill_name, input_params):
"""获取缓存结果"""
cache_key = self.get_cache_key(skill_name, input_params)
if cache_key in self.cache:
entry = self.cache[cache_key]
if time.time() - entry['timestamp'] < self.ttl:
return entry['result']
else:
del self.cache[cache_key]
return None
def set(self, skill_name, input_params, result):
"""设置缓存"""
cache_key = self.get_cache_key(skill_name, input_params)
self.cache[cache_key] = {
"result": result,
"timestamp": time.time()
}
记录每个技能的调用情况:
class SkillMetrics:
def __init__(self):
self.metrics = {
"call_count": defaultdict(int),
"success_count": defaultdict(int),
"error_count": defaultdict(int),
"avg_duration": defaultdict(list),
"token_usage": defaultdict(int)
}
def record_call(self, skill_name, duration, tokens, success):
"""记录技能调用"""
self.metrics["call_count"][skill_name] += 1
if success:
self.metrics["success_count"][skill_name] += 1
else:
self.metrics["error_count"][skill_name] += 1
self.metrics["avg_duration"][skill_name].append(duration)
self.metrics["token_usage"][skill_name] += tokens
def get_stats(self, skill_name):
"""获取技能统计"""
return {
"call_count": self.metrics["call_count"][skill_name],
"success_rate": (
self.metrics["success_count"][skill_name] /
self.metrics["call_count"][skill_name]
if self.metrics["call_count"][skill_name] > 0 else 0
),
"avg_duration": np.mean(self.metrics["avg_duration"][skill_name]),
"total_tokens": self.metrics["token_usage"][skill_name]
}
通过日志分析不断优化技能描述:
class SkillAnalyzer:
def analyze_failed_calls(self, skill_name):
"""分析失败的调用"""
failed_calls = self.get_failed_calls(skill_name)
# 分析失败原因
reasons = {
"wrong_skill_selected": 0,
"parameter_error": 0,
"execution_error": 0
}
for call in failed_calls:
if call.error_type == "wrong_skill":
reasons["wrong_skill_selected"] += 1
elif call.error_type == "parameter":
reasons["parameter_error"] += 1
else:
reasons["execution_error"] += 1
# 如果错误主要是"技能选择错误",建议优化 description
if reasons["wrong_skill_selected"] > len(failed_calls) * 0.5:
return {
"suggestion": "优化技能描述,增加更明确的触发条件",
"confidence": "high"
}
Agent Skills 往往拥有读写文件或访问网络的能力,必须建立强隔离机制。
1. 路径白名单
class PathValidator:
def __init__(self):
self.allowed_paths = [
"/tmp/",
"/workspace/",
# 禁止访问系统目录
]
self.blocked_paths = [
"/etc/",
"/sys/",
"/proc/",
"/root/",
]
def validate_path(self, path):
"""验证路径是否允许访问"""
# 检查是否在阻止列表中
for blocked in self.blocked_paths:
if path.startswith(blocked):
raise PermissionError(f"Access to {path} is blocked")
# 检查是否在白名单中
for allowed in self.allowed_paths:
if path.startswith(allowed):
return True
raise PermissionError(f"Access to {path} is not allowed")
2. 网络访问控制
class NetworkValidator:
def __init__(self):
self.allowed_domains = [
"repo1.maven.org",
"api.github.com",
# 只允许访问可信域名
]
def validate_url(self, url):
"""验证 URL 是否允许访问"""
from urllib.parse import urlparse
parsed = urlparse(url)
if parsed.netloc not in self.allowed_domains:
raise PermissionError(f"Access to {parsed.netloc} is not allowed")
return True
使用 JSON Schema 严格校验大模型生成的参数:
from jsonschema import validate, ValidationError
class SkillInputValidator:
def __init__(self, skill_schema):
self.schema = skill_schema
def validate(self, input_data):
"""验证输入数据"""
try:
validate(instance=input_data, schema=self.schema)
return True, None
except ValidationError as e:
return False, str(e)
示例 Schema:
{
"type": "object",
"properties": {
"groupId": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]*([.][a-z][a-z0-9_]*)*$"
},
"artifactId": {
"type": "string",
"minLength": 1,
"maxLength": 100
},
"version": {
"type": "string",
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+(-SNAPSHOT)?$"
}
},
"required": ["groupId", "artifactId"]
}
记录所有技能调用的参数与响应,用于行为回溯:
class AuditLogger:
def log_skill_call(self, skill_name, user_id, input_params, output, success):
"""记录技能调用"""
log_entry = {
"timestamp": datetime.now().isoformat(),
"skill": skill_name,
"user": user_id,
"input": input_params,
"output": output if success else None,
"error": None if success else str(output),
"success": success
}
# 写入审计日志(不可篡改)
self.write_to_audit_log(log_entry)
对技能的输出内容进行合规性扫描:
class ContentFilter:
def __init__(self):
self.sensitive_patterns = [
r"password\s*[:=]\s*\S+",
r"api[_-]?key\s*[:=]\s*\S+",
r"secret\s*[:=]\s*\S+",
]
def filter_output(self, content):
"""过滤敏感内容"""
for pattern in self.sensitive_patterns:
content = re.sub(pattern, "[REDACTED]", content, flags=re.IGNORECASE)
return content
模拟不同用户提问,测试技能是否能被正确触发:
class RecallTester:
def __init__(self, skill, test_cases):
self.skill = skill
self.test_cases = test_cases
def test_recall(self):
"""测试召回率"""
results = {
"total": len(self.test_cases),
"correct": 0,
"false_positives": 0,
"false_negatives": 0
}
for test_case in self.test_cases:
user_input = test_case["input"]
expected_skill = test_case["expected_skill"]
# 模拟技能选择
selected_skill = self.route_to_skill(user_input)
if selected_skill == expected_skill:
results["correct"] += 1
elif selected_skill is None:
results["false_negatives"] += 1
else:
results["false_positives"] += 1
recall = results["correct"] / results["total"]
return recall, results
测试技能被触发时,是否真的应该被触发:
class PrecisionTester:
def test_precision(self, skill_name, negative_cases):
"""测试精确率"""
false_positives = 0
for case in negative_cases:
# 这些用例不应该触发该技能
if self.should_trigger(skill_name, case):
false_positives += 1
precision = 1 - (false_positives / len(negative_cases))
return precision
测试不同版本的技能描述,选择效果最好的:
class ABTester:
def __init__(self, skill_variants):
self.variants = skill_variants
self.results = {variant: [] for variant in skill_variants}
def test_variant(self, variant, user_input):
"""测试技能变体"""
# 执行技能并记录结果
result = self.execute_skill(variant, user_input)
self.results[variant].append({
"input": user_input,
"result": result,
"timestamp": time.time()
})
def get_best_variant(self):
"""获取最佳变体"""
# 基于成功率、响应时间等指标选择最佳变体
scores = {}
for variant, results in self.results.items():
success_rate = sum(1 for r in results if r["result"]["success"]) / len(results)
avg_duration = np.mean([r["result"]["duration"] for r in results])
scores[variant] = success_rate / avg_duration
return max(scores, key=scores.get)
用户反馈
↓
问题分析
↓
技能优化(描述、示例、资源)
↓
A/B 测试
↓
部署新版本
↓
监控指标
↓
收集反馈(循环)
问题:开发者经常需要查找 Maven 依赖的坐标,但:
目标:将 3 分钟的手动过程缩短为 5 秒钟的对话
| 用例 | 用户输入 | 预期输出 |
|---|---|---|
| 按名称搜索 | "查找 Spring Boot" | Spring Boot 的 Maven 坐标 |
| 按坐标查询 | "Guava 的最新版本是什么?" | 最新版本号和坐标 |
| 生成依赖代码 | "帮我添加 Lombok 到 pom.xml" | XML 代码块 |
scripts/search_maven.py(搜索逻辑)reference/maven-coordinates.md(坐标规范)api/maven-central-api.md(API 文档)Frontmatter:
---
name: maven-search
description: |
Provides comprehensive guidance for searching and retrieving Maven
components from Maven Central Repository. Use when the user needs
to find, verify, or retrieve Maven dependencies, check component
versions, analyze dependency trees, or work with Maven coordinates.
license: Complete terms in LICENSE.txt
---
Body 结构:
#!/usr/bin/env python3
"""Maven Central Repository 搜索脚本"""
import requests
import json
import sys
def search_by_name(query, limit=10):
"""按名称搜索"""
url = "https://search.maven.org/solrsearch/select"
params = {"q": query, "rows": limit, "wt": "json"}
response = requests.get(url, params=params)
return response.json()
def get_latest_version(group_id, artifact_id):
"""获取最新版本"""
metadata_url = f"https://repo1.maven.org/maven2/{group_id.replace('.', '/')}/{artifact_id}/maven-metadata.xml"
# 解析 XML,提取 latest 标签
# ...
指标:
改进点:
文档版本:1.0.0
最后更新:2024-12-19
维护者:Full-Stack-Skills Team