AI摘要

本文全面解析了Agent Skills的概念、原理、架构和代码实现,以及AI工程化落地的策略。Agent Skills是一系列可被Agent在运行时发现、理解、调用和组合的能力模块,旨在解决AI助手在执行任务时缺乏稳定、可复用的能力链的问题。文章通过比较Skill和Prompt的区别,强调了Skill在发现性、执行性和复用性方面的优势。同时,文章还讨论了Agent Skills在工程架构中解决的三个痛点,包括巨型提示词灾难、执行能力和专业知识脱节以及企业级可靠性缺失。文章详细介绍了Agent如何决定调用哪个Skill的决策流程,以及Skill的结构和代码实现,包括使用OpenAI SDK和LangChain框架的示例。最后,文章讨论了Skills、Function Calling和MCP之间的边界,并分享了生产环境中常见的问题和解决方案。

一文读懂 Agent Skills:原理、机制、架构、代码与 AI 工程化落地全解析

作者:多模态智能体
发布时间:2026年6月9日 08:21
原始链接:https://mp.weixin.qq.com/s/hWtN3cPszsVKJd94tSQM7A

你有没有遇到过这种情况:写了一个函数,需要补充单元测试。你打开 AI 助手,发了一句话过去。

AI 给了你几个测试用例。但你看了一眼,发现边界条件根本没有覆盖,Jest 框架也没用,覆盖率更是没有任何说明。

你只好重新发一条:"要用 Jest 框架,要覆盖边界情况,要生成覆盖率报告……"

下周同样的事情再发生一次,你又要从头解释一遍。

或者这个场景:团队里积累了几十个 Markdown 文档,格式乱、目录缺、链接死。你告诉 AI "帮我整理一下这些文档",AI 只是改了改标题格式,其他什么都没动。你想要的是统一格式、提取关键信息、生成目录、验证链接——这些它压根不知道。

问题不是 AI 不聪明。问题是:它没有一套稳定的、可复用的能力链来完成这类任务。

每次都要重新解释,每次执行结果都不一样,每次都是一次性的。这才是真正的痛点。

Agent Skills 要解决的,就是这个问题。

1.png


什么是 Agent Skills

先给一个干净的定义:

Skill 是一个可被 Agent 在运行时发现、理解、调用和组合的能力模块。

这句话里有四个动词——发现、理解、调用、组合——缺少任何一个,它都不能叫 Skill。

用一个生活场景来理解:你手机里装了几十个 App。当你想导航的时候,不需要自己写路线规划算法,直接打开地图 App 就好。App 帮你把"导航这件事"封装好了——如何定位、如何规划、如何播报,全在里面。

Agent Skills 的逻辑完全一样。

  • Agent 是那部手机,负责理解你的意图并调度能力
  • Skill 是那个 App,给 Agent 增加一项可调用的能力
  • 生态 是应用商店,让能力被发现、安装和管理

再讲一个更贴近工程场景的类比是:你刚入职一家公司。你智商很高,学东西快,但完全不懂这家公司的业务。老板让你"处理一下这批退款并生成财务报告"。

如果没有任何参考资料,你大概率会做错。

但如果书架上有一本《退款处理与财务报告生成 SOP》,你只需要按照手册里的步骤,用公司的工具(电脑、系统账号)一步步执行,结果就会是对的。

这本手册,就是 Skill。

它给 Agent 发的不是命令,而是一张图书馆借阅证——不是强迫它背下整个图书馆,而是让它在需要的时候去查。


Skill 和 Prompt 有什么本质区别

很多人第一反应是:Skill 不就是一个很长的 Prompt 吗?写详细点,不就行了?

不一样,差别很大。

我们从三个维度来看:

发现性

Prompt 需要用户主动提供。你每次要用,就得自己把 Prompt 粘贴进去。Skill 不一样,Agent 可以根据你的任务描述,自动匹配到合适的 Skill,你不需要知道它叫什么名字。

执行性

Prompt 本质上是"告诉 AI 做什么",但执行过程是 AI 自由发挥的。Skill 内部有结构化的 Workflow,每一步要做什么、怎么做、不能做什么,全都有明确规定。

复用性

Prompt 是一次性的,散落在对话历史里,下次遇到同样的任务,你还得从头写。Skill 是独立的模块,一次定义,到处复用,还能持续迭代优化。

