☰
从提示词堆砌到技能化:LLM Agent技能库设计与实践
2026/10/7 17:26:59 网站建设 项目流程

如果你最近在折腾 LLM Agent,应该会明显感觉到圈内讨论的话题正在变化:大家的关注点已经从“怎么让模型回复得更像人”转向“怎么把模型的能力沉淀成一套可以复用的工程资产”。我这两周刚好在打磨一个叫 agent-skills 的技能库项目,把高频业务操作封装成带说明、带校验、带示例的技能文件,再让 Agent 按需加载、按步骤执行。做完这一轮之后,我基本改掉了以前那种“每个任务都现场写一段提示词”的坏习惯。这篇文章会把从技能库目录设计、核心机制拆解,到真正接入业务系统、再到在真实环境里反复翻车的完整过程都整理出来,给正在考虑做 Agent 技能化的同行一个参考。

1. 从“提示词堆砌”到技能化:agent-skills 要解决的第一个问题

1.1 为什么我放弃了“万能提示词”方案

在动手写 agent-skills 之前,我的 Agent 项目走的是最朴素的路子:写一个超级系统提示词,把角色设定、业务规则、输出格式、举例全部塞进去,然后期待模型面对任何请求都能稳定输出。

现实很快打了脸。

项目跑了一个月后,系统提示词从最初 2000 字膨胀到了 6000 多字。里面塞了十几个业务场景的规则。结果就是:A 场景的规则开始干扰 B 场景的判断;模型在上下文过长时,对提示词后半段的内容遵循度明显下降;每次新增一个业务模块,都要从头读一遍那坨提示词,改一行可能引发另外三处行为异常。

我后来做了一个统计:6000 字里真正在每次请求中都会生效的,可能不到 800 字。其他内容都是“防御性”的——为了防止某些边缘情况,结果把所有情况都拖慢了。

这让我意识到一个问题:提示词不是不能写,而是不能无限堆。Agent 的能力应该拆成一块一块的积木,按需取用,而不是一次性全部挂在主流程里。

1.2 什么样的任务才值得沉淀成技能

决定做 agent-skills 之后,我第一个动作不是去写代码,而是把项目里已有的任务清单拿出来做了一遍分类。分类标准很简单:

  • 这个任务出现的频率高不高?每周至少触发一次才值得做技能。
  • 任务的执行流程是不是相对固定的?如果每次的逻辑都完全不同,技能文件写出来也没法复用。
  • 任务对输出格式的约束是不是明确的?比如“必须输出表格”“必须包含某个字段”,这类强约束任务很适合技能化。

按这个标准,我当时筛出了两个高频场景:一个是周度销售对账报告生成,一个是客户工单的分类与流转建议。这两个任务流程稳定、输出格式要求严格、每周都会用到,非常适合封装成技能。

而一些探索型任务,比如“帮我想想这个投放活动怎么做”,不确定性太高,技能文件写出来也只能是空话,没必要硬套。

所以 agent-skills 这套方案的核心判断是:技能化解决的是“已知的重复问题”,而不是“未知的一次性问题”。后者应该靠模型本身的推理能力,而不是靠堆技能文件。

2. 技能库的目录设计与技能文件规范

2.1 为什么技能文件一定要有固定结构

我是从其他项目里吃过亏的。最早我尝试用 JSON 结构来定义技能,每个技能维护一个 JSON 配置,工具调用、参数说明全部写在 JSON 里。结果有两个问题:

  • JSON 对注释的支持很差,字段说明只能靠命名理解,跨月之后自己都忘了某个字段是干嘛的。
  • 大语言模型对 JSON 的遵循度,在复杂约束下不如自然语言指令稳定。让模型照着一段自然语言步骤执行,比让它解析深层嵌套 JSON 可靠太多。

所以 agent-skills 的技能载体用了 Markdown,外层用 YAML frontmatter 写元信息,正文用自然语言写执行步骤和规则。这样人看着清楚,模型读着也顺。

目录结构我最终定成了这样:

agent-skills/ ├── skills/ │ ├── weekly-sales-report/ │ │ ├── SKILL.md │ │ ├── scripts/ │ │ │ └── generate_report.py │ │ ├── resources/ │ │ │ └── report_template.xlsx │ │ └── tests/ │ │ └── test_skill.py │ └── ticket-classifier/ │ ├── SKILL.md │ └── scripts/ ├── index.json ├── loader.py └── README.md

