目录

1. 引言:从“会聊天”到“能干活”

大语言模型(LLM)的出现,让 AI 从“只能生成文本”进化到“能够理解复杂指令”。但在真实业务场景中,一个 AI Agent 光会回答问题远远不够,它还需要查询数据库、调用内部服务、操作文件、发送消息、执行审批流程等。这些“让模型落地到现实动作”的能力,就是 Agent 的“技能”。

早期人们用 Function Calling、Tools 等概念来描述这种能力。但随着 Agent 系统越来越复杂,单纯堆砌一堆“函数”已经难以维护。于是 Skills(技能) 作为一种更高级的能力组织方式逐渐流行起来,它把“可执行的动作”提升为“可发现、可描述、可组合、可治理”的第一等公民。

本文将从概念原理出发,一步步拆解 Agent 中 Skills 的设计,并给出一个可运行的工程实现骨架。

2. 什么是 Skill:概念澄清

在 AI Agent 语境下,Skill 并不是一个新鲜词,但不同框架对它的定义略有差异。我们需要先厘清几个容易混淆的概念。

2.1 定义

一个 Skill 可以被定义为:

一段经过封装、带有结构化描述、可以被 Agent 按需发现与调用的能力单元。

它通常包含三要素:

  • 能力本体:真正干活的东西,可能是函数、HTTP 接口、脚本、服务,甚至是另一个 Agent。
  • 结构化描述:告诉模型这个 Skill 是做什么的、入参出参是什么、什么时候应该用。
  • 运行时约束:权限、超时、重试、审计等执行层面的管控。

2.2 Skill 与 Tool 的区别

很多开发者认为 Skill 就是 Tool 的另一个名字。实际上它们有关联,但侧重点不同:

维度 Tool Skill
粒度 通常单一函数或 API 可以是单个动作,也可以是一组动作
描述方式 偏函数签名 偏语义化、面向任务
发现方式 显式注入 prompt 注册表 + 动态检索
治理能力 较弱 权限、版本、审计更完整
组合能力 一般 强,可以嵌套、编排

简单理解:Tool 是“函数”,Skill 是“可被模型理解与调用的能力包”。一个 Skill 内部可以封装多个 Tool。

2.3 Skill 与 Plugin 的区别

Plugin 通常指“外部扩展”,强调动态加载和隔离;Skill 更强调“模型可用性”,即它必须能被 LLM 理解和调度。一个 Plugin 可以承载多个 Skill,Skill 也可以作为 Plugin 暴露。

3. Skill 的核心原理

要让 LLM 真正“学会使用”一个 Skill,工程上需要解决几个关键问题。

3.1 可描述性:让模型知道该不该用

LLM 不执行代码,它只能根据上下文做决策。因此每个 Skill 必须携带足够清晰的描述,包括:

  • 名称与摘要
  • 适用场景
  • 输入参数 schema
  • 输出结构
  • 使用示例

常见做法是生成一段 function schema 或 prompt 片段,注入到系统提示词中。例如:

{
  "name": "query_user_order",
  "description": "根据用户ID查询最近的订单列表,用于回答订单相关问题。",
  "parameters": {
    "type": "object",
    "properties": {
      "user_id": {"type": "string", "description": "用户唯一标识"}
    },
    "required": ["user_id"]
  }
}

Agent 在运行时看到这个描述,就知道“当用户询问订单时,可以调用 query_user_order”。

3.2 可发现性:在合适的时候找到它

当 Skill 数量很少时,可以把所有描述都塞进 prompt。但真实系统可能有成百上千个 Skill,全部注入会导致上下文爆炸。

因此需要 Skill 注册表(Registry)检索机制

  • 静态注册:预先配置好的一组 Skill。
  • 语义检索:把 Skill 描述向量化,根据用户问题召回最相关的 Top-K。
  • 路由规则:按意图分类、关键词、业务域进行路由。

用户提问

意图理解

需要调用 Skill?

直接生成回答

语义检索候选 Skill

组装 Skill 描述到 Prompt

模型生成调用参数

执行 Skill

