AI Agent Skill 技术详解

什么是 Skill?

在 AI Agent 体系中,Skill(技能) 是对 Agent 能力的一种封装和组织方式。它比单个工具(Tool)更高层——一个 Skill 往往包含一组相关的功能,加上对应的提示词模板、输入输出规范,是可以被复用、可以被组合的能力单元。

一句话理解:Tool 是原子操作,Skill 是能力模块。

Agent
├── Skill: 信息处理
│ ├── Tool: 网页搜索
│ ├── Tool: 内容摘要
│ └── Tool: 实体提取

├── Skill: 代码能力
│ ├── Tool: 代码生成
│ ├── Tool: 代码执行
│ └── Tool: 错误调试

└── Skill: 写作能力
├── Tool: 草稿生成
├── Tool: 风格改写
└── Tool: 语法检查

Semantic Kernel 中的 Skill

微软的 Semantic Kernel 是最早系统化提出 Skill 概念的框架,它把技能分为两类:

1. Semantic Skill(语义技能)

纯粹由提示词驱动的技能,通过 LLM 完成任务:

my_skills/
└── WritingSkill/
├── Summarize/
│ ├── skprompt.txt ← 提示词模板
│ └── config.json ← 参数配置
└── Translate/
├── skprompt.txt
└── config.json

skprompt.txt 示例:

请将以下文本翻译成{{$language}},保持原文的语气和风格:

{{$input}}

翻译结果:

config.json 示例:

{
"schema": 1,
"description": "将文本翻译为指定语言",
"type": "completion",
"completion": {
"max_tokens": 1000,
"temperature": 0.3
},
"input": {
"parameters": [
{"name": "input", "description": "要翻译的文本"},
{"name": "language", "description": "目标语言", "defaultValue": "中文"}
]
}
}

使用方式(Python):

import semantic_kernel as sk

kernel = sk.Kernel()
kernel.add_chat_service("gpt4", AzureChatCompletion(...))

# 加载技能目录
writing_skill = kernel.import_semantic_skill_from_directory("my_skills", "WritingSkill")

# 调用翻译技能
result = await kernel.run_async(
writing_skill["Translate"],
input_str="Hello, world!",
input_vars={"language": "日语"}
)
print(result) # こんにちは、世界!

2. Native Skill(原生技能)

用代码实现的技能,适合需要精确计算或调用外部 API 的场景:

from semantic_kernel.skill_definition import sk_function, sk_function_context_parameter

class MathSkill:
@sk_function(
description="计算两个数字的和",
name="Add"
)
@sk_function_context_parameter(name="number1", description="第一个数字")
@sk_function_context_parameter(name="number2", description="第二个数字")
def add(self, context: SKContext) -> str:
a = float(context["number1"])
b = float(context["number2"])
return str(a + b)

@sk_function(
description="计算复利",
name="CompoundInterest"
)
def compound_interest(self, context: SKContext) -> str:
principal = float(context["principal"])
rate = float(context["rate"])
years = int(context["years"])
result = principal * (1 + rate) ** years
return f"{result:.2f}"

# 注册技能
kernel.import_skill(MathSkill(), "Math")

AutoGen 中的 Skill

微软的 AutoGen 框架里,Skill 以可执行的 Python 函数形式存在,Agent 可以动态学习和使用新技能:

from autogen import AssistantAgent, UserProxyAgent

# 定义一个技能函数
def plot_stock_price(stock_symbol: str, start_date: str, end_date: str):
"""
绘制股票价格走势图

Args:
stock_symbol: 股票代码,如 'AAPL'
start_date: 开始日期,格式 'YYYY-MM-DD'
end_date: 结束日期,格式 'YYYY-MM-DD'
"""
import yfinance as yf
import matplotlib.pyplot as plt

data = yf.download(stock_symbol, start=start_date, end=end_date)
plt.figure(figsize=(12, 6))
plt.plot(data['Close'])
plt.title(f"{stock_symbol} 股价走势")
plt.savefig(f"{stock_symbol}_price.png")
return f"图表已保存为 {stock_symbol}_price.png"

# 让 Agent 学习这个技能
assistant = AssistantAgent("analyst")
assistant.register_function(
function_map={"plot_stock_price": plot_stock_price}
)

AutoGen 还支持 Agent 自己生成代码来创造新技能,并存入技能库供后续复用,这让系统具备了一定的自我进化能力。


CrewAI 中的 Skill(Tool)

在 CrewAI 中,Skill 的概念通过 Tool 来实现,每个 Agent 被赋予一组工具,形成专属能力集:

from crewai import Agent, Task, Crew
from crewai_tools import SerperDevTool, WebsiteSearchTool

# 搜索专家:拥有搜索技能
researcher = Agent(
role="市场调研专家",
goal="收集和分析市场信息",
backstory="你是一位经验丰富的市场分析师",
tools=[
SerperDevTool(), # 搜索技能
WebsiteSearchTool() # 网页读取技能
],
verbose=True
)

