ARTICLE DETAIL

资讯详情

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

大模型Agent Skills实战:从概念到手写技能模块

大模型Agent Skills实战:从概念到手写技能模块 做AI应用这一年多skills这个词在我耳朵里出现的频率越来越高。从最初各家模型厂商把Agent技能封装成模块到团队内部把高频能力沉淀成可复用资产——Skills已经从一个概念名词变成了大模型工程里绕不开的基础设施。这篇文章想聊聊我对Skills的理解它到底解决了什么问题一份合格的Skill应该怎么设计以及我如何从零手写并接入一个可用的技能模块。说实话两年前我提到skills脑子里还是个人能力提升那套东西。但现在作为整天跟大模型打交道的工程师这个词代表的是Agent Skills——把AI的某种能力打包成自带说明书的独立模块让模型按需调用、即插即用。它的价值不亚于当年函数式编程对代码组织的改造。这篇文章适合正在做大模型应用、被提示词工程搞得焦头烂额、或者准备把Agent落地到具体业务场景的开发者。新手可以把它当作一份Skills入门手册有经验的人也许能从实操细节里捞到几个避坑技巧。1. 为什么技能成了大模型应用的关键词1.1 从Prompt到Skills的进化逻辑最早期做大模型应用大家都靠Prompt硬怼。让模型做一件稍微固定的事就得在系统提示词里写上一大段约束你要扮演什么角色、遵循什么步骤、输出什么格式、注意什么细节。第一次写还行第二次写就发现自己开始复制粘贴等类似需求攒了三五个维护成本直接失控。我有一个做数据报表的同事早期系统提示词写了接近三千字。每加一个新的报表需求就继续往里面追加描述。结果就是模型输出越来越不稳定今天记得用Markdown表格明天就给你整成JSON后天干脆把历史报表格式也搞混了。后来我们把每个报表类型拆成独立的Skill文件系统提示词只保留一行索引模型需要哪个技能就加载哪个。输出瞬间稳定下来维护也清爽了。Skills出现的本质是提示工程的一次模块化改造。它把固定的指令、示例、约束、配套脚本打包成独立单元像函数一样被Agent动态加载。这跟软件工程里高内聚、低耦合的追求一模一样。1.2 把AI当实习生一切就说得通了很多朋友问我怎么跟产品经理解释Skills我常用的一个类比把AI当成新来的实习生。你口述工作要求就是Prompt——说一遍是一遍记不住细节换个措辞效果就跑偏。要是你给实习生一本《岗位操作手册》里面写明工作流程、输出模板、注意事项还配了一个装好脚本的工具箱他每次按手册干活偶尔翻翻工具说明结果自然又快又稳。这份手册就是Skills。这个类比能解释很多设计细节。比如为什么Skill要有独立的描述信息因为实习生要先判断这个任务归不归我管再决定要不要翻开手册。为什么Skill里要写示例因为手册里光有流程不行得有样例让实习生照着模仿。为什么脚本要跟说明放在一起因为你不能指望实习生自己去找工具手册和工具必须放在同一个盒子里。一旦把Agent当成需要被管理的实习生你会发现团队里沉淀的很多管理经验可以直接迁移到Skills设计上。1.3 Skills和Tools、Prompt到底差在哪我整理过一张对比表方便大家快速理解。维度PromptToolsSkills本质一次性指令可执行的函数完整的能力包复用性低每次重写中需自行组合高独立加载触发方式始终在上下文中模型决定调用模型按描述触发内容组成纯文本代码接口说明书脚本示例知识维护成本越高越失控接口变更需同步独立版本迭代简单说Prompt是一次性上下文用完成本已付Tools是让模型能做什么动作的接口比如调用搜索、执行代码Skills则是知道什么时候该做什么事的完整方案它内部可能包含多段Prompts也可能调用多个Tools。Skills比Tools更重、更完整比Prompt更持久、更模块化。2. 搞懂SKILL.md技能的身份证与操作手册2.1 一份SKILL.md最少要有这三块任何一个能用的Skill目录下都必须有一个SKILL.md文件。这个文件是整个技能的入口结构上至少包含三块内容属性声明、使用说明、示例。我用Markdown的frontmatter格式写属性声明用正文写使用说明和示例。--- name: weekly_report description: 当用户提到周报本周总结工作汇报weekly report或者需要把零散的工作日志整理成结构化周报时使用。 ---name字段是机器可读的短标识尽量用英文小写加下划线别用中文或带空格的名字。description字段是给模型看的用来判断当前任务是否该触发这个技能。这两个字段虽然只占三行但直接决定了Skill会不会被正确唤起。正文部分写详细的执行步骤、输出格式、配套脚本的调用方式还可以放一两个示例片段。整份SKILL.md的定位是别人没看过你的代码只读这份文件就能正确使用这个技能。2.2 描述信息是给模型看的广告文案很多人写description容易踩一个坑把技术实现细节写进去比如本技能使用Python脚本统计工作日并判断是否为周五。模型读到这种描述根本不知道什么时候该用。正确的写法是写触发场景不写技术实现当用户需要整理周报、汇总日常工作产出时使用。好的description应该像广告文案——让模型一眼就懂什么场景下掏出来用。我自己的经验是要包含两部分一是明确的场景关键词二是场景所归属的意图。比如当用户提到周报、本周总结、工作汇报等词汇或者表达出汇总近期工作内容的意图时使用。我自己还会做一次盲测把description单独拿出来给另一个模型看让ta判断用户说帮我写一下这周的汇报该不该触发。如果它能判断出来说明描述合格如果犹犹豫豫那就继续改。2.3 目录与资源一个完整技能长什么样光有SKILL.md还不够真正的Skill是一个有组织结构的目录。我的标准布局是这样skills/ └── weekly_report/ ├── SKILL.md ├── scripts/ │ └── summarize_log.py ├── assets/ │ └── template.md └── references/ └── sample_output.mdscripts目录放配套的可执行脚本assets目录放模板或静态资源references目录放参考资料或样例输出。这样的好处是每个Skill都可以独立版本管理甚至独立分享给其他人。把scripts和SKILL.md放在一起还有一个实际考量模型在生成调用指令时可以直接引用相对路径不用把脚本内容全部塞进上下文。我见过有些团队把所有脚本集中放在一个tools目录里然后每个Skill只写一段说明文字。这种做法不是不行但一旦脚本更新你得同步检查所有引用它的Skill很容易漏。Skill自带资源目录本质上是在团队层面实现了能力封装。3. 实操全程手写一个周报生成Skill3.1 先定需求再选方案我挑了一个团队里真实跑过的例子——周报生成Skill。需求很朴素每个人平时在本地维护一个work_log.txt随手记录每天干了什么。周五下午要交周报手动从日志里挑重点、排优先级、格式化输出一小时就没了。这个场景特别适合Skills原因是需求稳定、有固定输出格式、还需要调用脚本来做数据整理。纯Prompt方式做不到读取本地文件Tools方式又缺一个说明书来约束输出结构。只有Skills能把说明、脚本、模板装进一个盒子里。3.2 搭建目录与清单文件先在项目里建好目录结构用简单的shell命令就能搞定mkdir -p skills/weekly_report/scripts mkdir -p skills/weekly_report/assets mkdir -p skills/weekly_report/references然后写SKILL.md完整内容。我实际用的是下面这个版本你可以直接参考--- name: weekly_report description: 当用户提到周报本周总结工作汇报weekly report或者表达出希望将零散的工作日志整理成结构化周报的意图时使用。该技能从指定日志文件读取本周工作记录按模板生成Markdown格式周报。 --- # 周报生成技能 ## 输入参数 - log_file: 工作日志文件路径默认 ./work_log.txt - end_date: 周报截止日期默认当前日期 ## 执行步骤 1. 调用 python3 scripts/summarize_log.py --log-file {log_file} --end-date {end_date} --template assets/template.md 2. 将脚本输出的Markdown内容直接返回给用户 3. 如果脚本执行报错检查日志文件是否存在、日期格式是否正确 ## 输出格式 周报必须包含以下四个部分 - 本周重点汇总本周完成的主要事项按影响面从大到小排序 - 数据与指标列出本周的关键数据或量化成果 - 问题与风险记录本周遇到并解决的问题、尚存的风险 - 下周计划列出下一个工作周期的计划事项 ## 示例 用户说写一下本周周报 输入日志如下 2025-01-06 完成登录模块重构 修复支付超时bug 优化首页加载速度首屏耗时从2.1s降到1.2s 输出应包含四段内容并量化结果。写SKILL.md时有个原则步骤要细到别人照着做不会产生歧义但又不能长到上下文装不下。我自己写的版本一般控制在80到120行之间既说清楚怎么用又不至于喧宾夺主。3.3 写辅助脚本让技能真正会算SKILL.md只负责说明真正干活的是scripts目录下的Python脚本。周报场景里脚本做三件事读日志、按日期分组、套模板输出。下面这个summarize_log.py就是可用版本#!/usr/bin/env python3 import argparse import re from datetime import datetime from pathlib import Path def parse_work_log(log_path): 解析工作日志返回 [(date, content), ...] 列表 entries [] current_date None with open(log_path, r, encodingutf-8) as f: for line in f: line line.strip() if not line: continue date_match re.match(r^(\d{4}-\d{2}-\d{2})$, line) if date_match: current_date date_match.group(1) continue entries.append((current_date, line)) return entries def filter_by_week(entries, end_date_str): 筛选截止日期前一周内的记录 end datetime.strptime(end_date_str, %Y-%m-%d) start end.replace(hour0, minute0, second0, microsecond0) week_start start.fromtimestamp(start.timestamp() - 6 * 86400) result [] for date_str, content in entries: if date_str is None: continue d datetime.strptime(date_str, %Y-%m-%d) if week_start d end: result.append((date_str, content)) return result def render_report(entries, template_path): 渲染Markdown周报 lines [] for date_str, content in entries: lines.append(f- {date_str}{content}) body \n.join(lines) template Path(template_path).read_text(encodingutf-8) return template.format(contentbody) def main(): parser argparse.ArgumentParser(description生成周报) parser.add_argument(--log-file, default./work_log.txt) parser.add_argument(--end-date, defaultdatetime.now().strftime(%Y-%m-%d)) parser.add_argument(--template, defaultassets/template.md) args parser.parse_args() entries parse_work_log(args.log_file) week_entries filter_by_week(entries, args.end_date) if not week_entries: print(最近一周没有工作记录请确认日志文件是否更新。) return print(render_report(week_entries, args.template)) if __name__ __main__: main()这个脚本功能不复杂但有两个细节值得说。第一日志格式约定为日期行加内容行脚本通过正则识别日期行后续内容挂到当前日期下面。这种格式人类写起来不费劲机器解析也稳定。第二模板文件单独放assets目录改周报样式不用动脚本改模板就行。配套的模板文件assets/template.md长这样## 本周重点 {content} ## 数据与指标 待补充 ## 问题与风险 待补充 ## 下周计划 待补充3.4 空跑一次验证技能到底好不好用技能写完之后不能直接丢给Agent用得先在命令行空跑一遍确认脚本本身没问题。cd skills/weekly_report python3 scripts/summarize_log.py --log-file ./work_log.txt --end-date 2025-01-10 --template assets/template.md我第一次跑这个脚本就翻车了报错是UnicodeDecodeError。排查后发现Windows环境下log.txt默认是GBK编码而脚本强制用了utf-8。后来我改成用encodingutf-8加上对编码错误的兜底处理问题才解决。这种简单问题在命令行里能一眼发现如果直接接进Agent会被当成神秘故障排查成本高得多。跑通脚本后再把Skill交给Agent做一次端到端测试。给Agent一句话帮我写一下这周的周报然后检查它是否成功加载SKILL.md、调用脚本、返回四段式周报。如果输出缺了数据与指标这一节说明模板化还不够强硬我会调整SKILL.md明确要求必须包含四部分即使无数据也要写无。4. 把Skills接进Agent工作流的三种落地方式4.1 全量注入把Skills塞进系统提示词最朴素的接入方式是把所有Skill的SKILL.md内容直接拼进系统提示词。优点是实现简单到几乎不需要额外代码模型在每一轮对话里都能看到全部技能说明触发准确率理论上最高。缺点同样明显Token消耗巨大。假设你有20个Skill每个SKILL.md平均5000字符光技能说明就要吃掉两三万Token。而且大量不相关的技能说明混在上下文里会让模型注意力被稀释反而影响关键信息的提取。我自己试过在项目早期用这种方式技能数量一多响应速度肉眼可见地变慢。所以全量注入只适合两种情况一是技能数量极少三五个以内二是上下文窗口充裕且对延迟不敏感的场景。否则不建议作为长期方案。4.2 按需加载先注册后取用更主流的方式是按需加载。系统提示词里只放一份技能索引包含所有技能的name和description但不放完整正文。模型根据用户输入判断需要哪个技能再通过专门的工具调用接口去加载对应SKILL.md。核心逻辑用伪代码描述是这样skills_index [ {name: weekly_report, description: 生成周报……}, {name: code_review, description: 代码审查……} ] SYSTEM_PROMPT 你有以下技能可用 json.dumps(skills_index) def load_skill(skill_name): skill_path fskills/{skill_name}/SKILL.md with open(skill_path, r, encodingutf-8) as f: return f.read()模型在对话过程中先读索引判断当前任务匹配哪个描述然后调用加载函数拿到完整SKILL.md。这有点像操作系统的页表机制先映射虚拟地址真正访问时才把物理页加载进内存。这种方式最大化节省了Token也避免了无关信息干扰。代价是你需要自己维护一套索引加载逻辑并且要保证description写得足够准。实际做下来这套逻辑大概三五十行代码就能搞定成本和收益比非常划算。4.3 混合方式与工程决策我目前更推荐混合方式把高频使用的两三个核心Skill全量注入保证它们始终在线、立即响应把长尾技能放在索引里按需加载。相当于给高频路径做了一次缓存兼顾速度和Token成本。做个简单的成本测算就很清晰假设每个Skill描述平均200字全量注入20个是4000字按需加载时索引只有400字省掉90%的开销。但如果你只有三个Skill全量注入的400到600字根本无伤大雅。决策的核心变量是你有多少技能、触发频率是怎样的。5. 真实踩坑记录这些问题我一周能遇到三次5.1 Skill压根没被触发最让人头疼的问题是用户输入明明和技能相关模型却完全没调用。排查下来十有八九是description写得太抽象或者太技术化。我见过有人写用于处理时间序列数据的统计分析需求真正用户说的是帮我看看这周流量趋势模型确实匹配不上。解决思路是站在用户语言角度重写description把可能出现的自然表述都列进去用口语词、场景词、近义词三管齐下。写完之后再找同事做盲测让ta故意用口语提问看模型是否能识别。5.2 触发倒是触发了输出完全跑偏比不触发更气人的是触发了但输出格式完全不是SKILL.md里规定的。我排查过几次发现原因几乎一致——SKILL.md里的示例不足、或者示例场景跟用户当前需求不匹配。比如我的周报Skill里只给了平时记录零散事项的示例用户突然丢来一份现成的Excel表让写周报模型就懵了。解决办法是给每个Skill配2到3个不同输入形态的few-shot示例覆盖高频变体。如果Agent框架支持还可以在SKILL.md里明确写出遇到输入格式与示例不一致时的处理步骤给一个兜底路径。5.3 脚本环境依赖本地好好的一上线就崩Skills里带脚本就一定会遇到环境依赖问题。最常见的有三类Python解释器版本不一致、第三方库没装、相对路径解析出错。我踩过最离谱的一次是脚本在本地能跑部署到容器里提示module not found原因是生产环境Python 3.11而本地是3.12某个库的行为不一致。现在的处理方法是每个Skill的SKILL.md里写清楚运行环境要求包括Python版本、需要pip安装的依赖列表、脚本的启动路径。部署流程中加一步环境自检脚本启动时自动验证依赖提前报错而不是等Agent运行时炸。5.4 上下文被多个Skill吃光了团队里技能一多很容易出现上下文被撑爆。尤其是全量注入派的人加技能一时爽等到模型开始频繁遗忘对话内容才反应过来。我见过最夸张的案例系统提示词里挂了30多个Skill上下文窗口还没怎么聊就用了三分之一。后来把方案改成按需加载又给每个Skill的description做了字数上限80字以内上下文压力才真正缓解。另外我养成了一个习惯每次加新Skill之前先跑一次Token统计这个用代码或平台的tokenizer工具十分钟就能出结果。一个好用的评估指标是所有全量注入内容的Token总量控制在上下文窗口的15%以内超过就考虑换方案。最后再分享一个小技巧Skills的维护不要一个人闷头做。我把SKILL.md当成代码一样走评审团队成员定期往公共技能库里提交新技能、修改描述。出过几次问题之后大家发现标准模板和描述规范比想象中重要得多。你现在就可以挑一个高频重复的日常任务把它变成你的第一个Skill跑通一次之后你会回来感谢自己的。
返回列表