结果回填并生成回答

3.3 可组合性:把简单能力串成复杂流程

单个 Skill 往往只能完成原子动作。复杂任务需要多个 Skill 组合。常见的组合方式:

  • 顺序编排:先查用户信息,再查订单,最后汇总回答。
  • 条件分支:根据上一步结果决定下一步调用哪个 Skill。
  • 并行调用:多个互相独立的 Skill 同时执行。
  • 嵌套调用:一个 Skill 内部再调用其他 Skill。

这就要求 Skill 的输入输出是结构化、可序列化的,方便在多个步骤之间传递。

3.4 安全与隔离:能力越大,责任越大

Skill 一旦接入真实系统,就可能带来风险:误删数据、越权访问、死循环、费用失控等。因此 Skill 的设计必须内置安全机制:

  • 最小权限原则:每个 Skill 声明自己需要的权限。
  • 沙箱执行:高风险操作在隔离环境中运行。
  • 人工确认:涉及资金、删除等操作前需要人类审批。
  • 审计日志:完整记录调用链、参数、结果。

4. Skill 的系统架构

一个典型的 Agent Skill 系统大致分为四层。

Agent 编排层

Skill 发现层

Skill 执行层

能力实现层

Skill 注册表

权限与审计

4.1 Skill 注册表

负责存储所有 Skill 的元数据,提供注册、注销、查询、版本管理能力。它是 Skill 目录的唯一真相源。

4.2 Skill 发现层

根据当前对话上下文,从注册表中筛选并返回候选 Skill。可以采用关键词匹配、向量召回或规则路由。

4.3 Skill 执行层

负责调用具体的 Skill 实现,处理参数校验、超时、重试、异常转换、结果序列化等横切关注点。

4.4 能力实现层

真正业务逻辑所在,可以是本地函数、远程 RPC、HTTP API、数据库存储过程或脚本。

5. 工程实践:从零实现一个 Skill 系统

下面我们用一个 Python 示例,搭建一个最小可运行、但又具备扩展性的 Skill 系统。

5.1 定义 Skill 元数据

先用数据类描述一个 Skill 的元数据和参数约束。

from dataclasses import dataclass, field
from typing import Any, Callable, Dict, List, Optional

@dataclass
class ParameterSpec:
    name: str
    type: str
    description: str
    required: bool = True


@dataclass
class SkillSpec:
    name: str
    description: str
    parameters: List[ParameterSpec]
    handler: Callable[..., Any]
    tags: List[str] = field(default_factory=list)

    def to_schema(self) -> Dict[str, Any]:
        """生成可供 LLM 识别的 JSON Schema 描述。"""
        properties = {}
        required = []
        for p in self.parameters:
            properties[p.name] = {
                "type": p.type,
                "description": p.description,
            }
            if p.required:
                required.append(p.name)

        return {
            "name": self.name,
            "description": self.description,
            "parameters": {
                "type": "object",
                "properties": properties,
                "required": required,
            },
        }

5.2 实现 Skill 注册表

注册表负责保存 Skill,并提供注册、查找、导出描述的能力。

class SkillRegistry:
    def __init__(self) -> None:
        self._skills: Dict[str, SkillSpec] = {}

    def register(self, spec: SkillSpec) -> None:
        if spec.name in self._skills:
            raise ValueError(f"Skill 已存在: {spec.name}")
        self._skills[spec.name] = spec

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

    def list_all(self) -> List[SkillSpec]:
        return list(self._skills.values())

    def export_schemas(self) -> List[Dict[str, Any]]:
        """导出所有 Skill 的 Schema,用于注入到模型上下文。"""
        return [skill.to_schema() for skill in self._skills.values()]

5.3 注册两个真实 Skill

第一个 Skill 查询用户订单,第二个 Skill 发送通知。

def query_user_orders(user_id: str, limit: int = 5) -> List[Dict[str, Any]]:
    """模拟查询订单。"""
    return [
        {"order_id": "20260831001", "amount": 199.0, "status": "paid"},
        {"order_id": "20260830002", "amount": 59.9, "status": "shipped"},
    ][:limit]


