ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

2.skill实战:用AI技能包构建自动周报生成器

2.skill实战:用AI技能包构建自动周报生成器 如果你也跟我一样经常被同一类杂事反复磨时间那你一定会理解为什么我电脑里躺着一排以 .skill 结尾的文件夹。今天文章的主角是其中的 2.skill它不是我第二个孩子也不是什么玄学编号而是我给自己正式入库的“第二个技能包”。这个技能包就干一件事把散落在工作日志、项目文档和聊天记录里的碎片信息自动整理成一份像样的周报。这篇博客不聊大架构只借 2.skill 这个具体例子把技能包的定位、目录规范、脚本写法、调试排坑和管理迭代一次性捋清楚适合已经在用 AI 助手处理日常任务、但总觉得它“差点意思”的开发者也适合想把重复劳动固定成标准流程的自动化爱好者。这套做法当然不是谁发明的标准答案而是我折腾了半年多以后反复试下来比较顺的一种通用套路。下面说的每一步你基本都可以照着落地。1. 先想清楚为什么要把技能“文件化”1.1 技能包到底是个什么东西很多人用过 AI 助手以后会有一个感觉聊天的时候它挺聪明但一遇到具体活儿就开始“自由发挥”每次给的格式都不一样有时候还会漏步骤。问题往往不在模型能力而在于你每次都临时把要求说一遍AI 只能在对话里现猜你的标准。技能包就是用来解决这个事的。一个技能包本质上是一个带固定结构的文件夹里面通常包含三样东西给 AI 看的说明文档、真正执行任务的脚本、以及可复用的模板资源。说明文档负责讲清楚“什么场景下用、按什么步骤做、哪些事不能做”脚本负责把“动手”这个环节补上模板资源负责保证输出格式一致。打个比方不带技能包的 AI 像一个听你口头描述就下厨的新手你说的快一点它就手忙脚乱带技能包的 AI 则像一个同时拿到菜谱、切好的食材和专用锅具的老师傅。它不关心你为什么做这道菜它只需要按既定流程把菜做出来。1.2 为什么叫“2.skill”先说名字。我的技能库里目录是按编号排的1.skill 是通用文本整理2.skill 就是周报生成。有人可能会问为什么不直接叫 weekly-report.skill非要搞得像个乱码我用编号的理由有三个。第一编号是稳定的 ID我在日常指令里只需要说“用二号技能帮我处理”不需要记一长串名字第二排序天然新技能永远往后排不会因为改名导致引用关系乱掉第三目录名不变意味着外部引用不会因为技能升级而失效我可以在里面随便改文档和脚本不用动调用方。不过这里有一个明显的代价别人看到 2.skill 根本不知道它是干什么的。所以我的习惯是个人本地库用编号一旦要把技能包分享给团队或者发布出去就改成语义化名称。2.skill 这个代号只在我自己的技能环境里有效。这也算是个人技能库和公共技能库的一个关键差别。2. 2.skill 的核心结构一个技能包到底该长成什么样子2.1 SKILL.md 是给 AI 看的“操作说明书”技能包里最核心的文件叫 SKILL.md。它不像普通 README 那样写给人看而是写给 AI 的语义引擎看。AI 决定什么时候调用这个技能、调用以后按什么逻辑执行主要靠的就是这份文件。这份文件通常以一段结构化元信息开头里面至少包含名称、描述、参数定义和示例。名称要唯一描述要写清楚“什么时候用、什么时候绝对不要用”参数要逐个说明格式和默认值示例最好给一两个贴近真实场景的句子。我以前写过一版特别简陋的描述只有一句“生成周报”结果 AI 经常在用户说“帮我写个总结”的时候也把这个技能拽出来输出一堆四不像。后来我把 description 改成下面这种写法--- name: weekly-report-generator description: 生成结构化周报。当用户说“写周报”“整理周报”“周报帮我写一下”或“这周汇报”时使用。适合处理包含多条工作事项的碎片信息。不要用于简历、项目计划或日报生成。 parameters: week_start: type: string description: 本周起始日期格式 YYYY-MM-DD default: 本周一 week_end: type: string description: 本周结束日期格式 YYYY-MM-DD default: 本周五 tone: type: string description: 语气风格正式或者简洁 default: 正式 ---描述里加上了常见说法和负面条件这个技能被误调用的概率立刻降了下来。SKILL.md 的正文部分则要像写菜谱一样把步骤拆到不能再拆。不要只写“汇总本周工作”而要写清楚先读哪个文件按什么规则筛选输出用什么模板遇到信息不够时该怎么处理。AI 在执行时偏好清晰指令你给模糊前提它一定还你模糊结果。2.2 脚本和资源真正干活的部件说明文档只是地图真正动手的是脚本。2.skill 里的脚本我推荐保持三个原则单文件、无状态、输入输出走标准输入输出。单文件好理解一个技能只对应一个主脚本不要搞出十几文件互相 import否则 AI 环境里很难优雅地调用。无状态指的是脚本不依赖上一次运行留下的临时状态每次运行都是全新的一次这样即使重复执行也不会出错。输入输出走标准输入输出意思是参数通过标准输入或命令行参数传进去结果打印到标准输出而不是弹个窗口或者写死某个绝对路径文件。AI 框架在调用技能时最容易接住的接口就是这种“终端式”接口。至于模板、配置、静态资源统一放在 resources 目录下。比如周报模板、要匹配的关键词列表、示例条目都可以放这里。脚本通过相对路径去读取 resources 里的文件而不是写死 /home/xxx 这种绝对路径这样整个技能包换个机器还能跑。2.3 一张完整的目录示例我直接把 2.skill 的目录结构贴出来。这个结构我后来几乎复刻到所有技能包里已经算是我的固定范式2.skill/ ├── SKILL.md ├── scripts/ │ └── generate_report.py ├── resources/ │ ├── templates/ │ │ └── weekly_report_template.md │ └── keywords/ │ └── important_topics.txt └── README.mdREADME.md 是写给人类看的说明这个技能是干嘛的、需要什么运行环境、脚本怎么手动测试。它不影响 AI 调用但对调试和团队协作非常重要。很多时候我隔了两个星期再回去看某个技能全靠 README 快速回忆上下文不然光看一堆代码根本想不起来当初的设计意图。3. 实操让 2.skill 真正跑起来3.1 先把需求拆成可执行的步骤以周报为例拆解以后其实就四步收集素材、按重要性排序、过滤掉无关内容、套模板输出。素材来源五花八门可能是某个工作日志文件、几条聊天记录、或者用户在对话里随口补充的信息。脚本不能要求所有素材都整齐划一反而要设计成“有结构化数据用结构化数据没结构化数据就给 AI 留出填空空间”。我在做 2.skill 之前统计过自己每周整理周报平均要花 25 到 40 分钟大部分时间浪费在回忆这周干了什么、怎么归纳分类上。所以我的核心诉求不是把周报写成花而是快速、稳定、不漏项。明确了这一点后面所有设计都围绕“稳”字展开。3.2 五个落地步骤第一步创建目录结构。按上面的树形结构建好目录这一步没什么花头但目录命名要一致后面脚本、说明文档、资源文件的位置全依赖这套结构。第二步写 SKILL.md。先把元信息填好再写正文。我会在正文里给 AI 定一条铁律必须按模板输出格式不允许自由发挥如果素材数量少于三条用户确认过再生成。这条铁律一开始没有后来被 AI 的“过度灵活性”教育了两次才补上。第三步写生成脚本。我用 Python 写了一个 generate_report.py核心逻辑是接收本周起止日期读取资源目录下的模板文件把传入的工作事项逐条格式化后填进去。为了让 AI 省心脚本还支持两种输入模式如果传入了 JSON 数据就用数据直接渲染如果没有 JSON脚本会输出一个待填写的 JSON 空壳提示 AI 自己补全内容。脚本片段大概是这样的import sys import json from pathlib import Path from datetime import date, timedelta BASE_DIR Path(__file__).resolve().parent.parent TEMPLATE_PATH BASE_DIR / resources / templates / weekly_report_template.md def default_dates(): today date.today() monday today - timedelta(daystoday.weekday()) friday monday timedelta(days4) return monday.isoformat(), friday.isoformat() def parse_input(): raw sys.stdin.read().strip() if not raw: return {} return json.loads(raw) def render(items, week_start, week_end, tone): template TEMPLATE_PATH.read_text(encodingutf-8) lines [] for idx, item in enumerate(items, 1): lines.append(f{idx}. 【{item.get(type, 任务)}】{item.get(content, )}) body \n.join(lines) return template.format( week_startweek_start, week_endweek_end, bodybody, tonetone, ) if __name__ __main__: params parse_input() ws params.get(week_start) or default_dates()[0] we params.get(week_end) or default_dates()[1] result render(params.get(items, []), ws, we, params.get(tone, 正式)) print(result)这个脚本最值得说的一点是特意不写死日期。如果 AI 没拿到日期参数脚本自己按本周一到本周五算保证流程不中断。我见过太多技能包因为一个参数缺失就整个崩溃设置默认值是最简单也是最有效的抗错手段。第四步手动测试脚本。在终端跑一遍python scripts/generate_report.py看默认输出是否正常再构造一份带 JSON 的输入测试带参数模式。这段测试时间一定不能省因为脚本一旦接入 AI 环境问题会被放大调试成本十倍不止。第五步接入 AI 环境联调。这一步最简单的验证方法是直接对 AI 说“用 2.skill 生成一下本周周报”然后观察它是否调用了技能、参数是否传对、输出是否符合模板。如果 AI 没调用技能八成是 description 写得不够清楚如果调用了但结果不对就要看传参和脚本逻辑哪里脱节了。3.3 参数设计的讲究参数设计看起来小事其实是决定技能包好不好用的关键。我的经验是参数能少就少每个参数都必须有默认值参数说明里要写清楚格式最好再给一个例子。2.skill 里我只留了三个参数week_start、week_end、tone。本来还想加一个“包含项目名称”的参数后来发现 AI 完全可以从素材里自动提取多一个参数反而多一个传错的可能。下面是参数表的最终版本参数是否必填类型默认值说明week_start否string本周一起始日期格式 YYYY-MM-DDweek_end否string本周五结束日期格式 YYYY-MM-DDtone否string正式正式或简洁实际调用的时候AI 大多数情况下只需要把用户说的话映射到这三个参数上。用户说“写这周的周报”AI 就知道两个日期都用默认值。用户说“简单点写”tone 就传简洁。好的参数设计就是让 AI 不需要追问就能干活。4. 调试与排坑实战中的问题清单4.1 技能老是“不被调用”这是所有技能包最容易踩的坑。你辛辛苦苦把文件夹建好脚本写得也没问题结果跟 AI 说了一大段需求它根本没用你的技能而是自己在对话里生成了一段普通文本。一开始我以为是框架的问题后来发现问题基本出在 description 上。AI 选择技能靠的是语义匹配description 里没有出现用户会说的那些词它就很难联想到你的技能。解决办法有三个把常见触发说法直接写进 description把负面场景也写进去防止被误调用在 description 开头第一句就点明核心用途因为有些语义匹配会更关注描述的前半部分。我后来把“写周报”“整理周报”“这周汇报”等说法全部写进去技能被调用的频率立刻上来了。4.2 脚本在 AI 环境里跑不通脚本本地能跑接进 AI 环境就各种报错这是另一个高频问题。最常见的原因是路径问题。本地测试时工作目录刚好在 2.skill 目录下脚本用相对路径能找到资源文件但 AI 执行时当前目录可能完全不是技能目录。解决方案就是我在脚本里写的那样通过Path(__file__).resolve().parent.parent定位到技能根目录再拼资源路径这样无论从哪里启动路径都不会跑偏。第二个常见原因是依赖缺失。如果你的脚本引用了第三方库但 AI 环境里没装整个技能就废了。我的建议是能用标准库就用标准库实在要用第三方库要么在 SKILL.md 里写清楚安装命令要么把依赖做成独立脚本。第三个坑是脚本里有交互式输入比如input()等待用户输入AI 环境里没人会在终端等你脚本直接挂住。所有输入都走参数或者标准输入不要在运行中途等待。4.3 输出结果不稳定技能包跑通了但每次输出格式都不一样这也很让人头疼。本质原因是你在 SKILL.md 里没有把规则说死。AI 的自由发挥本质上是规则的缝隙太大它不知道哪些地方绝对不能改。我的应对办法是两层校验。第一层在 SKILL.md 里写明“必须使用 resources/templates 下的模板禁止修改模板中的章节结构”第二层在脚本输出后做一次简单检查比如检测关键章节名是否都在如果缺失就报错让 AI 知道自己漏了步骤。这套逻辑说白了就是把标准和校验写进流程里而不是指望 AI 自觉。4.4 常见问题速查表现象可能原因解决办法AI 完全不调用技能description 缺乏触发词把常见说法、触发场景写进描述开头调用了技能但乱传参参数格式说明不清晰给参数补充类型、默认值和示例脚本报错找不到文件使用绝对路径或依赖当前目录用脚本自身路径定位资源目录脚本运行卡住存在交互式输入改为参数输入或标准输入输出格式每次都不一样SKILL.md 规则不够硬固定模板加入输出校验依赖库缺失使用了非标准库改用标准库或在文档里写明安装方式这张表基本是我所有技能包调试经验的浓缩遇到问题先对照看看大部分情况不用看堆栈就能定位原因。5. 技能库的迭代与管理从 2.skill 到 20.skill5.1 用 Git 管理技能库技能包不是写完就完了它需要持续迭代。我用一个独立仓库管理所有技能包每个技能一个目录提交信息写成固定格式比如feat(skill): 优化周报模板或fix(skill): 修复日期默认值。这样翻提交记录的时候一眼就能看出每个技能包改过什么。版本号我会放在 SKILL.md 的元信息里比如version: 0.3.0。更新脚本或模板时顺手升一下版本久了以后能很清楚地看到技能包的生命周期。Git 还有一个额外的价值你可以随时 diff 某个技能包的改动观察 SKILL.md 里哪一段描述变化最频繁。我后来发现凡是我反复修改的描述部分基本都是流程中最容易出问题、或者需求变化最快的地方这是一个非常有价值的信号。5.2 让别人也能用你的技能包好的技能包不是只有自己能跑而是换一台机器、换一个人、换一个环境也能跑。我把这个标准叫“技能包的可移植性”。要满足它有三条硬规矩第一不允许出现绝对路径资源定位全部基于技能包自身目录第二不允许依赖外部未声明的服务比如某个内网接口、某个本地数据库第三README 必须写清楚运行环境、依赖项和手动测试方式。共享技能包时一定要把内部编号改成语义化名称。2.skill 在你自己世界里没问题给别人时至少要叫 weekly-report-generator否则对方完全无从下手。发布之前再跑一次干净环境测试从克隆仓库到执行脚本全流程走通不要拿“我这边可以跑”当标准。5.3 写技能包这件事本身就是一次流程复盘我个人的体会是2.skill 最大的价值并不在于省下每周那半小时而在于它逼着我把“写周报”这件模糊的事变成了一条条可被检查和复用的规则。以前我以为自己很清楚怎么写周报真动笔写说明文档时才发现我连“哪些内容该进周报、哪些不该进”都没认真想过。这个发现其实有点震撼。技能的构建过程本质上就是把自己的工作方式重新梳理了一遍把隐性经验变成显性流程。我之后做其他技能包时也明显感觉到那些“我早就知道”的事一旦要写给别人看立刻会暴露出一堆想当然的地方。最后分享一个小技巧每次更新技能包后用 git diff 看一下自己改了哪些内容。改得最频繁的那一段描述就是你工作流程里最脆弱、最需要优化的那一环。顺着这个信号继续迭代你的技能库会滚动进化。2.skill 之后我的技能库已经排到了十几号每个包都还在稳定产生价值而这一切的起点就是当初那个不起眼的文件夹。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进