# 写作专家:拥有写作技能(无搜索工具)
writer = Agent(
role="内容撰写专家",
goal="基于研究成果撰写高质量报告",
backstory="你是一位专业的商业报告撰写人",
tools=[], # 只靠自身语言能力
verbose=True
)

自定义工具(技能):

from crewai.tools import BaseTool
from pydantic import BaseModel, Field

class CompanyDataInput(BaseModel):
company_name: str = Field(description="公司名称")

class CompanyInfoTool(BaseTool):
name: str = "公司信息查询"
description: str = "查询指定公司的基本信息、营收和市值数据"
args_schema: type[BaseModel] = CompanyDataInput

def _run(self, company_name: str) -> str:
# 实际实现中调用数据 API
return f"{company_name}:成立于2010年,2025年营收500亿,市值3000亿"

researcher.tools.append(CompanyInfoTool())

Claude Code 的 Skill 系统

Claude Code 内置了一套成熟的 Skill 机制,直接在日常 AI 编程助手里落地实现。深入研究它的技术细节,对理解 Agent Skill 设计很有参考价值。

Skill 的本质:上下文注入

Skill 的核心原理是提示词注入(Prompt Injection)——用户输入 /skill-name 时,系统把预先写好的指令内容作为一条消息注入到对话上下文,Claude 就会按这段指令行事。

用户输入:/commit

系统读取 commit/SKILL.md 文件

渲染变量、执行 Shell 预处理命令

将完整指令注入对话上下文(作为一条消息)

Claude 按指令执行:分析 git diff → 生成提交信息

文件结构

每个 Skill 是一个独立目录,核心是 SKILL.md 文件:

.claude/skills/
└── my-skill/
├── SKILL.md ← 必须,主指令文件
├── reference.md ← 可选,详细参考文档
├── examples/
│ └── sample.md ← 可选,示例输出
└── scripts/
└── helper.sh ← 可选,辅助脚本

SKILL.md 使用 YAML frontmatter + Markdown 正文格式:

---
name: code-review
description: 对代码进行专业审查,包含安全、性能、可维护性三个维度。当用户说"帮我看看这段代码"或"review 一下"时使用。
allowed-tools: Read Grep
argument-hint: "[文件路径]"
---

请对 `$ARGUMENTS` 进行代码审查,重点关注:

1. **安全漏洞**:SQL 注入、XSS、未验证的用户输入等
2. **性能问题**:不必要的循环、重复查询、内存泄漏
3. **可维护性**:命名规范、函数职责单一、重复代码

当前改动概要:!`git diff --stat`

请给出具体的改进建议,按严重程度排序。

存储位置与优先级

Skill 有多个存储层级,优先级从高到低

层级 路径 适用范围
企业级 由管理员统一下发 团队所有成员
个人全局 ~/.claude/skills/<name>/ 你的所有项目
项目级 .claude/skills/<name>/ 当前项目
插件 <plugin>/skills/<name>/ /plugin:skill 命名空间调用

同名 Skill 高优先级覆盖低优先级。修改 ~/.claude/skills/.claude/skills/ 中的文件无需重启即可在当前会话中生效。

变量替换与 Shell 命令预执行

Skill 支持两类动态内容,在内容发送给 Claude 之前处理完毕:

变量替换:

变量 含义
$ARGUMENTS 用户输入的完整参数字符串
$0$1$2 按空格分割的第 N 个参数
${CLAUDE_SESSION_ID} 当前会话 ID
${CLAUDE_SKILL_DIR} 当前 Skill 目录的绝对路径

调用 /deploy staging v1.2.3 时:$0 = staging$1 = v1.2.3。带空格的参数用引号包裹:/migrate "Auth Module"$0 = Auth Module

Shell 命令预执行:

发送给 Claude 之前,Shell 命令先执行,输出替换占位符:

# 内联形式(单行)
当前分支:!`git branch --show-current`
未提交文件:!`git status --short`

# 多行形式
```!
echo "Node: $(node -v)"
echo "当前目录: $(pwd)"

Claude 收到的是执行结果,而不是命令本身。这是 Skill 能感知实时环境状态(当前分支、文件变更、系统信息)的关键机制。

### 工具权限控制

通过 `allowed-tools` frontmatter,Skill 可以**预授权**特定工具,执行时不再弹出确认框:

```yaml
---
name: safe-commit
allowed-tools: Bash(git add *) Bash(git commit *) Bash(git status) Bash(git diff *)
---

请分析改动并创建提交...

支持通配符(git * 匹配所有 git 子命令),空格分隔多个工具名。这让自动化工作流成为可能,同时把权限边界限定在最小必要范围。

手动调用 vs Claude 自动选用

通过 frontmatter 控制 Skill 的触发方式:

# 默认:用户可手动调用(/skill-name),Claude 也可自动选用
---

# 只允许手动调用,Claude 不会自动触发(适合破坏性操作如部署)
disable-model-invocation: true
---

