☰
Agent技能库实战:从提示词硬顶到稳定技能体系的完整链路
2026/10/7 6:51:25 网站建设 项目流程

做Agent项目有一阵子了,从最早靠“写一个大大的System Prompt”硬撑,到后来被多轮对话折腾得怀疑人生,再到现在完全依赖技能库来组织Agent的行为——这条路走下来最大的感受就是:没有技能体系,Agent项目做到后面必崩。不管你是刚接触agent-skills这个概念,还是已经在自己的项目里塞了几个粗糙的“伪技能”,这篇内容都值得你花十分钟读完。我会把技能设计、拆解、落地、测试、迭代这条链路完整讲一遍,包括踩过的坑和现在正在用的模板。

1. 先搞清楚:Agent技能到底是什么,为什么不能靠提示词硬顶

1.1 技能不是工具,不是提示词,而是一套完整的能力封装

很多人在Agent开发里会把“技能”和“工具”混为一谈,这是第一个认知误区。工具是Agent可以调用的外部函数,比如get_weather(city)、send_email(to, content),它本身没有“判断能力”,只是机械化执行的接口。而技能是更高一层的抽象——技能定义了“在什么场景下、按照什么思路、用什么工具、以什么格式输出”这一整套可复用的行为模板。

举个例子。做客服Agent,你给模型暴露了一个refund_order(order_id)工具,这不算技能。真正的问题是:模型什么时候该调退款接口?退款前需不需要检查订单状态?需要校验什么条件?超时怎么办?拒绝退款的话怎么跟用户解释?这些决策逻辑、约束规则、边界处理,才是技能的核心。技能打包的是“决策脚本+工具调用+话术规范”,工具只是它手里的螺丝刀。

用生活化的类比来说:工具是超市货架上的原材料,技能是一道菜的完整菜谱。光有面粉、鸡蛋、黄油,模型能做一百种不同的东西,但每次发挥都不稳定;有了“戚风蛋糕技能”“曲奇饼干技能”,它就知道该预热烤箱多少度、搅拌到什么状态、烤多久,出品就稳定了。

1.2 技能体系要解决的四个真实痛点

为什么说靠“一个大System Prompt + 一堆工具定义”的方案必然出问题?因为在实际项目中,这套方案会撞上四堵墙:

  • 上下文失控:把大量规则塞进System Prompt,意味着每次对话都要重复消耗token,规则越多,留给实际对话和推理的空间越小。更麻烦的是,长上下文中模型对顺序靠后的规则关注度会显著下降,你辛辛苦苦写的规则根本进不了注意力窗口的核心区域。
  • 行为不可预期:同一类任务,模型这次这么做,下次那么做。没有固定的行为模板约束,输出格式、处理流程、兜底逻辑全凭模型当时的“心情”,线上问题排查起来极其痛苦。
  • 无法测试回归:没有技能边界,就没有明确的测试单元。你想验证“退款拒绝场景是否稳定”“多轮追问是否跑偏”,都不知道从哪里下手。
  • 复用几乎为零:每个Agent项目从零开始堆提示词,上一个项目的经验完全没法迁移。换个场景全重来,团队积累沉淀不下来。

技能库的存在,就是为了把这些问题从“运行时”前置到“设计时”。规则写死在技能文件里,Agent只在特定的技能触发点加载对应的指令块,上下文干净了,行为稳定了,测试有边界了,项目之间的复用也成了可能。

1.3 技能与工作流、子Agent的边界

还有必要厘清另外两个容易混淆的概念:工作流和子Agent。

工作流(Workflow)是技能的“编排层”,它描述多个技能以什么顺序、在什么条件下被调用。技能本身是单点的能力单元,工作流是把这些单元串起来的剧本。一个技能可以被多个工作流引用,一个好的技能设计一定跟具体工作流解耦。

子Agent则是一个独立的、持久的Agent实例,有自己的角色设定和上下文窗口。子Agent内部也可以挂载技能。区别在于:技能是“挂在Agent身上的能力模块”,而子Agent是“独立的执行主体”。设计时优先考虑技能,只有当某个任务需要独立的对话历史、独立的记忆状态、或者需要不同的人格设定时,才考虑拆子Agent。

2. 技能库规划:怎么把模糊需求拆成一张技能清单

2.1 从业务场景反推技能边界

我见过很多人一上来就拍脑袋列技能,比如“做个数据分析技能”“写个客服技能”,这种粒度完全没法用。正确的做法是从业务场景反推,先把高频的、重复出现的任务类型列出来,再逐一判断边界。

