Agent技能系统设计:从注册到路由的工程实践

发布时间:2026/10/21 4:36:15

Agent技能系统设计:从注册到路由的工程实践 1. 从“agent-skills”这个标题说起它到底在解决什么问题第一次看到“agent-skills”这个标题我脑子里蹦出来的不是某个具体框架而是一类很典型的需求让一个智能体Agent具备可复用、可组合、可插拔的能力模块。说白了就是别每次做一个新任务都从零写一遍提示词、工具调用逻辑和状态管理而是把“技能”抽出来像搭积木一样拼装。这个标题背后对应的场景非常广。往小了说是一个对话机器人在不同任务间切换时需要动态加载不同的处理逻辑往大了说是一个多智能体协作系统里每个 Agent 都要能按需获取“查资料”“算数据”“调接口”“写文件”这些能力。核心痛点就一个能力与执行体解耦。没有这层解耦代码会迅速膨胀成一团乱麻改一个功能牵动全身。我见过不少团队一开始图快把所有逻辑塞进一个巨大的提示词模板里结果就是加一个新技能要动主流程删一个旧技能又怕影响别的分支测试成本高得离谱。agent-skills 这个思路本质上是在做能力注册与发现机制让 Agent 在运行时知道自己“会什么”以及“怎么用”。适合谁来参考如果你正在做智能体应用、任务自动化、或者任何需要动态编排能力的系统这个标题下的内容都值得细看。哪怕你只是写一个稍复杂的脚本工具里面“技能注册—路由—执行—回传”这套模式也能直接借鉴。下面我会从设计思路、核心细节、实操落地和踩坑排查四个层面把这件事拆透。2. 整体设计与思路拆解为什么是“技能”而不是“功能”2.1 技能与功能的本质区别很多人第一反应是技能不就是功能吗我写个函数不就行了。这里有个关键差异。功能是面向开发者的调用单元技能是面向 Agent 的决策单元。功能只需要输入输出正确技能还必须附带“什么时候该用我”的元信息。举个例子你有一个“查询天气”的功能函数签名是get_weather(city)。但 Agent 面对用户说“明天出门要不要带伞”时它得自己判断这需要调用天气技能参数是城市而且可能还要结合降水概率做推理。所以一个完整的技能定义至少包含四部分技能名称、自然语言描述、参数模式、执行入口。描述是给模型看的参数模式是给校验用的执行入口是给运行时调的。我试过只写函数不写描述结果模型根本不知道有这个能力存在或者知道存在但不知道怎么填参数。后来把描述补上命中率立刻上来了。这就是技能和裸函数的第一个分水岭。2.2 为什么选择注册制而不是硬编码设计上我强烈建议走注册制。硬编码的意思是在主流程里写一堆 if-else 或者 switch根据意图分发到不同处理函数。这种做法在技能少于五个时还能忍一旦超过十个主流程就变成了一棵巨大的判断树而且每加一个技能都要改核心代码违反开闭原则。注册制的做法是每个技能自己声明“我是谁、我能干什么、我怎么被调用”然后统一注册到一个技能仓库里。Agent 运行时只跟仓库打交道通过检索或匹配找到合适的技能。这样新增技能就是新增一个文件主流程一行不用动。实测下来这种模式在技能数量增长到二三十个时维护成本几乎是线性的而硬编码是指数级上升的。注意注册制带来的一个副作用是“技能发现”问题。技能多了之后模型可能选错或者选重。所以描述的质量比数量更重要后面会专门讲怎么写好技能描述。2.3 技能粒度的取舍逻辑粒度太粗一个技能干太多事参数复杂、复用性差粒度太细技能数量爆炸模型选择困难。我的经验法则是一个技能对应一个明确的动作意图且这个意图在自然语言里有稳定的表达方式。比如“发送邮件”可以是一个技能但“发送邮件并记录日志并更新客户状态”就不适合做成一个技能应该拆成三个由 Agent 编排。判断标准很简单如果用户的一句话里经常同时包含这几个动作那可以考虑合并如果它们经常独立出现就必须拆开。这个取舍直接决定了后续路由的准确率。3. 核心细节解析与实操要点技能定义怎么写才靠谱3.1 技能描述的三段式写法技能描述是模型选择技能的唯一依据写得好不好直接决定成败。我总结了一个三段式模板动作 对象 边界。动作说明干什么对象说明对什么干边界说明什么情况下不该用。比如一个“文本摘要”技能差的描述是“总结文本”。好的描述是“对用户提供的长文本生成简洁摘要适用于文章、报告、会议记录不适用于代码文件或结构化数据表。”后面这句边界特别重要因为模型经常把代码文件也丢进来摘要结果输出一堆无意义的自然语言。我实测过加上边界说明后误触发率能降一半以上。这个细节很多教程不讲但实际用起来差别巨大。3.2 参数模式的设计与校验参数模式建议用 JSON Schema 来描述因为大多数模型对 JSON Schema 的理解已经比较成熟。每个参数要写清楚类型、是否必填、描述和示例。示例尤其关键模型看到示例后填参准确率明显提升。{ name: search_docs, description: 在内部文档库中检索信息适用于查找产品说明、流程规范不适用于实时数据查询。, parameters: { type: object, properties: { query: { type: string, description: 检索关键词建议使用名词短语, example: 报销流程 }, top_k: { type: integer, description: 返回结果数量默认5, default: 5 } }, required: [query] } }校验环节不能省。模型偶尔会漏填必填参数或者类型填错运行时必须做一次校验校验失败就返回明确的错误信息让模型重试。我一般会给两次重试机会两次还不行就降级到兜底回复。这个机制能挡住大部分参数层面的问题。3.3 技能执行入口的隔离设计执行入口最好做成无状态的所有上下文通过参数传入。这样做的好处是技能可以被并发调用也方便测试。如果技能内部依赖全局变量或者外部会话状态调试起来会非常痛苦。另外建议给每个技能加超时和异常捕获。一个技能卡住不能拖垮整个 Agent。我通常设 10 秒超时超时后返回“技能执行超时”让模型决定下一步。异常也要转成结构化信息返回而不是直接抛栈否则模型看不懂。提示技能执行结果建议统一成{status, data, message}格式。status 用 success/errordata 放实际内容message 放人类可读的说明。模型对结构化结果的处理能力远强于纯文本。4. 实操过程与核心环节实现从零搭一个技能系统4.1 技能仓库的目录结构我习惯用文件系统来组织技能每个技能一个目录里面放定义文件和执行脚本。这样新增技能就是复制目录改内容非常直观。skills/ search_docs/ skill.json handler.py summarize_text/ skill.json handler.py send_email/ skill.json handler.pyskill.json放名称、描述、参数模式handler.py放执行逻辑。启动时扫描 skills 目录把所有 skill.json 加载进内存形成一个技能列表。这个列表就是后续路由的依据。4.2 技能加载与注册的实现加载逻辑很简单遍历目录、读 JSON、校验必填字段、注册到内存字典。关键是要做去重和冲突检测。如果两个技能同名必须报错而不是静默覆盖否则排查起来很麻烦。import json import os class SkillRegistry: def __init__(self, skills_dir): self.skills {} self.skills_dir skills_dir def load_all(self): for name in os.listdir(self.skills_dir): path os.path.join(self.skills_dir, name, skill.json) if not os.path.exists(path): continue with open(path, r, encodingutf-8) as f: meta json.load(f) skill_name meta[name] if skill_name in self.skills: raise ValueError(f技能重名: {skill_name}) self.skills[skill_name] meta return self.skills这段代码我用了很多次稳定可靠。注意编码统一用 utf-8否则中文描述会乱码。4.3 技能路由的两种策略路由就是根据用户输入决定调用哪个技能。我常用两种策略视技能数量而定。技能少于十五个时直接把所有技能描述拼进提示词让模型输出技能名和参数。这种做法简单直接准确率也够用。技能多了之后提示词会超长成本和延迟都上去了这时候改用两阶段路由先用一个轻量检索模型比如基于关键词或向量相似度召回前五个候选技能再让主模型在这五个里选。实测下来两阶段路由在五十个技能规模下准确率只比全量低一点点但延迟降了六成。策略适用技能数优点缺点全量提示 15实现简单准确率高提示词长成本高两阶段路由 15延迟低可扩展召回环节可能漏选4.4 技能执行的完整链路一次完整的技能调用包含解析模型输出、校验参数、执行 handler、格式化结果、回传给模型。每一步都要有日志否则出问题根本不知道卡在哪。def execute_skill(registry, skill_name, params): if skill_name not in registry.skills: return {status: error, message: 技能不存在} meta registry.skills[skill_name] # 参数校验 required meta[parameters].get(required, []) for key in required: if key not in params: return {status: error, message: f缺少参数: {key}} # 动态加载 handler module_path fskills.{skill_name}.handler module importlib.import_module(module_path) try: result module.run(**params) return {status: success, data: result} except Exception as e: return {status: error, message: str(e)}这里用动态导入是为了避免启动时加载所有 handler减少内存占用。异常统一捕获成 error 状态模型看到后可以决定重试还是换技能。4.5 多轮对话中的技能状态管理Agent 经常需要多轮才能完成一个任务比如先查资料再总结再发送。这时候技能之间需要传递中间结果。我的做法是在会话上下文里维护一个scratchpad每个技能执行完把结果写进去下一个技能可以从里面读。但要注意scratchpad 不能无限增长否则提示词会爆。我一般只保留最近三轮的结果更早的做摘要压缩。这个细节在长任务里特别重要不然跑到第五轮就开始报上下文超限。5. 常见问题与排查技巧实录那些文档里不会写的坑5.1 技能选错或选重怎么办这是最高频的问题。模型面对两个描述相近的技能时经常随机选一个。排查思路分三步先看描述是否有重叠关键词再看参数模式是否相似最后看是否有边界说明缺失。解决办法有两个。一是强化边界描述明确写出“不适用于什么”。二是加负样本示例在描述里直接写“当用户说 X 时不要用本技能”。我试过在描述里加一句“本技能不处理实时数据”误触发立刻少了很多。5.2 参数填错或漏填的排查模型填参出错通常有三种类型错、漏必填、值不合理。类型错和漏必填靠校验能挡住值不合理比较麻烦比如把“明天”填成日期格式但格式不对。我的经验是在参数描述里给具体示例比给格式说明更有效。写“日期格式 YYYY-MM-DD例如 2024-01-15”比写“请传入标准日期”管用得多。另外可以在 handler 里做二次校验不合法就返回明确提示让模型重填。5.3 技能执行超时与降级外部接口慢或者技能逻辑重时超时是常态。一定要设超时并且超时后返回结构化错误而不是让整个流程挂住。我一般设 10 秒超时后返回“技能执行超时请稍后重试或换用其他方式”。模型收到后通常会尝试别的技能或者告知用户。注意超时时间不要设太短有些检索类技能本身就需要几秒。我踩过的坑是设了 3 秒结果正常检索全被截断误判成失败。5.4 常见问题速查表问题现象可能原因排查动作解决方向技能不被调用描述缺失或太模糊检查 skill.json 描述字段补全动作对象边界选错技能描述重叠对比候选技能描述加边界和负样本参数报错必填缺失或类型错看校验日志补示例加二次校验执行超时外部依赖慢看 handler 耗时设超时加降级上下文爆掉scratchpad 太长看上下文长度压缩历史只留三轮5.5 独家避坑技巧第一个技巧技能描述里不要用同义词堆砌。有人为了提升命中率把“查询、检索、搜索、查找”全写进去结果模型反而困惑。用一个最准确的词就行。第二个技巧handler 里不要做模型该做的推理。技能只管执行判断该不该调用是模型的事。我见过有人在 handler 里写一堆 if 判断用户意图这就本末倒置了维护起来极其痛苦。第三个技巧给每个技能加一个 dry-run 模式。调试时只校验参数不真正执行能快速定位是路由问题还是执行问题。这个模式在联调阶段能省大量时间。6. 技能系统的扩展方向与个人体会这套东西搭起来之后扩展空间其实很大。我目前尝试过两个方向效果都不错。一个是技能组合把常用的一串技能调用固化成一个复合技能减少模型编排负担。另一个是技能版本管理同一个技能保留多个版本灰度切换出问题能快速回滚。技能组合的思路是如果发现某三个技能经常连续出现就把它封装成一个新技能内部依次调用。这样模型只需要选一次而不是选三次准确率和速度都提升。但要注意别过度封装否则又回到粒度太粗的老路。版本管理则是给 skill.json 加一个 version 字段注册时保留多版本路由时按配置选版本。这个在技能逻辑变更时特别有用可以先小流量验证再全量。我个人在实际操作中的体会是agent-skills 这套模式的价值不在于技术多复杂而在于它强迫你把“能力”想清楚。很多团队做智能体做不下去不是因为模型不行而是因为能力边界没定义好导致模型在模糊地带反复横跳。把技能描述写清楚、参数定明白、边界划出来很多问题自然就消失了。最后再分享一个小技巧每次新增技能后拿二十条真实用户问法跑一遍回归看命中率和参数准确率比拍脑袋调描述靠谱得多。
延伸阅读

