从“会对话”到“会做事”:agent-skills 如何把智能体从玩具变成工具
先聊个我最近一直在琢磨的问题。
大模型刚火那阵,大家玩的都是聊天、写文案、改代码,这确实惊艳。但用久了你会发现,纯对话式的智能体,就像个满腹经纶但只会纸上谈兵的书生——你问它什么都答得头头是道,真让它去办件事,它反而不知道从哪下手。怎么下单?怎么查库存?怎么调接口?它一概不会。
于是就有了 agent-skills 这个方向:给智能体注入可复用、可组合、可维护的“技能”,让它从“会说”进化到“会做”。这半年我一直在折腾这套东西,今天把思路、踩过的坑、实测下来好用的方案,一次性分享出来。
agent-skills 说白了就解决三件事:让 Agent 知道自己能干什么(技能发现)、知道怎么干(技能调用)、干活过程中怎么处理新情况(技能学习与沉淀)。这三件事做好了,Agent 才真正像个“员工”而不是“百度百科”。
这篇文章适合谁看?如果你的项目已经用大模型接了一些简单工具,但总觉得 Agent 的行为不可控、能力边界模糊、每次加新功能都要改半天提示词,那这篇文章就是给你写的。不管你是独立开发者、AI 产品负责人,还是想搞清 Agent 底层原理的技术爱好者,这里面的设计思路和工程细节,应该能帮你少走不少弯路。
1. 整体设计思路:为什么“技能”比“提示词”更靠谱
1.1 从提示词到技能:思维模式的切换
很多团队一开始做 Agent,思路是“把一切都塞进 system prompt”。今天加一个工具说明,明天补一段业务规则,后天再改一下输出格式要求。结果就是提示词越来越长,Agent 的行为越来越飘,加了一个功能,之前稳定的场景反而开始出错。
我自己也经历过这个阶段,改提示词就像打地鼠——按下一个问题,冒出两个新问题。直到后来想明白一件事:提示词描述的是“怎么做”,但从来没有定义“能做什么”。而真正稳定的 Agent 系统,需要的恰恰是后者。
技能(Skill)这个概念,就是从这个缺口长出来的。一个技能是一个高度内聚的能力单元:比如“调用订单接口查询物流状态”是一个技能,“根据客户历史订单生成复购建议”是另一个技能。它有自己的名字、说明、输入输出定义、调用方式,甚至有自己的验证逻辑。Agent 运行的时候,不是靠“读提示词猜自己该干嘛”,而是靠“检索技能库匹配当前任务”。
这个思维切换带来的好处是很实际的:技能是代码和配置的组合,可以被测试、被版本管理、被复用。你给 Agent 加了新能力,不会影响到旧能力,因为它们各自封装好了。而提示词改一处崩一处的问题,其实不是模型不行,是架构扛不住复杂度。
1.2 技能的三层结构:基础、领域与元技能
在设计技能体系时,我强烈建议不要搞成平铺一坨。实测下来,把技能分成三类会让整个系统清晰很多:
- 基础技能(Base Skills):跟具体业务无关的通用能力,比如“网页抓取”“文件读写”“调用外部API”“执行Python代码”。这层通常一次建设,到处复用。
- 领域技能(Domain Skills):跟业务强相关的能力,比如“识别工单类型并自动分类”“计算项目的关键路径工期”“根据销售数据生成周报”。这层是大部分业务量的承载者,需要持续迭代。
- 元技能(Meta Skills):管理和编排其他技能的能力,比如“把任务拆解成子任务并分配给合适的技能”“当主技能失败时决定降级策略”“评估新技能的可用性”。这层决定了整个 Agent 系统的智能化上限。
这三层分开设计,本质上是在做“关注点分离”。基础技能解决复用问题,领域技能解决业务问题,元技能解决自主性问题。
1.3 一句话说清楚的设计原则
如果你只记得一句话:技能的粒度,以“人类同事能直接交办”为标准来定。比如你不会跟实习生说“解析一下这个 PDF”,你会说“把这 30 份简历提取成候选人表格”。所以一个技能应该是后者,而不是前者。这个判断标准帮我避免了很多过度拆解或过度粗放的坑。
2. 技能获取与沉淀:三条路,缺一不可
2.1 人工定义:先把最核心的业务动作固化下来
技能体系搭建初期,不要指望 Agent 自己长本事,老老实实人工定义是最快的路径。方法不复杂:把你业务里最高频、最标准化的操作流程,一条条写成人能看懂、机器能执行的技能文档。
具体操作我分四步走:
第一步,梳理业务动作清单。打开工作日志或客服聊天记录,把那些每天重复出现的动作全部列出来。以 SaaS 公司的实施顾问为例,高频动作就是“开通客户账号”“配置数据同步规则”“导出使用报告”“发送到期提醒邮件”。一个业务领域,通常也就二三十个核心动作。
第二步,定义技能文档的骨架。我习惯用统一的模板,字段不能多也不能少:
- 技能名称(要像函数名一样规范,可读且可检索)
- 一句话描述(说清楚这个技能是干嘛的,这是 Agent 检索时的核心依据)
- 适用场景(哪些情况下应该调用这个技能)
- 输入参数(JSON Schema 或自然语言描述,明确必填和可选)
- 执行步骤(就是把人工操作流程写出来,步骤要足够具体)
- 输出格式(明确返回什么,结构化的还是自然语言)
- 边界与异常(哪些情况技能会拒绝执行,报错时给出的提示信息)
第三步,让技能可执行。这一步是人工定义和“写在文档里的流程”的分水岭。每个技能必须有一个可以实际调用的入口:要么是 Python 函数,要么是 HTTP API,要么是内部服务的一个 CLI 命令。模型判定需要调用时,由 Agent 运行时模块去实际执行这个入口。
第四步,给技能配自测用例。每个技能至少配上两三条测试输入,给 Agent 冒烟试用。这些用例会变成后续回归测试的基础。我见过太多团队技能建了一堆,结果没人敢用,就是因为没自测过。
2.2 自动发现:让 Agent 在真实任务中长出“新本事”
人工定义能覆盖 80% 的标准动作,但总有些工作是执行过程中涌现出来的临场思路。比如 Agent 在处理一个复杂工单时,“需要对比两个 Excel 文件找出差异”这种事,提前很难定义成固定技能。这时候就需要自动发现机制。
我的方案是三层过滤:
行为记录层。Agent 在每次执行任务时,会记录完整的行动轨迹——调用了哪些技能、执行了什么代码、查看了哪些文件、结果如何。这些轨迹相当于 Agent 的“工作日记”。
模式识别层。定期(比如每周)对轨迹做一次聚类分析。注意不是看单次任务,而是看重复模式:如果一个两到三步的操作序列在多个不同任务里反复出现,那它大概率值得“升格”为独立技能。
人工审核层。机器提名的候选技能,必须有真人过目确认。我踩过的坑是:早期我搞过全自动沉淀技能,结果 Agent 学了一堆“歪招”——比如它把某个特定客户的名字写死在技能参数里,这样的技能根本不可复用。加了人审这关之后,质量和复用率都明显上来了。
2.3 在线学习:技能的精装修必须在真实反馈里打磨
技能不能像文物一样供着,它得在真实使用中变好。这也是我认为 agent-skills 项目里最容易被忽略、但长期价值最大的一块:基于执行反馈的技能迭代闭环。
具体怎么闭环?我给每个技能挂了两个计数器:调用成功率和调用失败率。当成功率连续低于阈值(比如 80%)时,触发一次技能维护流程。维护不是重写技能,而是四步排查:先看输入参数是不是定义不全,再看执行步骤是不是过于理想化(没有考虑真实环境的混乱),再看看边界条件是不是没写够,最后看是不是技能本身的产出已经跟业务脱节。
一个典型的例子:我早期写过一个“汇总团队周报”的技能,初版很简单——读取邮箱、提取五条要点、生成汇总文档。上线后成功率只有 60%。排查发现,很多周报带有图片截图,文字模型提取不到图中的关键信息。优化方案是让技能在文字提取之后,自动对截图做一层多模态识别,识别失败再返回“请作者补文字说明”。改完再跑,成功率直接拉到 95% 以上。
这背后其实就是工程领域常说的“反馈驱动开发”在 Agent 技能上的应用——不是一次把技能写完美,而是在真实摩擦中不断打磨。
3. 技能管理与编排:决定 Agent 上限的工程架构
3.1 注册中心:所有技能的统一目录
技能多了以后,最大的麻烦不是“没有技能”,而是“Agent 不知道自己有什么技能”。如果你把技能列表全部塞进上下文,几轮对话之后,模型就开始选择困难了。
正确的做法是做一个技能注册中心(Skill Registry)——一个包含所有技能元数据的目录。每个技能注册时,除了描述和参数定义,还要有标签、依赖关系、版本号、评估状态。Agent 运行时,不会直接看到所有技能详情,而是先看“技能广场”的一行摘要列表,再根据任务需要去拉取某个技能的具体 schema。
这个设计像什么?像你手机上的应用商店。你不需要同时打开所有 App,只会在要用的时候搜索、点开、用完退出。Agent 也是一样的消费逻辑。
3.2 路由与规划:让 Agent 自己编排技能还是由控制流决定?
这是所有做 Agent 的人都会纠结的问题:Agent 技能的调用过程,是让模型自由 decision 还是用固定工作流?
我的实测结论是:混合模式最稳。用控制流(比如状态机或工作流配置)负责的技能调用,适合那些流程固定、出错代价高的业务,例如支付、开票、权限变更。用模型自主编排的技能调用,适合开放性强、需要临场判断的任务,例如需求分析、内容创作、客户沟通。
怎么判断该用哪种?给一个我自己用着很顺的标准:这个任务的结果,是可预测的吗?可预测的,上工作流;不可预测的,上模型自由编排。像“新客户注册后自动开通基础服务”这种就是一个完美的状态机场景;“给这个客户制定一份个性化运营方案”则明显需要技能的自由组合。
3.3 技能版本化与灰度发布
技能也是软件,既然软件要升级,技能也得有版本。我见过太多团队“顺手改了一下技能描述”就把线上 Agent 带崩了——因为技能描述变了,Agent 的检索排序就变了,原来能正确触发的场景不能触发了。
所以技能的变更必须像发布软件一样谨慎。我给自己的规范是:任何技能改动,都要升级版本号;版本发布走灰度,先让 5% 的新请求使用新版本,观察 24 小时成功率,没问题再全量。听起来重,但在生产环境真的能救命。有一次我给“报销审核”技能加了一个新规则,灰度期间就发现新版本误杀了大量正常报销单,马上回滚,线上基本无感。
4. 从零到一:搭建一套最小可用的 agent-skills 系统
4.1 核心组件的选型逻辑
搭建这么一套系统,我的推荐技术栈不花哨,反而偏向务实:
- 编程语言:Python 是主力,生态丰富,尤其爬虫、数据处理、办公自动化相关的技能写起来最快。
- 模型层:基础对话能力用主流大模型 API 即可,关键是写好 Function Call 或者 Tool Use 的接口定义,让模型能结构化地请求技能调用。
- 技能执行层:不要自己撸一套沙箱,初期就用本地进程 + Docker 容器隔离。Docker 负责隔离危险操作(比如任意代码执行),本地进程负责跑轻量技能脚本。
- 注册中心:初期不需要上云端注册服务,一个带锁的 JSON 文件或 SQLite 数据库就够了。技能总数在 100 个以内,文件型存储完全顶得住。
- 轨迹记录:直接把每次调用的输入输出、耗时、成败、置信度存进日志表,后面做技能迭代和效果分析都靠它。
工具就这些,不花哨,但够用。
4.2 核心代码骨架:一个技能的定义与调度
光说不练假把式,我直接贴一段“技能注册 + 调度”的最小实现,这几乎是我每个项目都会复用起来的一段骨架代码:
# skill_base.py from dataclasses import dataclass, field @dataclass class Skill: name: str description: str input_schema: dict version: str = "0.1.0" def execute(self, **kwargs): """实际执行逻辑,子类重写""" raise NotImplementedError def validate_input(self, **kwargs): """轻量校验输入,防止脏参数传入""" required = self.input_schema.get("required", []) for key in required: if key not in kwargs: raise ValueError(f"缺少必填参数: {key}") return True# skill_registry.py import yaml from skill_base import Skill class SkillRegistry: def __init__(self, config_path: str): self.skills = {} with open(config_path, "r", encoding="utf-8") as f: self.config = yaml.safe_load(f) def register(self, skill: Skill): self.skills[skill.name] = skill def discover(self, task_description: str, top_k: int = 5) -> list[Skill]: """初版用简单的关键词匹配,后续可以换向量检索""" from difflib import SequenceMatcher scores = [] for skill in self.skills.values(): # 简单相似度 = 技能描述与任务描述的字符重合度 score = SequenceMatcher(None, skill.description, task_description).ratio() scores.append((score, skill)) # 过滤掉相似度过低的,取前 k 个可用技能 scores.sort(key=lambda x: -x[0]) return [s for s, _ in scores[:top_k] if s > 0.1]上面这两段是核心骨架。实际业务技能长什么样?以“自动生成周报”技能为例:
# skills/weekly_report.py from skill_base import Skill class GenerateWeeklyReport(Skill): def __init__(self): super().__init__( name="generate_weekly_report", description="根据项目纪要生成一周工作总结报告", version="1.2.0", input_schema={ "type": "object", "properties": { "scope": {"type": "string"}, "time_range": {"type": "string"} }, "required": ["scope"] } ) def execute(self, **kwargs): # 调用数据源接口、模板渲染等逻辑 ... return {"report": "..."}注意几个细节:每个技能都主动声明了自己的版本号,未来做热更新时免不了一查;描述写得简洁但信息密度高,因为模型靠这句描述来匹配任务;输入 schema 里既有必填字段又有可选字段,给模型足够的容错空间。
4.3 调度循环:让 Agent 自主决策调用哪个技能
注册中心做完了,还差一个“把任务转成技能调用序列”的大脑。我这里用的方案是经典的 ReAct 模式(Reason + Act):模型先推理当前需要做什么、需要哪个技能,然后发起调用,再根据调用结果决定接下来做什么。
核心调用逻辑大概长这样:
def run_agent(task_description: str, max_steps: int = 10): registry = SkillRegistry("skills.yaml") available_skills = registry.discover(task_description, top_k=5) messages = [{"role": "system", "content": "你是一个工作助手。"}] messages.append({ "role": "system", "content": f"当前可用的技能有: {[s.name + ': ' + s.description for s in available_skills]}" }) messages.append({"role": "user", "content": task_description}) for step in range(max_steps): response = call_llm(messages) decision = parse_skill_decision(response) # {"thought": "...", "skill": "...", "params": {...} or "finish": True} if decision.get("finish"): return decision.get("answer") skill = registry.skills.get(decision["skill"]) if not skill: messages.append({"role": "system", "content": f"技能 {decision['skill']} 不存在,请从已知技能中选择"}) continue try: result = skill.execute(**decision["params"]) messages.append({"role": "system", "content": f"执行结果: {result}"}) except Exception as e: messages.append({"role": "system", "content": f"执行失败: {str(e)},请尝试其他方案"}) return "轮次耗尽,任务未完成"这里面有两个关键的工程细节。一个是registry.discover的控制:它限制了模型每次看到的候选技能不超过 5 个,这就大大降低了模型乱选的风险。另一个是对执行失败的兜底:失败信息会回传给模型,让模型自己决定是换参数还是换技能,这比直接中断一次任务优雅得多。
4.4 真实项目复现:一键生成客户周报并推送
拿我最近的一个落地场景举例。需求很简单:每周一早上,Agent 自动从项目管理平台拉取上周任务进展,合并成一段周报文案,再通过企业微信机器人推送到客户群。
三个技能搞定:
fetch_project_events:调项目管理平台 API,拉取指定项目、指定日期范围内的事件列表。format_weekly_digest:把原始事件列表整理成结构化的周报摘要(按工作类型分组,按影响程度排序)。send_wecom_message:把文案通过企业微信机器人 webhook 推送到指定群。
调度逻辑也很直白:先调第一个技能拿原始数据,再调第二个技能做格式化,最后调第三个技能完成推送。三个技能串行执行,每个步骤的输入输出都很干净。这套系统上线后,每周一早上我就真的什么都不用管了,稳定跑了一个季度没出过大问题。
5. 实测避坑手册:八个常见问题和排查方案
这个部分我写得最掏心窝子,因为下面每一个问题,我都真金白银踩过:
| 问题现象 | 根因分析 | 排查与解决思路 |
|---|---|---|
| Agent 就是不用某个技能,明明很匹配 | 技能描述写成了功能描述而不是场景描述,模型不知道“什么时候该用” | 重写技能描述,明确写出“当用户想要...时使用本技能” |
| 技能调用时参数总传错 | 输入参数名含义模糊,模型只能猜 | 给每个参数加详解和取值示例;无效值在 schema 里就枚举齐全 |
| 技能 A 和技能 B 边界重叠 | 两个技能描述里都出现了同一个高频词,检索排序不稳定 | 重新划定边界:按“对象类型”或“产出类型”区分;在描述里明确“本技能不处理...” |
| 执行成功了,但结果质量差 | 技能内部逻辑没问题,但缺少后置处理(比如格式整理、异常过滤) | 在技能执行步骤里增加后处理环节,让技能输出天然符合下游输入要求 |
| 修改技能描述后,其它场景被带崩 | 模型对技能的语义理解随描述变化而变化 | 技能描述走版本管理,改动要灰度验证,改动后手动跑一遍该技能关联的历史用例 |
| Agent 在一个任务里循环调用同一个技能 | 技能执行结果没有改变环境状态,模型看不到“进展”,只能重复尝试 | 每次执行后积累临时上下文,让模型感知到“已经试过哪些方案”;限制同一技能的连续调用次数 |
| 技能数量超过 50 个后,发现列表频繁让模型看花眼 | 简单关键词匹配已经不起作用 | 把检索换成向量语义检索,注册中心加 embedding 索引 |
| 本地跑得好好的,上生产就超时 | 技能内部大部分时间耗在同步 IO 上,任务稍多就阻塞 | 所有技能统一走异步执行,通过队列限流,给每个技能设置最大执行时长配额 |
这里重点展开前三个,因为大多数团队的“Agent 表现不稳定”问题,其实源头都在这里。
坑一:描述没写清使用场景。我一开始给“生成项目周报”技能写的描述是“能够根据项目数据自动生成周报内容”,但 Agent 经常在客户问“最近项目进展如何”的时候不知道该不该调用,因为描述里说的是“根据项目数据”,客户问的是“进展”。后来改成“当用户询问某个项目的进度、本周完成事项或下周计划时,使用本技能从项目平台拉取数据并生成摘要”,命中率立刻上来了。核心逻辑就一句话:描述要写“在什么场景下用”,而不是“这个功能做什么”。
坑二:输入参数定义太随意。“时间范围”这么个参数,如果你只写“必填”,模型发挥空间就大了,填“最近几天”“上周”“9月1号到9月5号”的都有。正确做法是把枚举值或格式示例写清楚:可接受值如 2025-01-01 至 2025-01-31、本周、上周、近7天;若无法识别,需要先向用户确认。模型是复杂的文本引擎,你给它的余地越多,它发挥得越野。
坑三:技能边界重叠。我早期有“网页抓取”和“舆情监控”两个技能,描述里都写了“从网页获取信息”。结果 Agent 做舆情任务时经常随机选一个,两个的结果格式又不一致,下游就崩。解决方式也很直接:给“舆情监控”的描述加上“本技能仅用于从新闻和社交媒体页面抓取指定主体的公开信息,不适合作通用网页信息采集”,给“网页抓取”加一句“通用信息采集场景请使用本技能,舆情类场景请优先考虑使用舆情监控技能”。给技能之间划清权限和边界,是建制管理里很关键的一步。
6. 评估体系:怎么证明“技能变强了”这件事
最后再补充一块平时很少有人聊、但我认为很重要的一块:怎么科学评估技能变强了?很多团队做完技能迭代,就说“感觉好用了一些”,这种模糊的评估没法指导长期建设。我建议每个技能挂一张“健康卡片”,包含以下五个硬指标:
- 调用成功率(成功执行次数 / 总调用次数)
- 参数通过率(第一次参数校验就通过的比率,低于 70% 说明 schema 定义不够好)
- 中断率(技能执行超过 3 次重试仍失败的比率)
- 下游环节满意率(技能产出被下一个环节接受,没导致二次修改的比率)
- 平均执行耗时
每两周跑一次汇总,结合调用量排序,就能清晰地看到:哪些技能是高频核心,值得花大精力打磨;哪些技能调用量少又老失败,该考虑废弃或合并。
这套评估体系还有个额外好处——它帮你判断 Agent 系统的整体健康度。如果某个技能的问题总是反复出现,很可能不是技能实现的问题,而是基础数据源或上游接口的设计问题。顺着技能健康卡往下查,往往能揪出一些隐藏很深的业务逻辑问题。
7. 我在实操中最深的几点体会
项目做到现在,我最真实的体会有三条。
第一条,技能的建设是一个“熵减”过程,天然需要克制。因为技能的沉淀是不断把混乱变有序的工程,一旦失去秩序感,所有 熵 增都会以“Agent 行为不可控”的形式反弹回来。所以宁缺毋滥,一个技能如果没有想清楚“边界在哪、失败怎么办”,就不要轻易注册进系统。
第二条,模型的角色更像“调度员”而不是“执行员”。模型太擅长“发挥”了,如果你把技能的每个细节都交给模型临场自由发挥,那你得到的一定是一堆不可复现的结果。靠谱的做法是:模型只负责“选哪个技能、传什么参数、如何组合多个技能”,技能内部的具体执行逻辑必须由代码精确控制。
第三条,别等工具完美了再开始,最小闭环跑起来再迭代。如果你还在犹豫“要不要现在开始搭技能系统”,那就挑一个业务里最痛、最通用的动作,用两天时间把它定义成第一个技能跑起来。跑通了,你自然知道下一步该往哪使劲。
最后分享一个小经验:给技能起名字这件事,值得多花两分钟。技能排行榜上那些好用的技能,名字基本都能“望文生义”——send_daily_reminder、fetch_customer_history、query_inventory_status。名字起得好了,不仅模型容易选中它,团队内部沟通成本也能低不少。毕竟一套技能体系要做起来,从来不只是技术问题,更是组织协作的语言基础。