举个具体例子:

用 Prompt 的方式补充单元测试,你需要写:"帮我给这个函数补充单元测试,要用 Jest 框架,要覆盖边界情况,要检查覆盖率,要……"——每次都要重复,容易遗漏,结果不稳定。

用 Skill 的方式,你只需要说"帮我补充单元测试",Agent 识别到这是一个测试相关任务,自动加载 test-driven-development Skill,按照里面定义好的流程执行:理解代码 → 设计测试场景 → 编写测试代码 → 验证覆盖率。

结果一致,流程稳定,不用每次解释。


为什么需要它:三个架构级的痛点

上面说的是表层体验。从工程架构的角度看,Agent Skills 解决的是三个更深层的问题。

痛点一:巨型提示词灾难

很多团队的第一反应是:把所有业务规则、操作流程、边界条件全写进系统提示词。结果是上下文越来越长,几千 Token 变成几万 Token。

这不只是钱的问题。当提示词超过模型能高质量处理的范围,会出现一个现象叫"注意力丢失(Lost in the Middle)"——中间部分的指令,模型大概率会忽略。越长的 Prompt,指令遵循能力越差。

痛点二:执行能力和专业知识脱节

现在的 Agent 系统通常配了很多工具:能执行代码、能读写文件、能调 API。但拥有工具,不等于知道如何专业地使用工具

给 Agent 一个能执行 SQL 查询的工具,它能查。但它知道针对你这张五百万行的宽表应该怎么写查询吗?知道如何做多步关联验证吗?知道数据异常了该怎么回退吗?这些"操作智慧",工具给不了,必须单独注入。

痛点三:企业级可靠性缺失

生产环境有一个基本要求:相同输入,相同输出。但依赖动态生成的提示词,系统行为会有大量随机性。今天跑正确,明天换个措辞就跑歪了。

工程化系统需要确定性。需要把专家的隐性知识,转换成模型可以稳定执行的标准操作流程。

这三个痛点,指向同一个缺失:缺少一种"可插拔的领域专家大脑"机制。 Agent Skills 填的就是这个空。


Agent 是怎么决定调用哪个 Skill 的

这是一个很多人想搞清楚的问题:我说一句话,Agent 是怎么判断要不要调用 Skill,调用哪个的?

整个决策流程分五步:

2.png

第一步:识别任务类型。 Agent 先判断你的请求是什么性质的任务——测试相关?文档处理?代码审查?这一步相当于分类。

第二步:判断是否需要 Skill。 不是所有任务都需要 Skill。"Python 是什么语言"这种问题,直接回答就好,不需要任何流程。判断标准是:这个任务是否需要结构化的多步骤执行?是否有明确的验证标准?

第三步:语义匹配。 如果判断需要 Skill,Agent 会在所有可用 Skill 的描述(description)里做语义搜索,找到最匹配的那一个。

第四步:注入并执行。 找到 Skill 后,把 Skill 的完整内容注入到系统提示词,Agent 按照里面的 Workflow 一步步执行。

第五步:整合输出。 Skill 执行完成,结果返回给你。

这背后有一个精妙的设计——渐进式信息披露

系统启动时,只加载所有 Skill 的元数据(名字和描述),每个大约一百个 Token。等到识别到具体任务,才加载完整的 SKILL.md(几千 Token)。如果任务还需要外部参考资料,才在执行过程中按需加载(可能几万 Token)。

这种设计让 Agent 能"知道"几百个 Skill 的存在,但只在真正需要的时候才"学习"具体内容。既保持了广度,又控制了成本。


Skill 长什么样:从外到内拆解

理解了调用机制,我们来看 Skill 本身的结构。

从三个层次来看:

第一层:使用者视角(黑盒)

对用户来说,Skill 就是"我说一句话,Agent 按某套流程处理,然后给我结果"。不需要知道里面是什么。

第二层:文件夹视角

打开一个 Skill 的目录,通常长这样:

my-skill/
├── SKILL.md          # 必需:核心文件
├── scripts/          # 可选:可执行脚本
├── references/       # 可选:参考文档
├── assets/           # 可选:静态资产
└── templates/        # 可选:输出模板

只有 SKILL.md 是必须的,其他都是按需添加。

第三层:SKILL.md 的内部结构

一个典型的 SKILL.md 长这样:

