Agent Skills 简易实战讲解

3608
8 分钟阅读
Agent Skills 简易实战讲解
"AI摘要: AI摘要: Agent Skills(智能体技能)这个概念在 AI 工程界已经火了一段时间。随着我参与的项目越来越复杂,我对它的理解也经历了一个“看山是山 -> 看山不是山 -> 看山还是山”的演变过程。"

引言

Agent Skills(智能体技能)这个概念在 AI 工程界已经火了一段时间。随着我参与的项目越来越复杂,我对它的理解也经历了一个“看山是山 -> 看山不是山 -> 看山还是山”的演变过程。

很多人以为 Skills 只是给 AI 写几个 Prompt 模板。但如果深入到底层实现,你会发现它解决的其实是 Agentic AI 中最棘手的动态上下文管理(Dynamic Context Management)问题。

本文将从我的认知迭代出发,拆解 Agent Skills 到底是什么,解决了什么痛点,并以 OpenCode 为例,剖析其代码层面的实现原理。

一、 认知的三个阶段:Skills 到底是什么?

第一阶段:简单的协议层

起初,我认为 Skills 只是一个简单的协议或者规范。就像 MCP(Model Context Protocol)一样,Skills 似乎只是在工程上做了一些微小的优化,把 Prompt 包装得好看一点罢了,规规整整地放到统一的文件夹。我觉得它没什么新意,不过是“旧瓶装新酒”。

第二阶段:Workflow 的变体

后来,我开始认为 Skills 本质上就是 workflow 中的“意图识别”节点。 在传统的 workflow 中,我们需要通过意图识别(Router)来决定跳转到哪个分支,从而加载不同的 Prompt。我当时想:“哦,Skills 不就是把这个跳转逻辑挪到了对话内部吗?”,也就是说,我认为skills不过是意图识别节点的换皮。

第三阶段:动态能力的注入

最后,我意识到之前的理解都“窄”了。 意图识别(Intent Recognition)通常是外部的、显式的路由逻辑;而 Skills 是 Agentic AI(代理式 AI) 的核心能力体现。 它不是简单的“跳转”,而是一种**渐进式加载(Progressive Loading)**机制。它允许 Agent 在不知道未来会遇到什么问题的情况下,根据当前情境,主动去“书架”上取下一本特定的“说明书”来阅读。

Skills 解决了什么工程痛点?

如果只是为了让 AI做事,直接把 System Prompt 写长点不就行了吗?为什么要引入 Skills?核心在于三个问题:

  1. 成本与上下文限制: 随着能力增加,如果把所有工具的 Prompt 全部塞进 System Prompt,Context Window 会瞬间爆炸,Token 成本也会指数级上升。

  2. 注意力稀释: 让 AI 在几万字无关的工具描述中寻找一个简单的指令,会极大地降低模型的指令遵循能力, 迷失在成千上万和任务一点关系都没有的token中。Skills 实现了“用时即取”,保持了上下文的简洁和专注。

  3. 分发与复用: 通过标准化的定义,Skills 变成了一个可移植的“技能包”。这为社区分发(类似于 npm 之于 Node.js)提供了基础。

标准化定义:一个 Skill 长什么样?

目前主流的实现(如 Claude Code 风格)通常采用文件目录的形式来定义一个 Skill。一个典型的 .claude/skills 或 .opencode/skills 目录结构如下:

skill.md (必选):核心描述文件,包含 Prompt 和使用说明。

scripts/ (可选):具体执行的脚本(Python/Bash)。

assets/ (可选):静态资源文件。

这是一种“文档即代码”的设计哲学。

代码揭秘:Skills 是如何加载的?

这是本文的重点。很多文章只讲概念,我们直接看代码实现。 由于 Claude Code 未开源,我们以开源项目 OpenCode 的逻辑为例,复盘一个 Skill 从定义到被 AI 唤醒的全过程。

  1. 定义技能

假设我们在 .opencode/skills/git-release/SKILL.md 中定义了一个发布工具:

---
name: git-release
description: Create consistent releases and changelogs
metadata:
  audience: maintainers
---

## What I do
- Draft release notes from merged PRs
- Propose a version bump

## When to use me
Use this when you are preparing a tagged release.

  1. 注册与发现

系统启动时,不会把 SKILL.md 的正文内容(即 "What I do" 下面的详细 Prompt)喂给 LLM。 系统只会读取 Frontmatter 中的元数据,构建一个精简的 XML 列表注入到 System Prompt 中:

<available_skills>
  <skill>
    <name>git-release</name>
    <description>Create consistent releases and changelogs</description>
  </skill>
</available_skills>

关键点:此时,LLM 知道有这个技能,但不知道具体怎么用。这就极大地节省了 Token。

  1. 激活与注入

当用户提问:“帮我发布一个新版本”时,LLM 基于 System Prompt 中的描述,判断需要使用该技能。它不会直接执行 git 命令,而是先输出一个特殊的 Tool Call:

{
    tools: "skill({ name: 'git-release' })"
}
  1. 运行时循环 (The Runtime Loop) 系统捕获到这个请求后,才会读取 SKILL.md 的完整内容,并将其追加到当前的 Context 中。

以下是核心逻辑的伪代码实现:


context = system_prompt + available_skills_summary 

while True:
    # 1. LLM 思考
    llm_output = LLM(context)

    # 2. 判断是否结束
    if llm_output.type == "final":
        return llm_output.content

    # 3. 处理工具调用
    elif llm_output.type == "tool_call":
        tool_name = llm_output.name
        
        # 核心逻辑:区分是“加载技能”还是“执行动作”
        if tool_name == "skill":
             # 【关键一步】动态加载:读取 MD 文件内容
             skill_name = llm_output.args['name']
             skill_content = read_file(f".skills/{skill_name}/SKILL.md")
             
             # 将技能详情作为“观察结果”或“新规则”注入上下文
             result = f"[System: Skill '{skill_name}' loaded.]\n{skill_content}"
        
        else:
             # 执行普通的工具(如运行 shell 命令)
             result = run_actual_tool(tool_name, llm_output.args)

        # 将结果追加到对话历史,进入下一轮循环
        context += format_observation(result)

通过这个循环,LLM 在下一轮交互时,Context 中就拥有了 git-release 的详细 Prompt 指导,从而能够准确地执行发布任务。

结语

Agent Skills 的本质,是利用 LLM 的规划能力,实现 Context 的按需置换。

它不仅仅是代码层面的优化,更是一种架构模式的转变:从“给 AI 准备好一切”转变为“教会 AI 如何去获取它需要的能力”。理解了这一点,你也就理解了 Agentic AI 走向复杂的必经之路。