ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Agent Skills实战:从技能设计到系统落地的工程化指南

Agent Skills实战:从技能设计到系统落地的工程化指南 大概是从去年开始Agent这个词在技术圈的热度就一直没下来过。大家从最初的用Prompt调大模型聊天慢慢转向了让模型自己规划并执行任务。但说实话我见过太多项目卡在了同一个地方模型确实会写代码、会用工具但每次让它干一件稍微复杂的事它都要从零开始推理一遍既慢又容易跑偏。同样的错误反复犯同样的步骤反复搭感觉就像一直在手动挡开车累得不行。agent-skills这个主题我想聊的其实就是这个问题的解法如何把Agent的能力模块化、沉淀成可复用的技能包让它从会做事的实习生变成有方法论的老员工。这篇文章不讲空泛的概念我会结合过去半年实际构建和压测Agent技能系统的经验把架构设计、技能拆解方法、代码实现路径、以及那些文档里几乎不会写的坑一次讲清楚。无论你是刚接触Agent开发还是已经在做多Agent编排这篇应该都能给你一些能直接上手的参考。1. 先搞清楚Agent Skills究竟解决了什么问题想理解Agent Skills得先看一个让人头疼的典型场景。假设你想让Agent完成一个数据采集任务比如去某某技术社区抓取关于大模型部署的帖子标题和点赞数。在没有技能沉淀的系统里Agent大概会走这样的流程调用一个通用的浏览网页函数看到返回的HTML决定用BeautifulSoup去解析尝试定位元素失败再换XPath又失败折腾半天终于拿到数据但发现翻页逻辑不对继续调试最终可能成功但花掉了好几个小时烧了一堆token这还算好的。更常见的是整个流程本身就需要写代码去实现。Agent可能在第二步就开始胡乱编造选择器或者在拿到半干净的HTML后陷入选择困难。问题出在哪里出在每一次任务都被当成全新的。Agent不知道它上个星期已经做过类似的事情也不知道团队里其他人早就写过一个成熟的数据抓取函数。它永远在第一层循环里打转。Agent Skills的核心思想就是把过程性知识显式地建模出来。它不是一个简单的工具注册表而是一种可以让Agent在不同任务中反复调用、组合的行为模块。这个模块里不仅包含做什么还包含怎么做、什么时候做可能出问题、出问题后怎么办。我更喜欢用一个类比来解释Function Calling是给Agent一根杠杆而Agent Skills是教会它一套完整的撬动复杂系统的方法。前者解决能不能调用的问题后者解决能不能高效稳定地完成整个闭环的问题。这个概念在2025年的技术圈被反复提及代表性的实践像Claude官方引入的Agent Skills机制以及Anthropic开源仓库里的那些技能定义。我也见过很多团队在没搞清这个概念的情况下硬是把几十个function塞给模型结果上下文爆炸、决策混乱、调用错乱。归根结底他们没有意识到技能的粒度、结构和自省机制决定了Agent智能化程度的天花板。2. 技能的边界在哪里从pass1说起我在设计技能系统时第一个反复思考的问题是到底什么算一个技能粒度怎么把握是不是所有操作都应该沉淀为技能这里有一个很关键的参考指标——我把它叫做技能的pass1率。就是说Agent在执行某个子任务时不经过任何人工干预、不进行额外试错一次性成功的概率。如果一个子任务的pass1率很低说明它不适合做成技能或者技能的定义方式错了。举个实际的例子。我曾经试着把写一篇PPT大纲做成技能。结果发现每次Agent产出的东西风格飘忽有时偏行业报告有时偏营销方案。后来我理解了技能需要足够明确的输入输出契约和约束条件。后来我把技能拆成行业调研PPT大纲生成和竞品分析PPT大纲生成两个并给每个技能都定义了严格的输出结构模板和避免事项效果立刻稳定了很多。反过来有些操作看起来很简单却非常适合做技能。比如从一个JSON字符串中安全提取指定字段听起来弱爆了对吧但实际做Agent开发的人都知道大模型在提取字段时经常自作主张地修复JSON格式或者在字段不存在时返回编造的默认值。把提取逻辑写死成一个技能用正则加Schema校验双重保障一次通过率几乎100%。所以我的经验法则是这样的判断维度适合做技能不适合直接做技能复杂度中等复杂度步骤固定过于简单的一次性映射稳定性结果受输入微小扰动影响小高度依赖上下文和随机性复用频率多个任务场景都会用到仅为某个特定场景定制可验证性有清晰的验收标准结果主观、无法自动评估专业性需要特定领域的处理逻辑通用常识推理这个判断过程本质上是在重新审视什么能力值得固化。好的技能一定是在某个约束条件下稳定运行的。没有约束的技能就是一个没有SOP的岗位招谁来干全看人品。3. 从需求到技能拆解与抽象的艺术技能设计是整个链路里最考验功力的环节。我见过不少团队把技能文档写得像天书几百行markdown里面充斥各种术语却没说清楚输入长什么样、输出怎么验收。这种做法大模型看了都头疼。我习惯把技能拆解过程分成三步3.1 行为流水线提取先从历史日志里找那些反复出问题、但最终被人工/代码兜底解决的任务片段。每一段都会有一条流水线。比如用户问题解析 → 检索关键词 → 拼接搜索URL → 抓取页面 → 提取正文 → 去重 → 生成摘要这本质上是在做行为记录。把这些步骤写成伪代码就是一个技能的雏形。关键是要把决定走哪条分支的条件也梳理出来。比如搜索关键词为空时用问题本身作为关键词这种决策逻辑如果不写进技能Agent就会在运行时振振有词地编一个关键词出来。3.2 契约设计技能必须要有清晰到位的输入契约和输出契约。这不仅仅是描述参数的type而是定义语义边界。举个例子我的技能里有一个叫extract_article_content的技能。它的输入定义我会写成inputs: html_content: type: string description: 待解析的网页原始HTML或精简后的HTML片段 required: true notes: 建议传入经过pre_process_html之后的内容避免超长截断 content_type: type: enum values: [news, blog, docs, forum] default: blog description: 页面类型不同正文提取规则截然不同输出契约会更严格。对于content_typeblog我要求输出必须是{ title: ..., author: ..., publish_time: ..., content_text: ..., content_html: ..., word_count: 1234 }而且我会把什么时候算失败写进去。比如word_count 200时强制让Agent认为自己失败可以触发重试流程。这种显式的失败触发器特别有用因为大模型在自由发挥时往往会对看起来差不多的提取结果自我感觉良好。3.3 验证与负反馈技能设计完成并不算结束。我会在沙盒环境里跑至少20组典型输入观察哪些步骤容易翻车。然后把这个负反馈直接写进技能说明里。比如我设计搜索技能时第一次发现Agent在拼接URL时总是漏掉urlencode。于是我在技能说明里明确加了一条注意所有非ASCII字符和特殊符号必须经过urllib.parse.quote_else处理后再拼接到URL中。中文关键词不得直接拼进URL。曾有测试案例表明未编码的中文会导致搜索服务返回400错误。这个细节写进去之后这个技能的pass1率从70%直接升到了95%。Agent是懒惰的它默认会选择最快的路径只有当技能文档明确指出那条路是坑时它才会绕开。4. 一套可复用的技能骨架从技能描述到代码实现概念说完了接下来直接上实际的东西。我这里分享一套我在项目里沉淀出来的技能骨架写法它包含四个核心部分每一部分都有明确的职责。4.1 技能说明文件SKILL.md这是Agent的脑图它决定了Agent什么时候选择这个技能以及执行时的宏观策略。我建议里面至少包括技能名前缀例如web_scraping__extract_article适用场景描述写清楚当用户提到抓取某网页正文时不适用场景防止误用执行策略分步骤描述每一步说明意图失败处理策略什么情况下重试、什么情况下上报人工下面是实际例子# web_scraping__extract_article ## 适用场景 - 用户需要解析网页中的文章主体内容时 - 输入是HTML文档或URL输出需要结构化文章字段时 ## 不适用场景 - 页面为SPA动态渲染需要执行JavaScript才能获取内容时此时应改用 web_scraping__render_page 技能 - 需要提取的是表格数据而非文章正文时 ## 执行策略 1. 确认输入URL是否可达若无法访问尝试使用镜像站点列表 2. 抓取HTML剔除script、style、noscript、svg等标签 3. 基于标题标签h1、h2、title和meta标签提取标题和摘要 4. 正文提取优先尝试主内容区域选择器article、main、.post-content等 若未能匹配再使用基于文本密度的通用提取算法 5. 输出必须按schema中的字段严格对齐不得自由增删 ## 失败处理 - 若最终文本长度不足500字符视为失败调用同技能重试一次 - 若连续失败两次切换备用解析库如改用trafilatura - 若仍失败停止执行向用户说明“页面结构特殊无法自动提取”4.2 参数Schema文件很多团队忽略这个文件直接用自然语言描述参数结果模型总是漏传参数。我强烈建议用JSON Schema严格定义。{ name: extract_article_content, description: 从HTML内容中提取文章结构化信息必须严格遵循schema输出, parameters: { type: object, properties: { url: { type: string, description: 目标页面的完整URL必须是http或https开头 }, html: { type: string, description: 已抓取的HTML内容与url二选一传入若同时传入以html为准 }, language: { type: string, enum: [zh, en, auto], default: auto, description: 页面语言用于辅助正文提取和编码判断 } }, required: [url], additionalProperties: false } }有时候为了让模型更容易理解输入约定我还会在description里写示例字段。实测下来模型对带示例的schema理解准确率可以提升大概12%到15%这是个成本极低的优化手段。4.3 实际执行代码skill.py技能的最终落地还是需要具体代码。但是这里有个非常重要、也经常被误解的点代码不是用来替代模型推理的而是用来承载那些模型不擅长、或者做了会浪费时间Token的操作。比如正则匹配、DOM树遍历、字节编码转换、调用外部SDK、数据库读写。我的建议是代码尽量做成纯函数只处理输入输出不包含业务策略。业务策略放在Skill.md里让模型决策。这样做的好处是模型负责选择做什么和判断何时停代码负责怎么做好各司其职出问题了也好排查。下面是一个我线上在用的提取代码片段做了简化处理import re import json from typing import Optional from html.parser import HTMLParser from urllib.parse import urljoin, urlparse class _TextExtractor(HTMLParser): 简化版HTML文本提取器忽略script/style中的内容 SKIP_TAGS {script, style, noscript, svg, iframe} def __init__(self): super().__init__() self._skip_depth 0 self.text_parts [] def handle_starttag(self, tag, attrs): if tag in self.SKIP_TAGS: self._skip_depth 1 def handle_endtag(self, tag): if tag in self.SKIP_TAGS and self._skip_depth 0: self._skip_depth - 1 def handle_data(self, data): if self._skip_depth 0: cleaned re.sub(r\s, , data).strip() if cleaned: self.text_parts.append(cleaned) def extract_article(url: str, html: Optional[str] None, language: str auto) - dict: 使用轻量级HTMLParser提取正文字符避免引入过重的解析库。 更复杂的场景可切换为 trafilatura 或 readability-lxml 方案。 if html is None: # 理论上抓取逻辑应由外层技能处理这里仅做兜底 import requests resp requests.get(url, headers{User-Agent: Mozilla/5.0}, timeout10) html resp.text # 优先提取 title title_match re.search(rtitle[^]*([^])/title, html, re.I | re.S) title title_match.group(1).strip() if title_match else h1_match re.search(rh1[^]*([^])/h1, html, re.I | re.S) if h1_match and len(h1_match.group(1).strip()) len(title): title h1_match.group(1).strip() parser _TextExtractor() parser.feed(html) full_text \n.join(parser.text_parts) # 简单截断保护防止超长文本污染上下文 max_chars 5000 if language zh else 8000 content_text full_text[:max_chars] return { title: title, content_text: content_text, word_count: len(content_text), source_url: url, truncated: len(full_text) max_chars, }注意这段代码里我把从哪里抓取默认留给了外层技能去决定而不是在函数内部实现完整爬虫。这个设计是刻意的如果将来你想把爬取逻辑替换成Playwright渲染只需在SKILL.md里改策略代码函数完全不用动。4.4 技能元信息与依赖声明最后一个技能必须能自解释它的版本、依赖、环境要求、维护者。目的是为了方便后续的技能市场式管理。我会用一个精简的metadata.yamlname: web_scraping__extract_article description: 从网页中提取文章结构化字段 version: 1.4.2 depends_on: - requests2.31 - selectolax0.3.12 - trafilatura1.2.0 # 备用解析器 tags: - scraping - extraction - text maintainer: opsexample.invalid这套骨架下来一个技能的开发成本大约是半天到一天。但它形成的是一个标准的技能封装单元可以被测试、被版本管理、被多个Agent引用。这才是把Agent从demo玩具推向工程化系统的关键步骤。5. 技能不生效的排查链路真实踩坑记录纸上谈兵再多不如真的踩几个坑。这里记录一次典型的问题排查过程整个过程让我对Agent Skills的理解上了一个台阶。5.1 现象系统上线初期我负责的Agent经常出现一个奇怪的行为它明明已经加载了extract_article技能但在处理一个简单页面时却完全不按Skill.md里的策略走。它在没有尝试任何主内容选择器的情况下直接用了正则表达式去匹配正文结果把页面底部的相关推荐全部抓了进来。更奇葩的是它还在日志里自信地写已完成目标。5.2 排查第一步确认技能是否真的被检索到我首先怀疑的是检索环节。因为在整个技能调用的链路里第一步是系统从技能库中检索出与该任务相关的技能把相应的Skill.md和Schema作为上下文注入给模型。如果这一步就漏了后面自然全乱。我检查了检索日志发现确实没问题extract_article被正确选中且注入到了上下文中。问题更加奇怪了——context里有但模型就是不按策略走。5.3 排查第二步检查提示词上下文冲突这让我想到一个可能注入的技能描述可能与其他系统提示词产生了冲突。当时系统提示里有一条规则是为了节省时间优先使用直接的字符串处理方法而我的Skill.md里写的是优先尝试主内容区域选择器。模型把这两条叠加之后选择了一种妥协策略——直接用正则因为正则看起来够直接。这是Agent工程里非常典型的问题表面上是技能的问题实际上是全局指令优先级冲突的问题。修复方式也不是删掉某条提示词而是给技能说明增加优先级声明。我在Skill.md顶部加了一句本技能的读取优先级等同于系统级核心指令其他通用性优化提示如节省时间类建议不得覆盖本技能的执行步骤。加了这句话之后相同任务立刻恢复正常。5.4 排查第三步模型贪多导致的输出格式漂移你以为完了没有。几天后另一个任务又出现新问题。Agent倒是根据技能策略走了但在输出时并没有按照output schema返回JSON而是直接给了一大段文字导致下游解析器抛异常。这个问题让我意识到Skills框架的另一个关键点是输出校验。光在文档里写请输出JSON是完全不够的。我的解决方式是在技能执行管道的末端加了一个强制校验器如果模型的最终输出不符合Schema就自动触发一次修正调用def enforce_output_schema(result, schema): errors [] if title not in result: errors.append(missing title) if content_text not in result: errors.append(missing content_text) if errors: return { status: schema_failed, errors: errors, raw_output: result } return {status: ok, data: result}更有效的做法是当第一次输出不合规范时把校验错误信息连同模型上一次输出一起拼一条请修正输出使其符合schema的新消息再让模型重试一次。这样既不会浪费太多token又能把模型的幻觉式自信拉回正轨。5.5 排查总结这几次排查让我沉淀了下面这份自查清单之后每次技能行为不对我都按这个顺序看技能是否被检索命中并注入上下文技能描述是否与全局指令存在优先级冲突输入参数是否严格符合Schema特别是缺省值是否合理输出是否经过强制校验器还是裸奔到下游技能依赖的外部库、接口版本是否正常经常被忽略模型会猜测原因这套清单帮我省了非常多的时间强烈建议复制到自己项目里。6. 多技能编排从单一技能到技能工作流单一技能能解决单体任务但真实世界里的任务往往是复合的。把所有数据抓下来背后可能是搜索入口URL → 遍历分页列表 → 提取正文 → 过滤广告块 → 按时间排序入库。这种场景下Agent需要在多个技能之间做编排。我常用的方案有两种各有优劣6.1 线性流程描述法低复杂度场景在Agent的顶层提示词里显式地描述一条技能调用链。比如执行数据采集时按以下顺序调用技能 1. web_search 找到关键词相关的候选URL列表 2. web_scraping__extract_article 逐个解析URL正文 3. data_process__dedup 对比标题和正文相似度去除重复 4. data_store__upsert 写入数据库 每完成一步向用户汇报一次进度。若某步失败按该技能的SKILL.md中的失败处理策略执行。这种方式简单直观适合技能数量少、流程固定的场景。缺点也很明显一旦分支多了描述会变得臃肿模型容易遗漏某些分支条件。6.2 图状编排法高复杂度场景当技能数量超过10个我强烈建议把流程从模型自己临场决策改成预定义技能图。做法是把技能看成节点把触发条件看成边。比如workflows: - name: 内容聚合采集 trigger: 用户需要从多个渠道获取特定主题的最新内容 steps: - skill: web_search params_from: task.parsed_keywords next_on_success: web_scraping__extract_article next_on_failure: report_human - skill: web_scraping__extract_article params_from: step1.urls next_on_success: data_process__dedup next_on_failure: web_scraping__render_page - skill: data_process__dedup next_on_success: data_store__upsert这种做法本质上是把决策的确定性让位于代码让模型的灵活性专注于异常处理和用户交互。它牺牲了一部分通用性但换来了可预期性和可观测性。生产环境里这两点比炫技重要得多。我自己在多次压测后基本确定只有具备明确流程图的技能编排才配得上生产级这个说法。7. Agent Skills的活性与维护机制技能不是写完就一劳永逸的。一个技能在真实环境中被调用越频繁越需要有人持续喂养维护数据。我管这个叫技能活性管理。我的做法是给每个技能建立一个活性评分由三个维度加权而来调用频率过去7天被调用的次数稳定率成功执行次数/总调用次数负反馈率用户对结果点了不满意的比例每周我会跑一次评分低于阈值的技能会进入待重构池。重构的方式也不一定重写通常会先分析失败日志看是哪一步拉低了成功率。然后做针对性修复比如更新选择器、增加一个参数校验、或者替换失效的外部依赖。另外一个很容易被忽略的问题技能库自身的大小。我给Agent注入的上下文是有限的所有相关技能的Skill.md加起来不能超过一个预算值通常我控制在4K token以内。超过就得约减把一些对当前任务不关键的内容去掉。这个化繁为简的过程实际上比写新技能更耗精力因为它需要你非常清楚每个技能的本质是什么。我也看到一些团队会做一个内部的技能市场每个Agent用到了某个技能后如果产生了修正行为的地方可以回写出一份经验补丁提交回市场经过审核后更新版本。这基本就是一套企业内部的开源协作模式。这条路很值得尝试它会让你的技能体系像滚雪球一样越滚越厚而不仅仅是依赖少数工程师的局部经验。8. 从今年下半年的视角看Skills的发展方向写这篇文章的时候我注意到整个生态对Skills的讨论已经不局限于Prompt和函数调用了。更多团队在关注几个方向第一个方向是跨Agent的技能共享。以前每个Agent是自己的一套技能未来大概率会有中央技能库的理念一个Agent学到的处理经验可以被其他Agent调用。这套机制已经在一些开源项目里有了雏形我认为这是Agent从单兵作战走向组织协同的一个关键转折点。第二个方向是技能的自动生成。已经有一些工具可以根据Agent的历史行为日志自动提炼出潜在的技能模板然后由人来审核完善。虽然目前自动生成的质量还不高但胜在速度快、覆盖广可以作为技能体系冷启动的一种手段。第三个方向是技能的安全与审计。毕竟技能本身是代码加描述如果来源不可控等于给Agent植入了一个带后门的脑回路。企业级的Agent方案迟早需要对技能做签名、权限隔离和运行沙箱。这些东西我在实际项目中陆续接触了一些整体感受是Agent的智能上限不再取决于单次推理有多聪明而在于这个系统能沉淀多少高质量的可复用行动模式。这是skills这个词最微妙的地方——它让机器的行为越来越像团队里积累了多年经验、懂得什么时候该硬碰硬、什么时候该绕道走的老师傅。而作为工程师我们做的其实不是教模型知识而是帮它建立一个持续进化的能力库。
返回列表