AI Agent Skill(技能)开发全指南(3/5):从原理到搭建你的第一个 Skill
摘要随着大模型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 MCP2.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 HostClaude Desktop, IDE 等。MCP ClientHost 内的连接器。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 KeyOPENAI_API_KEYsk-your_key_here # 或者使用兼容 OpenAI 接口的国内模型 # OPENAI_BASE_URLhttps://api.moonshot.cn/v14.2 步骤一编写 Skill 的核心逻辑Tool我们先不关心 AI直接写一个纯粹的天气查询函数。创建weather_tool.pyimport httpx import os from typing import Dict, Any # 这里使用 Open-Meteo (免费无需 API Key) # 但为了演示我们假设有一个需要 Key 的商业 API 逻辑 # 实际调用https://api.weatherapi.com/v1/current.json?keyKEYqBeijing 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: fWeather 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.pySKILL_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.pyfrom 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(titleMy 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( modelgpt-3.5-turbo, # 或者 moonshot-v1-8k messagesmessages, tools[{type: function, function: s} for s in AVAILABLE_SKILLS], tool_choiceauto, # 自动决定是否调用工具 ) 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_code400, detailfUnknown 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_asciiFalse), }) # 第二轮将工具结果发回给 LLM让它生成最终回复 final_response await client.chat.completions.create( modelgpt-3.5-turbo, messagesmessages, ) 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_code500, detailstr(e)) if __name__ __main__: import uvicorn uvicorn.run(app, host0.0.0.0, port8000)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 通知 AgentAgent 再告知用户。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 SonnetSkill 可以直接操作图形界面点击按钮、填写表单从而控制那些没有开放 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 DocsOpenAI 中文文档https://platform.openai.com/docs/guides/function-calling官方指南含 Schema 定义、多工具调用、stream 模式等最新写法。Model Context Protocol (MCP)https://modelcontextprotocol.ioAnthropic 主导的开放协议SDKPython / TypeScript和 Server 样例都在这里。LangChain Toolshttps://python.langchain.com/docs/concepts/tools/LangChain 的 Tool / ToolCall / Agent 编排文档开箱即用的 Tool 封装大全。FastAPIhttps://fastapi.tiangolo.com官方文档含依赖注入、Pydantic 集成、异步路径搭 Skill 后端首选。Pydantichttps://docs.pydantic.devV2 文档重点看BaseModel、Field、model_validator——Skill 参数校验的核心。本文涵盖了从原理到实战的内容。如果需要我针对特定场景如企业内部系统、电商客服为你设计一个更复杂的进阶版 Skill请评论区留言