刚把智能体的“技能系统”重做了一遍,趁热打铁,把这个过程中的设计和踩坑都记下来。这里说的 Agent Skills 不是什么学术概念,就是一句话:把智能体能做的事,拆成一个一个独立、可描述、可被模型选择调用的模块。以前我的智能体靠一大段系统提示词驱动,现在我把这些能力描述从提示词里挪出来,换成一套技能注册表,由模型根据任务描述动态挑技能执行。这套东西落地之后,我才真正理解拆分的价值:不是代码结构变漂亮了,而是调试成本降了一个量级,迭代一辆新功能的速度也完全不一样。
这篇文章适合正在做 AI 智能体、工作流,或者想把自己的工具能力接入大模型应用的人看。我尽量不绕弯子,不讲空话,直接说清楚我做了什么、为什么这么做、中间踩了哪些坑。用的是我项目里的真实复盘,你可以直接拿来作参考。
1. 先搞清楚:Agent Skills 到底要解决什么问题
1.1 从一大段提示词到“技能包”的转变
最早那版智能体,能力边界完全是靠系统提示词撑的。提示词里写着“当用户询问天气时,调用天气接口”“当用户要查订单时,先调用订单查询接口,再调用用户验证接口”。听起来逻辑很顺,但实际一跑起来全是问题。
我把当时的痛点归成三类。
第一,可维护性差。提示词超过一定长度之后,模型对后段指令的遵循度明显下降。继续往里面堆能力描述,本质上就是让模型在每轮对话里重新“读一遍文档”,这很不稳定。更麻烦的是,提示词是一个整体,改了一个分支可能影响其他分支,上线之前还要拿回归样例整体过一遍,非常费时间。
第二,难以测试。所有能力混在一张大提示词里,测试时只能通过完整对话流去验证。一旦出了问题,很难定位是提示词引导的问题、上下文丢失的问题,还是接口本身的问题。我当时调试一个订单查询的 bug,花了整整一个下午,最后发现是提示词里“先查询订单再校验用户”和实际代码执行顺序不一致导致的。
第三,无法复用。项目里同时维护着好几个智能体,客服一个、运营数据分析一个、后台管理一个。它们的很多基础能力高度重叠,比如查用户信息、查订单、查库存。只要这些能力各自写在各自的提示词里,就完全没办法共享,改一个逻辑需要同步改所有地方,漏一个就出错。
Agent Skills 就是针对这三类问题才引入的。我当时定了一个原则:凡是智能体需要稳定执行的动作,尽量做成技能;凡是只影响表达方式和语气的内容,才留在提示词里。这个边界划清楚之后,整个项目一下子清爽了很多。
举一个实际的场景。客服智能体和数据分析智能体都需要“查询订单详情”的能力。以前各自在提示词里写一套,现在两个智能体挂在同一个技能库上,注册一次,两边复用。数据格式、鉴权方式、异常处理全部统一,对维护者来说能省非常多的时间。而且新同事接手项目时,不用再去啃那种几千字的提示词,直接看技能清单就能知道系统支持哪些能力。
1.2 拆技能不是目的,稳定和可控才是
我见过一些项目把技能拆得很碎,一个简单的“字符串格式化”也做成技能,结果是模型在选择技能上的开销比执行技能还大,调用链变长,反而更容易出问题。所以拆技能的粒度一定要克制,标准要清晰。
我的建议是,如果一项操作同时满足以下条件中的至少两个,才值得做成技能:
- 会被多个智能体或多个业务场景复用;
- 涉及外部系统或数据库调用;
- 执行结果需要被严格校验;
- 需要记录日志或做权限控制;
- 逻辑相对复杂,无法用几行提示词描述清楚。
如果只是“把用户输入转成小写”这种三行逻辑,让它留在提示词里就好。技能的真正价值在于把不确定的部分变成确定的单元,而不是把所有东西都变成技能。一个系统里技能越多,模型选择技能的决策难度也越大,所以宁可少而精,不要多而杂。
1.3 技能化之后,调试方式彻底变了
技能化之前,我调试智能体基本靠“给用户一个输入,看整段回复对不对”。技能化之后,我可以直接单测某一个技能,比如给商品搜索技能传一个关键字,看返回结果结构化数据是否正确。这种单点验证的能力,在大提示词架构下不存在。现在我每次改完技能代码,先跑一遍该技能的单元测试,再跑一遍集成测试,有把握了才合入主干,线上出问题的频率降了很多。
这其实就是工程化的第一步:把大模型应用从“写提示词”变成“写代码 + 配置技能”。提示词仍然重要,但它不再承担全部逻辑。
2. 技能系统的整体结构,我是这么设计的
2.1 一个技能由哪些部分组成
在我设计的技能系统里,每个技能是一个自包含的模块,包含四样东西。
第一是元信息。技能的名字、简介、输入参数定义、输出说明。这部分最主要的使用者是模型,模型靠名字和简介来决定要不要调用这个技能。元信息写得不好,技能写得再好也白搭,因为模型根本没机会走到执行那一步。
第二是执行逻辑。真正干活的代码,可能是调接口、查数据库、读写文件,也可能只是做一次字符串处理。执行逻辑是技能的核心,但也是最好写的一部分,因为它的输入输出都有清晰边界。
第三是上下文。技能运行时需要的环境信息,比如当前用户、当前会话、临时存储、页面状态。上下文由系统统一注入,技能不自己跑到全局环境里去翻东西。这样设计是为了避免“全局变量地狱”,也方便在测试时模拟上下文。
第四是事件和钩子。技能执行前后可以触发的回调,比如执行前校验权限、执行后记录日志。我最早没做钩子,后来发现日志代码散落在各个技能里,重复度太高,就统一抽象成了钩子。
拿“查询商品详情”技能举例。元信息告诉模型这个技能叫 product_detail,参数是 product_id。执行逻辑根据 product_id 调用商品服务接口,返回结构化 JSON。上下文注入用户身份,技能内部判断该商品对当前用户是否可见。钩子在执行完成后自动记录一条操作日志。这样一个技能,单独能测,单独能复用,权限和日志都统一处理。
2.2 参数定义:面向模型而不是面向代码
这是我在实际开发中感受最深的一点。技能的参数定义,首先是要给模型看的,其次才是给代码用的。模型通过参数定义中的描述来决定怎么填参,所以描述写得好不好,直接决定技能被调用的成功率。
我用的参数定义结构是这样,以 JSON Schema 风格为例:
{ "type": "object", "properties": { "keyword": { "type": "string", "description": "用户搜索的关键词,例如:保温杯、无线鼠标" }, "page": { "type": "integer", "description": "页码,从1开始" }, "page_size": { "type": "integer", "description": "每页返回的商品数量,最多50" } }, "required": ["keyword"] }这种结构有两个好处。第一,模型能直观看到每个参数的含义和取值范围,不会把 page_size 填成“第几页”,也不会把 page 填成“页大小”。第二,代码端可以拿同一份定义做参数校验,不需要手写两套校验逻辑,省去了很多重复代码。
我特别建议不要小看参数描述里的措辞。写“页码,从1开始”和写“页数”,模型生成出来的内容完全不一样。前者明确、可校验;后者容易让模型自由发挥,传个“下一页”或者 0 进去。参数描述本质上是给模型的提示词,措辞越具体,模型的表现越稳。
这里有一个我踩过的坑:最开始我把所有参数都标成 required,模型一旦漏传某个参数,整个技能就执行失败,然后进入重试循环,浪费了整整好几轮对话。后来我调整策略,把非关键参数全部给默认值,并在描述里明确标注“可选,默认取X”,调用成功率立刻就上来了。
2.3 技能注册中心:统一入口与命名规范
技能多了之后,最怕管理混乱。我实现了一个很简单的注册中心,所有技能在启动阶段注册进一个全局表,后续通过名字取用。代码大概长这样:
class SkillRegistry: def __init__(self): self._skills = {} def register(self, skill): if skill.name in self._skills: raise ValueError(f"技能 {skill.name} 已存在,禁止重复注册") self._skills[skill.name] = skill def get(self, name): if name not in self._skills: raise KeyError(f"技能 {name} 不存在") return self._skills[name] def all_skills(self): return list(self._skills.values()) def list_skills_for_llm(self): return [s.get_help() for s in self._skills.values()]注册中心的代码本身很简单,但有两个细节很关键。
细节一,命名唯一性检查。两个技能重名,模型会彻底茫然,系统也不知道该执行哪一个。我在注册时直接抛异常,宁可启动失败暴露问题,也不留到运行期爆炸。
细节二,提供面向模型的技能清单。智能体每次需要决策时,会把 list_skills_for_llm 的返回结果带到上下文里。这里有两个优化点:
- 技能数量较多时,不能每次都把所有技能塞给模型。我在调用前做一层预筛,根据任务关键词选 Top-K 个候选技能,再交给模型做最终决策。实测下来,候选技能控制在 10 个以内时,选择准确率明显更高。
- 技能的 description 要控制在合适长度。描述太冗长会挤占上下文空间,甚至影响模型对用户问题的理解;描述太短又表达不清。我的经验是控制在两到三句话,既点明触发场景,也说明关键动作。
2.4 为什么要把技能和提示词分开管理
干了这么久,我越来越觉得“技能”和“提示词”是两个维度的东西。提示词负责的是“怎么说”,技能负责的是“做什么”。如果把两者混在一起,模型每轮对话都要同时处理表达和执行两层逻辑,消耗的注意力太多。
分开管理之后,提示词可以保持精简,技能可以保持独立。模型只需要理解“当前用户想要什么”,然后去技能清单里找匹配的工具,而不是再从头理解一遍系统里有多少条业务规则。这个拆分思路不仅适合智能体,也适合很多把大模型能力封装成服务的场景。
我团队里有个同事,最初接手这个项目时完全看不懂原来那段长提示词。技能化之后,他看技能清单就能知道系统支持哪些能力,修改某个技能的代码也不影响其他模块。这种可读性和可维护性,是我做这次重构最满意的回报。
3. 核心落地过程:从抽象接口到可运行技能
3.1 技能基类与上下文注入
我先把所有技能统一到一个基类上。基类负责三件事:声明技能基本信息、定义执行入口、接收上下文。
from abc import ABC, abstractmethod from typing import Any, Dict, Optional class SkillContext: """技能运行时的上下文,由系统统一创建并注入。""" def __init__(self, user_id=None, conversation_id=None, memory=None): self.user_id = user_id self.conversation_id = conversation_id self.memory = memory or {} class BaseSkill(ABC): name = "" description = "" parameters = {} returns = {} idempotent = False def __init__(self, context: Optional[SkillContext] = None): self.context = context or SkillContext() def get_help(self): return { "name": self.name, "description": self.description, "parameters": self.parameters, "returns": self.returns, } @abstractmethod async def execute(self, **kwargs) -> Any: ...每个技能继承 BaseSkill 之后,只需要实现 execute,同时用自己的 name、description 覆盖类的属性。这样写的好处是,技能作者不需要关心系统的其他部分,只需要把注意力放在执行逻辑上。
上下文的注入方式也很重要。我一开始让技能自己从全局配置里读 user_id,后来发现不同技能的读取位置还不一样,有的读环境变量,有的读全局对象,出错好几次“权限判定不一致”的线上问题。改成由系统统一创建一个 SkillContext 再注入之后,所有技能拿到的都是同一份运行时状态,这个问题就消失了。
3.2 从零实现一个商品搜索技能
我用一个真实场景来说明整个落地流程。假设智能体需要支持用户搜索商品,我把技能名定为 product_search。
第一步,写元信息。我要考虑清楚这个技能什么时候被调用、有哪些参数、参数怎么描述。
class ProductSearchSkill(BaseSkill): name = "product_search" description = "根据关键词在商品库中搜索商品,返回商品列表。适合用户询问'帮我找一下某类商品'时使用。" parameters = { "type": "object", "properties": { "keyword": { "type": "string", "description": "搜索关键词,例如:保温杯、无线鼠标" }, "page": { "type": "integer", "description": "页码,从1开始", "default": 1 }, "page_size": { "type": "integer", "description": "每页数量,默认10,最大50", "default": 10 } }, "required": ["keyword"] } returns = { "type": "object", "description": "包含 success 和 data 的 JSON 对象,data 为商品列表" } idempotent = True第二步,写执行逻辑。假设底层有一个异步查询函数 query_product_db:
async def execute(self, **kwargs): keyword = kwargs.get("keyword") page = int(kwargs.get("page", 1)) page_size = min(int(kwargs.get("page_size", 10)), 50) if not keyword: raise ValueError("keyword 不能为空") data = await query_product_db(keyword, page, page_size) return {"success": True, "data": data}这里有几个细节需要注意。
第一,参数在 execute 内部做了一次归一化。kwargs 里可能的字符串数字会被转成 int,page_size 被限制在 50 以内。这层防护很重要,因为模型填参时不会总填成规范类型,提前做校验和转换,比等到底层接口报了类型错误再返回来处理要高效得多。
第二,keyword 为空直接抛异常。有些技能作者会返回 success=False,然后让模型自己猜发生了什么。我的习惯是,入参非法直接抛 ValueError,这样系统层能明确知道这次调用失败了,可以做统一的重试或降级,而不是把语义模糊的结果留给模型去“悟”。
第三,加 idempotent = True。商品搜索是读取型操作,重复执行不会产生副作用,所以允许系统在超时后自动重试一次。
第三步,把技能注册进系统:
registry = SkillRegistry() registry.register(ProductSearchSkill()) # 单独测试一下 import asyncio skill = registry.get("product_search") result = asyncio.run(skill.execute(keyword="保温杯", page=1, page_size=10)) print(result)这段代码跑通之后,商品搜索就成了一个挂在智能体上的独立能力。我可以脱离大模型单独测它,给它传参数,看返回结果是否符合预期。这条路径在“大提示词”架构下是不存在的,也是这个改造带给我最大的效率提升。
3.3 模型如何决定调用哪个技能
这里要讲一个核心交互:技能清单与模型决策。
当用户说“帮我找一个适合送长辈的保温杯”,系统会先把技能清单传给模型。清单包含 name、description、parameters。模型读完清单后,在回复中携带一个结构化调用指令,例如:
{ "action": "product_search", "arguments": { "keyword": "保温杯 送长辈" } }系统解析这段指令,从注册中心取出技能,执行,拿到结果,再把结果返回给模型。模型基于结果组织最终的自然语言回复。这个过程里,最见功力的其实是解析器。我的解析器不要求模型输出完美 JSON,而是做了三个容错处理:
- 支持多种调用格式,兼容纯 JSON 和代码块包裹的 JSON;
- 如果模型把整个用户问题当成关键词,比如传进来“帮我搜索一下保温杯”,我会在提取关键词时用规则去掉常见口语前缀;
- 如果模型没有提供某个可选参数,解析器按默认值填充,不报错。
这个容错机制对实际体验的提升非常大。我发现很多情况下模型的输出格式并不可控,它会加注释、会写多余的标点、会把参数名从蛇形改成驼峰。我的原则就是,不苛求模型格式完美,而是让系统尽量有弹性。解析器虽然多写了百来行代码,但换来的是调用成功率的显著提升。
3.4 技能超时与重试机制的设计
技能执行遇上外部接口超时,可以说是家常便饭。我给技能调用加了两层防护。
第一层是超时控制。用 asyncio.wait_for 包住技能执行,超时后直接放弃这一次调用,不让用户一直干等:
import asyncio async def run_with_timeout(skill, timeout=10, **kwargs): try: return await asyncio.wait_for(skill.execute(**kwargs), timeout=timeout) except asyncio.TimeoutError: return {"success": False, "error": "timeout", "message": "技能执行超时"}第二层是重试策略。不是所有技能都适合重试。只有具备幂等性的技能才允许自动重试,比如读取型操作。像“创建订单”这种非幂等操作,绝对不能盲目重试,否则会造成重复下单。我就是在技能元信息里加了 idempotent 字段,系统在调用前读一下这个字段,只有幂等技能才会在超时后自动重试一次。
这个设计对线上稳定性的帮助非常大。早期我不区分幂等性,所有技能失败就盲目重试,结果在一次压测里把上游服务搞出了好几条重复数据。那次教训之后,我养成了一个习惯:所有写操作型技能,默认不允许重试;如果确实要重试,必须在业务逻辑里做幂等处理,比如带上全局请求 ID,让上游去重。
3.5 技能执行的日志与观测
技能被执行之后,最重要的事情就是留下日志。我用的是一种很轻量的方案:在技能外面包一层装饰器,统一记录耗时、入参、出参和异常。
import time import logging logger = logging.getLogger("skill") def observable(skill): class ObservableSkill(skill.__class__): async def execute(self, **kwargs): start_time = time.time() try: result = await super().execute(**kwargs) logger.info( f"[skill:{self.name}] success args={kwargs} " f"cost={time.time() - start_time:.3f}s" ) return result except Exception as e: logger.exception( f"[skill:{self.name}] failed args={kwargs} " f"cost={time.time() - start_time:.3f}s error={e}" ) raise return ObservableSkill()有了这套日志之后,线上排查的效率高了很多。以前出问题只能翻对话记录,现在只要搜技能名,就能看到它被调用了多少次、每次入参出参是什么、耗时多少。再配合一个全局 trace_id,可以把同一次请求里的模型调用和技能调用串起来,快速定位是哪一环出了问题。
我的经验是,技能日志不要求花哨,一定要包含三个信息:技能名、关键入参、耗时。入参可能涉及隐私,所以我会在日志里做脱敏,比如隐藏手机号中间四位。这个细节上线前就要想清楚,不然后期补会很难受。
4. 常见问题与排查技巧实录
4.1 模型总是选错技能,怎么办
这是最常被问到的问题。我排查时一般按顺序检查这几项。
第一,技能描述是否够具体。描述里写“搜索商品”太模糊,要写成“当用户想根据关键词查找商品列表时使用”,最好还要带一个触发场景的示例。描述写得越具体,模型选择的准确率越高。
第二,技能之间是否有重叠。如果两个技能描述高度相似,模型当然会选错。可以合并成一个技能,也可以在描述里明确边界。比如“查询订单状态”和“查询订单详情”,如果功能确实接近,不如合并成“查询订单信息”,用一个参数区分粒度。
第三,技能清单是否过长。模型一次性看到几十个技能,注意力会被分散,选择准确率会明显下降。我用预筛策略,先根据用户输入提取关键词,从注册中心里召回候选技能,把候选数量控制在 10 个以内,再交给模型做最终决策。实测下来,这个叠加策略效果很好。
4.2 技能参数总填不对,怎么解决
参数的错大多出在描述上。有一次我在定义日期参数时只写了“date”,模型传入的格式五花八门,有带斜杠的、有带中文的、有时带时刻的。我改成“查询日期,格式为YYYY-MM-DD,默认取今天”之后,错误率立刻降了下来。参数描述本质上是给模型的提示词,越具体越好,最好直接给出格式范例。
还有一个细节:不要把参数名设计得过于抽象。比如用 filter、q、data 这种模糊的名字,模型只能靠猜。换成 keyword、start_date、end_date 这种一看就懂的名字,准确率会提高不少。参数名是给模型看的接口契约的一部分,不能只考虑代码端习惯。
4.3 技能执行报错但没有退出,来回重试
我遇到过技能内部捕获了所有异常然后返回空结果的情况,结果模型在下一轮找不到数据,就又调了一次同一个技能,形成了一个循环。排查之后发现,问题出在技能内部把异常吞掉了。
我的做法是:技能内部不要吞异常,处理不了的异常直接向上抛,让系统层统一记录并决定是否重试。返回空结果和抛出异常,语义上完全不同。前者表示“没有数据”,后者表示“执行失败”。模型对这两种情况的理解和处理方式不同,不能混在一起。下面的代码就是错误示范和正确示范的对比:
# 错误示范:把异常吞掉并返回空数据 try: data = await call_api() return {"success": True, "data": data} except Exception: return {"success": True, "data": None} # 正确示范:失败时抛出异常,让系统层处理 try: data = await call_api() except Exception as e: logger.error(f"调用失败: {e}") raise return {"success": True, "data": data}你可能觉得这很基础,但我在实际审查代码时,真的发现了很多吞异常的写法。究其原因,是写代码的人想让智能体“继续运行”,不希望一个技能失败就中断整个任务。但这样做只会掩盖问题,让故障在更深的流程里爆发。
4.4 并发环境下技能状态串了
技能实例如果设计成全局单例,在多轮并发对话里就会出问题。早期我把 context 直接存在技能实例上,两个用户同时调用同一个技能时,A 用户的 user_id 被 B 用户覆盖,导致权限判断错乱,数据串了。这个问题非常隐蔽,不压测根本发现不了。
解法很简单:技能实例只保存无状态定义,每次调用时从上下文变量里取动态数据,或者每次请求创建一个新的技能实例。我最终选了前者,用线程上下文保存当前调用者的信息,技能实例保持单例。这样既保留了统一调度的好处,又避免了数据串扰。
这里也要提醒一句:如果是用并发框架,不同的任务之间可能会共用变量,所有可变的共享状态都要放到上下文对象里,而不是技能类的属性里。
4.5 常见问题速查表
| 现象 | 可能原因 | 排查与解法 |
|---|---|---|
| 模型经常不调用技能 | 技能描述不够明确 | 用包含触发场景的描述重写 description |
| 调用了技能但参数错误 | 参数名抽象或描述模糊 | 改成直观名字,并提供格式范例 |
| 技能执行异常但无日志 | 内部吞掉异常 | 统一在系统层记录并向上抛出 |
| 两个用户数据串了 | 可变状态存在技能实例上 | 改为从调用时上下文获取 |
| 反复执行同一技能 | 返回值语义不清 | 明确区分“无数据”和“执行失败” |
| 技能响应太慢 | 外部依赖超时 | 加超时控制,幂等技能可重试 |
| 模型选技能不稳定 | 候选技能过多 | 用关键词预筛,限制候选数量 |
5. 技能协作与演进方向
5.1 技能编排:从一个技能到一条工作流
单个技能解决单点问题,多个技能协同才能完成复杂任务。我的做法是在技能之上加一层“规划器”,让模型基于前面技能的结果决定下一步调用哪个技能。
举一个典型的例子。用户问“这个保温杯和那个保温杯哪个保温时间长”。这里实际需要至少两个技能:先用 product_search 找到具体商品,再用 product_detail 获取每个商品的详细参数,最后做对比。规划器会把一个大目标拆成多个小步骤,按顺序执行技能,并把中间结果累积到上下文里。
这个编排层我不想设计得太重。最简单的实现是让模型交出一个动作序列,系统照着逐个执行;复杂一点可以加条件判断、循环和分支,但这会让调试难度成倍增加。我的强烈建议是:序列执行先跑通,确认稳定了,再考虑循环和分支。见过太多项目一上来就搞图形化工作流编排,结果连线性调用都没理顺,最后整出一堆现场事故。
5.2 技能复用与多智能体共享
我把技能设计成独立于具体智能体的模块,最直接的好处就是客服智能体和数据分析智能体能共用一套技能库。实际操作中,只需要在不同智能体里声明“启用哪些技能”,比如一个启用 product_search、order_detail,另一个启用 report_generate、data_query,技能内部实现完全不用改。
这么做还有一个隐含的好处:底层能力一致,上层表达可以各自不同。客服回答问题时把技能结果转成口语,数据分析生成报告时把技能结果转成图表数据。底层技能完全无感知,只负责输出结构化结果。这就像公司里各个前台部门共用同一套后端系统,前端展示各不相同,但数据源头是统一可靠的。
5.3 技能质量保障:测试用例与回归
技能越来越多之后,我专门给每个技能补了测试用例。我的测试分三层:单元测试、参数兼容测试、集成测试。
单元测试覆盖技能的核心逻辑,比如商品搜索的分页、排序、空结果场景。参数兼容测试用一组模拟的大模型输出作为输入,验证解析器能不能正确解析并映射到技能参数,覆盖一些格式不规范的案例。集成测试则是把技能接到真实的外部服务上,验证鉴权是否通过、接口是否正常返回。
我要求新增技能必须带两层测试才能合并:一层是技能自身的单元测试,一层是解析器的兼容性测试。虽然这增加了开发成本,但拉长来看收益非常明显。技能发布上线后的回归问题大幅减少,很多问题在编码阶段就被拦住了。
5.4 后续想做的几个方向
我自己现在在推进三个方向。
一是把技能调用的链路追踪做起来,用 trace_id 贯穿模型调用和技能调用,配合日志平台做全链路查询,这样线上问题定位能更快。
二是把技能的选择逻辑从纯粹依赖模型描述,改成结合少量样本的少样本选择。在复杂场景下,光靠 description 的文本匹配不够精确,加一两个典型调用示例,模型选错技能的频率会低很多。
三是把技能注册和配置做成可视化界面,让非技术同事也能维护技能描述。技能化的好处就是边界清晰,描述可以独立编辑,如果能提供一个简单的管理台,运营同事也能参与技能的调优。
这几个方向不一定马上落地,但整体思路很清楚:技能化不是终点,它是让智能体工程化和平台化的第一步。越往后做,越觉得智能体系统的核心竞争力,已经不仅仅是模型能力,而是围绕技能展开的工程能力。
我个人在实操中的体会是:Agent Skills 的价值不在于概念,而在于让原本混沌的智能体系统变得可测、可查、可复用。每次我想加一个新能力,不再是往提示词里塞一段话,而是写一个独立技能,注册进去,测试通过,上线。这种确定性带来的安全感,是做大型智能体项目时最珍贵的东西。希望这篇记录能帮你在自己的项目里少走几步弯路。