
1. 从“工具”到“伙伴”AI Agent与Skills的范式转变最近和几个做AI应用的朋友聊天发现一个挺有意思的现象。大家从去年开始疯狂追各种大模型API搞提示词工程但今年风向明显变了。讨论的焦点不再是“哪个模型回答更准”而是“怎么让这个AI能自己干活”。比如一个做电商的朋友不再满足于让Claude写商品描述而是想让它自动登录后台、抓取销售数据、生成周报、甚至根据库存情况调整广告预算。这个转变背后核心就是AI Agent和Skills这两个概念。简单来说如果把大模型比如Claude、GPT看作一个聪明但“手无缚鸡之力”的大脑那么Agent就是给这个大脑配上一个可以指挥的“身体”和“工具箱”。而Skills就是这个工具箱里一件件具体的“工具”或“技能”。以前我们和AI的交互是“你问我答”现在正在变成“你提要求它自己想办法完成”。这不仅仅是技术升级更是一种交互范式的根本性变革。为什么这个话题现在这么热因为大家发现单纯的语言模型能力再强也只是一个信息处理终端。它无法主动感知环境、无法操作外部系统、无法进行多步骤的复杂任务编排。而Agent通过引入规划、记忆、工具使用等能力让AI具备了“行动力”。Skills则是这种行动力的具体体现是连接AI智能与真实世界服务的桥梁。无论是通过API调用天气服务还是模拟点击操作一个网页按钮背后都是一个具体的Skill在起作用。这篇文章我们就抛开那些宏大的概念从一个一线开发者的视角深度拆解Skills。我会结合在Claude Code、Hermes Agent等具体项目和环境搭建中踩过的坑讲清楚Skill到底是什么、它的核心原理是什么、如何从零开始设计和开发一个实用的Skill以及在实际的Agent项目中集成和管理Skills的最佳实践。无论你是想了解AI Agent的架构师还是正准备动手开发第一个Skill的工程师希望这些从原理到实践的干货能帮你少走弯路。2. Skill的本质AI的“可执行函数”要理解Skill最直接的方式是把它类比为我们编程中的函数。一个标准的函数有明确的输入、处理逻辑和输出。Skill也是如此但它服务的对象是AI模型而非传统的程序代码。2.1 Skill的核心构成要素一个设计良好的Skill通常包含以下几个关键部分这比单纯看网络上的“skill推荐”列表要重要得多自然语言描述这是Skill的“说明书”用人类和AI都能理解的语言清晰定义这个Skill是干什么的、能解决什么问题、在什么情况下使用。例如“这是一个获取当前天气的Skill。当你需要知道某个城市当前的温度、天气状况和湿度时可以使用它。” 这部分内容会作为上下文提供给大模型帮助它理解何时该调用此Skill。结构化签名这是Skill的“接口定义”。它严格定义了Skill需要哪些输入参数每个参数的类型、格式、是否必填以及返回值的结构。例如一个“发送邮件”的Skill其签名可能包括to收件人字符串、subject主题字符串、body正文字符串等参数。结构化签名是机器可读的确保了调用的准确性。执行逻辑这是Skill的“函数体”。当Agent决定调用某个Skill后这里的代码就会被执行。它可能是一个简单的HTTP API调用如查询天气也可能是一段复杂的本地逻辑如处理Excel文件甚至是一系列模拟用户操作如通过RPA控制浏览器。执行逻辑负责将输入参数转化为具体的动作并产生结果。认证与安全配置很多Skill需要访问外部服务如Gmail、Notion、公司内部系统这就涉及API Key、OAuth令牌等安全凭证的管理。一个好的Skill框架会提供安全的凭证存储和注入机制避免在代码中硬编码敏感信息。2.2 Skill与普通API调用的区别你可能会问这听起来不就是封装了一个API吗确实很像但有几个关键区别决定了Skill是为AI Agent场景量身定做的面向自然语言设计Skill的描述和参数命名都更贴近自然语言方便大模型理解。一个参数可能叫city_name而不是locationCode。容错与重试机制由于大模型对参数的解析可能不完美比如把“北京”解析成“北京市”Skill的执行逻辑需要更强的鲁棒性可能包含参数标准化、模糊匹配和友好的错误提示以便Agent能理解失败原因并尝试调整。结果的自然语言化Skill执行后返回的原始数据如JSON格式的天气数据通常还需要一层处理将其转化为一段通顺的自然语言描述再返回给Agent或用户。例如将{“temp”: 22, “condition”: “sunny”}转化为“当前北京天气晴朗气温22摄氏度非常舒适。”理解了这些我们再去看像Anthropic官方技能库或OpenCode Skills里的一些示例就能明白它们为什么那样设计了。它们不仅仅是代码片段更是经过精心包装、便于AI理解和调用的能力模块。3. 主流生态中的Skill实现剖析目前AI Agent的开发尚未形成绝对统一的标准但几个主要的生态已经显现它们的Skill实现方式各有侧重。了解这些差异能帮助我们在技术选型时做出更明智的决定。3.1 Claude Code 与 “Claude Code Skills”Claude Code特别是其桌面应用 Claude Desktop是Anthropic推出的一个集成开发环境。它不仅仅是一个聊天界面更是一个内置了代码解释、执行和工具调用能力的AI工作空间。其Skill体系的核心思想是让Claude能够安全、可控地执行本地操作。原理Claude Code Skills 通常通过特定的配置声明或插件机制来扩展Claude的能力。例如你可以声明一个Skill允许Claude读写项目中的特定文件、执行某个构建脚本、或者查询本地数据库。其关键限制在于沙箱环境和权限管控。Claude Code会严格限制Skill能访问的资源如文件系统、网络端口以防止恶意操作。实践与踩坑在配置vscode配置claude code或进行claude code 安装时最常见的错误就是环境依赖问题。例如错误提示“virtual machine platform not available claude’s workspace requires the virt...” 这往往意味着宿主机的虚拟化支持如Windows的WSL2、Hyper-V没有启用或Docker环境异常。Claude Code的Workspace很可能依赖于容器化技术来隔离Skill的执行环境。解决方案是进入BIOS开启VT-x/AMD-V虚拟化支持并在Windows功能中启用“虚拟机平台”和“Windows Subsystem for Linux”。另一个典型问题unable to connect to anthropic services failed to connect to api.anthropic.c。这不一定是你网络的问题。首先检查Claude Code的配置中API Base URL是否正确某些地区或企业部署可能需要配置代理或特定的网关地址。其次检查系统代理设置是否被Claude Code正确继承。在Linux/macOS下可能需要设置http_proxy和https_proxy环境变量。3.2 AI Agent 框架中的Skill以Hermes Agent为例像Hermes Agent这类开源AI Agent框架提供了更通用、更灵活的Skill开发范式。它们通常不绑定某个特定的大模型而是设计了一套中间抽象层。原理这类框架会定义一个统一的Skill基类或接口。开发者通过继承这个基类实现description技能描述、parameters参数定义和execute执行方法等关键函数。框架负责将注册的所有Skill的描述和签名动态地拼接到给大模型的系统提示System Prompt中并在大模型输出中识别出工具调用Tool Call的意图然后路由到对应的Skill执行器。与Harness的区别这里需要厘清一个概念。Harness常被提及但它更像是一套基础设施层。正如一些讨论中指出的“Harness是一套包裹在AI Agent核心推理逻辑之外的基础设施层。它不负责代替Agent做决策而是提供监控、日志、持久化、部署、流量控制等‘运维’能力”。你可以把Harness理解为Kubernetes而Agent及其Skills是上面跑的应用。Harness管理Skill的生命周期和运行时但不定义Skill的具体逻辑。开发流程在Hermes Agent这类框架中开发一个Skill步骤非常清晰定义创建一个Python类写好描述和参数schema通常使用Pydantic模型。实现在execute方法中编写核心业务逻辑调用外部API或处理数据。注册将你的Skill类注册到Agent的Skill管理器中。测试通过框架提供的测试工具或模拟对话验证Agent能否正确理解并调用你的Skill。3.3 OpenAI 与 Anthropic 的API协议差异对Skill的影响如果你计划开发一个跨模型的Agent这一点至关重要。OpenAI和Anthropic的大模型的API接口协议分别是不同的实现方式。OpenAI的Function Calling / Tools这是最早流行起来的范式。在ChatCompletion请求中你可以传入一个tools参数里面是一个包含函数定义的JSON列表。模型会在回复中返回一个tool_calls字段指示它想调用哪个工具函数以及参数是什么。然后你需要本地执行这个函数并将结果以tool角色再次发送给模型让它基于结果继续回复。Anthropic的Tool Use (Beta)Claude也推出了类似的功能但名称和细节略有不同。你需要在消息中声明可用的工具Claude会在回复中通过特定的tool_use块来发起调用。你同样需要执行后将结果放在tool_result块中返回给对话流。实践影响这意味着为OpenAI GPTs设计的Skill描述格式不能直接复制到Claude的上下文中使用。虽然核心思想一致但JSON的字段名、结构可能有细微差别。一个健壮的Agent框架如Hermes应当在内部分别适配这两套协议对上层Skill开发者提供统一的接口这才是其价值所在。4. 动手开发你的第一个实用Skill以“智能周报生成”为例理论说了这么多我们来点实际的。假设我们要为一个内部项目管理Agent开发一个核心Skill“自动生成项目周报”。这个Skill需要连接Jira或类似系统拉取任务数据分析本周进展并生成一份格式清晰的Markdown报告。4.1 技能定义与设计首先我们不要急于写代码。先明确这个Skill的“契约”。自然语言描述“本技能用于自动生成指定项目的本周工作周报。它会从项目管理系统获取本周内状态发生变更的任务如新建、进行中、已完成按成员和任务类型进行分类汇总并生成包含关键数据如完成数、进行中数和简要概述的Markdown文档。”结构化签名输入参数project_key(字符串必填)项目的唯一标识如“PROJ-X”。start_date(字符串可选格式YYYY-MM-DD)周报起始日期默认为本周一。output_format(字符串可选)输出格式支持“markdown”或“html”默认为“markdown”。输出一个包含report_content(字符串) 和summary(字典包含任务统计) 的JSON对象。4.2 代码实现与关键细节我们以在Hermes Agent框架下的Python实现为例。import requests from datetime import datetime, timedelta from typing import Optional, Dict, Any from pydantic import BaseModel, Field from hermes_core.skill import BaseSkill # 假设框架提供了BaseSkill # 定义输入参数模型 class WeeklyReportInput(BaseModel): project_key: str Field(descriptionThe key of the project, e.g., PROJ-X) start_date: Optional[str] Field(defaultNone, descriptionStart date in YYYY-MM-DD format. Defaults to this Monday.) output_format: str Field(defaultmarkdown, descriptionOutput format: markdown or html) class WeeklyReportSkill(BaseSkill): Skill to generate weekly project report from Jira. name generate_weekly_report description Generates a weekly work report for a specified project by fetching task updates from the project management system. parameters WeeklyReportInput def __init__(self, jira_base_url: str, api_token: str): # 安全实践从环境变量或配置中心获取凭证而非硬编码 self.jira_base_url jira_base_url self.headers { Authorization: fBearer {api_token}, Content-Type: application/json } async def execute(self, input_data: WeeklyReportInput) - Dict[str, Any]: # 1. 计算日期范围 if input_data.start_date: start datetime.fromisoformat(input_data.start_date) else: today datetime.now() # 计算本周一 start today - timedelta(daystoday.weekday()) end start timedelta(days6) # 2. 构造Jira JQL查询语句 jql_query ( fproject {input_data.project_key} fAND updated {start.strftime(%Y-%m-%d)} fAND updated {end.strftime(%Y-%m-%d 23:59)} fORDER BY assignee, status ) # 3. 调用Jira API (这里需要处理分页) issues [] start_at 0 max_results 50 while True: search_url f{self.jira_base_url}/rest/api/3/search params { jql: jql_query, startAt: start_at, maxResults: max_results, fields: summary,assignee,status,issuetype,updated } response requests.get(search_url, headersself.headers, paramsparams) response.raise_for_status() data response.json() issues.extend(data.get(issues, [])) if start_at max_results data.get(total, 0): break start_at max_results # 4. 处理数据生成报告 report_content, summary self._generate_report(issues, start, end, input_data.output_format) return { report_content: report_content, summary: summary } def _generate_report(self, issues, start_date, end_date, format): # 实现数据聚合和报告生成的逻辑 # 按成员、状态统计任务 member_tasks {} for issue in issues: assignee issue[fields].get(assignee, {}).get(displayName, Unassigned) status issue[fields][status][name] if assignee not in member_tasks: member_tasks[assignee] {todo: 0, in_progress: 0, done: 0} # 简化状态映射 if status in [Done, Closed]: member_tasks[assignee][done] 1 elif status in [In Progress]: member_tasks[assignee][in_progress] 1 else: member_tasks[assignee][todo] 1 # 生成Markdown if format markdown: content f# 项目周报 ({start_date.date()} 至 {end_date.date()})\n\n content f**总任务更新数:** {len(issues)}\n\n content ## 成员任务统计\n for member, stats in member_tasks.items(): content f- **{member}**: 待办 {stats[todo]} | 进行中 {stats[in_progress]} | 已完成 {stats[done]}\n content \n## 本周更新任务列表\n for issue in issues[:10]: # 只列出前10条作为示例 content f- [{issue[key]}] {issue[fields][summary]} ({issue[fields][status][name]})\n if len(issues) 10: content f... 以及另外 {len(issues)-10} 项任务。\n else: # 生成HTML的逻辑类似... content html.../html summary { total_updated_issues: len(issues), period: f{start_date.date()} to {end_date.date()}, member_stats: member_tasks } return content, summary4.3 开发中的注意事项与避坑指南在实现这样一个Skill时我踩过不少坑这里分享几个关键点输入验证与默认值start_date参数是可选的并且有默认值本周一。在execute方法开始必须对输入进行验证和转换。如果用户传入的日期格式错误Skill应该返回一个清晰、友好的错误信息而不是抛出晦涩的异常这样Agent才能理解并可能提示用户修正。API调用的健壮性Jira API可能会因为网络、认证、速率限制等原因失败。代码中response.raise_for_status()是基础但更好的做法是使用重试机制如tenacity库和更细致的错误处理将“网络超时”和“项目不存在”这类错误区分开并返回给Agent不同的信息。分页处理生产环境的项目一周更新可能成百上千Jira API返回结果通常是分页的。上面的代码实现了简单的分页循环这是一个必须考虑的点否则你的Skill只能拿到第一页数据。异步执行注意execute方法被定义为async。对于需要调用多个外部API或执行耗时操作的Skill使用异步IO可以避免阻塞整个Agent的事件循环提升并发处理能力。如果你的Skill逻辑是CPU密集型如大量数据计算则可能需要考虑放入线程池执行。结果的自然语言化这个Skill返回了结构化的report_content和summary。在真实的Agent对话中Agent拿到这个结果后通常不会直接把Markdown扔给用户而是可能会说“已为您生成项目PROJ-X的本周周报。本周共有XX项任务更新其中已完成YY项。这是详细的报告内容[附上报告]”。这需要你在Agent的后续处理逻辑中或者在这个Skill的返回结果里增加一个更口语化的summary_text字段。5. 在复杂Agent项目中管理与集成Skills当你有了几个、几十个Skills之后如何有效地管理它们就成了一个工程问题。这远不止是“skills下载”和“find skills”那么简单。5.1 Skill的发现与注册机制一个成熟的Agent框架需要解决“如何让Agent知道有哪些Skill可用”的问题。常见模式有静态注册在Agent启动时从一个固定的目录加载所有Skill类或在一个中心化的模块中导入并注册。这种方式简单直接适合Skills数量较少、变化不频繁的场景。动态发现框架扫描特定的路径如skills/目录自动发现并加载符合接口规范的Skill类。这提供了更好的可插拔性新增一个Skill只需要将文件放到指定目录即可。远程注册更高级的架构中Skills可以作为独立的微服务部署。它们启动后向一个中心的“Skill注册中心”注册自己的描述和端点。Agent在需要时查询注册中心并通过RPC或HTTP远程调用Skill。这种方式实现了Skill与Agent的解耦和独立扩缩容。5.2 Skill的版本化与依赖管理随着业务发展Skill的逻辑可能需要升级。比如我们的周报Skill可能从只支持Jira升级为同时支持Jira和Asana。这就涉及到版本化管理。接口兼容性Skill的输入输出接口即Pydantic模型一旦发布就应尽量保持向后兼容。新增可选参数是安全的但删除或修改必填参数会导致已配置的Agent工作流失败。一种实践是使用API版本号如技能名称为generate_weekly_report_v2。依赖隔离每个Skill可能有不同的Python库依赖如jira库、notion-client库。最好的实践是为每个Skill提供独立的虚拟环境或容器镜像。这可以通过将Skill打包为独立的微服务或者利用像Poetry、Pipenv等工具管理每个Skill子目录的依赖来实现。避免将所有Skill的依赖都装在Agent的主环境里导致依赖冲突。5.3 安全性考量Skill的权限与沙箱这是Agent能否投入生产使用的生命线。一个能执行任意代码、访问任意网络的Agent是极其危险的。最小权限原则每个Skill在注册时应声明其所需的权限如“读取/var/log/app目录”、“访问api.github.com:443”。Agent框架或底层的Harness层负责在运行时实施这些权限控制。例如一个“发送邮件”的Skill不应该有权限读取本地密码文件。沙箱执行对于来自不可信来源的Skill比如用户自定义上传的必须在严格的沙箱中运行。这可以通过Docker容器、gVisor、Firecracker等轻量级虚拟化技术或语言级别的沙箱如PyPy的沙箱、WebAssembly来实现。目标是将Skill的破坏范围限制在可控的容器内。输入净化与审计对所有Skill的输入参数进行严格的验证和净化防止注入攻击。同时所有Skill的调用记录谁、何时、用什么参数、调用了哪个Skill、结果如何都必须有完整的审计日志便于事后追溯和安全分析。5.4 测试与监控确保Skill的可靠性Skills是Agent的“手”和“脚”它们的可靠性直接决定了Agent的可用性。单元测试为每个Skill的execute逻辑编写充分的单元测试模拟各种正常和异常的输入并验证输出是否符合预期。Mock外部API的调用。集成测试在测试环境中启动一个真实的Agent实例通过模拟对话测试Agent是否能正确理解意图、选择并执行目标Skill。这个测试需要覆盖端到端的流程。监控与告警在生产环境中需要监控每个Skill的关键指标调用量和耗时发现性能瓶颈或异常流量。错误率区分是Skill内部错误如代码bug、依赖服务错误如Jira API宕机还是输入错误用户参数不对。权限异常记录任何被沙箱或权限系统拒绝的访问尝试。 当错误率超过阈值或平均耗时异常时应及时触发告警。6. 从Skill到智能体架构设计与模式思考当我们掌握了单个Skill的开发后需要从更高维度思考如何将这些Skills有机地组合起来构建一个真正能解决复杂问题的智能体。这涉及到Agent的架构模式。6.1 任务规划与Skill编排一个复杂的用户请求如“帮我分析上个月销售数据找出表现最好的三个产品并给对应的产品经理写一封改进建议邮件”可能需要分解成多个步骤获取数据、分析数据、生成报告、查找联系人、撰写邮件、发送邮件。这对应了多个Skills的串联执行。规划器Agent的核心组件之一。它接收用户目标并生成一个执行计划Plan。这个计划是一个由子任务对应Skills组成的有向无环图。规划器可以基于大模型LLM进行零样本或少样本的规划也可以基于预定义的工作流模板。工作流引擎负责执行规划器生成的计划。它需要管理任务状态待执行、执行中、成功、失败、处理Skill之间的数据传递上一个Skill的输出作为下一个Skill的输入、处理异常如某个Skill失败后的重试或备选路径。像LangChain、AutoGen等框架提供了这方面的基础能力。上下文管理在整个多轮对话和多步骤执行中Agent需要记住用户的目标、已经执行过的步骤及其结果、以及当前步骤的中间状态。这通常通过一个“工作记忆”或“对话历史”模块来实现确保规划器和执行器能基于完整的上下文做出决策。6.2 几种常见的Agent-Skill架构模式在实践中根据复杂度和需求我观察到几种常见的模式单Skill代理最简单的模式。Agent本质上就是一个Skill的包装器。用户请求直接映射到一个特定的Skill。例如一个“天气查询机器人”其核心就是一个“获取天气”的Skill。这种模式适用于功能单一的场景。技能路由代理Agent内置一个“路由”逻辑可以基于规则也可以基于一个轻量级LLM判断根据用户请求的意图从技能库中选择一个最合适的Skill来执行。这是目前大多数聊天机器人助理采用的模式。规划-执行代理如上所述拥有独立的规划器和执行引擎。规划器将复杂目标分解为子任务序列执行引擎按顺序或条件分支调用相应的Skills。这是实现复杂任务自动化的关键。多代理协作更复杂的场景下可能需要多个专门的Agent协作。例如一个“数据分析Agent”负责调用数据处理Skills一个“文案Agent”负责调用写作Skills一个“协调Agent”负责管理它们之间的协作。这构成了一个多智能体系统。6.3 关于“AI Agent学习路线”与“基于C#开发的AI Agent开发框架”的思考看到很多人在搜索“AI Agent学习路线”和“基于C#开发的AI Agent开发框架”。这里谈谈我的看法。对于学习路线我认为可以分几步走第一步理解核心概念。搞清楚LLM、Prompt Engineering、Function Calling/Tool Use、Agent、Planning、Memory这些基础概念及其关系。第二步上手主流框架。不要一开始就自己造轮子。先用好LangChain、LlamaIndex偏重数据连接、AutoGen偏重多智能体这样的成熟Python框架快速实现一个能调用几个简单Skills的Agent理解其工作流。第三步深入原理与源码。选择一个你感兴趣的开源框架如Hermes Agent阅读其源码看它是如何注册Skill、如何构建提示词、如何解析模型返回的工具调用、如何执行和返回结果的。这是提升的关键。第四步解决实际问题。找到一个你工作或生活中的痛点尝试用Agent的思路去解决。从设计Skills开始到集成、测试、部署。在这个过程中你会遇到真实的技术挑战这才是最好的学习。至于“基于C#开发的AI Agent开发框架”.NET生态在这方面确实在快速发展比如Semantic Kernel就是微软推出的一个非常优秀的框架。它的设计理念与Python生态的框架类似也提供了Skills在Semantic Kernel里叫Plugins、Planner、Memory等核心抽象。如果你所在的团队技术栈以.NET为主Semantic Kernel是一个绝佳的起点。它的优势在于与Azure云服务、.NET现有类库的深度集成。学习路径和上面类似只是工具换成了C#和Semantic Kernel。7. 未来展望与当前局限Skill生态的挑战尽管Skills和Agent的概念令人兴奋但我们也要清醒地认识到当前的局限和挑战。可靠性问题LLM在理解用户意图和选择Skill时仍然会出错可能会选错Skill或误解参数。这需要我们在Skill设计时加入更充分的错误处理和备选路径并在Agent层面设计验证和确认机制例如“您是想查询天气吗请确认城市名称。”。长链条任务的稳定性一个包含10个步骤的复杂任务只要中间任何一个Skill失败整个链条就可能中断。如何设计鲁棒的故障恢复、状态持久化和补偿机制是工程上的大挑战。安全与成本如前所述安全是重中之重。此外每次调用LLM进行规划和决策都需要花费token复杂的任务可能导致高昂的API成本。需要对工作流进行优化比如缓存一些中间决策结果。评估与评测如何系统地评估一个Agent的好坏如何评测其Skill调用的准确率、任务完成率这还没有像机器学习模型那样的标准评测集更多依赖于端到端的场景化测试。Skills是AI Agent落地应用的基石。从原理上理解它作为“AI可执行函数”的本质从实践上掌握其设计、开发、集成的全流程并清醒地认识到当前的边界我们才能更好地驾驭这项技术构建出真正有用、可靠、安全的智能体应用。这条路还很长但每一步扎实的实践都在把我们带向那个更智能的未来。