拿一个企业级知识库问答Agent举例。表面需求是“回答员工关于公司制度的问题”,但这个需求背后至少能拆出:

  • 制度查询技能:回答考勤、报销、差旅等标准化制度问题,答案需引用制度原文章节。
  • 流程引导技能:当用户问“怎么请假”“怎么报销”时,输出分步骤操作指引,而不是罗列制度条文。
  • 表单填写辅助技能:用户问到具体表单字段含义时,结合表单结构逐字段解释。
  • 异常上报技能:当用户在制度里找不到答案,或者发现制度矛盾时,引导用户提交人工工单。
  • 多轮追问技能:用户描述不满时,通过结构化追问缩小信息范围,而不是一次性给一堆无关内容。

这样拆下来,每个技能的边界都是清晰的任务类型,而不是宽泛的能力域。判断技能边界是否合理的三个标准:一,技能描述能否用一句话说清“在什么情况下做什么事”;二,技能内部是否有固定的处理流程;三,技能是否具备可验证的输入输出。三条都满足,才值得做成独立技能。

2.2 技能粒度:拆大不拆小,拆重不拆轻

技能粒度是个典型的trade-off。拆得太粗,比如“全能助手技能”,等于没拆,跟拿System Prompt硬顶没有区别;拆得太细,比如把“获取用户名”都做成技能,库会爆炸,Agent在触发路由时也会混乱。

我自己的标准是“任务粒度优先”:一个技能对应一个用户可见的任务类型,而不是多个同类任务合成一个。比如“合同审核技能”可以是一个技能,但技能内部通过子步骤处理“金额校验”“条款缺失检测”“风险等级判定”等环节;而“发送邮件”“创建日历事件”这种单步操作,一般不单独成技能,直接作为工具挂在Agent的基础工具集里就行。

还要遵循“拆重不拆轻”原则:一个任务出现频率越高、出错代价越大、处理逻辑越复杂,越值得独立成技能;偶尔出现一次、处理逻辑简单的任务,可以在工作流里内联处理,别为了凑数硬造技能。

2.3 命名规范与技能注册表

技能命名不是小事,它直接决定Agent的路由准确率。我的建议是采用“动词短语+场景限定”的统一格式,动词在前,名词在后,必要时加限定词。比如:

  • query_leave_policy(查询请假制度)
  • submit_expense_workflow(引导差旅报销流程)
  • validate_reimbursement_form(校验报销单填写)
  • escalate_to_human_agent(升级人工客服)

这种命名有两个好处:第一,语义清晰,路由模型一看就懂;第二,动词开头让相似技能之间的区分度更高,避免路由混淆。

同时给每个技能维护一个注册表(Registry),记录技能ID、名称、版本号、一句话描述、触发场景关键词、依赖工具、负责人、最近更新时间。注册表是技能库的“索引”,既方便人查阅,也是后面做路由路由优化和效果评估的基础数据。

3. 核心实操:一个技能文件从0到1的完整写法

3.1 技能文件的标准结构

我现在用的技能模板是JSON格式,原因很简单:结构化字段方便程序读取,instructions字段里面的自然语言也是人能直接读懂的。一个完整的技能文件长这样:

{ "skill_id": "query_leave_policy", "name": "查询请假制度", "version": "1.3.0", "description": "在用户询问请假规则、请假天数、请假流程时触发。回答需引用公司《考勤管理制度》具体条款。", "trigger_keywords": ["请假", "年假", "病假", "事假", "调休"], "input_schema": { "type": "object", "properties": { "leave_type": { "type": "string", "enum": ["annual", "sick", "personal", "compensatory"], "description": "请假类型" }, "question_focus": { "type": "string", "description": "用户关心的具体角度,如天数上限、审批流程、所需材料" } }, "required": ["leave_type"] }, "output_schema": { "type": "object", "properties": { "answer": { "type": "string", "description": "面向用户的完整回答,必须标注引用条款编号" }, "source_refs": { "type": "array", "items": {"type": "string"}, "description": "引用的制度条款编号列表" }, "needs_human": { "type": "boolean", "description": "是否建议转人工,制度未覆盖或信息不足时为true" } }, "required": ["answer", "source_refs", "needs_human"] }, "tools": ["search_policy_doc", "get_employee_profile"], "instructions": "...此处放详细提示词,见下节...", "error_strategies": { "no_result_found": "提示用户换个关键词查询,并询问是否需要转人工", "doc_access_failed": "先降级使用内置制度摘要,再提示用户稍后重试", "ambiguous_leave_type": "向用户确认具体请假类型再回答,不要自行假设" } }