# 只允许 Claude 自动选用,不出现在命令列表(适合背景知识注入)
user-invocable: false
---

Claude 自动选用的判断依据就是 description 字段,所以描述要写触发场景,而不只是功能说明:

# 差:只说功能
description: 审查代码

# 好:说明触发场景
description: 对代码进行专业审查。当用户说"帮我看看这段代码"、"有没有 bug"、或需要 code review 时使用。

Skill 与 Hook 的配合

Skill 可以在 frontmatter 里定义 Hook,在工具执行前后插入自定义逻辑:

---
name: production-deploy
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "${CLAUDE_SKILL_DIR}/scripts/safety-check.sh"
---

执行生产环境部署流程...

Skill 激活时其 Hook 合并到全局 Hook 中生效,Skill 结束后自动移除——实现了作用域化的安全防护,只在特定 Skill 运行期间执行额外检查。

上下文生命周期与压缩

Skill 内容注入对话后,在整个会话期间持续有效。当对话上下文接近上限触发压缩时:

  • 每个 Skill 保留最近注入的前 5,000 Token
  • 所有 Skill 共用 25,000 Token 的总预算
  • 从最近调用的 Skill 开始填充,旧的 Skill 内容可能被整体丢弃

这解释了为什么在超长对话里,早期调用的 Skill 有时会”失效”——重新调用一次 /skill-name 即可恢复。

完整示例:自定义”每日站会摘要” Skill

mkdir -p .claude/skills/standup

.claude/skills/standup/SKILL.md

---
name: standup
description: 生成每日站会摘要。当用户说"帮我写今天的站会"或"生成站会报告"时使用。
allowed-tools: Bash(git log *) Bash(git diff *)
argument-hint: "[可选:额外备注]"
---

请根据今天的代码提交记录,生成一份简洁的站会摘要。

今天的提交记录:
```!
git log --oneline --since="yesterday" --author="$(git config user.name)"
```

未完成的改动:!`git status --short`

请按以下格式输出:
**昨天完成:** ...
**今天计划:** ...
**是否有阻塞:** ...

$ARGUMENTS

调用方式:/standup/standup 今天下午要开产品评审会


构建自己的 Skill 系统

以下是一个通用的 Skill 管理框架思路,可以用在任何 Agent 项目中:

import yaml
from pathlib import Path
from dataclasses import dataclass
from typing import Optional

@dataclass
class Skill:
name: str
description: str
prompt_template: str
input_vars: list[str]
model_config: dict

class SkillRegistry:
"""技能注册表:统一管理所有 Skill"""

def __init__(self, skills_dir: str = "./skills"):
self.skills: dict[str, Skill] = {}
self._load_from_directory(skills_dir)

def _load_from_directory(self, skills_dir: str):
"""从目录批量加载技能"""
for yaml_file in Path(skills_dir).glob("**/*.yaml"):
with open(yaml_file) as f:
data = yaml.safe_load(f)
skill = Skill(**data)
self.skills[skill.name] = skill
print(f"已加载技能: {skill.name}")

def get(self, name: str) -> Optional[Skill]:
return self.skills.get(name)

def invoke(self, skill_name: str, llm, **kwargs) -> str:
skill = self.get(skill_name)
if not skill:
raise ValueError(f"技能 '{skill_name}' 不存在")

# 填充提示词模板
prompt = skill.prompt_template.format(**kwargs)
return llm.invoke(prompt)


# 技能定义文件 skills/summarize.yaml:
"""
name: summarize
description: 将长文本压缩为简洁摘要
prompt_template: |
请将以下文本压缩为{length}字以内的摘要,保留核心信息:

{text}
input_vars:
- text
- length
model_config:
temperature: 0.3
max_tokens: 500
"""

# 使用方式
registry = SkillRegistry("./skills")
summary = registry.invoke("summarize", llm, text="很长的文章...", length=200)

Skill 与 Tool 的边界

实际工程中,Skill 和 Tool 的界限并不总是清晰的,大致可以这样区分:

维度 Tool Skill
粒度 单一操作 多步骤能力组合
驱动方式 代码函数 Prompt + 代码
复用单位 函数级 模块级
包含提示词 通常是
典型例子 search_web(query) 「竞品分析」能力

一个好的经验法则是:如果这个能力需要 LLM 参与才能完成,就封装成 Skill;如果是纯确定性计算或 API 调用,就封装成 Tool。


小结

Skill 是 AI Agent 工程化落地的重要抽象层,它解决的核心问题是:

  1. 能力复用:好的 Skill 写一次,到处用
  2. 协作分工:不同 Agent 配置不同 Skill,形成专业团队
  3. 可维护性:Skill 独立管理,便于迭代优化
  4. 标准化:统一 Skill 接口,降低 Agent 系统的集成难度

随着 Agent 系统越来越复杂,Skill 的管理(版本控制、测试、监控)会逐渐成为一个独立的工程方向,值得持续关注。