过去两个月,我一直在做同一件有点反直觉的事情:明明日常工作是写代码,我却花了大把时间研究"怎么让 AI agent 学会排版"。起因很普通,有一阵子我需要频繁把技术方案整理成带规范格式的 LaTeX 文档,试过让 Claude Code、Codex 这类 agent 直接生成,结果每次都差不多——第一版看起来有模有样,细看全是问题:包引用缺一个、英文与中文之间不加空格、编译警告一屏都放不下。最气人的是,我在对话里反复强调的规则,换一个新会话它又全忘了。
后来我把这些散落在对话里的修正经验,整理成了一个个独立的 skill,放进一个叫 agent-skills 的项目里。所谓 skill,就是一段结构化的"作业指导书":它包含了任务流程、规范细节、配套脚本和模板,agent 在需要时按需加载,而不是每次对话都背着几万字规则。这篇文章就围绕 agent-skills 这个项目,说说 skill 到底是什么、和 agent、prompt、harness 的边界怎么划、一个合格的 skill 目录应该长什么样、怎么从零开发一个 LaTeX 排版 skill,以及安装接入和测评那些容易翻车的细节。无论你是在研究 agent 开发,还是想让自己的 coding agent 更听话,这篇都值得看完。
1. 为什么我会专门做一个 agent-skills 项目
1.1 从一次让 agent 排版翻车说起
先说那次让我下决心做 skill 项目的翻车经历。当时我给了 agent 一个很具体的任务:把一份 Markdown 格式的周报转成 LaTeX,要求用 ctex 支持中文、代码块用 listings 宏包、正文五号字、页边距 2.5cm。agent 答得很快,几分钟就给了完整代码。我拿去编译,报错信息直接打脸:缺listings的颜色依赖,中文注释出现乱码,标题层级和目录对不上。我一条条回给它,它修完这一处又弄坏那一处,来回折腾了快四十分钟。
真正让我崩溃的是第二天。换了一个新会话,我把同样的要求发给 agent,它又犯了同样的错误,甚至把前一天我纠正过的约定全忘了。那一刻我突然意识到,问题不在 agent 笨,而在我没有给它一个稳定的知识载体。对话里的修正是一次性的,模型本身又不会在两次会话之间记住我的偏好——我需要一种机制,把"怎么排版才算对"沉淀下来,让 agent 随时能查、能照着做。
1.2 把一次性修正变成可复用能力包
所以我开始研究市面上各家 agent 的 skills 机制。发现各大工具虽然叫法不同,思路其实高度一致:给 agent 准备一个目录,目录里放SKILL.md作为入口文件,里面写清楚这个技能什么时候用、完整流程是什么、有哪些硬性规则,再配上脚本和模板做确定性补充。agent 在启动时会扫描这些目录,但不会把所有内容塞进上下文,只有判断任务匹配时才加载对应的那个 skill。
这个设计解决了我之前最头疼的问题——上下文污染。skill 是"按需翻开的手册",不是"每天都在耳边念的规矩"。它和 prompt 最大的区别就在这里:一份系统提示词无论多长都会全程占着上下文,而 skill 平时只占一行描述的空间,真正用到时才把几百行细节加载进来。对长会话来说,这种差异几乎是决定性的。
1.3 agent-skills 项目的定位
最终我建了一个叫 agent-skills 的项目,本质是一个 skill 合集,目前收录了 LaTeX 排版、结构图生成、图片生成、旧项目现代化改造等十几个 skill。这个项目在组织上有三个原则:
- 可复用:每个 skill 都是一份目录,能复制到任何机器、任何项目,不绑定某个特定会话。
- 可测评:每个 skill 都带一组测试用例和评分清单,改了不会偷偷变差。
- 可迭代:技能描述、指令正文、脚本三者分开维护,改其中一块不影响另外两块。
如果你问我 agent 开发学习路线里最该先搞懂什么,我的答案不是 RAG,不是多智能体协作,而是 skill。因为它是当前让 agent 真正"上手干熟练活"的最小单元,几乎所有编码 agent 都在往这个方向收敛。
2. 先捋清概念:skill、agent、harness 和 prompt 各管哪一段
2.1 一张表格看清四者的边界
跟同行聊 skill 的时候,发现很多人卡在概念上,尤其分不清 harness 和 agent 的区别。我用一个比较生活化的类比来理解:把整个 agent 系统想象成一个餐厅。
| 概念 | 类比 | 职责 | 例子 |
|---|---|---|---|
| harness | 餐厅的场地和厨房设备 | 运行环境、工具调用框架、上下文管理 | Claude Code、Codex CLI、opencode |
| agent | 主厨 | 理解任务、规划步骤、决定调用哪个工具 | 模型 + 循环决策逻辑 |
| prompt | 门口的招牌和菜单 | 一开始就告知的通用规则 | 系统提示词、CLAUDE.md |
| skill | 后厨的作业指导书 | 按需加载的专项流程与规范 | 排版流程、代码审查清单 |
harness 管的是"怎么做",agent 管的是"做什么",prompt 管的是"默认怎么想",skill 管的是"具体活怎么干"。四者配合,缺一不可。很多人把 harness 当 agent,是因为现在 Agent 这个词被用滥了——你买的其实是一套 harness 加上一个大模型,agent 能力是它们组合出来的结果。
2.2 为什么 skill 不能和 prompt 互相替代
有一个很常见的疑问:既然 prompt 也能写规则,为什么还要 skill?答案是代价不同。把规则写进 prompt,意味着每条规则都要在每一轮对话中参与计算,无论用不用得着。规则一多,模型注意力会被稀释,反而更容易忽略真正重要的约束。我之前试过把排版规范写进项目级 CLAUDE.md,结果 agent 在无关任务里也会莫名其妙套用排版术语,输出风格变得很奇怪。
skill 走的完全是另一条路。它的描述信息非常短,大致是"这个技能负责什么、什么时候启用",模型看到后先判断要不要用;一旦判定匹配,才把完整的 SKILL.md 正文加载进来。这种渐进式披露的思路和人类读书很像:先看目录和摘要,确定要看哪一章,再翻到那一章细读。这也是为什么像 superpower skills 这样的技能包会流行,它本质上是一堆组织良好的 skill,让 agent 在各种专项场景下都能快速对齐一套高质量工作流。
2.3 什么时候该写 skill,什么时候不该写
不是所有东西都值得做成 skill。我自己的判断标准是:如果这个任务满足下面至少两条,才值得写。
- 任务有固定流程,步骤基本不会变。
- 输出格式有硬性要求,错了就要返工。
- 规则相对稳定,不会三天两头推翻。
- 判断标准可以写成检查表或者脚本。
反过来,探索开放型任务就不适合做成 skill。比如"帮我想几个产品创意""分析这份数据有什么规律",这种任务没有标准流程,硬塞一个 skill 反而限制模型的发挥。还有一次性的小任务也不值得写,直接对话里解决就行,否则维护成本比收益还高。
3. SKILL.md 规范与目录结构:一个 skill 的标准长什么样
3.1 目录骨架与文件职责
我在 agent-skills 项目里用的目录模板是这样:
my-skill/ ├── SKILL.md ├── scripts/ │ ├── render.py │ └── check-env.sh ├── assets/ │ └── template.tex └── references/ └── latex-cheatsheet.mdSKILL.md是唯一必须存在的文件,也是 agent 加载这个 skill 时的唯一入口。scripts/放可执行脚本,用来做模型不擅长的确定性工作;assets/放模板文件,保证输出不会长出各种奇怪形状;references/放参考资料,正文里不需要展开的细节可以丢在这里,让 agent 按需查阅。这个分层结构遵循一个核心原则:指令负责告诉 agent 做什么,脚本和模板负责保证做出来的东西是对的。
3.2 frontmatter 字段逐个说
SKILL.md顶部需要一段 YAML frontmatter,定义这个 skill 的元信息。以我的 LaTeX 排版 skill 为例:
--- name: latex-typesetting description: 将 Markdown 或草稿转换为符合学术规范的 LaTeX 文档。当用户要求排版论文、报告或技术文档时使用。 license: MIT allowed-tools: bash, python ---name是唯一标识,尽量简短,用连字符而不是下划线。description是最关键的字段,它是 agent 决定是否加载这个 skill 的唯一依据,必须写清楚"什么时候用",而不是"这个技能有多好"。license字段对开源项目有意义,标明版权。allowed-tools限定这个 skill 里脚本可以调用的工具集,也是安全边界的一层控制。这些字段不是摆设,我在下文会专门解释它们怎么在实际运行中起作用。
3.3 progressive disclosure:为什么描述必须短
我在 agent-skills 项目早期犯过一个大错:把description写成了半篇说明文,什么"这个技能能够帮助用户以高效、专业、美观的方式生成 LaTeX 文档,支持多种中文字体和参考文献格式,同时保持代码简洁可维护"。写完我自己看着都累,agent 更是一头雾水。
后来我对照各家 skill 规范才想明白:skill 的索引阶段,模型只看得到description这一小段文字。它要从这段文字里判断"这个任务要不要用这个技能",就像检索系统靠摘要判断相关性一样。描述写得越长,信息密度越低,误判率越高。我踩过的最离谱的坑是:一个排版 skill 的描述里提到了"图片处理",结果 agent 在用户请求压缩图片时加载了排版技能。从那以后我把描述压缩成一句话模板:[能力对象] + [适用场景] + [触发条件]。
3.4 脚本、模板和静态资源怎么组织
SKILL.md 正文解决的是"流程和规则",但光有规则不够。规则写得再细,模型编译文档时还是可能忘加参数;即使没忘,输出格式也可能和手写不一致。所以我在每个 skill 里都尽量配上脚本和模板,把确定性部分从模型手里拿回来。
比如 LaTeX 排版 skill 里有一个check-tex.py,负责编译.tex文件并解析编译日志,把 overfull box、undefined reference、missing character 这类警告分类输出。模型只需要运行这个脚本,然后根据输出修正,不再需要自己"猜"有没有问题。模板文件则规定了正文结构、宏包引用顺序、章节标题样式,模型往里面填内容就行,稳定性和效率都大幅提升。这个思路可以推广到任何 skill:凡是能用代码确定判断的,就不要交给模型自由发挥。
4. 实操:从零开发一个 LaTeX 排版 skill
4.1 需求拆分:什么任务适合做成 skill
我以 LaTeX 排版为例,完整演示一遍开发过程。首先做需求拆分。日常排版任务可以拆成这几步:
- 读取源稿(Markdown 或纯文本),识别标题层级和正文结构。
- 套用学位论文或技术报告的 LaTeX 模板。
- 处理中文支持:ctex 宏包、中文字体、中文标点。
- 按规范格式化参考文献。
- 编译并检查警告,迭代直到无致命错误。
每一步都有明确的输入、输出和验收标准,这是做成 skill 的理想形态。拆分完成后,我先写一份测试用例,而不是先写指令——后面讲到测评时再说为什么。
4.2 编写主指令:把排版规范拆成可执行动作
接下来是 SKILL.md 的正文。我的写法是列出有序步骤,每一步都说明操作对象和验证方式,避免"总之要规范"这种空话:
# LaTeX 排版 ## 执行流程 1. 读取源稿,识别标题层级、列表、代码块和引用。 2. 复制 assets/template.tex 为同名 .tex 文件。 3. 将源稿内容填入对应章节,不要在文档中手写样式命令。 4. 代码块统一使用 listings 宏包,配置见模板,不要自行增删参数。 5. 运行 scripts/check-tex.py,根据分类结果修正。 6. 出现 undefined reference 时,检查 label 和 ref 是否成对,而不是新增宏包。 ## 硬性规则 - 中文与英文/数字之间加空格,中文标点前后不加空格。 - 目录使用 \tableofcontents,不用手工排版目录。 - 禁用 hyperref 默认蓝色链接样式,按模板配置改为黑色。注意第 6 条,这条来自我的真实教训:模型遇到编译错误的第一反应往往是"缺包",于是乱加宏包,反而引发更多问题。把"正确动作"写死在规则里,比让模型自由判断靠谱得多。
4.3 配套脚本与模板:补全 agent 不具备的确定性
模板文件的骨架,我抽几条关键配置说明:
\documentclass[12pt]{ctexart} \usepackage[margin=2.5cm]{geometry} \usepackage{listings} \usepackage{xcolor} \usepackage[hidelinks]{hyperref} \lstset{ basicstyle=\ttfamily\small, frame=single, breaklines=true }这些配置不是随便定的。hidelinks是为了满足"链接不要带彩色边框"的常见要求;breaklines=true是为了避免长代码行溢出页面。把这些写死在模板里,agent 就不会每次自由发挥。脚本部分的核心是解析编译日志,我会在脚本里把警告分成 error、warning、info 三级,并给每条 warning 附上修正建议,这样 agent 拿到的是可直接处理的结构化数据,而不是一堆难懂的原生日志。
4.4 本地验证:用真实文档跑一遍
写完 SKILL.md、模板和脚本后,最重要的一步是本地验证。我会准备三份不同风格的源稿:一份带大量代码块的技术文档、一份纯文字报告、一份带数学公式的草稿,然后新建一个干净会话,只给 agent 一个任务:按技能说明完成排版。验证时我不手把手纠正,就让 agent 自己读材料、自己调用脚本并迭代。任何需要我中途介入解释的地方,都说明 SKILL.md 写得不到位,回去补。
这样跑三轮之后,我通常会发现两个问题:一是描述写得还不够精准,agent 一开始没识别出该用这个技能;二是某条规则覆盖不到特殊场景。修完再跑,直到三轮无人工介入顺利通过。这个流程特别像给新员工做上岗培训,只不过我的"新员工"每次都是失忆的,所以我必须把所有知识都写到手册里。
5. 安装、接入与多端适配:Claude Code、Codex、opencode 的差异
5.1 三种常见的安装路径
skill 开发完,接下来是安装。目前主流 agent 的安装方式大体有三种:
- 用户级全局目录:放在
~/.claude/skills或~/.codex/skills这样的路径下,对当前用户的所有项目生效。 - 项目级目录:放在项目仓库内的
.claude/skills或.codex/skills目录,随代码库一起提交,团队成员自动共享。 - 第三方技能包安装:从网上下载别人打包好的 skill 仓库,然后复制或软链接到上述目录。
我的 agent-skills 项目采用第三种方式管理:所有技能维护在一个 git 仓库,每个技能一个子目录,另外提供一个install.sh脚本,根据参数把指定技能软链到各 agent 的全局技能目录里。这样同一个仓库可以同时服务 Claude Code、Codex 和 opencode,因为它们都认SKILL.md这个入口文件,差别只在目录路径。
5.2 全局还是项目级:怎么选
选择安装层级,我建议按这个标准判断:
| 场景 | 推荐层级 | 原因 |
|---|---|---|
| 个人写作、日常排版偏好 | 用户级全局 | 所有项目都能用,不影响团队 |
| 团队代码规范、统一提交信息 | 项目级 | 进 git,所有人默认加载 |
| 公司级安全审查、合规流程 | 项目级 + 代码评审 | 可审计、可回溯 |
| 实验性技能、还没调稳 | 先不装,本地测试 | 避免污染工作环境 |
需要特别提醒的是,项目级 skill 一旦进 git,所有协作成员都会被影响。所以我的建议是:未经过测评的技能不要进入团队共享目录。我在 1.3 节强调"可测评"原则,就是这个原因。
5.3 多 agent 共用一个 skill 仓库的实践
不同 agent 对 skills 目录的识别规则略有差异,但我的兼容策略很简单:在 install 脚本里做一层映射。脚本维护一个表格,把每个 agent 的 skills 根目录列出来,然后逐个创建软链接。实际使用的代码逻辑大致是:
#!/usr/bin/env bash # install.sh —— 把 agent-skills 里的指定技能软链到各 agent 目录 SKILL_NAME=$1 AGENT_DIRS=( "$HOME/.claude/skills" "$HOME/.codex/skills" "$HOME/.config/opencode/skills" ) for dir in "${AGENT_DIRS[@]}"; do mkdir -p "$dir" ln -sfn "$(pwd)/skills/$SKILL_NAME" "$dir/$SKILL_NAME" done这样我改一次技能内容,所有 agent 下次启动时都能加载到最新版本。注意软链接在 Windows 上可能需要管理员权限,跨平台使用时建议改成复制命令,或者在安装脚本里做一次平台判断。
5.4 接入后常见报错与排查
接入过程中最常遇到的报错是 "agent execution terminated due to error",这个错误提示看起来吓人,但通常原因不复杂,我列一张排查清单:
- 技能目录里脚本退出码非 0:检查
scripts/下脚本能否在终端独立运行。 description里提到了不存在的文件路径:agent 加载后找不到文件,直接中断。allowed-tools限制过严:脚本需要 bash 但清单里只写了 python,工具调用被拒。- 软链接失效:仓库目录移动后链接变成断链,agent 扫描时读到空目录。
- 技能内部引用了相对路径,但 agent 的工作目录不在技能目录下。
遇到报错,我第一件事永远是看完整日志栈,而不是盲目改技能内容。绝大多数情况下,把日志里最后几步操作还原出来,问题就一目了然了。
6. skills 怎么测评:不量化就不知道技能有没有用
6.1 eval 先行的设计方式
热词里有人问"skills 怎么测评",这个问题确实关键。我在开发每个技能时,都会先写一份eval.md,里面带上测试用例。拿 LaTeX 排版 skill 的 eval 举个例子:
| 用例编号 | 输入样例 | 期望输出 | 验收标准 |
|---|---|---|---|
| E1 | 2000 字纯文字报告 | 编译通过的 .tex | 无 undefined reference |
| E2 | 含 5 个代码块的技术文档 | 等宽字体代码块 | 无 overfull box |
| E3 | 含表格和交叉引用的草稿 | 完整目录和引用 | label/ref 全部成对 |
| E4 | 中文混排英文术语的正文 | 中英文间有空格 | 目测抽检 10 处均规范 |
这些用例在写 skill 正文之前就定好,等于先立验收标准再施工。没有验收标准的 skill 开发,很容易陷入"我觉得差不多行了"的盲目自信。
6.2 一套轻量评测方案
完整做 skill 评测不一定需要重型 eval 框架,我用的是轻量但有效的流程:建一个临时目录,把测试输入放进去,让 agent 在干净会话中只凭 skill 完成全部任务,然后按验收标准逐条打分。
打分时我关注三个维度:
- 完成度:是否产出目标文件,编译是否通过。
- 符合度:是否遵守硬性规则,可以写脚本自动检查一部分,比如扫描 tex 文件里有没有禁用的宏包。
- 稳定性:同样输入跑三次,输出差异大不大。差异过大说明规则还不够具体,agent 在自由发挥。
跑完一轮,把结果写回 eval.md,不达标的项就是下一轮迭代的重点。
6.3 回归测试:技能迭代最大的坑
技能的迭代天然存在回归风险。我刚把排版技能升级到支持参考文献格式时,曾经偷偷弄坏了表格样式的规则,因为那次改动只改了参考文献部分,没跑一遍旧的表格用例。等用户报告表格错乱,我才发现回归问题。
从那以后,我每次修改 SKILL.md 或脚本,都会完整重跑一遍 eval 用例集,并记录每次运行结果。技能开发其实和软件开发一样,需要把"改坏了"这件事尽早暴露出来,而不是等上生产环境才后悔。如果你只为 agent 写了一个 skill,没有配套测试,那它只能算半个技能。
7. 踩坑记录与个人经验
7.1 描述写太长的后果
我在 3.3 节提过描述要短,这里再展开一个具体的反面案例。agent-skills 早期有一个"结构图生成"技能,负责把文字描述转成流程图结构,我最初把 description 写成了三行,罗列了它支持的十几种图类型。结果在实际使用中,用户说"帮我梳理一下这个系统的模块关系",agent 竟然优先加载了别的技能,因为没有哪个技能的描述完全匹配。后来我把 description 改成一句"将系统或流程的文字描述转换为结构图定义,当用户要求画架构图、流程图、模块关系图时使用",识别准确率立刻上来了。描述不是功能清单,而是触发条件。
7.2 别在技能里塞太多主观风格
我在早期版本里写过类似"排版风格要专业、大气、有高级感"的规则,结果不同会话产出的文档风格差异很大。模型对"高级感"的理解并不稳定,今天可能给你加大留白,明天可能给你上深色调。后来我把所有风格要求全部改写为可检查的硬性规则:"正文统一五号字""标题用黑体加粗""页边距固定 2.5cm"。主观描述只会放大随机性,客观规则才能带来稳定输出。
7.3 第三方技能的安全意识
现在网上能下到很多现成的 skill 包,但我必须提醒一句:不要无脑安装你不了解来源的技能。skill 本质上是一段会被 agent 执行的指令,恶意技能可以诱导模型运行危险命令、读取本地敏感文件,甚至把内容回传到指定服务器。安装前至少做两件事:通读 SKILL.md 正文,检查里面有没有要求 agent 执行可疑操作的内容;检查 scripts 目录下的脚本,看有没有网络请求、文件删除等敏感操作。另外,不要把 API Key、密码等机密以明文形式存在技能仓库里,即使仓库是私有的,也应该用环境变量或密钥管理工具注入。安全是 skill 开发的底线,不是加分项。
7.4 我接下来会怎么扩展
这个项目后续我打算做三件事:一是把 skill 的公共部分抽取成共享模块,比如"编译检查"和"文件结构解析"这两段逻辑,在很多 skill 里都会用到,避免各写各的;二是给技能加版本号,配合 eval 结果做发布记录,升级时可对比前后版本的表现差异;三是实验让一个 skill 在流程中自动调用另一个 skill,比如排版技能需要生成结构图时,自动加载结构图技能。这条路走通之后,skill 就不再是孤立的指令包,而是一张可以组合的能力网。
做 agent-skills 这段时间,我最大的体会是:想让 agent 稳定地干活,重点不是找更聪明的模型,而是把经验沉淀成它随时能查的规范。模型的聪明是通用的,你的业务规范是私有的,skill 就是连接这两者的那座桥。每踩一个坑,就把它写进对应技能的规则里,你会眼看着 agent 越用越顺。