尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Agent技能库实战:从提示词堆砌到可复用能力模块化

发布时间:2026/9/25 8:45:07

资讯中心
01
ARTICLE

Agent技能库实战:从提示词堆砌到可复用能力模块化

Agent技能库实战:从提示词堆砌到可复用能力模块化
“我们的Agent又摸鱼了。”这是我在做智能体产品那段时间最常听到的抱怨。用户让Agent处理一份数据清洗任务它洋洋洒洒回复了一整段思路然后没有任何实际产出让它调用某个内部服务它把参数名猜得南辕北辙偶尔成功一次换个输入又崩了。问题大概率不在模型智商而在于我们只给了它对话能力却没给它一套稳定、可复用、可验证的“手脚”。于是我搭了一个叫agent-skills的技能库项目把所有可复用能力拆成独立技能包统一注册、统一加载、统一回归测试坚持改造了一段时间之后Agent才真正从“聊天机器人”变成了“能干活的小工”。这篇文章不聊概念包装只聊落地。我会从为什么需要技能库、目录结构怎么定、一个技能从想法到稳定可用要经历哪些阶段再到实测中踩过的坑和后续演进思路完整过一遍。无论你是在给个人助手加技能还是维护一个多Agent产品这套思路应该都能直接用上。1. agent-skills 的起源从提示词堆砌到能力模块化1.1 没有技能库的Agent本质上是一堆失控的Prompt很多项目的起点其实都差不多先写一个系统提示词把工具函数塞进去规则写清楚看起来能跑。但随着需求变多提示词越来越长函数越来越多问题开始暴露。我自己遇到过三种典型乱象。第一种是功能重复团队里两人各写了一个处理Excel的函数命名不同、参数不同、错误码也不同模型经常分不清该调哪个。第二种是不可复现某次重构把工具名改了提示词里没同步Agent就开始幻觉出一个不存在的函数名报错信息看半天才反应过来。第三种是没法做回归给Agent新增一个能力你很难说清上个版本到底行不行因为它没有“测试用例”这个概念。做agent-skills的核心动机就是把“能力”从提示词和散落的脚本中抽离出来变成一个个有标准结构、有明确描述、有测试用例的技能包。技能不是一段提示词也不是单个函数它是一次完整任务的执行方案是Agent能力的最小可交付单元。1.2 技能库与工具函数、插件机制的本质区别很多人会把技能库等同于“工具集合”其实差别很大。我列了一张表方便直接对照维度工具函数Tool技能Skill插件Plugin粒度单点操作如“发送HTTP请求”复合任务如“抓取网页并总结成报告”更重通常带UI、事件、状态管理驱动方式模型主动调用模型按描述匹配并编排宿主系统加载提供全局能力测试难度单元测试即可需要输入输出回归样本需要集成测试环境复用性需要自己拼天然带“完成一个目标”的语义通常绑定特定宿主打比方说工具函数像积木块技能像一份拼好图纸的乐高套装。模型如果要写一篇行业调研报告它不需要自己思考“先抓取、再清洗、再归纳、再排版”这些步骤而是直接调用“行业调研报告生成”这个技能技能内部按顺序执行子步骤。这就是复合技能和单纯工具列表最大的区别。1.3 什么阶段的项目才需要上技能库不是所有项目都要一上来就搭技能库。如果你只是在做一个Demo顺手写几个函数就够了。但出现下面三类信号就应该考虑工具函数数量超过15个模型开始出现“选择困难”调用错误率明显上升。同一能力在多处被重复实现改动一处导致另外几处失灵。你已经分不清当前Agent“到底会做什么、不会做什么”只能靠不断加提示词试探。我在项目里是等到工具数达到20多个、连续出现“模型选错函数”和“新功能覆盖旧功能”问题时才下决心重构。早点动手更省力太晚动手代价更大。agent-skills解决的正是这个阶段的治理问题。2. 技能包的目录骨架先把仓库结构定明白2.1 一个技能包内部的标准结构技能库的目录结构看似小事其实直接决定了后续可维护性。我的做法参考了目前社区主流的技能包规范如模型厂商推出的Skills格式结合自己项目做了微调。一个标准技能包长这样skills/ └── web_research/ ├── SKILL.md ├── scripts/ │ ├── fetch_page.py │ ├── clean_html.py │ └── summarize.py ├── assets/ │ └── template.md └── tests/ ├── sample_input.json └── expected_output.json每个技能包都有四个部分SKILL.md技能元信息包括名称、描述、适用场景、参数说明。这是模型判断是否调用该技能的关键。scripts/实际可执行的脚本一个技能可以有多个脚本由Agent按需要选择执行顺序。assets/辅助模板或静态资源比如生成的报告模板、需要参考的示例文件。tests/回归测试样本固定输入对应固定期望输出用来验证技能效果。这个结构我跑了很久最大的体会是结构不能太复杂太复杂会劝退贡献者但也不能简单到只剩一个脚本否则描述、测试、资源全都没地方放。2.2 SKILL.md 的描述写法模型怎么看你写的东西技能库的成败一半在SKILL.md的描述质量。模型不是人它不会去读你所有代码它只会通过描述来判断“这个技能适不适合当前任务”。描述写得烂技能再强也等于不存在。我见过最典型的坏描述本技能用于处理各种数据功能十分强大支持多种格式可以高效地帮助用户完成数据处理任务。这句话几乎没有信息量。“各种数据”“多种格式”“高效帮助”都是虚词模型无法据此判断何时调用。好描述应该像写给一个有点笨但很认真的同事看的说明书将用户提供的CSV文件清洗为标准化表格。适用场景字段缺失、日期格式混乱、重复行、编码乱码。输入参数file_path。输出清洗后的CSV路径。不适用场景图像识别、OCR、非表格类数据。这段话的关键点是第一句给出了精确的职责描述第二句列举了具体的适用信号也就是模型需要识别的“触发词”第三句明确了输入输出第四句写出了“不适用场景”用于防止模型误调用。另外一个经验是给技能打分级别的“触发优先级”。比如“处理Excel”和“处理CSV”两个技能可能会有重叠我会在描述里写清楚“当且仅当检测到.xlsx扩展名时使用本技能.csv请使用其他技能”。排他性描述能大幅降低抢活概率这点后面会专门展开。2.3 一级目录的划分粒度场景优先还是领域优先技能数量一多一级目录按什么划分就成了第一个纠结点。按领域分数据类、文本类、网络类和按场景分调研类、写作类、分析类都有道理我最后的选择是“场景优先、领域兜底”。场景优先的好处是最终用户以及模型是在具体任务场景下思考的比如“写周报”是一个场景它可能需要读取代码仓库数据、汇总本周变更、生成文档这些子能力跨了多个技术领域。如果按领域拆模型得自己跨目录组合多个技能中间很容易丢失上下文。我的目录长这样skills/ ├── article/ # 文章写作与改写 ├── data_process/ # 数据处理与清洗 ├── report/ # 报告生成 ├── web_tools/ # 网页抓取与信息核验 └── daily_tools/ # 日常办公小工具当然场景和领域之间总有灰色地带我的原则是如果某个技能被多个场景复用就放到领域目录如果只服务某一个高频场景就放场景目录。一句话按“最终要交付什么”分不按“底层用什么技术”分。3. 一个技能从“想法”到“稳定可用”的四个阶段3.1 定义技能边界输入、输出与不可为清单我踩过最大的坑是动手写脚本前没有把“技能边界”定清楚写着写着功能就膨胀了。一个技能想包办所有事最后往往什么都做得不深模型还容易调用错。现在我在新建技能前会用静态的“技能卡”做设计评审几行字而已但必须写清楚技能名称一句话说清“是什么”。触发信号哪些关键词、文件后缀、任务特征表明该用它。输入参数每个参数的类型、是否必填、取值范围。输出结构JSON还是文件字段有哪些错误时返回什么。不可为清单明确这个技能拒绝处理什么。比如“代码变更周报生成”技能的四要素我这么写的触发信号用户提到“周报”“本周变更”“commit记录”且目标是生成报告。 输入git仓库路径、时间范围、目标格式。 输出Markdown报告路径。 不可为不负责推送周报给钉钉/邮件只生成文件。这段“不可为”很重要。很多时候Agent不是傻而是太乐于助人你让它生成周报它会顺手想把周报发出去结果没有对应权限整个流程就卡住了。限定边界以后错误率降了一个档。3.2 实现层落地参数校验与结果标准化定义完边界就要写脚本。先说一个人人都会犯但很少写进文档的问题脚本必须对参数做严格校验并且所有输出必须标准化。模型调用脚本和人来执行脚本不一样人看到报错信息能自己修正模型遇到一段乱七八糟的Traceback时通常会尝试自行“修复”然后提出一个不太靠谱的参数再次调用。所以技能脚本要做到两点一是参数校验在脚本入口统一处理错误信息直接告诉模型“哪个字段错了期望什么类型”。import argparse import json import sys def parse_args(): parser argparse.ArgumentParser() parser.add_argument(--input, requiredTrue, help输入文件的绝对路径) parser.add_argument(--columns, requiredTrue, help需要保留的列名逗号分隔) args parser.parse_args() if not args.input.endswith(.csv): print(json.dumps({success: False, error: input必须为.csv文件当前为 args.input})) sys.exit(2) return args def main(): args parse_args() # 后续处理逻辑... result {success: True, output_path: /tmp/cleaned.csv, rows: 1024} print(json.dumps(result, ensure_asciiFalse)) if __name__ __main__: main()二是输出统一为JSON并且至少包含success、error、output_path这类机器可读字段。这样模型能从输出中准确拿到下一步需要的信息而不是在一堆print日志里猜。关于环境依赖每个技能包必须在首行注释或独立的 requirements.txt 中写清楚运行环境。我在实战中吃过这样的亏技能脚本在本地一切正常部署到另一台机器后Dateutil库版本不一致直接被模型判定为“技能不可用”。所以我在技能包结构里加了scripts/requirements.txt每个脚本开头也加一行注释说明Python版本。技能部署脚本时顺手生成一份依赖锁定文件后续在干净环境做一次全量测试能避开大部分环境坑。3.3 回归测试用真实调用样本守住下限技能库有没有价值很大程度要看测试做没做。我这里的“测试”不是传统意义上的单元测试而是“模拟模型真实调用方式的回归测试”。具体做法是每个技能包准备3到5组测试样本每组包括固定输入和期望输出。运行测试时用命令行直接调用技能脚本传入固定参数比对输出结果是否符合预期退出码是否正确。测试编号输入摘要期望输出主要验证点T1标准CSV3列含日期格式混乱清洗后的CSV日期统一为YYYY-MM-DD日期解析T2空文件返回successfalse错误信息明确异常兜底T3缺少必填参数columns退出码2返回参数错误信息参数校验T4包含100万行的超大CSV成功处理耗时低于60秒性能基线有了这套回归用例之后每当我调整脚本或升级依赖都能知道是否破坏了之前的行为。没有这套东西前我根本不敢随意重构技能内部逻辑——因为你不知道哪个隐藏调用方会突然崩溃。现在每次提交前跑一遍测试集几秒钟出结果省心太多。3.4 注册与装载让主Agent发现新技能技能写好了怎么让Agent“看到”它这是整个流程里最绕不开的一环。简单做法是在系统提示词里把所有技能描述全部塞进去。但技能数量一多提示词会失控上下文窗口浪费严重。更合理的做法是把技能注册信息做成一个独立索引文件由Agent运行时动态加载{ skills: [ { name: web_research, description: 抓取网页并生成摘要报告, tags: [research, web, summarize], entry: skills/web_research/scripts/fetch_and_summarize.py } ] }在加载逻辑里我目前采用的是“标签预筛 语义召回”混合模式先用固定标签做一个快筛选把明显不相关的技能排除掉再让模型在候选集合里根据完整描述做最终选择。这么做的好处是既不用把所有技能全文塞进上下文又能让模型在“小范围候选”里做准确决策。实测下来技能调用准确率从60%出头提升到了85%以上。4. 实测中的几类意外与我的处理方式4.1 技能之间抢活描述重叠导致选错工具技能库上线后第一个让我头疼的问题不是执行失败而是模型选错技能。我先后加了“网页信息抽取”和“网页内容总结”两个技能它们名字相近、描述也相近结果就是模型在处理“把这篇新闻摘要提取出来”时偶尔会调用“网页信息抽取”输出的是一堆原始段落而不是真正的摘要。这个问题的根源是描述的重叠度太高。处理方法不是把描述删得更简单而是要增加“排他性描述”。现在我的做法是给同领域的每个技能加上明确的差异标识“网页信息抽取”目标是结构化字段比如作者、发布时间、标题、正文。适用于需要保存字段的场景。“网页内容总结”目标是生成一段连贯的摘要文字。适用于只需要一句话或一段概述的场景。同时我在描述里会加“当请求中包含…时优先选择本技能若请求明确要求…请改用另一技能”。这种显式约束模型很吃这一套。4.2 工作目录与环境的“幽灵依赖”另一个让我排查到深夜的问题是技能脚本在A目录下运行正常在B目录下运行必挂。后来发现脚本默认读取相对路径一旦模型从某个交互目录发起调用文件就找不到了。这是个很典型的“隐性状态”问题。修复方式很粗暴也很有用脚本里所有路径必须显式处理要么接受绝对路径参数要么通过os.path.dirname(__file__)定位到技能包自己的目录。BASE_DIR os.path.dirname(os.path.dirname(os.path.abspath(__file__))) TEMPLATE_PATH os.path.join(BASE_DIR, assets, template.md)另外还要注意环境变量的污染某个技能在脚本里设置了export PYTHONPATH影响到了下一个技能的执行。现在我在技能脚本里对关键环境变量加了备份和恢复逻辑虽然看起来不够优雅但在多技能连续调用的场景下非常必要。4.3 技能数量膨胀带来的上下文压力技能库越做越大的时候出现了一个反直觉的问题技能能力很强但Agent反而变“笨”了。原因很简单所有技能描述都塞进系统提示词后上下文被大量无关信息占据模型真正处理当前任务的注意力被稀释了。我统计过一条技能描述平均200字左右30个技能就是6000字占掉了主流模型上下文不小的比例。这还只是描述再加上各种规则说明系统提示词膨胀到了失控边缘。解决办法就是我前面提到的动态加载。沿着这个思路继续优化可以从两条路同时走对技能进行状态分级核心技能常驻长尾技能按需召回。对描述做压缩缓存同一个技能在多次会话说中只用维护一份描述把重复的模型输入token省掉。我到目前的结论是技能库不是越大越好而是越精准越好。一个能覆盖80%高频任务的20个技能包远比100个花里胡哨的长尾技能更能让Agent稳定发挥。5. 技能库的长期维护与演进方向5.1 版本号与兼容策略技能库走到后期你一定会遇到“改技能导致旧任务失败”的问题。比如把“网页抓取”技能的脚本升级了一下使用的请求头变了结果之前依赖旧结构的任务开始报错。技能也要上版本号。我在技能包元数据里加了version字段并在索引文件里保留了compatible_versions信息。加载技能时如果检测到当前Agent会话的调用链依赖老版本可以有两种处理方式兼容模式继续调用旧版脚本直到当前会话结束。迁移提示让模型明确告知用户“该技能已升级输出格式有变化”。实际落地时我采用的是“同版本共存策略”新技能不直接覆盖旧技能而是以skill_name_v2、skill_name_v1的方式同时注册由模型根据用户需求自动选择。经过一段时间运行确认旧版本调用频率降到零后再将旧版归档。5.2 动态注册与技能发现协议现在技能库还只是一个本地仓库新技能通常是靠开发者在仓库里提交来加入这保证不了新技能的“可发现性”。我不确定产品最终的形态会偏哪个方向但我认为比较现实的一条演进路径是让技能库支持更灵活的注册方式技能新增后通过hook机制同步更新索引而不是通过手工编辑JSON。技能被调用时自动记录用途统计定期生成“高频技能”列表反向辅助Agent。每个技能有统一入口执行器由执行器统一管理超时、并发和权限而不是每个脚本自己独立处理一套环境。长期来看我觉得技能库一定会从“代码仓库”演进为“运行时能力注册中心”技能变成一种可热插拔的、带版本、带声明、带测试的标准化组件。这里的核心是定义一套简单的注册协议名称、入口、依赖、权限、调用方式。协议越轻生态越容易长出来。5.3 用执行数据反向校准技能质量我另一个坚持了半年以上的习惯是全量记录每个技能的调用日志。不是简单记“哪个技能被调用了”而是把一次技能执行的完整链路记下来触发时模型是怎么描述任务的、传入参数是什么、脚本返回结果是什么、用户是否编辑了结果、最终有没有重试。这组数据用来反向校准技能质量比任何主观评审都好用。我看几个核心指标指标含义我的处理阈值调用成功率脚本正常返回的比率低于90%就查日志无效调用率模型调用后用户直接丢弃结果的比率高于30%就重写描述参数错误率脚本因参数问题退出的比率高于20%就优化参数校验或降低技能复杂度平均执行时长从调用到结果返回的耗时超过30秒就考虑优化脚本有一次我发现某个统计报表技能的“无效调用率”高达40%点开日志后发现是模型经常拿它处理“非表格类数据”这说明它的描述在“不适用场景”上没有写清楚。我把不可为清单从一句话扩成了三条具体规则下一周该指标直接掉到了12%。用数据驱动技能迭代听着很抽象做起来其实很简单把每次调用的日志留下来按技能聚合看异常率。不用搞复杂指标体系先把“调用成功率”和“无效调用率”这两个基础指标盯住就能发现大量技能定义、描述和实现层面的问题。最后再说一条个人体会。做技能库这几年我见过太多人把它做成“复杂框架”其实服务好Agent并不是越复杂越好。把结构做简单把描述写清楚把测试做扎实把日志留下来这四件事比任何花哨的调度算法都管用。一个能被模型准确找到、稳定执行、持续验证的技能库才是真正让Agent长出手脚的东西。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。