摘要:随着大模型(LLM)从“聊天机器人”向“智能体(Agent)”演进,Skill(技能)已经成为 AI 能够真正操作现实世界的核心载体。本文将系统讲解 Skill 的技术本质、架构设计、通信协议(Function Calling / MCP)、开发全流程,并手把手带你从零构建一个可运行的 “天气查询 Skill”,最后探讨企业级 Skill 生态的构建思路。


一、什么是 Skill?—— 重新定义 AI 的能力边界

1.1 从 Chatbot 到 Agent:为什么需要 Skill?

早期的 ChatGPT 类应用本质上是一个文本预测引擎。它拥有海量的世界知识,但它被囚禁在数字世界里。它无法告诉你现在的天气,无法帮你订外卖,也无法查询你的私人日程。

Skill 就是打破这层壁垒的钥匙。

  • 没有 Skill 的 LLM:大脑发达,但手脚瘫痪。

  • 拥有 Skill 的 LLM:拥有了眼睛(视觉识别)、耳朵(语音识别)、手(执行 API 调用)和脚(导航)。

定义:Skill 是一种封装了特定业务能力、可被 AI Agent 动态发现、理解并调用的标准化接口模块。它允许大模型将用户的自然语言意图转化为具体的程序指令,从而完成感知、决策和执行。

1.2 Skill vs Plugin vs Function Calling vs Tool

这几个术语经常混用,但它们处于不同的抽象层级:

术语

定位

关系

Tool (工具)

最底层的具体实现。一段代码或一个 API 端点。

Skill 的组成部分。

Function Calling

一种技术机制(由 OpenAI 定义)。告诉模型有哪些函数可用,让模型输出调用这些函数的 JSON 参数。

实现 Skill 调用的主流技术手段。

Plugin (插件)

早期的概念(如 ChatGPT Plugins)。通常包含 API 描述文件和清单文件。

Skill 的早期形态,现多已被 Function Calling 和 MCP 取代。

Skill (技能)

业务视角的封装。它不仅仅是一个函数,可能包含鉴权、缓存、错误处理、业务逻辑编排。

面向 Agent 的最终交付物。

一句话总结:我们使用 Function Calling(或 MCP)技术,将底层的 Tools​ 封装成业务级的 Skills,供 Agent 使用。

1.3 Skill 的核心特征

  1. 自描述性 (Self-Describing):Skill 必须包含一个清晰的 Schema(模式),告诉 AI 它是干什么的,需要什么参数,返回什么结果。

  2. 幂等性 (Idempotency):理想情况下,多次调用同一个 Skill 产生的副作用应该是相同的(例如,查询天气是只读的,天然幂等;转账则需要通过唯一 ID 防止重复扣款)。

  3. 安全性 (Security):Skill 往往涉及数据隐私和系统权限,必须有严格的沙箱机制和权限控制。

  4. 可组合性 (Composability):高级 Agent 可以将多个 Skill 串联起来(ReAct 模式),完成复杂任务。


二、Skill 的技术原理与架构

2.1 经典的交互流程 (ReAct + Function Calling)

一个典型的 Skill 调用流程如下:

  1. 用户输入:"北京今天适合穿什么衣服?"

  2. 意图识别:LLM 分析发现,要回答这个问题,需要先知道北京的天气。

  3. 工具选择:LLM 在系统提示词(System Prompt)提供的 Skill 列表中,找到了 get_weather 这个 Skill。

  4. 参数提取:LLM 提取出参数:location = "Beijing"

  5. 输出指令:LLM 停止生成自然语言,转而输出一段结构化的 JSON:{"name": "get_weather", "arguments": {"location": "Beijing"}}

  6. 执行代码:后端程序解析这段 JSON,调用真实的 get_weather("Beijing") 函数(可能是调用第三方天气 API)。

  7. 返回结果:函数返回 "Sunny, 25°C"

  8. 二次推理:后端将结果塞回对话上下文,LLM 再次接管,结合天气数据生成最终回答:"北京今天晴天,25度,建议穿轻薄的长袖衬衫。"

2.2 Skill 的系统架构

一个生产级的 Skill 架构通常包含以下层次:

┌─────────────────────────────────────────────┐
│            AI Agent / Orchestrator           │
│  (负责思考、规划、调用 Skill)                 │
└───────────────────────┬─────────────────────┘
                        │  JSON Instruction
