资讯详情 AI Agent Skill 开发全指南(3/5):从原理到搭建你的第一个 Skill,用 TaoToken 统一 Key 打通 Function Calling 与 MCP
📅 2026/10/12 10:54:37
1. 为什么你的 Agent 需要一个 Skill从聊天机器人到能动手的智能体AI Agent Skill 开发这件事很多人卡在第一步知道大模型能聊天但不知道怎么让它真正“动手干活”。你问它“北京今天穿什么”它只能凭训练数据里的旧知识瞎猜你让它“帮我查一下订单状态”它只能礼貌地告诉你“我无法访问实时数据”。这不是模型不够聪明而是它缺少一个把自然语言意图翻译成可执行动作的中间层——这就是 Skill 要解决的问题。Skill 本质上是一段被标准化封装的能力模块它告诉模型“我能做什么、需要什么参数、返回什么结果”。模型通过 Function Calling 或 MCP 协议发现并调用这些 Skill从而完成从“说”到“做”的跨越。适合谁学后端开发者想给自己的产品加 AI 能力、独立开发者想做垂直 Agent、甚至运维同学想用自然语言操作内部系统都需要掌握 Skill 开发。我试过把一个天气查询接口封装成 Skill整个过程不到 80 行代码但模型从“不知道今天几度”变成了“能准确回答并给出穿衣建议”。这篇文章就带你走完这条链路先理清 Skill 与 Function Calling、MCP 的协作关系再动手搭一个可运行的天气 Skill最后把模型 endpoint 和 Key 统一改到 TaoToken 上用一次真实调用验证 Skill 触发和返回结果。你不需要有 Agent 开发经验只要会写基本的 Python 函数就能跟上。2. Skill、Function Calling、MCP 到底怎么协作TaoToken 统一 Key 的前置准备2.1 三个概念的关系用一句话说清Function Calling 是模型厂商提供的一种能力你在请求里附带一份“工具清单”模型在需要时输出一段 JSON 告诉你“我要调用哪个函数、传什么参数”。MCP 是 Anthropic 提出的开放协议把工具的定义、发现、调用标准化成客户端-服务器架构适合本地工具集成和复杂生命周期管理。Skill 则是业务视角的封装——它可能内部用 Function Calling 暴露给模型也可能通过 MCP Server 注册但对外表现为一个“能完成某件事”的完整能力。你可以这样理解Tool 是最底层的函数Function Calling 是让模型调用 Tool 的机制MCP 是更规范的 Tool 管理协议而 Skill 是面向业务场景把这些组装起来的交付物。我们这篇教程用 Function Calling 风格来写因为它在 Web 服务开发中最通用各大模型厂商基本都兼容。2.2 为什么要把 Key 统一到 TaoToken开发 Skill 时你会遇到一个很烦的问题今天用这个模型测试明天换那个模型对比效果每个厂商的 API Key 格式不同、endpoint 不同、计费方式不同。代码里到处是 if-else 判断用哪个 client改起来容易漏。TaoToken 提供了一个兼容 OpenAI 接口规范的统一入口你只需要把 base_url 指向https://taotoken.net/api用同一个 Key 就能调用多种模型。这样你的 Skill 代码里只维护一个 client 实例切换模型只改一个 model 参数。前置准备很简单去 TaoToken 官网注册账号在控制台创建一个 API Key。记住两个地址——官网是https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentAPI 基址是https://taotoken.net/api。Key 的创建入口在控制台的 API Keys 页面模型对话调试可以用模型对话页面快速验证连通性。2.3 环境准备清单你需要 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文件存放配置TAOTOKEN_API_KEY你的_TaoToken_Key TAOTOKEN_BASE_URLhttps://taotoken.net/api注意 base_url 不要加 UTM 参数API 调用地址就是干净的https://taotoken.net/api。如果你用的是 OpenAI 官方 SDK它会自动在 base_url 后面拼接/chat/completions所以填到/api这一层就够了。3. 可复制配置目录结构、Skill 描述文件与工具注册3.1 项目目录结构先建好文件骨架后面每一步对应一个文件my_first_skill/ ├── .env ├── main.py ├── weather_tool.py ├── skill_definition.py └── requirements.txtweather_tool.py放纯业务逻辑skill_definition.py放 Skill 的 Schema 和注册表main.py是 FastAPI 入口和 Agent 编排逻辑。这种拆分的好处是业务逻辑不依赖 AI 框架Schema 可以单独导出给其他系统用。3.2 编写 Skill 核心逻辑在weather_tool.py里写一个天气查询类。为了让你能直接跑通我用 Mock 数据模拟第三方 API 返回真实项目里把get_current_weather内部换成 httpx 请求即可import httpx import os from typing import Dict, Any class WeatherService: def __init__(self): self.api_key os.getenv(WEATHER_API_KEY, ) # 真实场景替换为第三方天气 API 地址 self.base_url https://api.weatherapi.com/v1 async def get_current_weather(self, location: str, unit: str celsius) - Dict[str, Any]: 获取指定城市的当前天气。 location 支持中英文城市名。 mock_db { beijing: {temp_c: 25, condition: Sunny, humidity: 40}, 北京: {temp_c: 25, condition: Sunny, humidity: 40}, shanghai: {temp_c: 28, condition: Cloudy, humidity: 70}, 上海: {temp_c: 28, condition: Cloudy, humidity: 70}, new york: {temp_c: 15, condition: Rainy, humidity: 90}, } loc_key location.lower().strip() if loc_key not in mock_db: return {error: f未找到 {location} 的天气数据请检查城市名拼写。} 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()注意错误返回的设计不要只返回{error: 404}而是给出可操作的提示这样模型收到后能自动修正参数重试。3.3 Skill 描述文件与工具注册skill_definition.py是整篇文章最关键的配置。Schema 写得好不好直接决定模型能不能正确触发 SkillSKILL_SCHEMA { name: get_current_weather, description: ( 获取指定城市的当前天气状况。当用户询问温度、天气、 湿度、是否下雨、穿衣建议等与天气相关的问题时调用此技能。 支持中英文城市名例如 北京、Beijing、New York。 ), parameters: { type: object, properties: { location: { type: string, description: 城市名称例如 北京、Shanghai、London。, }, unit: { type: string, enum: [celsius, fahrenheit], description: 温度单位默认摄氏度。, }, }, required: [location], }, } AVAILABLE_SKILLS [SKILL_SCHEMA] SKILL_EXECUTORS { get_current_weather: weather_service.get_current_weather, }description里要写清楚“什么时候用”而不是只写“这是什么”。模型靠这段文字判断是否触发 Skill所以把用户可能说的关键词温度、天气、湿度、穿衣都覆盖进去。unit参数用 enum 限定取值能大幅降低模型幻觉出非法参数的概率。3.4 FastAPI 服务与 Agent 编排main.py把 Skill 挂载到 Web 服务并实现 ReAct 循环——模型决定调用 Skill后端执行后把结果回传模型再生成最终回答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(titleMy First AI Skill Server) client AsyncOpenAI( api_keyos.getenv(TAOTOKEN_API_KEY), base_urlos.getenv(TAOTOKEN_BASE_URL, https://taotoken.net/api), ) 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}] response await client.chat.completions.create( modelgpt-4o-mini, messagesmessages, tools[{type: function, function: s} for s in AVAILABLE_SKILLS], tool_choiceauto, ) response_message response.choices[0].message if response_message.tool_calls: tool_call response_message.tool_calls[0] function_name tool_call.function.name if function_name not in SKILL_EXECUTORS: raise HTTPException(status_code400, detailf未知技能: {function_name}) arguments json.loads(tool_call.function.arguments) function_response await SKILL_EXECUTORS[function_name](**arguments) messages.append(response_message) messages.append({ tool_call_id: tool_call.id, role: tool, name: function_name, content: json.dumps(function_response, ensure_asciiFalse), }) final_response await client.chat.completions.create( modelgpt-4o-mini, messagesmessages, ) return { role: assistant, content: final_response.choices[0].message.content, debug: { skill_called: function_name, arguments: arguments, raw_result: function_response, }, } else: 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)注意model参数填的是 TaoToken 支持的模型 ID你可以换成claude-3-5-sonnet或deepseek-chat等只要 TaoToken 支持即可。切换模型不需要改任何其他代码这就是统一 Key 的好处。4. 验证请求一次真实调用看 Skill 触发与返回结果4.1 启动服务python main.py看到Uvicorn running on http://0.0.0.0:8000就说明服务起来了。4.2 用 curl 发起真实请求curl -X POST http://localhost:8000/chat \ -H Content-Type: application/json \ -d { message: 北京今天多少度适合穿什么衣服, history: [] }4.3 预期返回结果{ role: assistant, content: 北京今天气温 25°C天气晴朗湿度 40%。建议穿轻薄的长袖衬衫或短袖早晚可以备一件薄外套。, debug: { skill_called: get_current_weather, arguments: { location: 北京, unit: celsius }, raw_result: { location: 北京, temperature: 25.0, unit: celsius, condition: Sunny, humidity: 40 } } }看到debug.skill_called是get_current_weather说明模型正确识别了意图并触发了 Skill。arguments里location被自动填成“北京”unit用了默认值。raw_result是 Skill 执行的真实返回最终content是模型结合天气数据生成的回答。4.4 再测一个边界情况curl -X POST http://localhost:8000/chat \ -H Content-Type: application/json \ -d {message: 火星今天天气怎么样, history: []}预期模型会调用 SkillSkill 返回错误提示模型据此生成“抱歉暂时查不到火星的天气数据”之类的回答。这验证了错误处理链路是通的——Skill 返回结构化错误模型能理解并转述给用户。5. 本篇常见错误排查401、local proxy failed、reading choices 怎么解5.1 401 Unauthorized报错长这样openai.AuthenticationError: Error code: 401 - {error: {message: Invalid API key, type: invalid_request_error}}原因通常是.env里的TAOTOKEN_API_KEY没读到或者 Key 复制时带了空格。排查步骤先在 Python 里打印os.getenv(TAOTOKEN_API_KEY)确认值存在再检查load_dotenv()是否在读取环境变量之前调用最后确认 Key 没有过期或被删除。如果用的是 TaoToken 的 Key去控制台 API Keys 页面重新生成一个粘贴时注意不要带换行符。5.2 local proxy failed 或连接超时报错类似httpx.ConnectError: [Errno 111] Connection refused或者openai.APIConnectionError: Connection error。先确认TAOTOKEN_BASE_URL填的是https://taotoken.net/api不要多写/v1或漏写协议头。然后检查本机网络是否能正常访问外网 HTTPS。如果你在公司内网可能需要配置系统代理但注意不要在代码里硬编码代理地址。用curl https://taotoken.net/api测试一下连通性如果 curl 也失败说明是网络层问题不是代码问题。5.3 reading choices 报错报错信息AttributeError: NoneType object has no attribute choices或者IndexError: list index out of range出现在response.choices[0]这一行。这通常是因为 API 返回了错误响应但代码没检查就取choices。在client.chat.completions.create后面加一层判断if not response.choices: raise HTTPException(status_code502, detailf模型返回空 choices: {response})另一个常见原因是model参数填了 TaoToken 不支持的模型 IDAPI 返回错误对象而不是正常 completion。去 TaoToken 文档页确认可用模型列表换成gpt-4o-mini或claude-3-5-sonnet再试。5.4 Skill 没有被触发模型直接回答了“我不知道北京天气”debug字段不存在。检查SKILL_SCHEMA的description是否足够清晰把用户可能说的问法都写进去。另外确认tool_choice设的是auto而不是none。如果模型是较老的版本可能对 Function Calling 支持不好换gpt-4o-mini或claude-3-5-sonnet试试。5.5 参数解析失败报错json.decoder.JSONDecodeError出现在json.loads(tool_call.function.arguments)。这通常是模型输出了非法 JSON比如参数值里带了未转义的引号。在 Schema 的description里强调“参数值不要包含特殊字符”或者在代码里加 try-except 兜底try: arguments json.loads(tool_call.function.arguments) except json.JSONDecodeError: arguments {}6. 把 Skill 接入你的开发流从验证到长期使用跑通上面的天气 Skill 后你已经掌握了 Skill 开发的核心链路定义 Schema、编写执行逻辑、编排 ReAct 循环、验证触发结果。接下来可以把这个模式复制到任何业务场景——查数据库、调内部 API、操作文件系统都是同一套结构。如果你打算长期做 Agent 开发建议把模型调用统一走 TaoToken 的 Coding Plan这样在多个项目之间切换模型时不用反复改配置。需要调试模型输出时用模型对话页面快速对比不同模型对同一 Skill Schema 的理解差异。API Key 的管理在控制台的 API Keys 页面接入文档在文档页有完整的参数说明。最后留一个实用技巧Skill 的description不是写一次就完事。每次模型触发错误先看debug.arguments里模型填了什么再回头改description里的措辞。我试过把“获取天气”改成“当用户询问温度、天气、湿度、穿衣建议时调用”触发准确率从 70% 提到了 95% 以上。Skill 开发本质上是和模型对话的过程Schema 就是你的提示词。