def send_notification(user_id: str, message: str) -> Dict[str, Any]:
    """模拟发送站内通知。"""
    return {"success": True, "target": user_id, "message": message}


registry = SkillRegistry()

registry.register(
    SkillSpec(
        name="query_user_orders",
        description="根据用户ID查询最近的订单列表,用于回答订单相关问题。",
        parameters=[
            ParameterSpec("user_id", "string", "用户唯一标识"),
            ParameterSpec("limit", "integer", "返回的最大订单数量", required=False),
        ],
        handler=query_user_orders,
        tags=["order", "query"],
    )
)

registry.register(
    SkillSpec(
        name="send_notification",
        description="向指定用户发送一条站内通知,用于主动触达场景。",
        parameters=[
            ParameterSpec("user_id", "string", "接收通知的用户标识"),
            ParameterSpec("message", "string", "通知内容"),
        ],
        handler=send_notification,
        tags=["message", "write"],
    )
)

5.4 实现 Skill 执行器

执行器负责参数校验、异常处理、超时和结果封装。真实场景还可加入权限校验和审计日志。

import inspect
import json
import logging
import time
from typing import Any, Dict, Tuple

logger = logging.getLogger("skill_executor")


class SkillExecutor:
    def __init__(self, registry: SkillRegistry, timeout_seconds: float = 5.0):
        self.registry = registry
        self.timeout_seconds = timeout_seconds

    def execute(self, name: str, arguments: Dict[str, Any]) -> Tuple[bool, Any]:
        """执行指定 Skill,返回 (是否成功, 结果或错误信息)。"""
        skill = self.registry.get(name)
        if skill is None:
            return False, f"未找到 Skill: {name}"

        try:
            self._validate_arguments(skill, arguments)
            start = time.time()
            result = skill.handler(**arguments)
            elapsed = time.time() - start
            logger.info("skill=%s elapsed=%.3fs", name, elapsed)
            return True, result
        except Exception as exc:  # noqa: BLE001
            logger.exception("skill=%s execution failed", name)
            return False, str(exc)

    @staticmethod
    def _validate_arguments(skill: SkillSpec, arguments: Dict[str, Any]) -> None:
        allowed = {p.name for p in skill.parameters}
        unknown = set(arguments) - allowed
        if unknown:
            raise ValueError(f"存在未知参数: {unknown}")

        for p in skill.parameters:
            if p.required and p.name not in arguments:
                raise ValueError(f"缺少必填参数: {p.name}")

5.5 模拟一次 Agent 调用

这里我们不接入真实 LLM,仅用一段伪代码演示 Agent 拿到 Schema 后,如何生成参数并执行 Skill。

def simulate_agent(schemas: List[Dict[str, Any]]) -> Dict[str, Any]:
    """模拟 Agent 根据用户问题选择 Skill 并生成参数。"""
    # 真实场景中,这里会调用 LLM,并把 schemas 注入 Prompt。
    # 这里简化:用户问“查一下用户 10086 的订单”,模型选择了 query_user_orders。
    return {
        "skill_name": "query_user_orders",
        "arguments": {"user_id": "10086", "limit": 3},
    }


if __name__ == "__main__":
    executor = SkillExecutor(registry)
    schemas = registry.export_schemas()

    # 1. Agent 根据上下文和 Schema 做决策
    action = simulate_agent(schemas)

    # 2. 执行器调用 Skill
    ok, result = executor.execute(action["skill_name"], action["arguments"])

    # 3. 打印结构化结果
    print(json.dumps({"ok": ok, "result": result}, ensure_ascii=False, indent=2))

运行后可以得到类似输出:

{
  "ok": true,
  "result": [
    {"order_id": "20260831001", "amount": 199.0, "status": "paid"},
    {"order_id": "20260830002", "amount": 59.9, "status": "shipped"}
  ]
}

5.6 让 Skill 具备权限与审计能力

上面的示例是最小骨架。生产环境中,建议把权限校验和审计做成装饰器或中间件,避免侵入业务代码。