┌───────────────────────▼─────────────────────┐
│         Skill Gateway / Proxy                │
│  (鉴权、限流、日志、参数校验)                 │
└───────────────────────┬─────────────────────┘
                        │  Internal Call
┌───────────────────────▼─────────────────────┐
│             Skill Executor                   │
│  ┌─────────┐ ┌─────────┐ ┌─────────────────┐│
│  │ Weather │ │ Search  │ │ Internal DB CRUD││
│  │  Skill  │ │  Skill  │ │     Skill       ││
│  └─────────┘ └─────────┘ └─────────────────┘│
└───────────────────────┬─────────────────────┘
                        │  HTTP/gRPC
┌───────────────────────▼─────────────────────┐
│          External Services (APIs)            │
│      (Weather.com, Google, Internal RPC)     │
└─────────────────────────────────────────────┘

2.3 两种主流协议:OpenAI Function Calling vs MCP

2.3.1 OpenAI Function Calling

这是目前最普及的方案。开发者定义一个 JSON Schema 来描述函数。

优点:生态成熟,各大模型厂商(OpenAI, Anthropic, Gemini, 国产大模型)基本都兼容。

缺点:强依赖于 Prompt Engineering,缺乏标准化的生命周期管理。

2.3.2 MCP (Model Context Protocol)

由 Anthropic 提出的开放协议,旨在成为 AI 应用的“USB-C”接口。MCP 定义了一个标准的客户端-服务器架构。

  • MCP Host:Claude Desktop, IDE 等。

  • MCP Client:Host 内的连接器。

  • MCP Server:封装了具体 Skill 的服务端。

优点:解耦彻底,支持双向通信(Server 可以主动请求资源),更适合复杂的本地工具集成(如操作文件系统、数据库)。

缺点:相对较新,生态还在建设中。

本文后续示例将主要基于 OpenAI Function Calling 风格,因为这是目前 Web 服务开发中最通用的方式。


三、Skill 的设计哲学与最佳实践

3.1 单一职责原则 (SRP)

一个 Skill 只做一件事。

❌ 错误示例:handle_user_request(处理查询、修改、删除用户)。

✅ 正确示例:query_user_by_id, update_user_email

3.2 良好的命名与描述 (Naming & Description)

LLM 不是编译器,它是通过语义来理解 Skill 的。命名和描述比代码本身更重要。

  • 函数名:使用动词+名词,如 calculate_loan_interest

  • 描述:详细解释功能、适用场景和限制。

    • 差:Get weather.

    • 好:Retrieves the current weather for a specified city. Use this when the user asks about temperature, humidity, or weather conditions. Note: Supports Chinese and English city names.

3.3 参数设计的艺术

  1. 枚举约束 (Enums):如果参数是固定的几个值,一定要用 enum,这能极大降低幻觉。

    • 例如:unit 参数只能是 ["celsius", "fahrenheit"]

  2. 必填与选填:区分 required 字段。对于非必填项,在描述中说明默认值。

  3. 自然语言兜底:有时候用户会说“明天”而不是日期。可以在 Skill 内部做一个日期解析层,或者让 LLM 调用一个专门的 parse_date Skill。

3.4 防御性编程

永远不要相信 LLM 的输出。

  • 类型校验:即使 Schema 定义了 Integer,也要在代码中验证。

  • 范围校验:如果是查询分页,检查 page_size 是否超过上限。

  • 注入防护:如果 Skill 涉及数据库查询,防止 SQL 注入(虽然 LLM 输出的是参数,但仍需警惕)。


四、动手实践:搭建你的第一个 Skill

接下来,我们将使用 Python + FastAPI​ 搭建一个简单的 Web 服务,并实现一个 “天气查询 Skill”

4.1 环境准备

确保安装了 Python 3.9+。

mkdir my_first_skill && cd my_first_skill
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate
pip install fastapi uvicorn httpx openai python-dotenv

创建 .env 文件存放 API Key:

OPENAI_API_KEY="sk-your_key_here"
# 或者使用兼容 OpenAI 接口的国内模型
# OPENAI_BASE_URL="https://api.moonshot.cn/v1"

4.2 步骤一:编写 Skill 的核心逻辑(Tool)

