Agent技能抽象层设计与实战:从工具调用到可维护的技能体系 📅 发布时间:2026/9/16 7:40:01 👁 浏览次数: 去年我带团队给一个企业内部的知识助手做了一次大版本升级。升级之前那个助手的对话体验实在算不上好经常是用户问一个稍微绕一点的问题它就生硬地回一句“这个问题我暂时无法回答”或者干脆从知识库里抓出一堆毫不相干的文档。当时我们复盘了很久结论其实很扎心模型本身没问题RAG 链路也通但助手“会什么、能调哪些工具、按什么流程办事”这件事完全是散的。后来我们花了几周时间把所有能力重新梳理了一遍推倒重来做了一套结构化的技能体系效果立刻不一样了。这次重构的核心就是围绕怎么设计、实现和维护 Agent 的“技能”来展开的。“agent-skills”这件事我从项目实战的角度拆开聊聊。这套东西说白了就是给 AI Agent 建立一套清晰、可复用、可编排的“能力说明书”。对刚接触 Agent 开发的朋友来说它解决的是“我的 Agent 到底会干什么”的问题对已经在做复杂 Agent 的团队来说它解决的是“多技能之间怎么配合、怎么不打架、怎么容易维护”的问题。哪怕你现在只是在玩个人助手类的项目这套思路也能帮你少踩很多坑。所以这篇文章适合正在做 Agent 落地的开发者、想优化人机协作流程的产品经理以及所有觉得“明明加了工具Agent 还是不够聪明”的人。1. 先把“技能”这件事想清楚它不只是一个函数很多项目一上来就写一堆 Python 函数然后把函数名塞给模型让模型自己决定“要不要用”。早期我们也是这么干的后来发现这条路走不远。原因很简单函数是程序员视角技能是任务视角。两者之间的鸿沟就是 Agent 翻车的高发区。1.1 技能不是工具不是工作流也不是插件聊“agent-skills”之前得先把几个容易混的概念掰开。工具Tool是最底层的一次性调用比如“执行一条 SQL”“发送一封邮件”技能Skill则是围绕一个完整任务目标封装出来的能力单元它内部可以编排多个工具调用也可以包含预设的判断逻辑。比如“查询订单物流”这个技能里面可能包括识别订单号、判断是售前还是售后、选择不同物流接口、必要时回退到人工客服。这些都是工具层面的散点但在技能层面它们是一整个流程。工作流Workflow跟技能的区别又在于灵活性。工作流往往是固定好的流程模板A 完了走 BB 完了走 C技能则允许 Agent 在运行中做决策可以根据上下文决定执行哪条子路径。至于插件Plugin更多是“第三方能力的封装形式”而技能更强调“围绕任务的组织方式”。所以你会发现技能确实可以调工具、也可以组合成工作流、也可以借插件的壳发布出去但它的本质是一层“任务级抽象”这是理解 agent-skills 的第一把钥匙。1.2 为什么技能抽象层是 Agent 项目的分水岭我见过很多团队的 Agent 项目刚开始 demo 跑得飞快因为场景就一两个。但一旦把场景扩到十来个问题就来了模型不知道该优先选哪个能力、工具描述互相冲突、新增一个功能要改一大堆配置。这些问题本质上都是缺少抽象层导致的。技能抽象层相当于给 Agent 的“能力地图”做了一层目录索引。模型看到的不再是几百个散落的函数名而是一份经过筛选的技能清单每个技能有清晰的名称、描述、适用条件和输入输出格式。这样模型做决策的时候复杂度从“从300个选项里挑”降到了“从5个相关技能里挑”意图识别准确率能提升一个量级。我自己实测过的经验是没有技能层的时候多工具场景下的调用准确率大概在 75% 左右加了技能抽取和路由之后同样的测试集能到 92% 以上。1.3 技能矩阵给 Agent 的“能力地图”做规划前面说了这么多概念真正落地时第一步要做的是“技能矩阵”。别一上来就堆技能先把你的 Agent 需要响应的所有任务场景列出来然后做一个矩阵横轴是业务场景比如客服场景、数据查询场景、审批场景纵轴是能力类别比如信息获取、事务处理、生成创作、分析决策。每个交叉格子里填上“需要什么技能”没有需求的就空着。这个矩阵的价值在于它逼着你想清楚每个技能存在的意义。我们当时画完矩阵后发现很多听起来很重要的工作流其实是可以合并的也有一些场景往里填的时候才发现“这还缺一个技能”。矩阵本身不是交付物但它决定了你后续技能设计的边界和优先级能省掉后面大量的反复。2. 技能描述是 Agent 的“岗位说明书”写不好全盘皆输技能描述到底有多重要我举个例子你就明白了。同样一个“查天气”的能力如果你在技能描述里只写“查询天气”模型可能在任何闲聊场景下都想调它试试但如果你写的是“根据用户提供的城市名和日期查询天气预报仅当用户明确表达天气查询需求时调用日常闲聊场景请勿调用”模型几乎不会误触发。技能描述不是给人类注释看的它是给模型看的决策依据。2.1 技能描述的五要素模板我现在的习惯是每个技能描述都套一个固定模板尽量做到结构化而不是写一整段小作文。五要素分别是技能名称、技能功能概述、适合调用的触发条件、禁止调用的边界条件、关键参数说明。技能名称最好用“动词宾语场景限定”的形式比如“查询订单物流-电商后台”这样名称本身就携带了大量信息。功能概述控制在三五十字以内讲清楚这个技能能完成什么任务产生什么输出。触发条件要写得具体最好能列出典型句式比如“当用户提到‘我的快递到哪了’““我想查一下物流”等。禁止调用的边界条件同样重要我遇到过不少 Agent 被用户拐跑的场景就是因为技能描述里少了“这一句不要做”。参数说明要写清楚每个参数的含义、格式和获取方式避免模型调用时出现“把日期格式传成中文”这种低级错误。2.2 描述里要写的隐性要求上下文、权限、前置校验很多技能描述写不好的原因是只写了“怎么做”没写“什么时候必须不能做”。这背后其实是一套对模型行为做约束的经验我总结了三个必须写进技能描述的隐性要求。第一是上下文依赖。有些技能必须结合前面的对话才能执行比如“给用户发送订单确认短信”需要依赖前面已经识别出来的用户身份。如果上下文不完整就贸然调技能会形成垃圾数据。描述里要写清楚“需要前置上下文包含用户手机号和订单号缺少时先向用户询问”。第二是权限边界。做企业级应用时技能有没有权限做某个操作必须描述清楚。比如财务类的“生成付款单”技能要写明“仅限财务角色用户调用非财务人员调用时提示无权限”。第三是前置校验。比如“外呼提醒”这个技能必须写清楚“每天晚9点后不允许调用”“周六日不要打扰用户”。模型本身不懂这些业务规则你不写它就不可能做对。2.3 描述模板实战同一个功能两个写法给人什么感觉空讲原则没什么用直接对比一下写法差异。假设我们要给 Agent 做一个“周报生成”技能。写法一是大多数新手会写的”生成周报。根据用户的工作日志生成一份周报。“模型拿到这个描述的时候能理解个大概但触发条件完全不明确用户是提了“周报”才能调还是只要聊到工作就生成一篇周报的时间范围怎么定这些都没说。写法二是我建议的写法”生成周报-自动汇总工作日志当用户要求生成周报、总结本周工作、汇报进度时调用。根据用户设定的时间范围默认当周从工作日志系统中聚合任务完成情况然后按‘本周重点、数据进展、风险问题、下周计划’四个模块生成结构化文本。若用户未明确时间范围默认取最近7天。若工作日志为空直接提示用户暂无日志可汇总不建议编造内容。“对比这两个描述高下立判。写法二给了模型所有需要的决策信息它执行起来几乎不需要再猜。这就是我在所有项目里反复要求团队做到的标准技能描述要写到“模型不需要临场发挥也能正确执行”的程度。3. 实操从零搭建一个带技能库的 Agent理论说了这么多现在进入正题直接过一遍我搭建带技能库 Agent 的完整流程。这里我以 Python 生态为例因为它是目前做 Agent 项目最成熟的语言底层思路其实都是通用的。3.1 需求场景设定与技能拆分我不太喜欢在没有具体场景的情况下谈技术方案先说清楚我们要做的东西。这次要搭的是一个个人日程助理的 MVP它需要支持以下几个任务添加日程、查询日程、调整日程时间、按日期范围汇总日程。看上去很简单对不对但如果没有技能层直接写函数模型很容易把“添加”和“调整”搞混因为两者的参数有重叠。所以我拆成了四个技能创建日程事件、查询日程列表、更新日程时间、生成日程汇总。每个技能对应一个独立的 Python 类输入输出都走 JSON 格式。从外面看它就是一个标准接口内部封装的可以是本地存储、日历 API 或者其他外部系统这都不重要。3.2 技能注册中心让 Agent 知道“自己会什么”技能拆完之后就需要一个注册中心把它们管理起来。注册中心的作用有两个一是维护一份“技能清单”供模型查询二是在运行期把模型选中的技能名路由到具体的执行函数。我用一个简单的 SkillRegistry 类来实现。每个技能注册时需要提供一个元信息字典包括名称、描述、参数 schema 和 handler 函数。参数 schema 我用的是 JSON Schema 格式这几乎是当前主流 Agent 框架的标准做法模型可以直接根据 schema 生成符合格式的参数。class SkillRegistry: def __init__(self): self._skills {} def register(self, skill): self._skills[skill.name] { description: skill.description, parameters: skill.parameters_schema, handler: skill.execute } def list_skills(self): return [ {name: name, description: info[description], parameters: info[parameters]} for name, info in self._skills.items() ] def run(self, name, **kwargs): skill self._skills.get(name) if not skill: return {success: False, message: fSkill {name} not found} try: result skill[handler](**kwargs) return {success: True, result: result} except Exception as e: return {success: False, message: str(e)}这个类本身不复杂但它定下了整个技能体系的运行骨架注册、发现、执行。后续不管加多少新技能都不需要改这层代码只需要新增一个技能类然后 register 进去就行。3.3 技能执行器从模型意图到真实动作的桥有了注册中心还要一个“执行器”负责把模型理解到的用户意图转成一次具体的技能调用。这里我用的是最直接的办法先把用户输入交给 LLM要求它从技能清单中选择最合适的一个技能并提取参数然后调用注册中心的 run 方法执行最后把执行结果再交给 LLM生成面向用户的自然语言回复。这段伪代码是这个流程的关键部分可以看到整个链路并不复杂核心在于每一步都只做自己该做的事def handle_user_input(user_input): # Step 1: 从注册中心获取技能清单 skills registry.list_skills() # Step 2: 让 LLM 选择技能并抽取参数 decision llm.choose_skill( user_inputuser_input, skill_listskills, instructionSelect the best matching skill from the list. ) if not decision or skill_name not in decision: return 抱歉我暂时没有找到适合处理这个请求的技能。 # Step 3: 执行技能 result registry.run(decision[skill_name], **decision.get(arguments, {})) # Step 4: 将结果转成自然语言 if result[success]: return llm.format_response(result[result]) else: return 操作失败了: result[message]这段逻辑之所以这样设计是因为它把“决策”和“执行”彻底分开了。决策层交给 LLM享受它的语义理解能力执行层交给确定性代码保证动作一旦确定下来就不会变成“看似合理但错得离谱”的幻觉输出。这种混合架构是我做了这么多 Agent 项目后最推荐的组合。3.4 一个完整执行实例演示假设用户输入的是“帮我查一下明天下午有没有空”。流程走一遍你就知道技能层做了什么贡献。LLM 拿到技能清单后会看到“查询日程列表”这个技能描述明确写了“当用户询问某时间段的日程安排、空闲情况时调用。参数 start_time 与 end_time 为 ISO 格式的日期时间字符串若未明确时间段默认取查询当天。”于是它提取 start_time 为明天下午 2 点、end_time 为明天下午 6 点具体而言是 2025-01-07T14:00:00 和 2025-01-07T18:00:00调用 registry.run(query_schedule, start_time..., end_time...)。执行器拿到参数后直接调用 handler从日历存储里查出这段时间已有的日程列表返回 JSON。最后 LLM 把这个 JSON 转成自然语言“你明天下午 2 点到 4 点有一个项目评审会其他时间目前是空闲的。”整个过程里“模型乱猜参数”“参数格式不统一”“执行出错没有反馈”这些常见问题都在技能层的约束和代码的确定性中绕开了。这就是为什么我说核心环节不是写那一行 invoke而是前面设计技能描述和注册机制的过程。4. 常见问题与排查技巧实录技能越多越要小心技能体系一旦超过十个就会出现各种幺蛾子。这里我把实操中最常遇到的几个问题整理一下每个都是踩过坑之后总结出来的。4.1 问题排查速查表现象根本原因排查步骤解决方案示例模型频繁调用错误的技能技能描述边界不清触发条件太宽泛逐个查看技能描述看它们之间是否会有重叠场景给每个技能描述增加“禁止调用”边界甚至加示例一个请求触发了多个技能技能粒度太粗一个技能里塞了太多子任务检查技能矩阵看是否需要对技能做进一步拆分把“日程管理”拆成“创建”“查询”“更新”“汇总”四个细分技能技能执行失败返回信息用户看不懂执行器缺少错误格式化直接把异常抛给了 LLM检查 registry.run 中对 exception 的处理定义错误码和标准错误消息模板执行失败时返回结构化错误信息新增技能后旧场景效果下降技能清单变长模型决策难度上升检查注册中心是否做了技能分组或预筛选增加技能分组标签先按场景粗筛一次再让 LLM 做精细选择4.2 技能“误触发”问题与描述边界调整技巧误触发是 Agent 技能体系里最常见的故障几乎每加一个新技能都会有一批老场景受影响。一次我们加入了“查询股票行情”技能后用户随口说一句“今天心情像是买了股票”模型都要去调一次行情接口。这类问题的根因大多不是模型不行而是技能描述里“适用场景”写得太宽。调整的时候有个实用技巧把“正面例子”和“反面例子”都写进描述里。比如在“查询股票行情”的描述里除了写“当用户询问股价、涨跌幅、大盘行情时调用”还要补一句“如果用户只是通过股票进行比喻并未明确要求查询行情数据则不要调用”。加了这句话之后误触发率立刻下降了大半。另一个技巧是给技能描述标注“优先级”。对于容易出现歧义的技能在描述里写明它属于第二优先级比如“当用户意图同时匹配本技能与行程查询技能时优先执行行程查询。”这相当于在模型决策空间里加了一点人工引导效果立竿见影。4.3 上下文丢失与多轮对话中的技能协作问题多轮对话里Agent 经常出现一种尴尬情况第一轮用户说“帮我订酒店”Agent 设置了上下文第二轮用户说“要靠地铁站的”Agent 反而不知道这跟订酒店技能有什么关系直接把第二轮独立处理了。这是上下文丢失导致的。要解决这个问题不能只在技能描述层面做文章还得在调用层留一个“上下文持久化”的口子。我现在的做法是在注册中心里加一个 session 对象每个技能调用时把必要的上下文信息存进去在下一轮用户输入时把上下文摘要和当前输入一起交给 LLM。class SessionMemory: def __init__(self): self.history [] def add_turn(self, user_input, skill_name, result): self.history.append({ user: user_input, skill: skill_name, result: result }) def get_context_summary(self): return self.history[-5:] # 只取最近五轮这个方法简单但很有效。它不依赖模型靠记忆能力去关联上下文而是主动把关键信息采下来再作为决策时的输入。多轮协作场景下Agent 能不能做好往往就看你有没有做这一层。5. 技能不是写一次就完的版本管理与持续演进最后说一个很容易被忽略、但长期跑下来非常关键的话题技能的持续维护。很多人把技能写完、上线、测试通过就觉得大功告成了实际上技能跟代码一样是有生命周期的需要持续的版本管理、效果评估和淘汰机制。5.1 技能评测集给 Agent 的能力做“体检”没有评测集你就既不能说自己的技能好用也不能发现它哪一天开始变坏了。我建议上岗第一件事就是为每个技能建一个最小评测集每个技能准备 20-30 条典型用户话术标注好期望调用的技能名和期望提取的参数。这个评测集有两个用途。第一是回归测试每次改技能描述、加新参数或者升级底层模型都跑一遍评测集看看哪些用例的命中率下降了。第二是竞品对比换模型或者调 Prompt 的时候用评测集分数说话而不是凭感觉做好坏判断。我遇到过不少团队上线后没建评测集结果换了新版本模型后之前的技能误触发率暴增了三倍但因为没有基线对比折腾了将近两周才发现是模型行为漂移导致的。有了评测集这类问题基本当天就能定位。5.2 灰度发布与技能退役谨慎对待每一次调整技能变更看着小影响范围却可能很大所以我也习惯把技能的更新做成灰度发布新技能先对 10% 的流量开放跑几天看评测集分数和线上日志确认没有引入异常后再全量放开。这个过程完全不依赖复杂的框架在注册中心加一个 enable_ratio 字段就行随机数小于比例才注册进去。至于技能退役我踩过一个教训直接下线了一个看起来没人用的技能结果有个低频但关键的流程立刻挂了。后来我改成“先观察后下架”的策略把准备下架的技能标记为 deprecated让它继续运行但向监控系统发提示观察一个月确认没有任何流量后再真正移除。5.3 技能库的沉淀从项目资产到组织资产单个项目里的技能库随着时间推移会变成一个组织级的资产。同一家公司里客服 Agent 的“查订单”技能和运营 Agent 的“查订单”技能底层逻辑高度相似完全可以抽成一个公共技能供多个 Agent 复用。我推荐的做法是在技能注册中心之上维护一张“公共技能清单”和“项目技能清单”每个技能都标注归属方、维护者和依赖接口。这样技能库既有复用的可能性又不会因为过度耦合导致牵一发动全身。这个做法也许听起来有点“重”但当你的 Agent 数量超过三五个的时候它带来的维护效率提升是肉眼可见的。写在最后的几个实操心得做 agent-skills 这一路我最深的体会是这个环节做得好的项目未必是最炫的但它一定是最稳的。技能拆得好、描述写得清、评测跟得上Agent 的行为就会越来越可控反过来这几个环节糊弄过去线上一定会用各种奇怪的方式找补回来。我个人现在做任何 Agent 项目都坚持先花至少三分之一的时间在技能体系设计上绝不为了尽早看到一个能聊天的 demo 而跳过它。如果你手上正好也在做 Agent我的建议是别急着堆功能先把现有的能力列成一个矩阵拆成技能逐个写清楚描述再跑通一个最小链路。等你体会到“加一个新技能只需要新增一个类、注册一次、跑一遍评测集”的顺畅感就知道这套思路的价值了。后面再遇到任何 Agent 不够聪明的问题都能沿着技能层一条条排查到位。