这里面的每一项都有讲究。description是Agent路由判断是否触发该技能的关键依据,一定要写清楚“什么场景下用、解决什么问题”,千万不能写成产品宣传稿,比如“本技能为公司员工提供全面、高效的请假制度查询服务,助力员工清晰了解公司政策”——这玩意儿路由模型看到就头晕,根本不知道什么时候该触发。

input_schema和output_schema是结构化约束的骨架,作用有两个:一是告诉模型“进入这个技能后应该收集什么信息”,二是强制输出格式稳定,方便下游逻辑继续处理。

3.2 技能内部提示词(instructions)的写法:这套模板值得抄

instructions字段是技能的灵魂,也是最见功力的地方。我不建议写一大段杂乱的指令,而是按固定章节组织,让模型知道每一部分在说什么。模板如下:

# 角色定位 你是公司考勤制度查询专员。你的职责是基于《考勤管理制度》回答员工关于请假规则的询问,不回答与请假无关的问题。 # 知识来源 - 优先使用search_policy_doc工具检索制度原文,引用时标注条款编号。 - 不得根据个人经验推测制度内容,不得编造条款。 # 执行步骤 1. 解析用户意图:从用户输入中提取请假类型(年假/病假/事假/调休)。 2. 如果请假类型不明确,先向用户确认,不要自作主张。 3. 调用search_policy_doc检索对应条款。 4. 根据检索结果组织回答,回答格式固定为:「根据《考勤管理制度》第X条,你询问的Y情况是Z。具体说明...」 5. 如果检索无结果或制度未覆盖,将needs_human设为true,并引导用户提交工单。 # 输出规范 - 回答必须包含条款编号引用。 - 以下情况必须转人工:制度明确写“以HR解释为准”的、用户遭遇特殊情况(如长期病假)、用户两次以上追问未获满意答案。 - 禁止直接输出“我不知道”,必须给出替代方案。

这个模板的妙处在于:先把模型的身份钉死(角色定位),再把它的信息来源锁死(知识来源),然后用“执行步骤”限制推理路径,最后用“输出规范”兜住边界。四个章节各有分工,覆盖了“我是谁、我知道什么、我怎么做、我如何停”的完整闭环。

写技能提示词时有几个细节容易踩坑。第一个坑:允许模型自由发挥的内容给太多。“你可以根据实际情况灵活回答”这种说法越少越好,灵活性应该靠分情况的条件分支给出,而不是靠模型的临场判断。第二个坑:输出规范只写了“应该”,没写“禁止”。大模型对“不能做什么”的服从度往往高于“要做什么”,每条关键边界都要显式写出禁止项。第三个坑:转人工的条件不够具体。“觉得不确定就转人工”等于没说,要写清楚“两次追问未获满意答案”“用户明确表达不满”“制度无覆盖”这类可观测的条件。

3.3 触发条件与输入整理的工程细节

技能文件里的trigger_keywords只是辅助信号,真正的主力是description+ 用户当前意图的语义匹配。在工程实现上,常见做法是给每个技能计算一个“触发评分”,评分由三部分构成:

  • 描述与当前对话语义相似度(用向量嵌入算,权重最高)
  • 关键词命中数量(权重中等)
  • 上下文连续性(比如用户上一轮就在问请假,那么本轮优先触发请假相关技能,权重低但能兜底)

这个评分逻辑要放在Agent的主循环里,在每次收到用户消息后执行。另外注意:技能触发锁定在单轮还是多轮,需要显式设计。我的经验是,一旦某个技能触发,至少在后续两轮内保持“技能状态激活”,防止用户追问一句“那如果我想请三天呢”就直接跳回通用对话,导致上下文断裂。

输入整理也值得提一句。input_schema定义的字段必须由调度器或模型在触发技能时从对话中抽取,这个抽取过程本身不要交给技能内部处理,而是在技能触发前完成。触发后,技能专注于“基于给定输入进行处理”,不要指望它自己从客气的闲聊里提取结构化参数。

4. 技能库落地:目录组织、版本管理、测试与迭代

4.1 技能库目录结构与注册表设计

技能库的目录组织直接影响团队协作和维护成本。我个人首选这种按领域分层的结构:

