资讯动态

Agent技能库实战:用可复用模块替代长提示词,提升任务完成率

发布时间:2026/10/14 9:12:35 来源:尧图企业网站定制
做Agent开发这段时间我最大的一个转变是不再沉迷于把提示词越写越长而是开始认真给Agent建技能库agent-skills。起因特别朴素——我需要对一个数据报表Agent做增强让它能够自动读取CSV、按维度分组汇总、最后生成一份Excel报表。一开始我走了几乎所有人都会走的老路把每一步操作、每一个判断规则、每一种异常处理全塞进System Prompt。结果Prompt本身比业务逻辑还长模型在长上下文里丢三落四一个很简单的“上个月各地区销售额汇总”任务它都能漏掉分组字段或者算错汇总口径。后来我参考了社区里Agent Skills的思路把这类可复用的能力拆成独立的技能模块一段给模型看的“使用说明”加上一段真正干活的代码。改动之后同样一个Agent不换底层模型、不调一个训练参数任务完成率肉眼可见地涨了。这篇文章就围绕agent-skills这个主题说说我踩过的坑、拆过的结构、跑通的完整链路以及一些代码里不会写出来的经验。如果你正在给Agent扩展能力、纠结提示词怎么优化、或者打算从零搭一套可复用的技能库这篇内容应该能帮你少走不少弯路。1. 为什么提示词越写越长反而越不可靠Skills机制想要解决的痛点1.1 提示词膨胀的恶性循环很多人在给Agent加能力时的第一反应和我一样把“怎么做”写清楚最好写成操作手册塞进提示词。刚开始确实有效模型会照着步骤走。但功能一多问题就来了。假设一个Agent要同时处理Excel报表、调用内部API同步数据、定时生成周报摘要。这三个能力如果都写进System PromptPrompt会变成一份混杂着表格处理规则、接口鉴权说明、摘要写作风格的超长文本。模型在每轮对话里都要带着这段几十KB的指令做推理注意力会被分散。最典型的表现是新任务来了之后模型可能还在执行上一个任务的规则或者把A功能的格式要求套到B功能上。另一个隐蔽的问题是上下文的“淹没效应”。大语言模型在读取长文本时靠前和靠后的内容更容易被记住中间段的规则经常被忽略。把十几种能力规则平铺在Prompt里排在中间的那几个技能基本处于“半失忆”状态。你问它会不会它说会真让它做它就按自己的理解自由发挥。我团队里有个同学统计过当System Prompt超过一定长度之后每增加一批规则任务成功率反而下降。规则之间互相干扰、边界不清是提示词方案的天花板不是靠“写得更详细”能解决的。1.2 Skills的两个核心贡献复用能力与按需加载Skills机制解决的正是这个矛盾。它的思路不是把所有说明都塞给模型而是把“能力”本身做成一个个独立的模块每个模块包含两部分一段描述性说明什么时候该用、参数是什么、输出是什么和一段可执行逻辑脚本、命令、代码调用。模型在真实对话中先根据用户的任务描述去匹配技能模块匹配到了才把该模块的说明和调用接口加载进来。这个模式有两个核心贡献。第一是复用。一个技能模块可以被多个Agent引用不同的Agent按需挂载自己的技能集合不用每个Agent都重复写一遍相同流程的Prompt。比如“CSV分组汇总出Excel报表”这个技能我这边有五个Agent都在用但各自的Prompt里都不用出现任何跟Excel操作有关的步骤只需要在技能清单里注册这一个模块。第二是按需加载。模型每轮对话只需要关注当前任务涉及的少量技能说明Prompt长度大幅缩短上下文里的规则干扰也随之减少。这就像人工作一样你不会把公司所有流程手册都背下来再干活而是接到具体任务时再翻对应那本手册。1.3 与常规函数调用的本质差别有读者可能会说这不就是function calling吗确实有相似之处但差别也很关键。对比维度常规函数调用Skills技能库加载方式每次请求都把所有函数定义、参数说明塞给模型按任务描述检索命中后才加载对应技能说明关注点给模型提供“可调用的工具”接口精确给模型提供“可复用的行为包”内含说明、逻辑、依赖实现形态单个函数明确入参出参描述文件加脚本加依赖清单可包含多步骤业务流程维护粒度函数级改动频繁业务能力级一次沉淀反复使用失败处理函数自身返回错误码技能模块内约定结构化结果与状态字段常规函数调用适合“接口稳定、参数明确”的场景比如查天气、算汇率。但Agent的真实任务往往是多步骤的比如“读取文件、清洗数据、聚合计算、导出报表、生成总结”中间还有判断分支。这种流程更适合被封装成一个技能模块让模型只需要理解“这个技能能做什么、什么情况下调用”而不需要理解内部每一步怎么做。搞清楚了这个区别你再看Agent Skills的设计就不再觉得它是函数调用的换皮而是从“给模型一堆工具”升级为“给模型一套可调用的工作方法”。2. Skills的结构本质一份给模型看的路由说明加一段真正干活的代码2.1 一个标准Skill的目录长什么样我最早接触Agent Skills时第一反应是去找官方文档结果发现不同框架的目录命名略有差异但核心组件都差不多。下面是我实际在用的一个最小结构skills/ ├── csv_group_report/ │ ├── SKILL.md │ ├── script.py │ ├── requirements.txt │ └── examples/ │ └── sample_input.jsonSKILL.md给模型看的描述文件。包含技能名称、使用场景、触发词、参数说明、输出约定。模型就是靠这份文件决定“要不要调用这个技能”。script.py真正干活的脚本。接收一个标准化的输入执行处理逻辑返回一个标准化的输出。requirements.txt依赖清单。比如pandas、openpyxl或者常见的HTTP请求库。examples/示例输入输出。这部分容易被忽略但实测对模型理解参数很有帮助。整套设计的思想很明确把“描述”和“实现”解耦。描述文件是面向模型的语言脚本文件是面向机器的语言。模型不需要看懂脚本内部逻辑脚本也不需要理解用户任务的具体语境。2.2 描述文件就是模型的路由头SKILL.md是整个技能库能否被正确调用的关键我把它称为“路由头”。模型在一堆技能里怎么找到当前任务需要的那个靠的就是描述文件里的场景描述和触发词。一个失败的描述长什么样我早期写过name: excel_report description: 生成Excel报表。这种描述等于没写。模型根本不知道这个技能适用于什么场景、输入什么参数、跟其他技能怎么区分。结果就是它要么不调用要么在多个相似技能之间乱选。后来我学到的写法是把用户任务里可能出现的自然语言表达映射进去。比如name: csv_group_report description: | 当用户需要对CSV文件按某一列分组再对其他列做汇总计算并输出为Excel报表时使用。 常见触发场景用户说“按地区汇总销售额”“统计每个品类的订单量”“把CSV转成Excel分组报表”。 输入参数 file_path: 字符串必填CSV文件所在路径。 group_by: 字符串必填用于分组的列名。 agg_column: 字符串必填需要做汇总计算的列名。 agg_func: 字符串可选默认sum支持sum/mean/count/max/min。 output_path: 字符串可选默认为当前目录下的report.xlsx。 返回结果 返回一个JSON字符串包含status字段和output_path字段。这份描述的写法有个核心技巧把用户可能说的话直接写进“常见触发场景”。模型在语义匹配时看到用户说“按地区汇总销售额”就能跟这里的“按地区汇总销售额”匹配上。不要只写抽象的技术术语要多写用户视角的常用表达。2.3 输入输出契约不写清楚模型就会乱传参数描述文件里除了场景说明还要有明确的输入输出契约。很多人忽略这一点结果就是技能偶尔调用成功偶尔传参传得乱七八糟。输入参数要标清楚三点参数名、类型、必填还是可选。如果某个参数允许默认值一定要写默认值是什么。比如agg_func默认是sum那么模型在用户没指定聚合方式时就会用默认值而不是自己瞎猜一个。输出也要约定好。我习惯让所有技能脚本返回统一的JSON结构{ status: success, data: { output_path: /tmp/report.xlsx, rows: 32 } }如果出错就返回{ status: error, error: 文件不存在或格式错误 }统一的结构有一个好处模型的判断成本低。它看到status字段就能决定下一步是继续追问还是总结结果不需要从一大段人话里抽取状态。这个习惯帮我省了大量调试时间。3. 完整实操把“CSV分组聚合出报表”做进技能库3.1 选型判断自己写轻量加载器还是直接接框架搭建Agent技能库第一步不是写代码而是选型。市面上成熟的Agent框架基本都内置了Skills机制的雏形直接接框架的好处是有现成的加载器、解析器和调用链路缺点是你得跟着框架的约定走改起来不够灵活。我个人的判断标准有三个。如果你已经基于某开源Agent框架做开发团队里其他人也在用建议直接复用它内置的技能加载方式减少维护成本。如果你只想做一个独立模块后续可能要移植到不同环境下那就写一个轻量加载器读目录、解析描述文件、把技能说明挂到模型的工具列表里总共也就百来行代码。第三种情况是团队有统一的服务编排层技能要作为独立服务被远程调用那就要把脚本包装成HTTP接口再配置白名单和鉴权。我自己在早期阶段选择了轻量方案只做一个本地加载器把skills/目录下的每个子目录扫一遍读取SKILL.md解析出名称、描述、参数定义然后注册成调用工具。这样不依赖特定框架后面想接任何Agent都能用。3.2 动手实现目录、描述、脚本三件套下面以“CSV分组聚合出报表”这个技能为例走一遍完整实现。这个场景很常见也足够说明问题。第一步建目录。在skills/下新建csv_group_report/放SKILL.md、script.py、requirements.txt、examples/。第二步写描述文件。我参考了上一节说的写法把用户可能的表达、参数说明、输出格式都写清楚。实际生产环境的版本还会加上更多示例但核心就是这个结构。第三步写脚本。脚本要设计成“接收JSON参数返回JSON结果”。下面是去掉注释的简化版import sys import json import pandas as pd def run(payload): result {status: success, data: None} try: file_path payload.get(file_path) group_by payload.get(group_by) agg_column payload.get(agg_column) agg_func payload.get(agg_func, sum) output_path payload.get(output_path, report.xlsx) df pd.read_csv(file_path) if agg_func sum: report df.groupby(group_by)[agg_column].sum().reset_index() elif agg_func mean: report df.groupby(group_by)[agg_column].mean().reset_index() elif agg_func count: report df.groupby(group_by)[agg_column].count().reset_index() else: raise ValueError(funsupported agg_func: {agg_func}) report.to_excel(output_path, indexFalse) result[data] { output_path: output_path, rows: len(report) } except Exception as e: result[status] error result[error] str(e) return json.dumps(result, ensure_asciiFalse) if __name__ __main__: payload json.loads(sys.argv[1]) print(run(payload))脚本里有两个细节值得注意。第一异常处理把所有可能的失败都转成统一的status:error格式避免模型拿到一段混乱的traceback不知道如何应答。第二入口设计为命令行接收一个JSON字符串这样加载器不需要额外依赖就可以调用Python脚本也能方便转成HTTP服务。第四步放一个示例输入输出。examples/sample_input.json{ file_path: sales.csv, group_by: region, agg_column: amount, agg_func: sum }示例的作用是给描述文件作补充。模型有时候理解了抽象描述但看到具体参数组合时会更确定。如果有条件在描述文件里直接粘贴一份示例JSON会更好。3.3 注册加载与首次调用的验证链路技能脚本写完还要过一遍加载和调用验证否则到了真实Agent环境里还是会出问题。我的验证链路分四步。第一步是本地直跑脚本用命令行带参数执行python script.py {file_path:sales.csv,group_by:region,agg_column:amount,agg_func:sum}确认能输出正确的JSON。第二步是确认加载器能正确读取SKILL.md把名称、描述、参数定义都解析出来。这一步经常遇到的问题是YAML格式错误、字段名拼写不一致。第三步是做一个最小的Agent对话测试给它一句“请按地区汇总销售金额并输出Excel”看它会不会主动调用这个技能。第四步是检查返回的JSON能不能被模型正确理解并转成人话总结。第一次跑通的时候最大的惊喜是那句“请按地区汇总销售金额并输出Excel”被模型准确映射到了csv_group_report技能参数也都传对了。这比我之前写一大段Prompt让模型自己决定怎么处理CSV稳定太多。4. 不是所有失败都出在代码上三个典型坑的定位与修复记录4.1 坑一模型死活不调用技能问题出在检索词不匹配技能库搭好之后遇到的第一个问题就是模型就是不调用技能遇到任务直接自己硬编输出一堆根本不存在的结果。一开始我怀疑是脚本有问题后来看日志发现模型压根没触发技能。排查链路是这样的先看请求日志里技能列表有没有正确挂载再对比用户语句和描述文件的语义匹配度。结果发现用户说的是“统计一下各地区的销售额”而描述文件里写的是“按地区分组对金额列做汇总”两边词汇重叠度很低。修复方案不是在代码里做而是在描述文件里加触发词和近义表达。我把“统计”“各地区”“销售额”“生成报表”所有这些用户可能会说的词都铺了进去还加了几个典型场景例句。改完之后再测调用率明显提升。这个坑的根因其实不复杂模型判断是否调用技能靠的是描述文件里的语义相关性。描述写得太“官方”就会跟用户真实口语对不上号。写描述时要把自己当成用户去想用户会怎么说这个需求而不是用开发者的术语去描述。4.2 坑二参数明明是必填模型却传了空值第二个坑更隐蔽。技能描述里指定group_by是必填但用户只说“按地区汇总一下”模型需要自己推断出分组列是region。结果它推断不出来直接把group_by传成脚本一执行就报错。排查后发现问题出在参数设计上我把group_by设计成强依赖用户原话的字段但真实对话里用户通常不会精确说出列名。用户的原始数据表里有一列叫region_name而他只会说“地区”模型缺乏映射依据。修复方案是三步走的。第一步在描述文件里补充“常见列名映射”说明明确告诉模型用户说地区时对应列通常是region或region_name用户说金额时对应列通常是amount、sales或revenue。第二步在脚本里加一层列名模糊匹配逻辑优先精确匹配匹配不到就做子串包含匹配。第三步给所有参数都设计好兜底默认值即使模型漏传任务也不会直接崩掉。这件事让我意识到技能的参数设计不能只从函数接口的角度想还要从对话场景的角度想。参数最好自带“如何从用户表达中推断”的线索而不是假设模型能凭空知道列名。4.3 坑三脚本执行失败结果却返回“任务完成”第三个坑最气人。脚本因为文件路径错误抛了异常但由于异常信息没有被正确处理模型只拿到了一个空输出它居然回复用户“报表已生成”。用户自然不满意因为根本没拿到文件。定位过程比较曲折。先是错觉是文件权限问题检查了目录可写权限发现没问题。后来直接手动执行脚本复现了异常才发现脚本在pd.read_csv时就抛了FileNotFoundError。问题出在调用链路里异常的traceback经过一次转换后变成了空白字符串模型的最终输出里没有捕捉到status字段是error。修复分两个层面。脚本层面所有异常都必须以结构化JSON输出禁止让traceback直接暴露出来Agent层面增加一条调用后检查规则只有status为success时才允许向用户确认任务完成否则必须向用户说明失败原因或重新尝试。这个坑给我的教训很直接技能脚本的返回值不只要“人类可读”更要“模型可判断”。如果模型从输出里无法判断任务到底成没成它就会按自己的惯性回答。统一的状态字段就是在降低模型的判断成本。5. 技能库的验证方法调用率不等于可用持续迭代要盯好的四件事5.1 用三层指标做体检调用率、成功率、任务完成率技能库上线之后我开始设计验证指标。如果只盯一个“调用率”很容易出现模型疯狂调用但结果一塌糊涂的情况。我最终采用了三层指标调用率、成功率、任务完成率。调用率衡量的是模型有多大比例在应该使用技能时确实发起了调用。成功率衡量的是脚本层面执行成功的比例比如文件读取正常、计算没有报错。任务完成率是端到端的从用户提出需求到最终拿到正确结果包括模型正确理解了结果、给了用户合理回复。拿我那个报表技能来举例。早期调用率只有不到六成因为描述文件触发词覆盖不够后来通过补充语义描述调用率上去了。但成功率一度只有七成因为各种数据格式问题导致脚本报错。最后把脚本里的异常分支补全、列名做了模糊匹配成功率才稳定在九成以上。三层指标分开看你才能知道瓶颈在哪个环节。5.2 回归测试脚本与技能版本管理技能库会持续迭代描述文件改一句话、脚本加一个分支都可能影响线上Agent的行为。所以回归测试不能省。我现在的做法是维护一个固定的测试集覆盖每种技能最典型的场景和边界场景。比如对csv_group_report测试集里就包含按地区汇总、按品类计数、输入文件路径错误、聚合方式不合法等十来个场景。每次改动技能后我都会跑一遍完整测试集记录每一条用例的通过情况。版本管理方面我在SKILL.md里增加了一个version字段每次改动都递增版本号。技能文件同步纳入版本控制提交记录里写清楚改动原因。这样一旦线上Agent行为出现回退我能快速定位是哪一次技能版本变更引起的。5.3 从个人技能库走向小组共享库的组织规范技能库做多了之后另一个问题浮现出来不同人写的Skill风格差别很大有的人描述文件写得很详细有的人就一句话。这些技能混杂在一个仓库里模型的行为会变得难以预测。我们后来定了几条组织规范。第一所有描述文件必须包含场景描述、触发词、参数说明、输出格式、示例输入五个区块缺一不可。第二脚本必须统一入口和输出格式禁止一个技能一个样子。第三每个技能必须有负责人改动要经过小组评审避免悄无声息地改坏公共能力。这套规范落地后技能库的维护成本直线下降。新同学上手时不需要猜某个技能是怎么设计的照着模板写就行。团队里不同Agent共享同一套技能库也更容易保持行为一致。最后再分享一点我个人的体会。做agent-skills这段时间我最深的感受是模型的能力边界其实不是靠堆提示词突破的而是靠把流程沉淀成可复用的技能模块来突破的。提示词负责告诉模型“当前任务是什么”技能库负责告诉模型“这个任务可以用什么方法完成”。两者配合Agent的稳定性和可维护性都会上一个台阶。如果你手头也有一个反复调Prompt都搞不定的Agent任务不妨试试把其中一步拆成独立技能对比一下前后效果大概率会有意外收获。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