---
name: test-driven-development
description: 在实现任何新功能或修复 Bug 时使用,必须在编写实现代码之前调用
---

# 测试驱动开发

## Goal
引导使用 TDD 方法论来实现具体功能。

## Workflow
1. 理解需求:阅读功能描述,识别验收标准
2. 设计测试用例:列举测试场景,按优先级排序
3. 测试先行:用项目指定框架实现测试,确保初始失败
4. 实现代码:编写最小代码使测试通过
5. 重构:在不改变行为的前提下提升代码质量

## Constraints
- 绝对不能在写测试之前写实现代码
- 代码覆盖率最低要求 80%
- 每个测试必须独立、可重复运行

结构很清晰:frontmatter 是路由的入口,body 是执行的指南。

description 字段尤其关键,它直接决定了这个 Skill 会不会在合适的时机被找到。写好和写差的区别很大:

根据复杂度不同,Skill 分三种类型:

  • 简单型:只有一个 SKILL.md,3-5 步线性流程,比如生成 Git 提交信息。五分钟就能写完。
  • 多步骤工作流型:仍然只有 SKILL.md,但 Workflow 有分支逻辑,5-10 步,比如 TDD 开发流程。
  • 资源依赖型:SKILL.md 加上外部资源目录,适合需要参考文档、执行脚本、使用模板的复杂任务。
    • *
代码落地:两种框架的完整实现

理论说完了,来看代码。我们用一个具体任务串起来:实现一个天气查询 Skill,分别用 OpenAI SDK 和 LangChain 两套框架落地。

先定义这个 Skill
---
name: get-weather
description: 获取指定城市的天气信息。在用户询问天气、气温或出行建议时使用。
---

# 获取天气信息

## Goal
根据用户询问,提供准确的天气信息。

## Workflow
1. 从用户请求中提取城市名称
2. 获取该城市当前天气数据(温度、天气状况、湿度、风速)
3. 以自然对话口吻返回结果
4. 如需对比多城市,分别获取后整合展示

## Constraints
- 默认使用摄氏度
- 只提供当前天气,不做长期预测
- 空气质量差时主动提醒

## Examples
用户:"北京今天天气怎么样?"
回答:"北京今天晴天,25°C,空气质量良好,适合出行。"
核心引擎架构

两套框架共用同一套三层架构:

SkillRegistry(加载所有 Skill)
    ↓
SkillRouter(根据用户输入选择 Skill)
    ↓
SkillExecutor(注入 Skill 文档并执行)
方案一:OpenAI SDK 版本
import os, re, json
from pathlib import Path
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass
from openai import OpenAI
from dotenv import load_dotenv

load_dotenv()

client = OpenAI(
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url=os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com")
)

@dataclass
class SkillMetadata:
    name: str
    description: str
    file_path: str
    full_content: Optional[str] = None

class SkillRegistry:
    """扫描目录,加载所有 Skill 文件"""

    def __init__(self, skills_dir: str):
        self.skills_dir = Path(skills_dir)
        self.skills: Dict[str, SkillMetadata] = {}
        self._load_all_skills()

    def _parse_skill_file(self, file_path: Path) -> Optional[SkillMetadata]:
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()

        # 提取 YAML frontmatter
        match = re.search(r'^---\s*\n(.*?)\n---', content, re.DOTALL)
        ifnot match:
            returnNone

        frontmatter = match.group(1)
        name_match = re.search(r'name:\s*(.+)', frontmatter)
        desc_match = re.search(r'description:\s*(.+)', frontmatter)
        ifnot name_match ornot desc_match:
            returnNone

        return SkillMetadata(
            name=name_match.group(1).strip(),
            description=desc_match.group(1).strip(),
            file_path=str(file_path),
            full_content=content
        )

    def _load_all_skills(self):
        skill_files = (
            list(self.skills_dir.glob("*.skill")) +
            list(self.skills_dir.glob("*.md"))
        )
        for f in skill_files:
            skill = self._parse_skill_file(f)
            if skill:
                self.skills[skill.name] = skill

    def get_all_skills_summary(self) -> str:
        return"\n".join(
            f"- {s.name}: {s.description}"
            for s in self.skills.values()
        )

