摘要:随着大模型(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 的核心特征
自描述性 (Self-Describing):Skill 必须包含一个清晰的 Schema(模式),告诉 AI 它是干什么的,需要什么参数,返回什么结果。
幂等性 (Idempotency):理想情况下,多次调用同一个 Skill 产生的副作用应该是相同的(例如,查询天气是只读的,天然幂等;转账则需要通过唯一 ID 防止重复扣款)。
安全性 (Security):Skill 往往涉及数据隐私和系统权限,必须有严格的沙箱机制和权限控制。
可组合性 (Composability):高级 Agent 可以将多个 Skill 串联起来(ReAct 模式),完成复杂任务。
二、Skill 的技术原理与架构
2.1 经典的交互流程 (ReAct + Function Calling)
一个典型的 Skill 调用流程如下:
用户输入:"北京今天适合穿什么衣服?"
意图识别:LLM 分析发现,要回答这个问题,需要先知道北京的天气。
工具选择:LLM 在系统提示词(System Prompt)提供的 Skill 列表中,找到了
get_weather这个 Skill。参数提取:LLM 提取出参数:
location = "Beijing"。输出指令:LLM 停止生成自然语言,转而输出一段结构化的 JSON:
{"name": "get_weather", "arguments": {"location": "Beijing"}}。执行代码:后端程序解析这段 JSON,调用真实的
get_weather("Beijing")函数(可能是调用第三方天气 API)。返回结果:函数返回
"Sunny, 25°C"。二次推理:后端将结果塞回对话上下文,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 参数设计的艺术
枚举约束 (Enums):如果参数是固定的几个值,一定要用
enum,这能极大降低幻觉。例如:
unit参数只能是["celsius", "fahrenheit"]。
必填与选填:区分
required字段。对于非必填项,在描述中说明默认值。自然语言兜底:有时候用户会说“明天”而不是日期。可以在 Skill 内部做一个日期解析层,或者让 LLM 调用一个专门的
parse_dateSkill。
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 步骤四:运行与测试
启动服务:
python main.py使用 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 响应。
解决方案:异步回调机制。
LLM 调用
create_poster_taskSkill。Skill 立即返回一个
task_id:“任务已提交,ID 为 xxx”。后台 Worker 执行任务。
任务完成后,通过 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。
六、常见陷阱与避坑指南
过度依赖 LLM 的推理能力:不要把复杂的业务逻辑完全交给 LLM 去判断。Skill 内部要有完整的校验和兜底逻辑。
忽略负面反馈:当 Skill 执行失败时,返回给 LLM 的错误信息要友好且具有指导性。例如,不要只返回
404,而是返回{"error": "City not found, please check spelling or suggest nearby cities."}。这样 LLM 才能修正后重试。上下文窗口溢出:Skill 返回的数据可能很大(例如长文档)。需要对返回内容进行截断、压缩或摘要,防止超出模型的上下文限制。
循环调用:Agent 可能会陷入死循环(调用 A -> 调用 B -> 发现需要 A -> 调用 A...)。需要设置最大调用步数(Max Steps),例如 ReAct 循环最多 10 次。
七、未来展望:Skill 将走向何方?
标准化 (Standardization):MCP 等协议的成熟将使得 Skill 像 NPM 包一样流通。你将不再需要从头写天气 Skill,而是直接
pip install mcp-weather-server。GUI 自动化:Skill 不再局限于 API 调用。结合 Computer Use 模型(如 Claude 3.5 Sonnet),Skill 可以直接操作图形界面,点击按钮、填写表单,从而控制那些没有开放 API 的老旧软件。
自主进化:未来的 Agent 或许能够根据用户的需求,自动编写新的 Skill 代码并进行测试部署(Code -> Test -> Deploy),实现真正的自我进化。
去中心化市场:开发者可以将自己编写的优质 Skill 放到区块链上进行确权、交易和分发,形成一个繁荣的 AI 技能经济生态。
八、总结
Skill 是 AI Agent 的基石。它将大模型从“纸上谈兵”的理论家,变成了能够“撸起袖子加油干”的实干家。
构建 Skill 的过程,本质上是将人类的业务逻辑翻译成机器可执行、AI 可理解的接口。这要求我们不仅要懂后端开发,还要懂 Prompt Engineering,更要懂 AI 的行为模式。
回顾我们的第一个 Skill:
定义逻辑:编写了
get_current_weather函数。定义接口:创建了 JSON Schema,作为 AI 的“说明书”。
编排流程:实现了 ReAct 循环(思考 -> 调用 -> 观察 -> 回答)。
这看似简单的三步,正是通往通用人工智能应用的第一步。希望这篇长文能为你打开 AI Skill 开发的大门,期待你构建出改变世界的智能应用!
附录:推荐阅读与工具
OpenAI Function Calling Docs: 官方文档永远是最好的起点。
Model Context Protocol (MCP): Anthropic 的最新协议,值得关注。
LangChain Tools: 提供了大量开箱即用的 Tool 封装。
FastAPI: 构建 Skill 后端服务的首选框架。
Pydantic: 数据验证的利器,非常适合定义 Skill 参数模型。
📚 附录链接补全
OpenAI Function Calling Docs
OpenAI 中文文档
https://platform.openai.com/docs/guides/function-calling
官方指南,含 Schema 定义、多工具调用、stream 模式等最新写法。
Model Context Protocol (MCP)
https://modelcontextprotocol.io
Anthropic 主导的开放协议,SDK(Python / TypeScript)和 Server 样例都在这里。
LangChain Tools
https://python.langchain.com/docs/concepts/tools/
LangChain 的 Tool / ToolCall / Agent 编排文档,开箱即用的 Tool 封装大全。
FastAPI
https://fastapi.tiangolo.com
官方文档,含依赖注入、Pydantic 集成、异步路径,搭 Skill 后端首选。
Pydantic
https://docs.pydantic.dev
V2 文档,重点看
BaseModel、Field、model_validator——Skill 参数校验的核心。
本文涵盖了从原理到实战的内容。如果需要我针对特定场景(如企业内部系统、电商客服)为你设计一个更复杂的进阶版 Skill,请评论区留言!