每个技能一个独立目录,至少包含一个 SKILL.md 文件。脚本、模板资源、测试脚本都放在对应技能目录下,互不干扰。这样做的直接好处是:技能的增删改不需要改动主程序,只要这个目录还在,Agent 就能动态发现它。

2.2 SKILL.md 的字段设计和描述写法

SKILL.md 的 YAML frontmatter 部分,我保留了这几个核心字段:

字段作用我的写作建议
name技能唯一标识用短横线命名,比如weekly-sales-report
description技能检索时被比对的关键文本写清楚“触发场景 + 输入 + 输出”,这是整个技能库最重要的字段
dependencies执行技能需要的 Python 依赖或外部工具加载时检查和安装,避免运行时才发现缺失
tools技能可能需要调用的脚本或 API显式声明,避免 Agent 自己发挥去乱猜工具名

正文部分我强烈建议分成几个小节:目标、执行步骤、输出格式、注意事项、示例。其中“执行步骤”要写成编号列表,每一条必须是明确的动作,不能出现“视情况而定”这种话。模型在看到一个编号步骤列表时,按序执行的概率远高于阅读散文式说明。

关于description的写法,我踩过很大的坑,后面专门讲。这里先给一个正确示例:

--- name: weekly-sales-report description: 当用户需要生成周度销售对账报告时使用。输入为周一日期;输出为包含销售额、退款率、异常订单三部分的 Markdown 报告。 dependencies: [pandas, openpyxl] tools: [scripts/generate_report.py] ---

关键字是“当用户需要……时使用”,这种以触发条件开头的描述,在检索阶段命中率远高于“该技能用于生成销售报告”这种功能定义式写法。因为用户请求通常是“帮我写下上周的销售汇总”,这里既有“销售”又有“上周”,描述里只有“销售报告”其实匹配不到时间语义,但通过“生成周度销售对账报告”这种完整句式,模型能更好理解。

3. 核心机制拆解:技能注册、检索与执行怎么打通

3.1 技能注册:把 Markdown 变成可查询的索引

Agent 要使用技能,首先要能发现技能。我在 loader.py 里做了一个目录扫描器,启动时会遍历skills/下的所有子目录,读取每个 SKILL.md 的 YAML frontmatter,生成一份index.json。

import os import yaml import json SKILLS_DIR = "skills" def load_skills_index(skills_dir=SKILLS_DIR): index = [] for skill_name in os.listdir(skills_dir): skill_path = os.path.join(skills_dir, skill_name) skill_file = os.path.join(skill_path, "SKILL.md") if not os.path.isfile(skill_file): continue with open(skill_file, "r", encoding="utf-8") as f: content = f.read() # 简化的 frontmatter 解析,实际可引入 python-frontmatter 库 parts = content.split("---") meta = yaml.safe_load(parts[1]) meta["path"] = skill_path meta["content"] = parts[2].strip() if len(parts) > 2 else "" index.append(meta) with open("index.json", "w", encoding="utf-8") as f: json.dump(index, f, ensure_ascii=False, indent=2) return index

这个扫描动作必须和服务启动分离。技能文件是静态的,不需要每次请求都重新扫描。我这边放在服务初始化时执行一次,后面如果手动改了技能文件,再通过一个接口触发重新加载。只有这样做,Agent 主流程的逻辑才能保持简单,不需要关心技能库内部的组织方式。

3.2 检索策略:为什么我只靠语义向量还不够

技能注册好之后,下一步是检索。最直觉的方案是把所有技能的description向量化,然后对用户请求做语义检索。我一开始就是这么干的,但在一个内部业务场景里出现了漏召回:用户说的是“对一下上周的账”,而技能描述里写的是“周度销售对账报告”,语义相近但用了不同的词,向量相似度排到了三四名开外,Agent 就没选中它。

后来我把检索改成了“向量 + 关键词”的混合模式:

def retrieve(query, top_k=3): keyword_hits = keyword_match(query, index) vector_hits = vector_search(query, index) # 合并去重,关键词命中加权重,向量命中按分数排序 combined = merge_and_rank(keyword_hits, vector_hits, top_k) return combined

关键词匹配负责兜底,把“账”“对账”“销售”这类强信号词直接命中;向量检索负责处理“帮我看看上周的数据”这种语义表达。两者合并后取 TopK,效果立刻上来了。这个策略可能不是最优解,但胜在简单可控,也方便排查问题。你可以根据实际把关键词命中分数调高一点,我在项目里设置了关键词命中额外加 0.3 的权重。

3.3 执行链路:一次技能调用到底发生了什么