from functools import wraps
from typing import Any, Callable

AUDIT_LOG: List[Dict[str, Any]] = []


def audited(required_permission: str) -> Callable[..., Callable[..., Any]]:
    """带权限校验与审计日志的 Skill 装饰器。"""

    def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
        @wraps(func)
        def wrapper(*args: Any, **kwargs: Any) -> Any:
            # 这里替换为真实的权限系统调用
            current_permissions = {"order:read", "message:write"}
            if required_permission not in current_permissions:
                raise PermissionError(f"缺少权限: {required_permission}")

            result = func(*args, **kwargs)
            AUDIT_LOG.append(
                {
                    "skill": func.__name__,
                    "args": str(args),
                    "kwargs": kwargs,
                    "permission": required_permission,
                }
            )
            return result

        return wrapper

    return decorator


@audited("order:read")
def query_user_orders_safe(user_id: str, limit: int = 5) -> List[Dict[str, Any]]:
    return query_user_orders(user_id, limit)

这样,权限和审计逻辑既能统一治理,又方便测试和替换。

6. 与主流框架的对比

了解市面上的实现,有助于选型和借鉴。

6.1 OpenAI Function Calling

OpenAI 将 Function 的 JSON Schema 传入模型,模型返回 function_call 参数。它本质上是 Tool/Function 级别的调用规范,适合轻量场景;但缺少版本、权限、审计等企业级治理能力。

6.2 LangChain Tools

LangChain 的 Tool 继承了 BaseTool,有名称、描述和 _run 方法,和本文中的 Skill 非常接近。但 LangChain 的 Tool 更偏“执行单元”,组合与治理要依赖 AgentExecutor、Callback 等外围机制完成。

6.3 MCP(Model Context Protocol)

MCP 提供标准化的“工具发现与调用”协议,通过 Server/Client 模式把外部能力暴露给模型。MCP 偏向协议层和生态互通;而 Skill 偏向应用层的组织方式。两者可以结合:用 MCP 承载 Skill 的远程能力实现。

7. 生产环境最佳实践

7.1 错误分类与重试

不是所有错误都适合重试。建议把错误分为:

  • 可重试错误:网络超时、临时限流。
  • 不可重试错误:参数非法、权限不足。
  • 未知错误:保留现场,人工介入。

在 SkillExecutor 中可以根据异常类型决定是否重试,并设置最大重试次数与退避策略。

7.2 超时与熔断

每个 Skill 都应设置独立超时;对依赖外部服务的 Skill 增加熔断器,避免上游故障拖垮整个 Agent。

7.3 幂等设计

对于写操作类 Skill,例如发送通知、创建订单,应尽量设计为幂等。可以在请求中传入 idempotency_key,由服务端做去重。

7.4 结果回填策略

Skill 返回的结果可能非常大。直接全量塞回 LLM 会浪费 token,还可能导致模型“遗忘”重要信息。建议:

  • 对结果做摘要或截断。
  • 只返回模型决策所需的关键字段。
  • 将大数据写入外部存储,仅把引用 ID 回填。

7.5 人机协同与安全确认

对高风险 Skill(转账、删除、发布上线)设置“人类审批”环节。Agent 先提出调用意图,由人工确认后再执行,从而形成“模型建议、人类决策、系统执行”的闭环。

8. 总结与展望

Skill 是 AI Agent 从“语言模型”走向“业务助手”的关键抽象。它把零散的工具调用统一为“可描述、可发现、可组合、可治理”的能力单元。一个稳健的 Skill 系统通常包含注册表、发现层、执行层、能力实现层,并在权限、审计、超时、幂等、结果回填等方面做足工程化。

本文给出的 Python 骨架虽然简单,但已经具备了扩展为生产级系统的基础。未来,随着 MCP 等标准协议的成熟,Skills 有望进一步标准化,成为类似“软件包”一样可共享、可编排、可交易的 AI 能力资产。

对开发者来说,现在开始有意识地把业务能力沉淀为 Skills,比以后被动重构要容易得多。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