class SkillRouter:
    """调用 LLM,根据用户输入选择合适的 Skill"""

    def __init__(self, registry: SkillRegistry):
        self.registry = registry

    def route(self, user_input: str) -> Optional[str]:
        prompt = f"""
你是一个 Skill 路由器。根据用户输入选择最合适的 Skill。

可用 Skills:
{self.registry.get_all_skills_summary()}

用户输入:{user_input}

只返回 Skill 名称,没有合适的返回 NONE。
        """
        response = client.chat.completions.create(
            model=os.getenv("DEEPSEEK_MODEL", "deepseek-chat"),
            messages=[{"role": "user", "content": prompt}],
            temperature=0,
            max_tokens=50
        )
        skill_name = response.choices[0].message.content.strip()
        return skill_name if skill_name in self.registry.skills elseNone

class SkillExecutor:
    """加载完整 Skill 文档,注入系统提示词后执行"""

    def __init__(self, registry: SkillRegistry):
        self.registry = registry

    def execute(self, skill_name: str, user_input: str) -> str:
        skill = self.registry.skills.get(skill_name)
        ifnot skill:
            returnf"Skill '{skill_name}' 不存在"

        system_prompt = f"""
你现在要执行以下 Skill:

{skill.full_content}

严格按照 Workflow 执行,遵守所有 Constraints,直接返回结果。
        """
        response = client.chat.completions.create(
            model=os.getenv("DEEPSEEK_MODEL", "deepseek-chat"),
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": user_input}
            ],
            temperature=0.7
        )
        return response.choices[0].message.content

class AgentSkillsEngine:
    """系统入口,协调 Registry / Router / Executor"""

    def __init__(self, skills_dir: str):
        self.registry = SkillRegistry(skills_dir)
        self.router = SkillRouter(self.registry)
        self.executor = SkillExecutor(self.registry)

    def process(self, user_input: str) -> Tuple[str, Optional[str]]:
        skill_name = self.router.route(user_input)
        ifnot skill_name:
            return"抱歉,没有找到合适的 Skill 处理这个请求。", None
        result = self.executor.execute(skill_name, user_input)
        return result, skill_name

    def list_skills(self) -> List[str]:
        return list(self.registry.skills.keys())

调用方式:

engine = AgentSkillsEngine(skills_dir="./skills")

result, skill = engine.process("北京今天天气怎么样?")
print(f"回复:{result}")
print(f"使用 Skill:{skill}")

result, skill = engine.process("对比北京和长沙的天气")
print(f"回复:{result}")
方案二:LangChain 版本

LangChain 版本在架构上完全一致,换成 LCEL(LangChain Expression Language)的写法,用 | 操作符串联各组件:

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

class SkillRouter:
    def __init__(self, registry):
        self.registry = registry
        self.llm = ChatOpenAI(
            model=os.getenv("DEEPSEEK_MODEL", "deepseek-chat"),
            api_key=os.getenv("DEEPSEEK_API_KEY"),
            base_url=os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com"),
            temperature=0
        )
        self.prompt = ChatPromptTemplate.from_messages([
            ("system", """你是一个 Skill 路由器。

{skills_summary}

规则:只返回 Skill 名称,没有合适的返回 NONE。"""),
            ("human", "{user_input}")
        ])
        # LCEL 链:提示词 → 模型 → 解析输出
        self.chain = self.prompt | self.llm | StrOutputParser()

    def route(self, user_input: str) -> Optional[str]:
        result = self.chain.invoke({
            "skills_summary": self.registry.get_all_skills_summary(),
            "user_input": user_input
        })
        skill_name = result.strip()
        return skill_name if skill_name in self.registry.skills elseNone

class SkillExecutor:
    def __init__(self, registry):
        self.registry = registry
        self.llm = ChatOpenAI(
            model=os.getenv("DEEPSEEK_MODEL", "deepseek-chat"),
            api_key=os.getenv("DEEPSEEK_API_KEY"),
            base_url=os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com"),
            temperature=0.7
        )
        self.prompt = ChatPromptTemplate.from_messages([
            ("system", """执行以下 Skill:

{skill_content}

严格按照 Workflow 执行,直接返回结果。"""),
            ("human", "{user_input}")
        ])
        self.chain = self.prompt | self.llm | StrOutputParser()

    def execute(self, skill_name: str, user_input: str) -> str:
        skill = self.registry.skills.get(skill_name)
        ifnot skill:
            returnf"Skill '{skill_name}' 不存在"
        return self.chain.invoke({
            "skill_content": skill.full_content,
            "user_input": user_input
        })