agent-skills 的执行链路不长,但每一步都有讲究。完整过程是:

  1. 用户请求进入主 Agent。
  2. 主 Agent 先把请求和技能索引中的description做匹配,选出候选技能。
  3. 选中的技能正文被拼入当前上下文。
  4. Agent 按技能文件中的编号步骤逐步执行,如果技能声明了scripts/下的脚本,Agent 会在适当节点生成运行脚本的命令。
  5. 脚本输出或查询到的数据回填到对话中,Agent 整理成最终结果。
  6. 输出校验模块检查结构是否合规,不合规就回到第 4 步重试一次。

这里有个关键设计:技能文件不负责直接调用工具,它只描述步骤和规则,真正的脚本在需要时才被拉起。好处是技能文件保持结构简单,Agent 的推理负担小;坏处是脚本的输出如果不按预期返回,Agent 可能没法顺利消化。所以我在脚本的返回里强制要求带一个status字段,来标记这次执行是否成功。

4. 把 agent-skills 接进业务项目的完整过程

4.1 项目背景与第一个技能选型

我改造的是一个内部销售运营看板项目。之前每次周会前,运营同事都会在群里喊“帮忙出一下上周的销售对账”,我当时的 Agent 每次都从零开始理解任务,生成一段临时提示词去调数据库、算指标、出报告。结果就是:输出的表格字段偶尔会缺列,数字口径偶尔不一致。

这次我决定先封装weekly-sales-report这个技能。选择它的原因很简单:流程固定、输入明确、输出结构严格,而且每周至少触发一次,投入产出比最高。

技能正文我写成了这样:

--- name: weekly-sales-report description: 当用户需要生成周度销售对账报告时使用。输入为周一日期;输出为包含销售额、退款率、异常订单三部分的 Markdown 报告。 dependencies: [pandas, openpyxl] tools: [scripts/generate_report.py] --- ## 目标 根据销售数据库中的订单数据,生成截至指定周的周度对账报告。 ## 执行步骤 1. 从用户输入中提取周一日期,格式为 YYYY-MM-DD。 2. 调用 scripts/generate_report.py,传入该日期和输出路径。 3. 等待脚本执行完成,读取生成的 report.md。 4. 检查报告是否包含“销售额统计”“退款率统计”“异常订单列表”三个部分。 ## 输出格式 最终输出 Markdown 报告,包含以下部分: - 销售额统计:本周总销售额、环比上周变化、目标完成率。 - 退款率统计:退款笔数、退款金额、退款率。 - 异常订单列表:金额超过阈值或状态异常的订单,最多 10 条。 ## 注意事项 - 日期必须用 YYYY-MM-DD 格式,否则脚本会报错。 - 如果本周无数据,不要编造数字,明确说明“暂无数据”。

4.2 与主 Agent 的接入方式

主程序仍在用大模型驱动,agent-skills 只提供技能发现和加载能力。接入接口很简单,我给核心流程加了三个函数:list_skills()、retrieve_skills(query)、load_skill(name)。业务代码不需要感知技能内部结构,只要拿到技能正文塞进 prompt 就行。

实际跑了两周之后,效果最明显的变化是上下文占用下来了。以前每条请求就算跟销售报告无关,系统提示词里也有一大段销售规则压着;现在只有任务命中技能时,相关的几百字才会进上下文。单次请求平均 Token 消耗下降了差不多 30%,同时输出格式的稳定性提升明显,报告缺列的情况基本消失了。

5. 实测中的翻车现场与调优记录

5.1 翻车一:技能描述写得太“正式”,Agent 根本检索不到

刚把第一个技能放进去时,我写的description是“销售对账报告生成技能,用于处理销售数据的汇总与统计,输出周度报告”。这个描述看着没毛病,但实际调用时出现了很尴尬的情况:用户说“帮我看下上周的销售情况”,Agent 居然没召回这个技能,而是自己现场发挥。

排查下来发现,问题出在描述里缺少“用户视角的触发场景”。模型做匹配时,它是在把用户输入和技能描述做语义对齐,描述里全是名词堆砌,缺少“周度”“对账”“上周”这种和真实请求能直接配对的动作词。

修复方式就是前面说的,把描述改成“当用户需要生成周度销售对账报告时使用,输入为周一日期,输出为……”这种包含触发条件的完整句式。改完当天,召回归零问题就解决了。

5.2 翻车二:技能步骤留了“自由发挥”的口子

第一个版本的第 4 步写的是“对数据质量进行合理检查”。这句话现在回头看属于灾难级别的表达。“合理”是个非常主观的词,模型根本不知道什么算合理。结果就是,有时候它自己脑补数据,有时候它跳过了异常校验,直接把原数据输出。