我们先不关心 AI,直接写一个纯粹的天气查询函数。

创建 weather_tool.py

import httpx
import os
from typing import Dict, Any

# 这里使用 Open-Meteo (免费,无需 API Key)
# 但为了演示,我们假设有一个需要 Key 的商业 API 逻辑
# 实际调用:https://api.weatherapi.com/v1/current.json?key=KEY&q=Beijing

class WeatherService:
    def __init__(self):
        # 为了演示,我们不真的调用收费 API,而是 Mock 数据
        # self.api_key = os.getenv("WEATHER_API_KEY")
        # self.base_url = "https://api.weatherapi.com/v1"
        pass

    async def get_current_weather(self, location: str, unit: str = "celsius") -> Dict[str, Any]:
        """
        模拟获取当前天气。
        在实际应用中,这里会调用 httpx 请求第三方 API。
        """
        # Mock Data based on location
        mock_db = {
            "beijing": {"temp_c": 25, "condition": "Sunny", "humidity": 40},
            "shanghai": {"temp_c": 28, "condition": "Cloudy", "humidity": 70},
            "new york": {"temp_c": 15, "condition": "Rainy", "humidity": 90}
        }
        
        loc_key = location.lower()
        if loc_key not in mock_db:
            return {"error": f"Weather data for '{location}' not found."}

        data = mock_db[loc_key]
        
        # 单位转换
        temp = data["temp_c"]
        if unit == "fahrenheit":
            temp = (temp * 9/5) + 32
            
        return {
            "location": location,
            "temperature": round(temp, 1),
            "unit": unit,
            "condition": data["condition"],
            "humidity": data["humidity"]
        }

# 实例化服务
weather_service = WeatherService()

4.3 步骤二:定义 Skill 的 Schema(说明书)

这是最关键的一步。我们需要告诉 LLM 如何调用这个函数。

创建 skill_definition.py

SKILL_SCHEMA = {
    "name": "get_current_weather",
    "description": "Get the current weather for a specific location. Use this whenever the user asks about the weather, temperature, or climate conditions in a city.",
    "parameters": {
        "type": "object",
        "properties": {
            "location": {
                "type": "string",
                "description": "The city name, e.g., 'Beijing', 'London', 'New York'. Support both Chinese and English.",
            },
            "unit": {
                "type": "string",
                "enum": ["celsius", "fahrenheit"],
                "description": "The temperature unit. Defaults to celsius.",
            },
        },
        "required": ["location"],
    },
}

# 将所有 Skill 汇总
AVAILABLE_SKILLS = [SKILL_SCHEMA]

# 映射 Skill 名称到实际的执行函数
SKILL_EXECUTORS = {
    "get_current_weather": weather_service.get_current_weather
}

4.4 步骤三:搭建 FastAPI 服务与 Agent 逻辑

现在,我们将 Skill 挂载到一个 Web 服务中,并处理与 LLM 的交互。

创建 main.py

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from openai import AsyncOpenAI
from dotenv import load_dotenv
import json
import traceback

from weather_tool import weather_service
from skill_definition import AVAILABLE_SKILLS, SKILL_EXECUTORS

load_dotenv()

app = FastAPI(title="My First AI Skill Server")

# 初始化 OpenAI Client
client = AsyncOpenAI()

class ChatRequest(BaseModel):
    message: str
    history: list[dict] = [] # 用于维护多轮对话

