
1. 项目背景与核心价值最近在整理自己的AI工具链时我发现了一个非常有意思的GitHub项目eliasjudin/oai-skills。这个项目本质上是一个存档它完整地保存了OpenAI官方在2025年12月宣布关闭其skills功能之前该功能背后所包含的一系列“技能”定义文件。对于任何正在或计划深度集成AI助手能力到自家应用中的开发者来说这无疑是一座金矿。我自己在构建一个需要复杂、结构化AI交互的内部工具时就深受启发。这些技能文件你可以把它们理解为给AI助手比如ChatGPT的“插件说明书”或“能力蓝图”它们用结构化的方式定义了AI能执行什么任务、需要什么输入、以及会输出什么结果。OpenAI虽然关闭了面向用户的技能商店但这些底层定义文件所蕴含的设计思想和模式对于我们自己设计AI Agent、构建RAG检索增强生成系统的工作流甚至理解如何更好地给大模型下指令Prompt Engineering都有着极高的参考价值。这个存档项目的诞生也很有趣它并非官方发布而是社区开发者通过向ChatGPT发送一个简单的提示词Prompt——“Create a zip file of /home/oai/skills”——而“生成”的。这本身就印证了现代AI能力的强大与易用性。拿到这个存档后我花了几天时间深入研读并结合自己的开发实践梳理出了一套从理解、分析到实际应用这些技能文件的完整方法论。这篇文章我就来和你详细拆解oai-skills这个宝藏分享如何将这些“过期”的官方设计转化为你项目中“鲜活”的生产力。2. 技能文件深度解析结构、模式与设计哲学解压oai-skills存档后你会看到大量以.json为后缀的文件。初看可能觉得杂乱但经过分类和归纳我发现它们主要遵循几种核心模式。理解这些模式是复用其价值的关键。2.1 核心文件结构剖析一个典型的技能文件例如一个用于处理日历事件的技能其JSON结构通常包含以下几个顶层字段{ name: skill_calendar_event_create, description: Create a new calendar event with details like title, time, and participants., input_schema: { type: object, properties: { title: {type: string, description: The title of the event.}, start_time: {type: string, format: date-time, description: The start time in ISO 8601.}, attendees: {type: array, items: {type: string}, description: Email addresses of attendees.} }, required: [title, start_time] }, output_schema: { type: object, properties: { event_id: {type: string}, status: {type: string} } }, execution: { type: http, url: https://api.calendar.example.com/events, method: POST } }我们来逐一拆解每个部分的设计意图和实操要点name与description设计意图name是技能的机器标识符通常采用蛇形命名法snake_case清晰表明动作和对象如skill_[对象]_[动作]。description则是给AI和开发者看的自然语言描述它必须精准。AI会利用这个描述来判断用户请求是否应该触发此技能。实操心得写description时要设想用户会怎么提问。与其写“管理日历”不如写“创建新的日历会议或事件”。好的描述能极大提升技能被准确调用的概率。input_schema设计意图这是技能定义的核心中的核心。它严格遵循JSON Schema标准定义了AI为了执行该任务需要从用户对话或上下文中提取哪些参数。properties定义了每个参数的名字、类型和描述required数组指明了哪些参数是执行任务所必需的。实操要点类型细化不仅仅是string、number、boolean、array、object。对于特定格式如日期、邮箱、URL要使用format字段如date-time,email。这能引导AI更准确地理解和格式化数据。描述的力量每个property下的description字段至关重要。例如对于start_time描述“The start time in ISO 8601 format (e.g., 2024-01-15T14:30:00Z)”比单纯说“开始时间”要好得多。这本质上是给AI的“小提示”。结构化数据对于复杂对象不要害怕嵌套object类型。这能强制产出结构良好的数据方便后端处理。output_schema设计意图定义了技能执行成功后返回给AI并最终可能呈现给用户的数据结构。这保证了输出的一致性。实操心得即使你的后端API返回的数据很复杂在output_schema中也应该定义最精简、对用户有意义的字段。比如创建事件后返回event_id和summary就足够了不需要把API的全部响应都塞进来。execution设计意图指明了如何实际运行这个技能。最常见的是http类型指向一个API端点。这体现了“定义与执行分离”的思想技能文件只关心“做什么”和“需要什么”不关心“怎么做”的具体实现后者由你的后端服务完成。实操要点在实际应用中这里的url可能是一个相对路径由你的AI Agent框架在运行时根据配置动态补全。methodGET, POST, PUT, DELETE必须与后端API匹配。2.2 从官方技能中提炼的四大设计模式通读上百个技能文件后我总结出OpenAI工程师常用的几种设计模式这些模式可以直接移植到我们的项目中查询Query模式特征execution.method通常为GET。input_schema包含搜索、过滤参数如query,filter_by,max_results。output_schema是一个对象数组。案例搜索邮件、查询数据库、查找文档。例如一个skill_search_emails的技能输入可能是query关键词和label标签输出是邮件列表。应用建议在设计RAG系统的检索技能时这几乎是标准模板。你可以定义一个skill_search_knowledge_base的技能输入是用户问题输出是相关的文档片段列表。操作Action模式特征execution.method为POST、PUT、DELETE。input_schema包含创建或修改一个实体所需的全部字段。output_schema通常包含操作状态和生成的ID。案例创建日历事件、发送邮件、在CRM中新建客户。这就是上面日历例子的典型。应用建议当你需要AI驱动的工作流改变外部系统状态时就使用这个模式。务必在input_schema中明确required字段防止AI尝试用不完整的数据调用你的API。计算/转换Computation/Transformation模式特征可能没有execution块或者指向一个内部计算函数。input_schema接收原始数据output_schema返回处理后的结果。案例格式化日期、计算税费、将文本翻译成另一种语言、提取摘要。应用建议这类技能非常适合封装那些轻量级、无副作用的数据处理逻辑。它们可以作为更复杂工作流中的一环。例如在总结一篇长文章前先调用一个skill_extract_main_text的技能来净化HTML。复合Orchestration模式特征一个技能的execution可能并不直接调用HTTP API而是触发一系列其他技能的执行。这通常在更上层的Agent框架中实现。设计思想这是构建复杂AI Agent的关键。官方技能文件本身是原子性的但它们的价值在于可以被像编排器一样的“大脑”组合使用。例如一个“安排团队周会”的用户请求可能被分解为1)skill_find_team_members 2)skill_find_free_slots 3)skill_create_calendar_event。应用建议在设计你的技能库时要有意识地追求技能的“原子性”和“可组合性”。一个技能只做好一件事这样它们就能像乐高积木一样被灵活组装。注意在分析这些技能文件时切记它们来自一个特定的历史版本和环境/home/oai/skills。直接复制粘贴其中的API端点URL是无效的。我们汲取的是其模式、结构和设计思想而非具体的实现内容。3. 实战应用将官方模式融入你的AI项目理解了技能的设计模式后关键在于如何将其应用到我们自己的项目中。下面我以构建一个“智能研发助手”为例分享从零开始定义、实现和集成自定义技能的完整流程。这个助手能帮开发者查询Bug、提交代码、生成周报。3.1 第一步定义你的技能清单Skill Inventory不要一上来就写代码。先像产品经理一样列出你的AI助手需要具备哪些能力。参考oai-skills的分类方式我为“研发助手”规划了以下技能技能名称 (name)描述 (description)模式核心输入参数预期输出skill_bugzilla_search在Bugzilla系统中根据条件搜索Bug报告。查询query(关键词),status(状态),assignee(负责人)Bug列表ID, 摘要, 状态skill_gitlab_create_mr在GitLab仓库中创建合并请求Merge Request。操作source_branch,target_branch,title,descriptionMR的ID和Web URLskill_jira_get_sprint_tasks获取当前Jira迭代中分配给当前用户的任务。查询sprint_id(可选默认为当前迭代)任务列表Key, 摘要, 状态skill_generate_weekly_report基于本周的代码提交、解决的Bug和任务记录生成周报草稿。计算start_date,end_date周报文本Markdown格式这个表格就是你开发的“地图”。它迫使你思考技能的边界、输入输出的契约。3.2 第二步编写技能定义文件接下来为每个技能创建对应的JSON定义文件。我们以skill_gitlab_create_mr为例展示一个高度仿真的定义// skills/gitlab_create_mr.json { name: skill_gitlab_create_mr, description: Create a new Merge Request in the specified GitLab project. Useful for submitting code changes for review., input_schema: { type: object, properties: { source_branch: { type: string, description: The name of the branch where the changes are implemented. E.g., feature/add-user-auth. }, target_branch: { type: string, description: The name of the branch you want to merge into. Usually main, master, or develop., default: main }, title: { type: string, description: A concise title for the Merge Request that summarizes the change. }, description: { type: string, description: Detailed explanation of the changes, including motivation, implementation details, and any testing performed. Markdown is supported. }, project_id: { type: string, description: The numeric ID or path of the GitLab project. If not provided, the default project from configuration will be used. } }, required: [source_branch, title] }, output_schema: { type: object, properties: { mr_id: { type: number, description: The internal ID of the created Merge Request. }, mr_iid: { type: number, description: The project-specific IID (instance ID) of the Merge Request. }, web_url: { type: string, description: The direct URL to view the Merge Request in the GitLab web interface. }, status: { type: string, description: Creation status, e.g., created, failed. } } }, execution: { type: http, url: {{GITLAB_API_BASE}}/projects/{{project_id}}/merge_requests, method: POST, headers: { PRIVATE-TOKEN: {{GITLAB_TOKEN}} } } }关键实现细节与避坑指南默认值default注意target_branch设置了default: main。这是一个非常实用的技巧。这意味着当用户只说“为分支feature/xxx创建MR”时AI可以自动将target_branch补全为main而无需反复追问用户。合理使用默认值能极大提升对话流畅度。动态变量{{...}}execution块中的{{GITLAB_API_BASE}}、{{project_id}}、{{GITLAB_TOKEN}}不是硬编码的。在实际的AI Agent框架如LangChain、AutoGen或自定义框架中这些变量会在运行时被替换为真实的值。这实现了技能定义的可移植性和安全性令牌不暴露在定义文件中。描述即文档description字段不仅给AI看也是给未来维护此技能的开发者看的。写得越清晰后续集成和调试成本越低。3.3 第三步实现技能的执行器Executor技能定义只说了“做什么”真正的“怎么做”需要执行器来完成。执行器是一个后端服务它接收AI Agent解析好的参数调用真实的第三方API或内部服务。以下是一个用Python FastAPI实现的skill_gitlab_create_mr执行器示例# skill_executors/gitlab.py import os import requests from fastapi import APIRouter, HTTPException from pydantic import BaseModel router APIRouter() # 从环境变量获取配置安全且灵活 GITLAB_API_BASE os.getenv(GITLAB_API_BASE, https://gitlab.com/api/v4) GITLAB_TOKEN os.getenv(GITLAB_TOKEN) DEFAULT_PROJECT_ID os.getenv(DEFAULT_GITLAB_PROJECT_ID) class CreateMRRequest(BaseModel): source_branch: str target_branch: str main # 与服务端默认值保持一致 title: str description: str project_id: str None router.post(/gitlab/create_mr) async def execute_create_mr(request: CreateMRRequest): 执行创建GitLab MR的技能 if not GITLAB_TOKEN: raise HTTPException(status_code500, detailGitLab token not configured.) # 确定项目ID优先使用传入的否则用默认的 project_id request.project_id or DEFAULT_PROJECT_ID if not project_id: raise HTTPException(status_code400, detailProject ID must be specified.) # 准备调用GitLab API url f{GITLAB_API_BASE}/projects/{project_id}/merge_requests headers {PRIVATE-TOKEN: GITLAB_TOKEN} data { source_branch: request.source_branch, target_branch: request.target_branch, title: request.title, description: request.description, remove_source_branch: True # 可以根据需要添加更多选项 } try: response requests.post(url, headersheaders, jsondata, timeout30) response.raise_for_status() mr_data response.json() # 格式化输出严格匹配技能定义中的output_schema return { mr_id: mr_data.get(id), mr_iid: mr_data.get(iid), web_url: mr_data.get(web_url), status: created } except requests.exceptions.RequestException as e: # 详细的错误处理便于AI向用户解释 error_msg fFailed to create MR: {str(e)} if hasattr(e.response, text): error_msg f Response: {e.response.text[:200]} raise HTTPException(status_code500, detailerror_msg)实操心得执行器设计的黄金法则契约优先执行器的输入和输出必须与技能定义文件中的input_schema和output_schema严格一致。这是AI Agent和你后端服务之间的“合同”任何偏差都会导致调用失败或数据混乱。错误处理要友好不要只是抛出晦涩的HTTP 500错误。像上面代码一样捕获异常并返回对AI和最终用户都有意义的错误信息。AI可以利用这些信息向用户做出更友好的解释比如“创建MR失败可能是因为源分支不存在”。配置外置API令牌、基础URL等敏感或可变信息一定要通过环境变量或配置中心管理绝对不要硬编码。3.4 第四步集成到AI Agent框架这是最后一步也是让技能“活”起来的一步。你需要一个AI Agent框架或自己构建一个调度中心来加载你定义的所有技能JSON文件。将这些技能的描述和输入模式“告知”大语言模型如GPT-4、Claude或本地模型。当用户发出指令时框架让LLM判断是否需要调用技能以及需要传入哪些参数。框架调用对应的执行器并将结果返回给LLM由LLM组织成自然语言回复给用户。这里以使用LangChain的Tool概念为例展示如何包装我们的技能# agent_core.py import json from langchain.tools import BaseTool from langchain.agents import initialize_agent from langchain_openai import ChatOpenAI from pydantic import BaseModel, Field from typing import Type # 1. 动态加载技能定义 def load_skill_definitions(skills_dir): skills [] for file in os.listdir(skills_dir): if file.endswith(.json): with open(os.path.join(skills_dir, file), r) as f: skill_def json.load(f) skills.append(skill_def) return skills # 2. 为每个技能创建一个动态的LangChain Tool class DynamicSkillTool(BaseTool): name: str description: str args_schema: Type[BaseModel] executor_url: str # 对应执行器的端点 def _run(self, **kwargs): # 这里简化了实际应调用对应的执行器HTTP服务 # 例如requests.post(self.executor_url, jsonkwargs) response call_executor_service(self.name, kwargs) return json.dumps(response) # 返回字符串供LLM读取 async def _arun(self, **kwargs): # 异步支持 pass # 3. 根据技能定义动态创建Pydantic模型用于参数验证 def create_args_model_from_schema(schema): # 这是一个简化示例实际需要递归处理JSON Schema fields {} for prop_name, prop_def in schema[properties].items(): field_type map_json_type_to_python(prop_def[type]) description prop_def.get(description, ) default ... if prop_name in schema.get(required, []) else prop_def.get(default, None) fields[prop_name] (field_type, Field(defaultdefault, descriptiondescription)) return type(DynamicArgs, (BaseModel,), {__annotations__: fields}) # 4. 组装Agent def create_agent(): llm ChatOpenAI(modelgpt-4, temperature0) skill_defs load_skill_definitions(./skills) tools [] for def_ in skill_defs: ArgsModel create_args_model_from_schema(def_[input_schema]) tool DynamicSkillTool( namedef_[name], descriptiondef_[description], args_schemaArgsModel, executor_urlfhttp://localhost:8000/execute/{def_[name]} ) tools.append(tool) agent initialize_agent(tools, llm, agentstructured-chat-zero-shot-react-description, verboseTrue) return agent # 5. 运行Agent if __name__ __main__: agent create_agent() result agent.run(帮我基于feature/auth-branch创建一个合并请求到develop分支标题是用户认证模块上线) print(result)这个流程将官方技能的定义模式与现代AI开发框架结合了起来。你的AI助手现在就能理解“创建MR”这样的复杂指令并自动完成它了。4. 进阶技巧与避坑实录在实际集成和使用的过程中我踩过不少坑也总结出一些能让技能系统更稳健、更智能的经验。4.1 技能描述的优化艺术技能的description字段是AI决定是否调用它的主要依据。写得好坏天差地别。反面例子“处理邮件。”正面例子“搜索用户的收件箱中的电子邮件。可以根据发件人、主题关键词、接收时间范围进行过滤并返回匹配的邮件列表。”优化技巧使用动词开头以“Search for...”, “Create a new...”, “Calculate the...”开头明确动作。包含关键参数在描述中自然提及主要的输入参数如上例中的“发件人、主题关键词、接收时间范围”。说明适用场景可以加一句“当用户想查找特定邮件时使用此功能。”避免歧义不要用“处理”、“管理”这种泛义词要具体。4.2 处理模糊的用户输入用户不会总是说“请调用skill_search_emails参数query‘预算’ label‘重要’”。他们更可能说“找我上周收到的关于预算的那封重要邮件。”这就需要你的AI Agent具备参数推断和补全的能力。这通常通过以下方式实现LLM的推理能力在调用工具前让LLM根据对话历史和技能描述自行推断并补全缺失的参数。例如LLM能推断出“上周”对应一个时间范围“重要”可能对应标签label。设置合理的默认值如前所述在input_schema中设置default值。设计确认环节对于关键且无法推断的参数比如创建MR时的title如果用户没提供框架应让AI主动追问用户而不是直接调用失败。4.3 技能依赖与执行顺序有些复杂任务需要按顺序调用多个技能。例如“总结我上周的工作”可能需要1) 从Jira获取任务2) 从GitLab获取提交3) 从邮箱获取相关邮件4) 调用总结生成技能。实现策略使用规划型Agent采用ReAct、Plan-and-Execute等模式的Agent让LLM自己规划步骤。设计高层“编排技能”你可以专门创建一个skill_generate_weekly_report的技能它的执行器内部按固定顺序调用其他几个数据获取技能最后再调用LLM生成总结。这样对用户来说还是一个简单的指令。4.4 常见问题排查表在开发和调试技能系统时你大概率会遇到以下问题问题现象可能原因排查步骤与解决方案AI完全不调用技能1. 技能描述不清晰LLM无法匹配。2. LLM温度temperature设置过高导致输出不稳定。3. 技能定义未正确加载到Agent上下文中。1. 优化description使其更贴近用户自然语言。2. 将LLM的temperature调低如0.1增加确定性。3. 检查框架日志确认技能列表已成功注册。在Prompt中明确告诉LLM“你可以使用以下工具”。AI调用了错误的技能技能之间的描述区分度不够。重写技能描述突出其独特性和专用场景。例如“创建日历事件”和“创建待办事项”要明确区分。技能调用失败参数错误1. LLM推断的参数类型或格式不对。2.input_schema中的required字段缺失。3. 执行器输入验证失败。1. 在技能描述的property中提供更详细的格式示例如“日期格式YYYY-MM-DD”。2. 确保required字段正确。3. 在执行器端添加详细的输入验证和错误日志明确返回哪个参数有问题。技能执行成功但AI回复内容不佳执行器返回的数据过于原始或复杂LLM不知道如何组织语言。1. 优化output_schema只返回关键、有意义的信息。2. 可以在执行器返回数据后设计一个固定的回复模板让AI填充。例如“已成功创建合并请求MR编号为#{{mr_iid}}您可以在此处查看{{web_url}}”。技能执行超时或网络错误第三方API响应慢或执行器本身有性能问题。1. 在执行器调用外部API时设置合理的超时如30秒。2. 实现重试机制对于幂等的GET请求。3. 为执行器添加监控和告警。4.5 安全与权限考量当你赋予AI调用真实系统API的能力时安全是重中之重。最小权限原则为AI Agent使用的API令牌Token申请最小必要权限。例如一个只用于搜索邮件的技能绝不应该拥有删除邮件的权限。用户上下文隔离确保技能在执行时操作的是当前对话用户的数据而不是其他用户的数据。这通常需要在执行器层面将AI传入的参数与当前已认证的用户会话进行绑定。输入验证与清理执行器必须对所有输入参数进行严格的验证和清理防止注入攻击。即使LLM是“友好”的也要防范恶意构造的输入。审计日志记录每一次技能调用的详细信息谁用户、何时、调用了什么技能、输入参数是什么、结果如何。这对于问题排查和安全审计至关重要。回过头看eliasjudin/oai-skills这个项目提供的远不止是一堆JSON文件。它提供了一套经过大规模实践检验的、用于构建可交互AI系统的接口设计规范和思维模式。通过深入剖析这些技能文件我们学到了如何用结构化的方式定义AI的能力边界如何设计人机交互的契约以及如何将复杂的用户指令分解为可执行的原子操作。将这些模式应用到自己的项目中能让你设计的AI助手不再是简单的聊天机器人而是真正能融入工作流、提升效率的智能伙伴。整个过程中最关键的转变在于思维模式从“让AI生成一段文本”变为“让AI协调一系列工具来完成一个任务”。