skills/ ├── registry.json # 技能注册表,全库索引 ├── hr/ │ ├── query_leave_policy/ # 每个技能一个独立目录 │ │ ├── SKILL.json # 技能定义主体 │ │ ├── test_cases.json # 测试用例集 │ │ └── CHANGELOG.md # 版本变更记录 │ ├── submit_expense_workflow/ │ └── validate_reimbursement_form/ ├── finance/ │ ├── invoice_query/ │ └── budget_forecast/ └── common/ ├── escalate_to_human_agent/ └── clarify_user_intent/

按领域分目录,按技能分文件夹,每个技能自带测试用例和变更日志。registry.json是全局索引,记录所有技能的元信息,运行时服务启动时加载它来构建技能路由表。

技能的版本号我遵循主.次.修订的格式:修订号变化表示修正措辞、修补边界条件;次版本号变化表示行为逻辑有调整、输出规范有变化;主版本号变化表示技能触发场景或核心流程重设计。版本变更记录必须写清“改了什么、为什么改、影响哪些场景”,否则技能库迭代两三轮之后就会变成一团糊涂账。

4.2 测试集设计:黄金数据、场景覆盖、回归机制

测试是技能库区别于普通提示词工程的关键。没有测试,你不知道一次改动是改好了还是改坏了。我目前实践下来比较有效的测试体系分三层:

  • 黄金用例集(Golden Set):每个技能准备10-30个标注好的输入-期望输出对。输入是真实的用户说法,输出是专家人工标注的理想回答。跑回归时,用技能跑一遍这些用例,比对关键字段(答案是否包含条款引用、是否触发转人工、输出是否合规)。
  • 场景维度覆盖:黄金用例要覆盖“主流程”、“边界情况”、“异常情况”三个维度。主流程是正常询问;边界情况是模糊请假类型、制度未覆盖、用户一次问多个问题;异常情况是工具检索失败、用户情绪化表达、连续追问。光有主流程用例不行,技能出问题基本都在边界和异常场景。
  • 回归机制:任何技能修改,触发全库回归测试。跑完后关注两个指标:一是本技能的黄金用例通过率,二是非目标技能的用例通过率有没有下降(防“技能间串味”)。

自动化跑测试的时候,我习惯把LLM输出抓进JSONL日志,比对时不看生成文本的完全相等,而是看结构化字段是否符合断言,外加用一个大模型判官模型对回答质量做打分。这样既有客观指标,又能捕获逻辑层面的劣化。

4.3 全库联动:技能间冲突检测与编排

技能多了之后,最要命的不是单个技能写不好,而是技能之间互相咬合出问题。两个常见场景:

一是触发冲突:用户说“我想请假”,但“请假制度查询”和“请假流程引导”两个技能都有可能触发。解决办法除了细化各自描述外,还有一个实用技巧:在技能描述里显式声明“不做什么”。比如流程引导技能写“不回答具体天数上限,天数问题交给制度查询”,描述里的互斥声明能让路由准确率显著提升。

二是编排时序:多个技能应该按什么顺序串行执行,这需要工作流定义。工作流文件里以节点方式声明技能调用顺序、条件分支和结果传递。比如:

{ "workflow_id": "leave_request_full_process", "steps": [ {"step": 1, "skill": "query_leave_policy", "when": "user asks about policy"}, {"step": 2, "skill": "validate_reimbursement_form", "when": "user asks about materials"}, {"step": 3, "skill": "escalate_to_human_agent", "when": "policy not found or user unhappy"} ] }

技能是积木,工作流是图纸。把技能设计得足够原子、可组合,工作流层才能灵活编排。如果技能之间隐式耦合(比如技能A内部偷偷调用了技能B的逻辑),编排层就完全失控了。

5. 避坑指南:技能库开发中我踩过的那些坑

5.1 技能描述写成“能力简介”导致路由失灵

最早期我写技能描述特别“正规”,每个都是“本技能为企业高效地提供xx服务,助力xx目标”,结果实测触发准确率惨不忍睹。后来改成直接描述“该技能在什么时候被触发、做什么事情、不做什么事情”,触发准确率立竿见影地上来了。核心思路是:描述是给路由模型看的说明书,不是给领导看的汇报材料。

较好的描述对比:

  • 失败:本技能为员工提供全面、便捷的请假制度查询服务,帮助员工快速了解公司考勤政策,提升人事服务体验。
  • 成功:当用户问到请假规则、请假天数上限、请假审批流程时触发此技能。它检索制度原文并回答,引用条款编号。不回答请假之外的问题,不处理请假审批单的实际提交。

5.2 技能内部指令过于宽松,回答千奇百怪