@app.post("/chat")
async def chat_endpoint(request: ChatRequest):
    try:
        messages = request.history + [
            {"role": "user", "content": request.message}
        ]
        
        # 第一轮:让 LLM 决定是否需要调用 Skill
        response = await client.chat.completions.create(
            model="gpt-3.5-turbo", # 或者 "moonshot-v1-8k"
            messages=messages,
            tools=[{"type": "function", "function": s} for s in AVAILABLE_SKILLS],
            tool_choice="auto", # 自动决定是否调用工具
        )
        
        response_message = response.choices[0].message
        
        # 检查是否有工具调用请求
        if response_message.tool_calls:
            # 执行 Skill
            tool_call = response_message.tool_calls[0]
            function_name = tool_call.function.name
            
            if function_name not in SKILL_EXECUTORS:
                raise HTTPException(status_code=400, detail=f"Unknown skill: {function_name}")
            
            # 解析参数
            arguments = json.loads(tool_call.function.arguments)
            
            # 执行对应的函数
            function_response = await SKILL_EXECUTORS[function_name](**arguments)
            
            # 将消息历史拼接起来
            # 1. 用户的原始请求
            # 2. LLM 返回的带有 tool_calls 的消息
            # 3. 工具执行的结果
            messages.append(response_message)
            messages.append({
                "tool_call_id": tool_call.id,
                "role": "tool",
                "name": function_name,
                "content": json.dumps(function_response, ensure_ascii=False),
            })
            
            # 第二轮:将工具结果发回给 LLM,让它生成最终回复
            final_response = await client.chat.completions.create(
                model="gpt-3.5-turbo",
                messages=messages,
            )
            
            return {
                "role": "assistant", 
                "content": final_response.choices[0].message.content,
                "debug": {
                    "skill_called": function_name,
                    "arguments": arguments,
                    "raw_result": function_response
                }
            }
        else:
            # 如果不需要调用工具,直接返回 LLM 的回复
            return {
                "role": "assistant",
                "content": response_message.content
            }
            
    except Exception as e:
        print(traceback.format_exc())
        raise HTTPException(status_code=500, detail=str(e))

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

4.5 步骤四:运行与测试

  1. 启动服务:

    python main.py
  2. 使用 curl 或 Postman 测试:

    curl -X POST "http://localhost:8000/chat" \
    -H "Content-Type: application/json" \
    -d '{
        "message": "北京今天多少度?",
        "history": []
    }'

预期返回结果

{
    "role": "assistant",
    "content": "北京今天的气温是25°C,天气晴朗。",
    "debug": {
        "skill_called": "get_current_weather",
        "arguments": {
            "location": "北京",
            "unit": "celsius"
        },
        "raw_result": {
            "location": "北京",
            "temperature": 25.0,
            "unit": "celsius",
            "condition": "Sunny",
            "humidity": 40
        }
    }
}

🎉 恭喜!你已经成功构建了第一个 AI Skill。


五、进阶:构建企业级 Skill 生态

当你有了 10 个、100 个 Skill 时,上述简单的架构将面临挑战。企业级落地需要考虑更多维度。

5.1 Skill 注册中心 (Registry)

不能把所有的 Skill Schema 都硬编码在代码里。需要一个中心化的注册表(可以是数据库或配置文件)。

# skills/weather.yaml
name: get_current_weather
version: 1.0.0
description: ...
endpoint: http://internal-api/weather
auth: api_key
schema:
  type: object
  properties: ...

服务启动时,自动扫描并加载这些配置。

5.2 权限与隔离 (Auth & Isolation)

  • 用户级权限:用户 A 不能调用用户 B 的私有 Skill(如查询私人日历)。

  • 租户隔离:SaaS 环境下,Tenant A 的 Skill 不能被 Tenant B 看到。

  • 沙箱机制:如果 Skill 允许用户上传代码执行(极端危险),必须使用 Docker 或 WASM 进行强隔离。

5.3 异步 Skill (Async Skills)

有些任务耗时很长,比如“生成一张复杂的海报”或“训练一个模型”。

此时不能让 LLM 等待 HTTP 响应。

解决方案:异步回调机制

  1. LLM 调用 create_poster_task Skill。

  2. Skill 立即返回一个 task_id:“任务已提交,ID 为 xxx”。

  3. 后台 Worker 执行任务。

  4. 任务完成后,通过 WebSocket 或 Webhook 通知 Agent,Agent 再告知用户。

5.4 RAG + Skill 的结合

很多时候,用户的问题既需要知识检索,又需要工具调用。

例如:“帮我总结一下昨天关于 Q3 财报的邮件,并对比去年同期数据。”

  • RAG:检索昨天关于 Q3 财报的邮件内容。

  • Skill:调用 query_database 获取去年同期的财务数据。

  • LLM:融合两者生成总结。

这需要在 Prompt 层面进行精细编排,或者使用 LangChain/LlamaIndex 等框架的 Agent 模块。

5.5 监控与可观测性 (Observability)

你需要知道:

  • 哪个 Skill 调用最多?(热门功能)

  • 哪个 Skill 经常失败?(稳定性问题)

  • Token 消耗在哪里?(成本控制)

  • LLM 是否产生了幻觉参数?(质量评估)

