如果你最近在研究AI Agent,一定遇到过类似的困局:模型什么都能聊,但一落到具体业务就抓瞎。我去年接手了一个智能客服项目,最初的方案是“一个大模型 + 一套大而全的Prompt + 一份工具列表”,结果模型频繁选错工具、传错参数,维护成本高到怀疑人生。后来我把整个Agent的能力体系重构为“agent-skills”技能库项目——把每一个业务能力封装成独立技能模块,每个技能自带描述文件、参数校验、测试用例和调用日志,让Agent既能“知道自己会什么”,又能“按规矩调什么”。这个项目解决了我在真实业务里遇到的大部分稳定性问题,如果你的工作也涉及Agent落地、智能客服、自动化办公或RPA类场景,这篇文章值得你看下去。
我用agent-skills模式跑通订单查询、物流追踪、售后处理、库存查询等多个客服场景后,最大的感触是:Agent能力的核心瓶颈其实不在模型选得多强,而在于你能否把能力“工程化”。技能库表面上是一堆目录和脚本,本质上是一套控制LLM行为边界的机制。下面我会从为什么拆技能、技能文件怎么设计、注册调用怎么实现、以及我踩过的坑这几个维度,完整复盘这个项目的每一个关键细节。
1. 为什么我把Agent能力拆成了技能库
1.1 最初的问题:一个巨型Prompt驱动的Agent如何失控
项目刚启动时,我采用了最主流的做法:把客服业务里的所有动作——查订单、查物流、发优惠券、登记售后——全部写成函数,塞进一个大数组里作为tools传给模型,同时在一份系统Prompt里强调“当用户询问订单信息时调用order_query,当用户询问物流时调用logistics_query”。听起来没什么问题,但真跑起来全是坑。
第一个坑是工具列表膨胀。业务场景一多,工具超过了十五个,模型开始频繁选错。比如用户问“我的包裹怎么还没到”,模型居然去调了coupon_query。我仔细看了调用日志,发现模型不是不聪明,而是工具描述之间的区分度太低,两个工具名称里都有query,描述里都有“查询订单信息”,LLM根本分不清边界。第二个坑是参数幻觉。模型经常把自然语言里的信息直接填进参数,比如order_id字段它传“用户说他的订单号是三个七”,这根本不是合法的订单号。第三个坑是维护成本极高,改一个工具的描述,可能影响其他工具的选择逻辑,回归测试困难。
我实测过一个数据:工具数量从8个上升到17个时,工具选择的准确率从95%左右掉到82%以下。对一个客服系统来说,选错工具意味着直接给用户错误答案,这是不可接受的。后来我意识到,问题不在于“工具不够多”,而在于“工具没有被结构化地组织”。
1.2 技能(Skill)模式的本质:从“一个大脑”到“大脑+工具箱”
我换了个思路看待这件事。一个刚入职的新员工,你给他一份公司手册,他能干的事非常有限。但如果你带他走遍仓库、认全每台设备、每样工具贴在什么位置、什么场景下用哪个,他很快就能独当一面。Agent也是一样,它不能只靠一份Prompt里的文字描述去“想象”如何工作,它需要一个真实的、结构化的工具箱。
agent-skills项目做的就是这件事。它把每个业务能力封装成独立的Skill,每个Skill包含四个绑定在一起的部分:给LLM看的能力说明书(SKILL.md)、给程序看的参数约束(schema.json)、实际执行逻辑(skill.py)、以及保证回归的测试用例(tests)。这个模式和很多Agent框架里简单的tools列表最大的区别在于:tools列表只有“名字+描述+函数”,而Skill把“什么时候用、参数怎么传、调用后怎么验证、坏了怎么排查”全部固化了下来。
我当时做了一个类比,tools是一个工具箱里的各种零散零件,Skills是一整套带说明书和质检标准的工具卡。前者适合demo,后者适合生产。如果你只是自己玩一玩,tools列表完全够用;一旦要接真实业务,面对真实用户、真实订单数据、真实售后流程,你需要的是一套能让工程团队和LLM都理解的能力规范。
1.3 agent-skills项目的整体目标与设计红线
做agent-skills之前,我先给自己定了三条设计红线,后来证明这几条红线帮我避开了很多麻烦。
第一条,技能之间不允许互相依赖。也就是说,一个Skill不能直接import另一个Skill的内部函数,所有技能只能通过统一执行器间接协作。这样做的好处是技能可以独立替换、独立升级,坏了也不至于连环崩溃。第二条,每个技能必须自带SKILL.md和schema.json。没有说明文档和参数约束的技能不允许注册进技能库,这是硬校验,不是自觉遵守。第三条,每个技能都要有日志和测试用例。技能执行必须输出结构化日志,包含技能名、调用参数、耗时、结果状态,方便线上排查;测试用例则保证后续改动不破坏已有能力。
这三条红线在早期看起来很麻烦,尤其是给每个技能写说明和测试特别耗时。但项目运行了三个月后,我越来越觉得这是最值得的一笔投入。因为客服场景的Agent一旦出错,影响的不只是用户体验,还有后续工单、订单状态、售后流程等一系列连锁反应。能力可以扩展得慢一点,但不能脏乱差地膨胀。
2. 技能库的目录结构与技能文件设计
2.1 一个技能长什么样:SKILL.md、skill.py、schema.json、tests
我在项目里采用了非常直接的目录结构,每个业务能力占一个文件夹,所有文件夹放在skills根目录下。下面是一个订单查询技能的标准结构:
agent-skills/ ├── skills/ │ ├── order_query/ │ │ ├── SKILL.md │ │ ├── skill.py │ │ ├── schema.json │ │ ├── requirements.txt │ │ └── tests/ │ │ └── test_order_query.py │ ├── logistics_query/ │ │ └── ... │ └── coupon_send/ │ └── ... ├── registry/ │ └── index.yaml ├── core/ │ ├── loader.py │ ├── executor.py │ └── logger.py └── main.py你可能会问,为什么不把schema直接写在SKILL.md的YAML头里,还要单独放一个schema.json?我一开始也是这么干的,但后来发现SKILL.md是给LLM读的,越轻量越好;而schema.json是给程序做参数校验用的,需要严格、完整、可机器执行。两者混在一起,会导致Markdown文件里塞大量JSON,既影响LLM理解,也容易在解析时出错。
SKILL.md的核心作用是告诉LLM“你什么时候该用我、用了我会得到什么、我需要你提供什么”。skill.py是真正干活的函数,我统一约定它对外暴露一个run(params)入口,接收字典参数,返回字典结果。schema.json约束参数格式,比如订单号必须是特定前缀加数字、手机号必须是11位、时间字段必须是timestamp。tests目录是给工程人员用的回归保护,用pytest跑,每次改动技能库前先跑全量测试。
一个技能文件夹里的四个部分各司其职,谁出问题都能快速定位。比如用户反馈“Agent乱用技能”,先看SKILL.md写得好不好;反馈“技能报了参数错误”,看schema.json的约束;反馈“执行结果不对”,看skill.py的业务逻辑和测试用例。这种结构让整个项目好查、好改、好交接。
2.2 SKILL.md怎么写:LLM能读懂的能力说明书
SKILL.md是整个技能库的“灵魂”,写法好坏直接决定Agent的技能选择准确率。我踩过很多次坑之后,形成了一套比较稳定的写法规范。核心原则是:写清楚触发条件、输入要求、输出结构,并且一定要写“绝不使用”的场景。
下面是我在order_query技能里用的一份SKILL.md示例:
--- name: order_query version: 1.0.0 description: 查询用户的订单状态、发货时间与售后进度。 triggers: - 用户询问“我的订单到哪了” - 用户询问“货发了吗” - 用户询问“订单什么时候到” - 用户要求查看最近订单状态 non_triggers: - 用户询问“怎么退货” - 用户询问“退款到账时间” - 用户询问“优惠券怎么领” inputs: order_id: type: string description: 平台生成的订单号,以ORD开头 required: true user_id: type: string description: 用户唯一标识 required: true outputs: - 订单当前状态 - 预计送达时间 - 最近物流节点 example: "用户说:我的订单ORD20240115001还没发货吗? -> 输入:order_id=ORD20240115001, user_id=U100234" ---写这份文档的时候,我特别强调triggers和non_triggers。LLM在选择技能时,本质上在做一道语义匹配题,描述里给出正面触发词能帮它命中正确技能,给出反面排除项能帮它避开相似技能。比如order_query和return_apply配置单看“查询”两个字很容易混淆,但order_query里明确写了“不处理退货”,return_apply里写了“不查询物流状态”,两个技能的边界一下子就清晰了。
另外,我强烈建议在description里写一句“查询类动作,不会修改任何数据状态”,这句话在Agent决定是否调用某些只读技能时非常有用。部分场景下,Agent会为了避免改变系统状态而选择不调用技能,有了明确的只读声明,它能放心大胆地使用。
2.3 参数Schema:用JSON Schema约束Agent不要乱来
LLM生成参数时经常出现“多传、漏传、错传”的问题。我在不做参数校验时,遇到过模型把用户输入的订单号“我的订单号是三个七”直接塞给order_id,也遇到过把两个参数合并成一个传的情况。用JSON Schema做参数校验之后,这些问题大幅减少——因为非法参数会被直接拦截,而不是带病进入业务逻辑。
我针对order_query技能写的schema.json大致如下:
{ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "order_id": { "type": "string", "pattern": "^ORD[0-9]{12}$", "description": "平台订单号,ORD加12位数字" }, "user_id": { "type": "string", "minLength": 6, "maxLength": 32, "description": "用户唯一标识,不能为空" } }, "required": ["order_id", "user_id"], "additionalProperties": false }很多人会忽略additionalProperties这个字段,但它极其重要。默认情况下,JSON Schema允许额外字段,模型有时候会自己发明一个叫“order_detail”的参数塞进去,如果不把它设成false,这些垃圾参数会被带进skill.py,导致业务代码里到处都是防御性判断。设成false以后,不合规的参数在入口处就被挡掉,代码清爽很多。
再补充一个细节:pattern校验对订单号这类格式稳定的字段非常有效,但对用户ID、昵称等自由文本就别上太严的pattern了。我一开始给user_id加了“必须是数字”的pattern,结果大量存量用户ID带字母,频繁报错。参数校验的粒度要和真实数据匹配,该严的地方严,该松的地方松。
3. 技能注册、调用与编排的工程实现
3.1 技能发现与加载:动态扫描 + index.yaml
技能库搭建初期的核心问题是怎么让Agent“知道自己会什么”。我调研了许多Agent框架,发现有的把技能列表写死在一个大数组里,有的靠向量数据库做实时检索。写死数组简单但扩展性差,向量检索重但适合技能数量很大的情况。作为一个中间态方案,我用“目录自动扫描 + registry/index.yaml 索引”的组合,兼顾了扩展性和可控性。
目录自动扫描的逻辑很简单:程序启动时遍历skills目录下的所有子目录,每个子目录必须包含SKILL.md、schema.json、skill.py,否则跳过并输出告警日志。这样新增一个技能时,只需要新建一个文件夹,无需修改注册代码。index.yaml则用于补充扫描无法获取的元信息,比如技能版本、维护人、分组、启停状态。
skills: - name: order_query version: 1.0.0 group: order enabled: true owner: team_order - name: logistics_query version: 1.1.0 group: order enabled: true owner: team_logistics - name: coupon_send version: 0.9.0 group: marketing enabled: false owner: team_marketingenabled字段是我后来加上的。早期没有这个字段时,已经开发但未验收的技能会直接被LLM使用,出过几次事故。加了enabled字段后,新技能默认false,验收通过才改成true,相当于多了一道人工审核闸门。group字段则用于打标签,Agent在规划时可以先按业务分组缩小候选技能范围,减少选择压力。
加载器还有一个容易被忽略的细节:技能目录的扫描顺序不能影响结果。我用字典存储技能名到Skill对象的映射,不依赖文件系统顺序,这样即使在多台机器上部署,同一份代码加载出来的技能集合和技能行为都是一致的。
3.2 统一执行器:Agent调用技能的接口设计
技能加载只是第一步,真正麻烦的是让Agent以统一的方式调用技能。我设计了一个核心执行器core/executor.py,对外只暴露一个execute方法。所有技能的调用都走这个入口,不在业务代码里直接调skill.py。
# core/executor.py import json import jsonschema import time class SkillExecutor: def __init__(self, registry): self.registry = registry def execute(self, skill_name, params, request_id): skill = self.registry.get(skill_name) if skill is None: return {"status": "error", "error": "skill_not_found", "data": None} try: jsonschema.validate(params, skill.schema) except jsonschema.ValidationError as e: return {"status": "error", "error": f"invalid_params: {e.message}", "data": None} start = time.time() try: result = skill.implementation(params) return {"status": "success", "data": result, "error": None} except Exception as e: return {"status": "error", "error": str(e), "data": None} finally: duration = time.time() - start logger.log_execution(skill_name=skill_name, params=params, result=result, duration=duration, request_id=request_id)这段代码有几个细节我后来才体会到价值。第一,参数校验放在技能执行之前,而不是在skill.py里做,保证了所有技能的错误行为一致。第二,返回值统一为{status, data, error}三元组,LLM能轻松消费,不需要自己去猜返回结构。第三,finally里的日志记录无论如何都会执行,哪怕技能抛异常,调用链也能在日志里找到。
统一执行器还有一个好处:它让所有技能都具备“可观测性”。每次调用都会记录调用了哪个技能、传入了什么参数、耗时多长、返回了什么。这些日志既是排查问题的依据,也是后续做技能质量评估的数据基础。我甚至从这个日志里统计出哪些技能经常被调用、哪些技能老是报错、哪些技能调用耗时过长,然后针对性优化。
3.3 多技能编排:让Agent自己决定调用顺序
单个技能的调用解决了“怎么干一件事”,但真实业务里,用户一句话往往需要多个技能配合。比如用户问:“我昨天买的手机怎么还没发货?如果再不发货我就退了。”这句话里面既有订单查询的需求,又可能触发退货登记的需求。如果Agent只调一个技能,很难给出完整回复。
我采用的是“LLM规划 + 顺序执行”的轻量编排机制。模型收到用户问题后,先根据技能库生成一个执行计划,计划是技能名和参数的列表,然后交给Executor逐项执行。我封装了一个编排函数:
def run_agent_plan(user_question, skill_names, executor, request_id): plan = llm_planner.create_plan( question=user_question, available_skills=skill_names ) results = [] for step in plan["steps"]: result = executor.execute(step["skill"], step["params"], request_id) results.append(result) if result["status"] == "error": break return results这里最关键的并不是代码,而是plan的生成策略。我早期让LLM自由发挥,结果它经常生成不存在的技能名,或者在第一步失败后仍然继续执行后续步骤。后来我在planner的prompt里加上两条硬规则:只能从候选技能列表里选择技能名;第一步失败则立即终止整个计划并回复用户“暂时无法完成”。
编排层同样需要日志。我会把每一步的计划、实际执行结果、每一步耗时都记录下来。这样如果用户说“机器人答非所问”,我可以直接回放整个计划执行过程,快速定位是哪一步的判断出了问题。相比黑盒式的Agent反馈,这种白盒可观测的模式在团队协作和故障定责上优势明显。
4. 实操过程:从零跑通一个订单查询技能
4.1 需求梳理与技能边界确定
用一个具体案例来把前面的设计串起来:我需要在agent-skills里新增一个“订单查询”技能。需求来自客服团队——用户高频问题之一是“我的订单到哪了”“发货了吗”“多久能到”,客服希望机器人能直接查订单状态并回答。我梳理出这个技能的边界:
- 查询范围:订单状态、预计送达时间、最近物流节点。
- 不处理:退款、退货、改地址、催发货(有另两个技能负责)。
- 输入参数:order_id和user_id,两者都必填。
- 数据来源:内部订单系统的HTTP接口。
边界确定后,我把它写进SKILL.md的triggers和non_triggers里。这一步不能省,因为客服场景下,用户表达极其口语化,“货呢”“我的件儿呢”“怎么还没到”这些说法都要在triggers里覆盖到。我甚至从历史工单里拉了500条真实用户问题,人工过了一遍,把高频表达都提炼出来写进触发词。
4.2 创建技能目录并编写实现
我按标准结构创建了文件夹skills/order_query,然后写核心实现。技能实现本身并不复杂,关键在于接入内部订单接口时的封装方式。我将外部API调用和参数解析分离,避免在skill.py里堆太多杂逻辑。
# skills/order_query/skill.py import requests API_ENDPOINT = "https://api.internal.example.com/order/status" def run(params: dict) -> dict: order_id = params["order_id"] user_id = params["user_id"] resp = requests.get( API_ENDPOINT, params={"order_id": order_id, "user_id": user_id}, timeout=3, ) resp.raise_for_status() data = resp.json() return { "order_status": data.get("status"), "estimated_delivery": data.get("estimated_delivery"), "recent_logistics": data.get("recent_logistics"), "raw_text": f"您的订单当前状态为:{data.get('status')},预计送达时间:{data.get('estimated_delivery')}" }这里有一个重要的注意事项:不要在skill.py里硬编码密钥或敏感配置。我把API的base_url和鉴权信息放在环境变量里,技能代码从os.environ读取。这样技能库可以自由地在不同环境间切换,不用改一行代码。另外,接口调用设置了timeout=3,避免第三方接口挂起导致Agent长时间无响应。
你会发现skill.py里没有做任何参数校验逻辑,因为校验已经由executor通过schema.json完成了。这让我在写业务函数时非常轻松,只需要信任入参是合法且完整的,专注处理数据获取和结果组装。
4.3 注册技能并验证加载链路
技能写完以后,需要先做本地校验,再把它正式注册启用。我在项目里写了一个validate命令,它会自动检查技能的SKILL.md格式、schema.json合法性、skill.py能否被正常导入、测试用例是否通过。
python main.py validate skills/order_query执行后发现有两个问题。第一个问题是schema.json的order_id pattern写成^ORD[0-9]{12}$,但测试数据里的订单号是ORD开头加14位数字,校验失败。这个不算bug,是我对数据口径理解不对。第二个问题是测试用例里mock接口返回格式和真实接口不一致,导致返回的estimated_delivery字段解析出错。修正这两处后,validate全部通过。
注册完成后,我立即启动了一个最小版的Agent服务,在本地用真实用户问题做了一次端到端对话测试。我输入“你好,我的货怎么还没到”,Agent先通过LLM判断这属于order_query技能,然后从对话中抽取出参数,发现user_id缺失,主动追问“请提供一下您的手机号或用户ID”,拿到参数后调用了order_query并回复查询结果。整个链路在本地跑通。
4.4 线上日志与调用链观察
技能上线运行后,我重点观察执行器的日志。这是agent-skills项目里最让我有成就感的部分——不再需要靠猜来定位问题。一次普通的订单查询调用,日志大概长这样:
request_id=req_20240315_001 skill=order_query action=execute status=success duration=187ms request_id=req_20240315_001 skill=order_query params={"order_id": "ORD202403150001", "user_id": "U100234"} request_id=req_20240315_001 skill=order_query result={"order_status": "shipped", "estimated_delivery": "2024-03-20", "recent_logistics": "已到达杭州转运中心"}从这份日志里,我能清晰看到某次请求调用了哪个技能、传了什么参数、多久返回、返回了什么。如果用户投诉“机器人回答错误”,我直接按request_id在日志系统里检索,几秒钟就能还原现场。这样的日志在任何Agent项目里都应该成为标配,但在实际接触过的很多团队里,Agent调用是没有日志的,出了问题只能复现,非常痛苦。
5. 常见问题与排查技巧实录
5.1 技能匹配错乱:为什么模型总选错技能
我在技能库上线初期最头疼的问题是“模型选错技能”。用户问退货,它却调了订单查询;用户问物流,它却调了库存查询。排查了一段时间后,我发现主要原因有两个:一是SKILL.md里的description太泛,比如只写“查询订单信息”,没有写明“仅查询,不包含退货和物流”;二是相似技能的边界没有说清楚,候选技能越多,这个问题越严重。
解决思路是给每个技能增加negative triggers和非目标场景描述。比如order_query里明确写“不处理退换货、不处理退款、不处理物流轨迹实时追踪”,logistics_query里写“只处理物流节点信息,不处理订单支付状态”。这样增加描述后,技能选择准确率从约86%提升到了94%。我还做了一个小实验,把技能名也改成更语义化的方式,比如把order_query改名为query_order_status,模型的选择准确率没有显著变化,说明问题主要出在描述内容,而不是命名方式。
5.2 参数校验失败:模型传参太随意怎么办
参数校验失败是上线第一天就出现的问题。模型在抽取用户输入时经常漏掉字段,或者把“我的订单号”这类描述文本直接当作订单号。我试过在prompt里反复强调“只能传订单号”,效果有限。后来我在executor前面加了两层处理。
第一层是参数抽取的独立prompt,让LLM先针对用户问题做字段抽取,再决定调用哪个技能;第二层是正则兜底,比如订单号字段先尝试匹配ORD[0-9]{12,14},如果没有匹配到,就直接返回需要用户补充信息的提示,而不是把脏数据传进技能。这两层合起来,让参数校验的失败率降低了一个数量级。
5.3 并发冲突与技能状态污染
技能数量多起来以后,我碰到过一类比较隐蔽的问题:Agent的某个技能在一定并发量下会偶发返回错误结果。排查了很久,最终定位到coder在skill.py里使用了一个模块级全局变量缓存用户信息,导致不同用户的请求之间产生了数据污染。这个问题本质上违反了我在项目初期定的“技能无状态”红线。
修复方案是把所有请求相关的数据都放到run(params)局部作用域里,禁止使用模块级可变变量。为了长期防住这个问题,我在code review规范里加了一条:skill.py内不允许出现全局可变对象。同时把并发测试纳入了标准测试流程,模拟多个不同用户同时调用同一个技能,验证结果是否互相干扰。这个教训让我深刻体会到,设计红线必须通过工程规范和测试用例来固化,否则很容易被后来的改动突破。
5.4 排查Agent技能问题的三板斧
如果有人问我在agent-skills项目里最实用的排查方法是什么,我会回答:日志、最小复现、降级方案。
第一板斧是日志。每个技能调用都带request_id,日志里必须有技能名、参数、结果、耗时。没有日志的Agent项目,排查问题基本靠猜。第二板斧是最小复现。发现某个技能有问题后,我通常会写一个几十行的脚本,直接调用executor.execute,绕开LLM和编排层,确认问题是出在技能本身还是出在LLM的选择/规划环节。这一步能快速切分责任边界。第三板斧是降级方案。当技能依赖的第三方服务不可用或者技能本身报错时,Agent要能从“调用技能模式”降级为“人工兜底模式”,返回一句“暂时无法查询,已为您转接人工客服”,而不是胡编一个答案。
5.5 避坑清单:agent-skills开发速查表
我把项目里遇到的高频问题整理成了一张速查表,开发新技能时可以逐条对照:
| 问题 | 根因 | 对策 |
|---|---|---|
| 技能被选错 | SKILL.md描述太宽泛,缺少边界 | 写清楚triggers和non_triggers,举具体例子 |
| 参数被传错 | 缺少JSON Schema校验 | 为每个技能配置schema.json并启用additionalProperties: false |
| 技能执行超时 | 外部接口慢或无超时限制 | 所有HTTP调用设置timeout,并在executor外增加超时熔断 |
| 并发返回脏数据 | skill.py使用全局可变变量 | 技能无状态化,状态只放params局部作用域 |
| 新技能没启用 | index.yaml未更新 | 新技能默认enabled: false,验收后手动开启 |
| 日志查不到 | 没传入request_id或日志级别不对 | 链路入口统一生成request_id,日志落盘到独立索引 |
| 测试没覆盖 | 只测了主路径 | 补边界输入、异常返回、并发场景测试 |
我建这个项目的过程中,几乎把表里每一行都踩过一遍。这些坑单独看都不复杂,但组合在一起,往往会让一个Agent项目从“demo能跑”到“生产可靠”之间隔着一道鸿沟。而agent-skills的价值,正是把这条路上所有容易出问题的点,都变成可以格式化的工程规范。
6. 这个项目的未来扩展与我的个人体会
技能库模式跑通以后,我发现它的想象力远不止订单查询。首先,技能可以跨项目复用。同一套订单查询技能,既可以用在客服Agent里,也可以用在后台运营的数据助手Agent里,只需要复用同一份技能文件夹,不用重新实现。其次,技能可以做得更细。比如把“订单查询”继续拆成“订单状态查询”和“物流轨迹查询”,按业务场景动态组合。再次,技能还能做灰度发布——新版本技能先在部分流量中试运行,观察日志和成功率,再逐步放量,这对生产环境非常友好。
如果后续继续深化,我可以为技能库建设一套“技能质量评估体系”。比如用自动化测试集跑全量技能,统计每个技能的成功率、平均耗时、参数校验失败率,并按周输出质量报表。这样技能库就不再是一堆静态文件,而是一个可持续运营的能力资产。
我个人在实际操作中的体会是,技术方案本身没那么高深,真正难的是改变团队对Agent能力的认知。以前大家在Prompt里堆词,寄希望于模型“更聪明一点”;agent-skills把问题转化为工程问题,让每个能力都有边界、有校验、有日志、有测试。这个转变带来的稳定性提升立竿见影。如果你现在也正被Agent的乱调用、参数幻觉和排查困难困扰,我建议你不要急着加更多Prompt,先把能力拆成一个个干净、可测试、可观测的技能。最后再分享一个小技巧:每开发完一个技能,强制自己写一个“这个技能绝不做什么”的清单放进SKILL.md,你会发现模型后期选错技能的概率低到超乎预期。