另一个让我痛苦了很久的问题是:技能写好了,但执行质量还是飘。后来细查发现,问题出在instructions里大量出现“根据实际情况灵活处理”“如果合适的话可以适当补充”这类模糊指令。模型每次都会“灵活”出不同的花样来。

解决办法就是前面说的:把灵活性拆成显式条件分支。比如“如果用户请假类型不明确,使用预设话术追问,选项为年假/病假/事假/调休”远比“必要时可以询问用户”稳定得多。指令越像决策树,输出越像流水线;指令越像散文,输出越像散文。

5.3 工具调用异常未捕获,技能直接崩

技能依赖的工具不可能100%可用,搜索结果为空、外部API超时、权限校验失败都是家常便饭。最开始我只在工具调用成功时定义了行为,失败时模型要么硬编答案,要么直接死机。

现在的做法是在技能文件里显式定义error_strategies,为关键失败场景逐个写明降级策略。比如制度检索失败时,降级到内置摘要数据源,同时告诉用户“全文检索暂时不可用,当前回答基于摘要,如需细节点这里转人工”。这个设计逻辑有点像代码里的try-catch,但比异常捕获更高一层——它连降级后的话术都替你写好了。

5.4 上下文污染问题:技能结束后未清理技能状态

技能执行完成并不等于事件结束。如果不显式清理“当前处于哪个技能上下文”的状态,两个技能执行后状态会混在一起。最典型的翻车现场:用户先问休假政策,技能结束后追问“那我如果因为做手术要请两周呢”,触发了一个全新的medical_leave_policy技能,但上一技能的历史记录还滞留在上下文里,模型开始用休假制度回答医疗期问题。

解决思路有两条:一是技能框架层在每次技能开始前做一次上下文裁剪,只保留对当前技能有用的对话摘要;二是在技能切换时做一个显式的“意图重定位”,把上一技能的历史压缩成一句摘要,而不是原样堆在上下文里。

5.5 版本漂移:技能没变,模型升级后行为变了

这个坑最隐蔽。技能文件一字未改,但底层模型从A版本升级到B版本后,同一批测试用例的通过率掉了15%。原因很简单:技能指令是在特定模型能力前提下调出来的,模型换了,隐含的行为模式也跟着变。

应对策略有两个层面:第一,任何底层模型升级前必须跑全量技能回归测试,不允许跳过;第二,写技能指令时尽量降低对模型“悟性”的依赖,需要几步就说几步,把推理路径显式化,这样模型换血时的行为偏移会小很多。技能文件写得越“手把手”,模型升级带来的风险越低。

6. 几个进阶思考:多模态技能、技能市场与成长型技能库

技能体系做到后期,可以往三个方向延伸。

第一个方向是多模态技能。现在很多Agent已经不满足于纯文本交互,视觉技能开始冒头,比如截图理解、图片生成、图表解读。这类技能的难度在于input_schema和output_schema要扩展出图像字段,而且模型对视觉输入的推理路径要比纯文本更不确定,测试集需要额外准备视觉样本。

第二个方向是技能市场的思路。团队内部积累了一套技能库,完全可以沉淀出通用技能包,在多个项目之间复用;更大的想象空间是跨组织的技能交易生态,好技能像插件一样被更多人安装使用。当然这要求在技能设计上有更强的规范性和文档文化,否则技能质量参差会让整个市场体验崩掉。

第三个方向是成长型技能库。传统技能是静态的,而现在开始有团队在做自演化技能——根据线上失败案例自动生成补充规则,由人工审核后合入技能文件。这个方向现在还比较早期,但我觉得是重要趋势,因为技能库想要持续有价值,就必须形成“跑线上→发现问题→改技能→回归测试→再上线”的闭环,把这个闭环从手工操作逐步半自动化,是值得投入的方向。

我个人在实际操作中最大的体会是:Agent技能库的搭建,本质上是把“跟模型对话”变成“给模型编手册”的过程。每一次把模糊需求变成结构化技能文件,都是在给Agent的行为确定性加一分。这活儿枯燥,但扎实。如果你正在做Agent项目,无论规模大小,尽早建立技能库意识、哪怕是先写两三个核心技能,后面你会发现整体系统的稳定性和迭代效率都会有质的提升。

最后分享一个小技巧:给每个技能写一条“信号钩子”,也就是一句话讲清“用户在什么语境下最可能触发你”。这一句话记在description最前面。我试过把钩子写在末尾,路由准确率明显下降。别让你的技能在关键时刻找不到主场,这也算是我用一次次踩坑换来的经验了。

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

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

立即咨询