建议使用 OpenTelemetry 或类似工具,为每个 Skill 调用生成 Trace ID。


六、常见陷阱与避坑指南

  1. 过度依赖 LLM 的推理能力:不要把复杂的业务逻辑完全交给 LLM 去判断。Skill 内部要有完整的校验和兜底逻辑。

  2. 忽略负面反馈:当 Skill 执行失败时,返回给 LLM 的错误信息要友好且具有指导性。例如,不要只返回 404,而是返回 {"error": "City not found, please check spelling or suggest nearby cities."}。这样 LLM 才能修正后重试。

  3. 上下文窗口溢出:Skill 返回的数据可能很大(例如长文档)。需要对返回内容进行截断、压缩或摘要,防止超出模型的上下文限制。

  4. 循环调用:Agent 可能会陷入死循环(调用 A -> 调用 B -> 发现需要 A -> 调用 A...)。需要设置最大调用步数(Max Steps),例如 ReAct 循环最多 10 次。


七、未来展望:Skill 将走向何方?

  1. 标准化 (Standardization):MCP 等协议的成熟将使得 Skill 像 NPM 包一样流通。你将不再需要从头写天气 Skill,而是直接 pip install mcp-weather-server

  2. GUI 自动化:Skill 不再局限于 API 调用。结合 Computer Use 模型(如 Claude 3.5 Sonnet),Skill 可以直接操作图形界面,点击按钮、填写表单,从而控制那些没有开放 API 的老旧软件。

  3. 自主进化:未来的 Agent 或许能够根据用户的需求,自动编写新的 Skill 代码并进行测试部署(Code -> Test -> Deploy),实现真正的自我进化。

  4. 去中心化市场:开发者可以将自己编写的优质 Skill 放到区块链上进行确权、交易和分发,形成一个繁荣的 AI 技能经济生态。


八、总结

Skill 是 AI Agent 的基石。它将大模型从“纸上谈兵”的理论家,变成了能够“撸起袖子加油干”的实干家。

构建 Skill 的过程,本质上是将人类的业务逻辑翻译成机器可执行、AI 可理解的接口。这要求我们不仅要懂后端开发,还要懂 Prompt Engineering,更要懂 AI 的行为模式。

回顾我们的第一个 Skill:

  1. 定义逻辑:编写了 get_current_weather 函数。

  2. 定义接口:创建了 JSON Schema,作为 AI 的“说明书”。

  3. 编排流程:实现了 ReAct 循环(思考 -> 调用 -> 观察 -> 回答)。

这看似简单的三步,正是通往通用人工智能应用的第一步。希望这篇长文能为你打开 AI Skill 开发的大门,期待你构建出改变世界的智能应用!


附录:推荐阅读与工具

  • OpenAI Function Calling Docs: 官方文档永远是最好的起点。

  • Model Context Protocol (MCP): Anthropic 的最新协议,值得关注。

  • LangChain Tools: 提供了大量开箱即用的 Tool 封装。

  • FastAPI: 构建 Skill 后端服务的首选框架。

  • Pydantic: 数据验证的利器,非常适合定义 Skill 参数模型。

📚 附录链接补全

  1. OpenAI Function Calling Docs

OpenAI 中文文档

https://platform.openai.com/docs/guides/function-calling

官方指南,含 Schema 定义、多工具调用、stream 模式等最新写法。

  1. Model Context Protocol (MCP)

    https://modelcontextprotocol.io

    Anthropic 主导的开放协议,SDK(Python / TypeScript)和 Server 样例都在这里。

  2. LangChain Tools

    https://python.langchain.com/docs/concepts/tools/

    LangChain 的 Tool / ToolCall / Agent 编排文档,开箱即用的 Tool 封装大全。

  3. FastAPI

    https://fastapi.tiangolo.com

    官方文档,含依赖注入、Pydantic 集成、异步路径,搭 Skill 后端首选。

  4. Pydantic

    https://docs.pydantic.dev

    V2 文档,重点看 BaseModelFieldmodel_validator——Skill 参数校验的核心。

本文涵盖了从原理到实战的内容。如果需要我针对特定场景(如企业内部系统、电商客服)为你设计一个更复杂的进阶版 Skill,请评论区留言!

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