
1. 项目概述从零到一解锁你的第一个AI技能最近在开发者圈子里Skill技能这个词的热度一直居高不下。无论是想给AI助手增加一个查询天气的小功能还是想打造一个能自动整理会议纪要的智能工具Skill开发都成了实现这些想法的直接入口。但很多刚入门的朋友一看到“开发”两个字就有点发怵觉得这背后是复杂的代码、晦涩的文档和一堆看不懂的术语。其实写一个能用的Skill真没想象中那么难。今天我就以一个过来人的身份手把手带你走一遍完整的流程从环境搭建到代码调试再到最终部署保证你跟着做下来就能拥有一个属于你自己的、真正能跑起来的Skill。我们不会去碰那些复杂的底层框架就从最实用、最能立刻看到效果的功能开始。想象一下你只需要对AI说一句话它就能帮你完成一项特定任务这种“创造”的成就感是单纯使用工具无法比拟的。这篇内容就是为你准备的无论你是编程零基础的小白还是有一定基础想快速上手的开发者都能找到清晰的路径。2. 核心思路与工具选型为什么选这条路在开始敲代码之前想清楚“怎么做”比“做什么”更重要。市面上能用来开发Skill的平台和框架不少比如OpenAI的GPTs、Claude的Custom Actions或是基于开源模型自建后端。对于新手来说我们的核心目标是低门槛、快反馈、易部署。基于这个原则我强烈推荐从“函数调用Function Calling 轻量级Web服务”这个组合拳入手。为什么是函数调用这是目前大模型与外部世界交互最主流、最标准的方式。你不用去学习某个平台特定的插件语法或SDK只需要按照OpenAI等主流API定义的JSON格式告诉模型“你有什么能力”即函数描述模型在理解用户意图后就会返回“请调用哪个函数并传入什么参数”。剩下的就是你用熟悉的编程语言比如Python去执行这个函数逻辑。这种方式通用性强一次学习多处适用。为什么是轻量级Web服务因为你的Skill逻辑需要有一个在线的、随时可访问的“家”。当AI模型决定调用你的技能时它会向这个“家”一个特定的URL发送请求。我们不需要一开始就搞什么Kubernetes、Docker集群用一个简单的Python Web框架比如FastAPI就完全足够了。它写法直观性能也不错非常适合快速原型开发。工具栈确定如下编程语言Python。语法简洁生态丰富是AI领域的事实标准。Web框架FastAPI。自动生成交互式API文档异步支持好部署简单。模型APIOpenAI ChatGPT API 或 国内可顺畅访问的同类大模型API如DeepSeek、智谱GLM等。我们主要利用其函数调用能力。开发环境VSCode。轻量、插件丰富对Python和API测试支持极好。部署服务可选后期Vercel、Railway 或 国内的云服务如腾讯云云函数SCF。它们支持直接部署Python Web应用有免费额度。这个方案避开了需要复杂配置的本地模型部署也绕开了某些平台封闭的生态让你聚焦于核心逻辑。它的流程非常清晰用户对话 - AI模型理解并请求调用函数 - 你的Web服务处理请求并执行真实逻辑 - 返回结果给AI - AI组织语言回复用户。你只需要关心中间“执行真实逻辑”那一步。3. 环境准备与基础搭建3.1 Python与必要库的安装首先确保你的电脑上安装了Python。建议使用Python 3.8或以上版本。去Python官网下载安装包安装时务必勾选“Add Python to PATH”这样才能在命令行里直接使用。安装好后打开终端Windows上是CMD或PowerShellMac/Linux上是Terminal我们创建一个专属的项目目录并安装核心库。# 创建一个项目文件夹 mkdir my_first_skill cd my_first_skill # 创建虚拟环境强烈推荐避免包冲突 python -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # Mac/Linux: source venv/bin/activate # 安装核心库 pip install fastapi uvicorn openai python-dotenv requests这里简单解释一下每个库的作用fastapiuvicorn前者是我们的Web框架后者是ASGI服务器用于运行FastAPI应用。openaiOpenAI官方的Python SDK方便我们调用API。如果你选用其他国内模型可能需要安装对应的SDK如zhipuai、dashscope等。python-dotenv用于管理环境变量比如你的API密钥不要硬编码在代码里。requests一个通用的HTTP库如果你的Skill需要调用其他外部API比如查天气、查股票会用到它。3.2 初始化FastAPI应用与API密钥配置在项目根目录下创建两个文件.env和main.py。首先在.env文件中存放你的敏感信息OPENAI_API_KEY你的OpenAI_API密钥 # 如果使用其他模型例如 # DEEPSEEK_API_KEY你的DeepSeek_API密钥 # 国内模型平台通常提供免费额度足够学习和测试注意.env文件务必添加到.gitignore中千万不要提交到公开的代码仓库否则密钥泄露会带来财产损失。然后编写main.py的基础骨架from fastapi import FastAPI from pydantic import BaseModel import openai import os from dotenv import load_dotenv # 加载环境变量 load_dotenv() # 初始化FastAPI应用 app FastAPI(title我的第一个Skill服务) # 配置OpenAI客户端示例可根据需要替换为其他模型客户端 openai.api_key os.getenv(OPENAI_API_KEY) # 定义请求/响应数据模型 class FunctionCallRequest(BaseModel): arguments: dict # 模型传来的函数参数 app.get(/) def read_root(): return {message: Skill服务运行正常} if __name__ __main__: import uvicorn uvicorn.run(app, host0.0.0.0, port8000)现在在终端运行python main.py访问http://localhost:8000你应该能看到{message: Skill服务运行正常}的JSON响应。访问http://localhost:8000/docs你会看到FastAPI自动生成的漂亮交互式API文档。这一步的成功意味着你的Web服务基础已经搭好了。4. 第一个Skill实战创建一个“天气查询”技能我们用一个最经典的例子——天气查询来贯穿整个开发流程。这个技能的功能是当用户问“北京天气怎么样”时AI能自动调用我们的技能获取真实天气并回复。4.1 定义技能函数与模型交互整个Skill的核心在于两部分一是告诉AI模型“我有什么函数”二是实现这个函数的具体逻辑。首先在main.py中我们定义一个获取天气的函数描述。这个描述是给AI模型看的需要清晰说明函数的功能、参数及其含义。# 在main.py中新增函数描述和实现 import requests # 1. 定义给AI模型看的“函数工具” weather_function_tool { type: function, function: { name: get_current_weather, description: 获取指定城市的当前天气情况, parameters: { type: object, properties: { location: { type: string, description: 城市名称例如北京、上海、San Francisco, }, unit: { type: string, enum: [celsius, fahrenheit], description: 温度单位摄氏度或华氏度, default: celsius } }, required: [location], }, }, }关键点解析name: 函数名后续模型会指定调用这个名字。description: 至关重要模型根据这个描述判断何时该调用此函数。要写得准确、简洁。parameters: 定义参数。properties里是每个参数的详情required数组指明哪些参数是必填的。这里的enum限定了unit参数只能取指定的值。接下来我们实现这个函数的真实逻辑。这里为了演示我们使用一个免费的公共天气API如Open-Meteo实际生产中你可能需要注册更稳定的服务。# 2. 实现真实的天气获取函数 def get_real_weather(location: str, unit: str celsius) - dict: 根据城市名获取真实天气。 这里使用Open-Meteo免费API作为示例。 # 一个简单的城市名到坐标的映射实际项目应使用地理编码API city_coords { 北京: {latitude: 39.9042, longitude: 116.4074}, 上海: {latitude: 31.2304, longitude: 121.4737}, 广州: {latitude: 23.1291, longitude: 113.2644}, 深圳: {latitude: 22.5431, longitude: 114.0579}, } if location not in city_coords: return {error: f暂不支持城市: {location}} coords city_coords[location] url fhttps://api.open-meteo.com/v1/forecast params { latitude: coords[latitude], longitude: coords[longitude], current_weather: True, timezone: auto } try: response requests.get(url, paramsparams, timeout10) data response.json() current data.get(current_weather, {}) temperature current.get(temperature) # 单位转换示例Open-Meteo默认返回摄氏度 if unit fahrenheit: temperature temperature * 9/5 32 return { location: location, temperature: round(temperature, 1), unit: unit, weather_code: current.get(weathercode), wind_speed: current.get(windspeed), description: _parse_weather_code(current.get(weathercode)) } except Exception as e: return {error: f获取天气失败: {str(e)}} def _parse_weather_code(code: int) - str: 将天气代码解析为可读描述简化版 weather_map { 0: 晴, 1: 主要晴朗, 2: 局部有云, 3: 阴天, 45: 雾, 48: 雾, 51: 小雨, 61: 雨, 80: 阵雨 } return weather_map.get(code, 未知天气)4.2 创建Skill调用端点并测试现在我们需要创建一个API端点专门用于处理AI模型的函数调用请求。# 在main.py中新增端点 app.post(/call-function) async def call_function(request: FunctionCallRequest): 接收AI模型的函数调用请求执行对应的真实函数。 # 在实际场景中请求体里会包含函数名和参数。 # 这里我们简化处理假设只处理天气查询。 func_name get_current_weather # 通常从request.body中动态解析 arguments request.arguments if func_name get_current_weather: location arguments.get(location) unit arguments.get(unit, celsius) result get_real_weather(location, unit) return {result: result} else: return {error: f未知函数: {func_name}}如何模拟测试我们写一个简单的测试脚本test_skill.py来模拟AI模型调用我们服务的过程# test_skill.py import requests import json # 1. 模拟用户输入 user_query 今天上海天气怎么样 # 2. 模拟AI模型的理解与决策此处简化直接构造调用请求 # 在实际中这一步由大模型API根据我们提供的weather_function_tool自动完成。 mock_ai_decision { function_name: get_current_weather, arguments: {location: 上海, unit: celsius} } # 3. 向我们的Skill服务发送请求 skill_endpoint http://localhost:8000/call-function response requests.post( skill_endpoint, json{arguments: mock_ai_decision[arguments]} ) print(Skill返回结果) print(json.dumps(response.json(), indent2, ensure_asciiFalse))运行这个测试脚本确保main.py的服务正在运行你就能看到从你的Skill服务返回的、结构化的天气数据。这证明了你的Skill后端逻辑是通的。4.3 与大模型整合完成对话闭环最后一步也是让Skill“活”起来的一步是将我们的函数描述“喂”给大模型并让模型在对话中自主决定何时调用。我们需要一个“编排层”它负责将用户消息和函数描述一起发送给大模型。接收模型响应判断是否需要调用函数。如果需要则调用我们的Skill服务即上面的/call-function端点。将函数执行结果再次发送给模型让模型生成最终的自然语言回复给用户。在main.py中我们可以创建一个新的端点/chat来完成这个编排# 在main.py中继续添加 from fastapi import Body class ChatMessage(BaseModel): role: str content: str class ChatRequest(BaseModel): messages: list[ChatMessage] app.post(/chat) async def chat_with_skill(chat_request: ChatRequest Body(...)): 完整的聊天端点集成大模型与自定义Skill。 # 准备发送给OpenAI的消息 messages [{role: m.role, content: m.content} for m in chat_request.messages] # 第一步让模型决定是否需要调用函数 response openai.chat.completions.create( modelgpt-3.5-turbo, # 或 gpt-4 messagesmessages, tools[weather_function_tool], # 把我们定义的函数工具传进去 tool_choiceauto, # 让模型自动决定是否调用 ) response_message response.choices[0].message tool_calls response_message.tool_calls # 第二步如果模型决定调用函数 if tool_calls: # 通常一次只调用一个工具我们取第一个 function_name tool_calls[0].function.name function_args json.loads(tool_calls[0].function.arguments) # 调用我们自己的Skill服务这里为了简化直接调用本地函数 if function_name get_current_weather: function_response get_real_weather(**function_args) else: function_response {error: 函数未实现} # 第三步将函数执行结果返回给模型让它生成最终回复 messages.append(response_message) # 加入模型的第一次回复包含工具调用 # 加入一个代表“工具执行结果”的消息 messages.append({ role: tool, tool_call_id: tool_calls[0].id, content: json.dumps(function_response), }) # 让模型基于工具执行结果生成最终回复 second_response openai.chat.completions.create( modelgpt-3.5-turbo, messagesmessages, ) final_reply second_response.choices[0].message.content return {reply: final_reply, used_tool: True} # 如果模型没有调用函数直接返回其回复 else: return {reply: response_message.content, used_tool: False}现在你可以使用Postman或curl测试这个/chat端点。发送一个包含用户消息“查询一下北京的天气用摄氏度表示”的请求你会收到一个整合了真实天气数据的、流畅的自然语言回复比如“北京目前天气晴朗气温大约为22摄氏度风力较小。”。至此一个完整的、端到端的Skill就开发完成了。5. 技能优化与高级技巧一个能跑的Skill只是起点要让它在实际中稳定、好用还需要一些优化技巧。5.1 错误处理与健壮性提升我们的技能不能因为一次网络超时或API返回异常就崩溃。必须在关键位置添加健壮的错误处理。在函数实现中增加重试与降级import time from tenacity import retry, stop_after_attempt, wait_exponential # 使用tenacity库实现优雅重试 retry(stopstop_after_attempt(3), waitwait_exponential(multiplier1, min2, max10)) def fetch_weather_api(latitude, longitude): 封装对天气API的调用包含重试逻辑 # ... 原有的requests.get调用 ... def get_real_weather_robust(location: str, unit: str celsius) - dict: try: # ... 获取坐标 ... data fetch_weather_api(coords[latitude], coords[longitude]) # ... 处理数据 ... return result except requests.exceptions.Timeout: return {error: 请求超时请稍后再试, location: location} except requests.exceptions.ConnectionError: return {error: 网络连接异常, location: location} except KeyError: return {error: 数据处理出错, location: location} except Exception as e: # 记录详细日志便于排查 print(f[ERROR] 获取天气未知错误: {e}) return {error: 服务暂时不可用, location: location}在Web端点中验证输入from pydantic import validator, Field class FunctionCallRequest(BaseModel): arguments: dict validator(arguments) def validate_location(cls, v): if location not in v: raise ValueError(参数中必须包含 location) if not isinstance(v[location], str) or len(v[location].strip()) 0: raise ValueError(location 必须是有效的非空字符串) return v app.post(/call-function) async def call_function(request: FunctionCallRequest): try: # ... 原有逻辑 ... except ValueError as e: return {error: f参数验证失败: {str(e)}}5.2 技能描述的“咒语”工程函数描述description和参数description的质量直接决定了AI模型调用的准确性。这有点像给模型下“咒语”。写得好它召之即来挥之即去写得不好它要么乱调用要么该调用时不调用。反面例子差{ name: get_weather, description: 获取天气, parameters: { location: {type: string} } }这个描述太模糊。“获取天气”可能被理解为获取天气预报、历史天气、气候概况等。模型无法精准判断。正面例子好{ name: get_current_weather, description: 当用户询问当前、此刻、现在、今天非未来某个城市或地区的天气状况、温度、风力、是否下雨下雪等实时气象信息时调用此函数。如果用户询问的是明天、下周等未来天气或气候、季节等长期概况则不调用。, parameters: { location: { type: string, description: 用户明确提及的城市、地区或地点名称。例如‘北京’、‘纽约’、‘我家附近’。必须是一个具体的地理位置名词。 }, unit: { type: string, enum: [celsius, fahrenheit], description: 用户明确要求的温度单位。如果用户说‘摄氏度’、‘℃’或未指明默认使用‘celsius’如果用户说‘华氏度’或‘℉’则使用‘fahrenheit’。 } } }这个描述非常具体明确了调用的触发条件询问当前天气和排除条件询问未来天气并对参数做了清晰限定。在实践中你需要像这样不断根据模型的“误判”案例来迭代优化你的描述这个过程就是“咒语工程”。5.3 管理多个技能与路由当你拥有多个技能时需要一个清晰的路由机制。不要在/call-function里写一堆if-else。改进方案使用函数注册表# skill_registry.py class SkillRegistry: def __init__(self): self.functions {} self.descriptions [] def register(self, func, description: dict): 注册一个技能函数及其描述 self.functions[description[function][name]] func self.descriptions.append(description) def get_function(self, name): return self.functions.get(name) def get_all_descriptions(self): return self.descriptions # 初始化注册表 registry SkillRegistry() # 注册天气技能 registry.register(get_real_weather, weather_function_tool) # 假设我们还有另一个技能计算器 calculator_tool {...} def calculate_expression(expression: str): ... registry.register(calculate_expression, calculator_tool) # 在/call-function端点中 app.post(/call-function) async def call_function(request: FunctionCallRequest): func_name request.function_name # 假设请求体中新增了function_name字段 arguments request.arguments func registry.get_function(func_name) if func: result func(**arguments) return {result: result} else: return {error: f未找到函数: {func_name}} # 在/chat端点中传给模型的工具描述直接从注册表获取 tools registry.get_all_descriptions()这样每新增一个技能你只需要实现函数逻辑、定义描述然后注册一下即可主流程代码完全不用动符合“开闭原则”。6. 部署上线与持续迭代本地测试通过后就可以考虑部署到线上让任何人都能通过你的AI助手使用这个技能。6.1 选择部署平台对于个人项目或小型Skill推荐使用Serverless无服务器平台它们管理简单按需付费通常有免费额度。Vercel / Railway (国际)对Python Web应用支持友好关联Git仓库后自动部署。腾讯云云函数SCF / 阿里云函数计算FC (国内)更适合国内访问与微信小程序、API网关等生态结合紧密。以Vercel为例部署FastAPI应用非常简单在项目根目录创建requirements.txt文件pip freeze requirements.txt创建vercel.json配置文件{ builds: [ { src: main.py, use: vercel/python } ], routes: [ { src: /(.*), dest: main.py } ] }将代码推送到GitHub。在Vercel官网导入你的GitHub仓库它会自动识别为Python项目并完成部署。部署成功后你会获得一个类似https://your-app.vercel.app的公共URL。将你AI助手的技能配置中的端点地址之前是http://localhost:8000替换成这个URL即可。6.2 添加认证与安全措施公开的API端点必须考虑安全防止被恶意滥用。最简单的HTTP Basic认证from fastapi import Depends, HTTPException, status from fastapi.security import HTTPBasic, HTTPBasicCredentials security HTTPBasic() def verify_credentials(credentials: HTTPBasicCredentials Depends(security)): correct_username os.getenv(API_USERNAME, admin) correct_password os.getenv(API_PASSWORD, secret) if not (credentials.username correct_username and credentials.password correct_password): raise HTTPException( status_codestatus.HTTP_401_UNAUTHORIZED, detail认证失败, headers{WWW-Authenticate: Basic}, ) return credentials.username app.post(/call-function) async def call_function(request: FunctionCallRequest, username: str Depends(verify_credentials)): # 只有通过认证的请求才能执行 # ... 原有逻辑 ...然后在AI助手平台配置技能时将用户名和密码填入HTTP请求的Header中通常是Authorization: Basic base64编码的username:password。更生产级的方案是使用API KeyX-API-KeyHeader或JWT令牌。6.3 日志、监控与迭代技能上线后你需要知道它运行得怎么样。日志使用Python的logging模块记录每次函数调用的参数、结果、耗时和错误。可以将日志输出到控制台并配置Vercel等平台将其收集到日志服务中。监控在关键端点添加简单的健康检查/health返回服务状态和版本号。可以利用平台自带的监控看板或定期手动测试。迭代根据日志中发现的错误如某个城市查不到和用户的实际反馈不断优化你的技能逻辑和函数描述。这是一个持续的过程。7. 避坑指南与常见问题在实际开发和部署中我踩过不少坑这里总结几个最常见的问题一模型总是不调用我的函数或者错误调用。排查首先检查函数描述是否足够清晰、无歧义。用一些典型的用户问题正例和反例去测试聊天端点看模型的决策是否符合预期。技巧在函数描述的description里多用“当用户询问...时调用”、“如果用户问的是...则不调用”这样的句式来划定边界。参数描述也要具体比如“完整的、有效的城市中文名”。问题二函数执行超时导致AI回复卡住或失败。排查Skill服务的响应时间必须在模型提供商规定的时限内通常5-10秒。检查你的函数逻辑特别是网络请求部分是否可能因外部API慢而超时。解决为所有外部HTTP请求设置明确的超时如timeout5。实现异步处理。如果某个操作很耗时如生成报告可以改为“异步回调”模式立即返回一个“任务已接收”的响应然后在后台处理处理完后通过其他方式如Webhook通知用户。但这需要更复杂的架构。问题三部署后访问不了或返回CORS错误。排查CORS跨域资源共享错误是因为你的前端如ChatGPT界面和Skill服务你的Vercel域名不同源。浏览器出于安全会阻止这种请求。解决在FastAPI应用中添加CORS中间件。from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins[*], # 生产环境应替换为具体的前端域名如[https://chat.openai.com] allow_credentialsTrue, allow_methods[*], allow_headers[*], )问题四如何调试线上问题本地重现尽量在本地模拟线上请求进行调试。使用Postman或curl构造和线上完全一样的请求头和请求体。日志是关键确保所有异常都被try...except捕获并将详细的错误信息包括堆栈跟踪记录到日志中。在Vercel等平台的控制台可以查看这些日志。简化复现如果问题复杂尝试写一个最小的、可复现的脚本剥离无关因素聚焦核心问题。开发Skill是一个从“跑通”到“好用”再到“稳定”的渐进过程。不要指望第一个版本就完美无缺。从小功能开始快速验证收集反馈持续迭代。当你看到自己写的代码真正被AI调用并解决了实际问题时那种感觉是非常棒的。