更多相关文章

2026/10/21 4:36:15

智能体技能体系设计:从提示词膨胀到可复用技能拆解与调度

1. 从“agent-skills”这个标题说起:它到底在解决什么问题第一次看到“agent-skills”这个标题,我脑子里蹦出来的第一个念头是:这大概率不是一个单纯的工具库,而是一套围绕“智能体能力”做拆解、封装、复用的方法论或者工程框架。…

2026/10/21 4:36:15

litellm:统一多模型调用接口的Python库与代理服务实践

1. 项目概述与核心定位litellm 这个项目,最早是我在做一个多模型路由网关时被同事安利的。当时我们的业务需要同时对接好几家不同厂商的大模型接口,每家的 SDK、鉴权方式、请求参数、返回结构都不一样,代码里到处是 if-else 分支,…

2026/10/21 4:36:15

从全量微调困境到低秩适配:LoRA原理、实战与避坑指南

做模型微调的人,可能都经历过被显存和训练时间支配的恐惧。一个十几B的大模型,全量微调意味着要把每一层权重都更新一遍,梯度和优化器状态叠在一起,普通工作站基本扛不住。LoRA(Low-Rank Adaptation,低秩适…

2026/10/21 7:46:59

自学Python第5天:函数与字典实战,突破编程分水岭

打卡第5天。说实话,前四天我过得很轻松:环境装好、print() 打出第一行字、变量赋值、字符串拼接、if 判断,跟着视频敲一遍,运行通过就截图收工,当时觉得 Python 也不过如此。今天开始明显不对劲,函数和字典…

2026/10/21 7:46:59

AI不会杀死SaaS,但正在淘汰旧商业模式:企业软件如何转型?

1. 为什么说 AI 先杀的,不是 SaaS 本身先放结论:这场 AI 浪潮里,SaaS 公司几乎都活得好好的——今天你打开后台用的协同工具、设计软件、报表系统,底层依然是订阅付费,用户量没崩,收入也没崩。真正开始疼的…

2026/10/21 7:46:59

STM32 IAP固件升级防变砖:环形队列与自愈链路设计

做嵌入式开发的人,十有八九都要碰一次IAP升级。我上次处理的一批工控板卡,客户想更新算法参数,用串口ISP刷到一半,因为现场线缆接触不良加上上位机发包太急,几十台里有十几台烧死在半路,返厂拆壳用仿真器救…

2026/10/21 7:41:59

MySQL主从复制与双主双从:从binlog到GTID搭建详解

1. 项目概述:一主一从与双主双从分别解决什么问题先说说这次动手搭建的背景。很多团队在数据库从单机走向高可用的过程中,第一步想到的就是“主从复制”。MySQL主从复制本身并不难,但不少人把一主一从配通之后,就开始纠结还要不要…

2026/10/21 4:53:08

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/21 0:00:35

交通流仿真实战:IDM跟驰与MOBIL换道模型参数调优与避坑指南

简介:这份资源是面向交通工程学习者与Python开发者的微观交通流仿真项目,核心实现智能驾驶模型(IDM)下的车辆跟驰与换道行为,可用于理解速度、密度与流量之间的动态关系,适合具备Python基础、希望深入交通模…

2026/10/21 0:00:35

揭穿智能体表面顺从:用追踪代码重建任务执行确定性

1. 表面顺从不是智能体的“礼貌”,而是任务执行链路上的隐性断点最近在调试一个跨平台长周期任务调度系统时,我反复遇到一种令人困惑的现象:智能体在用户指令发出后,几乎秒级响应“已收到”“正在处理”“请稍候”,UI上…

2026/10/21 0:00:35

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事:AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里,按宣传口径,本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”,而是这话背后的含金量——它把过去只能…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