LangChain 版本和 OpenAI SDK 版本的逻辑完全相同,差别只是表达方式。如果你的项目里已经在用 LangChain 的生态,直接接入就好;如果是新项目,两种都可以,OpenAI SDK 版本更轻量。


怎么选:Skills、Function Calling、MCP 的边界

用了这么久,有一个绕不开的问题:这三个东西什么时候用哪个?

先理清各自的定位:

  • Function Calling:代码级函数调用。你定义函数签名,AI 决定什么时候调用它,调用后你处理结果。执行是确定的,代码写什么就跑什么。
  • MCP(Model Context Protocol):协议级能力接入。标准化的接口协议,让 AI 能接入各种外部系统(数据库、文件系统、第三方服务)。解决"如何连接"的问题。
  • Agent Skills:语义级能力模块。用 Markdown 描述"做什么、怎么做、不能做什么",AI 按照描述执行。解决"如何专业地完成"的问题。

判断用哪个,看这几个维度:

确定性要求高不高? 金融交易、数据库写入、触发支付——这些场景不允许任何随机性,必须用 Function Calling。给定相同输入,一定要得到相同输出。

需不需要快速迭代? 如果你还在验证阶段,需求每周都在变,Agent Skills 的优势很明显。改 Markdown 比改代码快,也不需要重新部署。等流程稳定了,再考虑要不要换成 Function Calling。

团队里有没有非技术人员? 产品经理、运营想定义工作流,但不会写代码——Agent Skills 是唯一的选项。描述清楚流程和规则,就能用起来。

需不需要跨平台复用? 同一套能力要在多个 AI 系统里用,MCP 的标准化协议价值才体现出来。如果只在一个系统里用,MCP 的开发成本未必值得。

实际项目里,这三个通常是组合使用的:高层工作流用 Skills,底层精确操作用 Function Calling,跨系统集成用 MCP。 不是非此即彼,是各司其职。


生产环境里最容易踩的四个坑

最后说一些真正上线之后才会遇到的问题。

坑一:路由瘫痪——Skills 互相打架

当你的 Skill 库积累到几十个,如果各个 Skill 的 description 语义重叠,Agent 就会开始"选择困难"。用户说"处理这个文件",Agent 在 file-processordocument-analyzerdata-extractor 之间反复横跳。

解法:每个 Skill 的 description 要有清晰的边界,而且要加"负向触发器"——明确写出"本 Skill 不处理 X 类任务"。比如:

description: |
  当用户需要提取文本或处理 PDF 结构时使用。
  不用于处理纯文本文件或代码仓库扫描。

坑二:上下文隐性膨胀

Skill 里定义了"读取 references/ 目录下的文档",但没有限制读取量。执行时 Agent 把整个目录的内容全加载进来,几万 Token 一下就打满了。

解法:在 SKILL.md 里明确限制:"每次最多读取 5000 Token,使用 grep 过滤后再读取,不要全量加载。"

坑三:description 写成了说明书

新手最常见的错误:把 description 写得很详细,一段话解释背景、功能、使用场景、注意事项。结果适得其反——description 太长太复杂,路由器反而判断不准了。

description 要短、精、关键词密度高。一两句话,清晰说明触发时机和核心能力。

坑四:Skill 过了保质期

Skill 里引用了某个外部接口的格式,接口升级了,Skill 没更新。或者团队的代码规范变了,Skill 里的标准没跟上。这种"静默降级"很隐蔽——系统不报错,但结果越来越偏。

解法:建立定期审计机制。每隔一段时间,人工检查 Skill 的 Workflow 和 Constraints 是否还与实际情况对齐。


最后说一句

Agent Skills 不是什么革命性的新概念。它做的事情,说穿了很朴素:把专家的知识和流程,结构化地写下来,让 AI 能稳定地照着执行。

就像公司有了流程手册,新人上手更快,老员工犯错更少,团队整体输出更稳定。

AI 也一样。给它一本好的手册,它就能更好地工作。

Prompt 是你告诉 AI"去做这件事"。Skill 是你提前准备好了"这件事怎么做"。

区别就在这里。

扫码加入猫哥的AI群
最后修改:2026 年 06 月 13 日
点赞的人是最酷的