我后来把所有“合理”“适当”“必要时”这类词全部从技能文件里清掉,改成可验证的行为描述,比如“检查是否存在退款金额大于订单金额的记录,如有则列入异常订单列表”。要让模型可靠,技能步骤就必须是确定性的动作,不是开放式的判断题。

5.3 翻车三:技能文件过长,关键步骤反而被忽略

另一个问题是技能文件越写越长,尤其我在正文里加了很多说明性文字后,文件到了两千多字。然后有个奇怪的现象:模型对后面的“输出格式”部分遵循得挺好,但前面的“执行步骤”第 3 步却偶尔会漏。这其实就是上下文注意力分布不均导致的。

解决办法是瘦身:技能正文里只保留模型必须执行的步骤和硬性约束,原理说明、背景知识这类内容全部移到resources/目录下的附加文档里,或者干脆删掉。技能文件的最佳篇幅在 500 到 800 字之间,超过这个量级就该考虑拆分子技能了。

5.4 调试方法:给技能命中留痕

前期调技能时最大的痛苦是“不知道 Agent 为什么没选这个技能”。我在检索模块加了日志,把每次请求召回的前五个技能名称和得分都记录下来。这样就能对着日志看:某次任务到底是因为描述不匹配没召回,还是召回了但 Agent 没选,定位问题的速度会快很多。这个排查链路强烈建议提前做,别等技能多了再补。

6. 和几种主流做法对比后,我发现 agent-skills 的适用边界

6.1 它与提示词模板、微调、工具函数有什么本质差异

技能化不是唯一的路。市面上还有几种主流做法,我实际都试过或调研过,直接列一个对比:

方案维护成本可解释性扩展新能力效果稳定性适合场景
全能提示词模板低,但后期失控差,长提示词难以审查只能继续加长中,互相干扰流程简单的原型项目
agent-skills 技能库中,按目录维护好,每个技能独立审查好,加目录即可高,按需加载多场景、多流程的生产项目
微调模型高,需要训练和评估差,行为难以解释低,每次迭代成本高高,但只对固定格式好输出风格固定、无工具调用需求
纯工具函数(function calling)中,工具参数靠代码定义较好好高,但对复杂流程弱单步原子操作,如查天气、算价格

agent-skills 的核心差异在于:它把“技能”的载体从代码或模型参数变成了可读可写的文档。这意味着非工程师也能参与维护业务规则,也意味着每次改动可以直接走代码评审流程,行为变更完全可追踪。这一点在需要合规审查的场景里尤其值钱。

6.2 哪些场景不应该硬套技能化

虽然我在这个项目里收益明显,但还是要泼点冷水。有三种场景不太适合 agent-skills:

第一种是频率极低的一次性任务。比如“帮我研究一下某个新竞品的定价策略”,这种任务写成技能,等下次用的时候需求早变了,纯属浪费维护精力。

第二种是高度依赖数值推理的任务。技能文件里的自然语言步骤,对这类任务的约束力有限。比如涉及多表 join 和复杂计算的财务分析,让 Agent 自己按步骤推理容易出错,不如直接写一个计算能力强的脚本做后端,技能文件只负责调度脚本。

第三种是技能之间边界模糊的任务。如果你发现几个技能的 description 写出来高度相似,说明业务场景本身没有清晰边界。这时候硬拆技能会导致召回混乱,不如先把业务梳理明白。

6.3 我下一步的计划:版本化与回归评估

agent-skills 目前跑得挺稳,但我已经看到了新的痛点:技能文件改起来很容易,但你怎么知道这次改动没有让另一个场景变差?所以我下一步准备给每个技能配一个小型评估集,里面放几组“输入 + 预期行为”的测试用例。每次改完技能文件,就自动跑一遍评估集,看输出是否符合预期。

再往后就是技能的版本化。给每个技能加上版本号,让 Agent 在加载技能时可以感知当前版本,并在技能行为变更时给出明确提示。

最后说点我自己的体会。这次做 agent-skills 最大的收获,不是把报告生成任务的准确率拉高了多少,而是改变了我对 Agent 工程化的理解:生产级 Agent 不应该把全部智能压在模型身上,而是要把组织已有的最佳实践拆解成模型可以稳定执行的资产。技能文件就是这种资产的载体。如果你也在做类似的技能库,我的建议是先挑一个最痛的高频场景跑通全链路,再慢慢把其他技能沉淀进来。这套路走得通,后面会顺畅很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询