
说实话过去半年我一直在折腾 AI Agent 方向的项目最深的感受就一句话让大模型“会谈”很容易让它“会做”很难。你问它怎么处理一份合同、怎么排查一个线上问题它能给你讲得头头是道但真要它自己去调用工具、按步骤执行、出错后自动恢复立刻露馅。这也是我最初关注到“agent-skills”这个思路的原因——它试图解决的正是 Agent 从“聊天机器人”进化为“能干活的工作流”之间的那段空白。如果你也在做 Agent 应用或者正打算把大模型接进自己的业务系统这篇文章值得看完。我会从一个一线开发者的角度拆解 agent-skills 到底在解决什么问题、它是怎么设计的、我落地过程中踩过的坑以及最后总结的一套可以直接拿去用的技能化改造方案。1. Agent 卡在“会谈不会做”的根源能力碎片化1.1 提示词堆出来的 Agent为什么一接真实任务就崩先说说我踩过的第一个坑。早期做 Agent 原型时我的做法和大多数人一样把任务指令写进 system prompt洋洋洒洒上千字从角色设定到输出格式全给它规定好。你是一个智能助手。当用户需要查询天气时你要调用 weather_api。当用户需要订机票时你要调用 flight_api。如果用户询问时间请直接回答当前时间。……看起来没什么问题对吧但真实跑起来全是问题。第一个问题是上下文长度不够用。业务场景一复杂你可能要告诉模型几十个工具的存在、用途、参数格式、调用时机再加上用户输入、历史消息、中间结果几千个 token 的上下文窗口根本撑不住。第二个问题是模型会“忘”尤其是 GPT 这类模型在长对话里经常出现中段指令丢失的情况。用户多问两句它就忘记自己该调用哪个工具了。第三个问题更隐蔽——提示词互相干扰。你让它“语气友好”和“优先调用工具”并存时它有时会为了讨好用户直接编造一个结果而不是真的去调工具。我统计过一组数据单纯靠 system prompt 堆工具说明的 Agent在 50 轮真实任务测试里正确调用工具的次数不到 60%剩余 40% 里有一半是调错参数另一半是干脆不调。这种稳定性放在真实业务里根本没法用。1.2 技能和工具到底是不是一回事很多人会把“技能”理解成“工具函数的集合”这是一个根本性的误区。工具是什么是一个可以被调用的函数比如get_weather(city)、create_ticket(title, priority)。但技能是什么技能是“完成一个任务目标的能力单元”它内部可能调用多个工具可能包含中间判断逻辑甚至可能有自己的状态。举个例子。get_weather(北京)是一个工具它只做一件事查天气。但“安排一次户外团建”是一个技能它需要先确认人数、再看天气、再根据天气推荐场地、最后生成通知文案。你没法用一个工具函数实现这件事它需要的是一个有流程、有决策、有容错的处理单元。这就是 agent-skills 这类方案的核心思路把大模型需要执行的复杂任务拆解成一组可以独立定义、独立测试、独立复用的“技能模块”。每个技能模块有明确的触发条件、输入输出契约和处理流程。大模型不再需要“知道”每个工具的细节它只需要“识别”当前应该调用哪个技能剩下的步骤由技能模块自己去完成。这个概念一转变整个架构就清晰了。模型负责“决策”技能负责“执行”。这也就是 agent-skills 名字的由来——给 Agent 装配一组可插拔的“技能包”。2. 技能化设计的三层拆解从输入规约到验证闭环2.1 技能的输入输出契约先定义清楚边界我落地 agent-skills 时做的第一件事不是写代码而是把每个技能的“接口契约”定下来。一个技能模块应该有三样东西触发条件、输入参数、输出结果。触发条件是决定这个技能什么时候被调用的描述。比如“当用户要求安排团建活动时触发”“当检测到日志中出现超时异常时触发”。触发条件写得越明确模型就越容易在对话中匹配到正确的技能。输入参数是技能执行需要的数据。这里我特别强调一点参数必须扁平化、显式化不要设计成嵌在自然语言里的隐式字段。比如“安排团建”技能参数就写成{ 人数: 23, 日期: 2025-06-15, 城市: 北京 }而不是让模型从对话里自由提取。这样后续做参数校验、缺参追问、系统对接都方便得多。输出结果则要约定两件事结构化的数据结果和面向用户的自然语言结果。数据结果用于技能之间的流转自然语言结果用于最终呈现给用户。这两个东西分开存避免混在一起造成后续解析困难。我自己用的技能描述模板长这样name: outdoor_team_building_planner description: 根据人数、日期和城市推荐户外团建方案并生成通知文案 triggers: - 用户要求安排团建 - 用户询问团建建议 - 用户提到需要户外活动策划 inputs: people_count: integer date: string city: string outputs: plan: object message: string这个 YAML 文件放在技能目录里Agent 启动时扫描加载。模型端其实不需要完整看到这个文件加载器会为它生成一段精简的技能说明向量用于匹配。2.2 技能描述与检索让模型知道“什么时候该用哪个”定义好技能之后下一个问题是大模型怎么知道当前用户的话该触发了哪个技能这里有两种主流做法我都试过。第一种是基于 embedding 的语义检索。把每个技能的描述和触发条件做成向量用户输入也做成向量然后算相似度取 Top-1 或 Top-K。这种做法的好处是速度快、不依赖模型推理缺点是匹配质量取决于 embedding 模型的语义理解能力对于同义表达较多的情况偶尔会翻车。第二种是交给大模型推理决策。把所有技能的名称和一句话描述塞给模型让它从候选项里选一个最匹配的。好处是准确率高坏处是费 token——每轮对话都要把技能列表发给模型技能一多上下文开销立刻上来。我最终采用的是混合策略先向量检索粗筛再让模型精排。向量检索直接过滤掉明显不相关的技能把候选列表从几十个压缩到三五个再把这几个候选的描述发给模型做最终决策。实测下来既控制了 token 成本又保证了决策准确率。值得一提的是技能描述里有一个容易忽略的细节描述里要写清楚“这个技能不做什么”。比如“天气查询”技能描述里加一句“不提供空气质量指数”就能减少模型误调用的概率。负面约束有时候比正面描述更能提高匹配准确率。2.3 技能验证与回退执行出错时不能直接摆烂Agent 技能比普通函数调用复杂的地方在于它面对的是动态环境。API 可能超时、服务可能返回异常数据、依赖的外部系统可能临时不可用。我在最初的设计里完全没考虑容错结果上线没多久就出了事故技能调用支付接口超时Agent 直接把超时异常原样抛给用户体验极其糟糕。后来我补上了验证与回退机制这是整个技能体系里我认为价值最高的一层设计。首先是输入验证。技能参数不是模型说什么就是什么必须做类型检查和枚举校验。比如日期字段必须符合YYYY-MM-DD格式城市字段必须在支持的城市列表里。验证不通过时技能模块返回一个“缺参/错参”的信号由 Agent 反问用户补齐信息而不是带着脏数据去执行。然后是执行结果验证。调用外部 API 之后不能默认返回结果就是对的。我会在技能里定义一个 validator检查返回数据的结构、关键字段的值域。一个印象很深的案例查询库存的技能返回了{ stock: -5 }这个负数值显然不对但如果没有验证器Agent 就会拿这个错误数据继续往下走。最后是回退策略。技能执行失败时至少要准备一条备选路径。比如查天气的 API 挂了可以回退到另一个数据源如果所有数据源都挂了那就明确告诉用户“当前无法获取该数据”并把错误标记到日志里。绝不能把原始异常堆栈抛给用户更不能让模型“自由发挥”编一个结果出来。3. 落地一套技能库从零搭建的完整过程3.1 目录结构与元信息设计我现在的技能库是放在项目里的一个独立目录每个技能一个子目录结构如下skills/ ├── __init__.py ├── registry.py # 技能注册与发现 ├── base.py # 技能基类 ├── weather_query/ # 天气查询技能 │ ├── SKILL.yaml # 技能元信息 │ ├── handler.py # 执行逻辑 │ └── validator.py # 结果验证 ├── team_building/ # 团建策划技能 │ ├── SKILL.yaml │ ├── handler.py │ └── validator.py └── order_create/ # 创建订单技能 ├── SKILL.yaml ├── handler.py └── validator.py核心文件是registry.py它负责扫描目录、加载 SKILL.yaml、注册技能实例。这个设计让我在加新技能时只需要新建一个目录、写好三个文件不用改任何既有代码。SKILL.yaml 里的元信息不只有描述和参数我还加了几个字段version版本号、owner负责团队、sla预期执行时间、dependencies依赖的其他技能或外部服务。这些字段在技能数量多了以后非常有用——你能快速知道每个技能是谁维护的、预期耗时多少、依赖是否健康。3.2 技能注册与发现的机制细节技能注册的机制说起来不复杂registry.py启动时扫描技能目录加载每个 SKILL.yaml将技能名映射到一个 handler 类。但真正写的时候有几个细节值得注意。一个是技能加载顺序。如果技能之间有依赖关系加载顺序不能随便来。我维护了一个依赖图先加载无依赖的技能再按拓扑排序加载后面的。最开始没做这个结果有个技能导入另一个技能的模块时直接抛 ImportError。另一个是热更新。技能迭代频繁时不可能每次都重启 Agent 服务。我在 registry 里加了一个 watchdog监听技能目录的文件变动自动重新加载变更的技能。这里有个教训YAML 文件写错了会导致加载失败所以热更新必须配合“加载失败则保留旧版本”的策略否则一个语法错误就能让你的 Agent 全盘瘫痪。3.3 执行引擎与上下文传递技能执行引擎是我整个 agent-skills 架构里的调度中枢。它的职责是根据决策模块选出的技能名实例化对应的技能模块传入处理好的参数执行并返回结果。这里的核心设计是上下文对象的传递。我不想让技能模块直接接触 LLM 的原始消息列表那会造成强耦合。我定义了一个SkillContext对象里面只放三样东西当前任务 ID、技能输入参数、一个只读的共享存储引用。共享存储是整个执行链路里非常关键的设施。它本质是一个字典技能在运行中产生的中间数据可以写入后续别的技能可以读取。比如“识别发票”技能把发票号码提取出来存进共享存储“查询发票状态”技能启动后直接从共享存储里拿号码省去了重复提取的步骤。class SkillContext: def __init__(self, task_id, params, shared_store): self.task_id task_id self.params params self.shared_store shared_store class SkillBase: def execute(self, ctx: SkillContext): raise NotImplementedError执行引擎本身还要负责两件事超时控制和日志记录。每个技能的执行都套在一个asyncio.wait_for里超时直接按失败处理走回退流程。日志则统一记录技能名、参数摘要、执行耗时、结果状态后续做评估和优化全靠这些数据。4. 实测中的翻车记录与排查链路4.1 同一个需求触发了三个技能决策冲突的根因上线第二周我就遇到了一个诡异的问题用户说了一句“帮我查一下北京的天气”Agent 居然同时触发了“天气查询”“穿衣建议”“户外活动推荐”三个技能。一开始我以为是向量检索相似度阈值设得太低但调低之后问题依然存在。后来我查了决策模块的日志才发现问题出在技能描述上。我把“穿衣建议”的触发条件写成了“当用户询问天气、温度或穿着建议时触发”这导致“查天气”这个请求被同时匹配上了两个技能。这就是典型的触发条件过宽问题。解决办法说起来简单给每个技能的触发条件加上“核心意图”限定。triggers: - 用户明确要求获取天气数据 - 用户提供城市并询问天气情况 negative_triggers: - 仅询问温度对应的穿衣搭配除了加negative_triggers我还在决策层加了一个“意图消解”规则当多个技能的匹配分数都超过阈值时取分数最高的那个为主技能其余技能如果需要执行必须由主技能的编排逻辑主动调用而不是直接并发。4.2 技能内部状态混乱全局变量引发的幽灵数据另一个翻车的案例更有代表性。我有个技能是“多轮对话中收集用户信息然后创建订单”这个技能内部维护了一个临时状态用来跨轮次暂存用户陆续提供的信息。我当时图省事把状态放在了一个模块级全局变量里。结果就是用户 A 和用户 B 同时发起对话A 填了一半的信息被 B 的最新输入覆盖了。最后 A 收到的订单 B 的信息直接造成一笔错误订单。这个事故让我彻底明白技能的临时状态必须挂在任务级别的上下文上永远不允许挂在全局。修复方法不复杂状态跟着SkillContext走# 错误做法模块级全局变量 _temp_state {} # 正确做法上下文对象的属性 ctx.state.setdefault(collected_info, {}) ctx.state[collected_info][name] user_input所有技能执行之前执行引擎会创建一个全新的、隔离的上下文任务结束就销毁。多轮对话场景则通过任务 ID 从持久化存储中恢复上下文。4.3 技能返回了 200但业务上其实是失败的还有一种翻车更隐蔽我要重点讲一下。某个技能调用第三方发票识别服务HTTP 状态码返回 200解析出来的 JSON 也合法但“发票号码”字段是空的。我的技能模块没有对这个字段做空值校验直接当作成功拿着空值的发票号去查了后续系统导致整个流程在最后一步报错。从那以后我把结果验证提到了和功能开发同等重要的位置。每个技能的 validator 里除了结构校验还必须包含业务规则校验。比如发票识别技能的业务规则包括发票号码不能为空且必须匹配“8 位数字”或“10 位数字”的格式发票金额必须为正数开票日期不能晚于当前日期规则不通过就标记为“数据异常”走重试或人工介入流程。这件事让我重新理解了“验证闭环”不是一句口号它是技能质量的最后一道防线。5. 进阶技能编排与组合策略5.1 从单技能到多技能串联编排器的设计思路单技能能解决的任务有限真实业务的 Agent 往往需要多个技能协作。用户说“帮我安排下周的团队建设”这背后至少涉及查近期天气、查场地可用性、生成活动方案、发通知文案。我把这类多技能协作的逻辑放进了编排层。编排层有两种实现方式一种是让大模型动态规划技能调用顺序另一种是预先用 DSL 写死流程。我两种都用过这里说说各自的适配场景。大模型动态规划适合开放性强、不可预测的任务。比如“帮我对接客户需求并输出方案”你没法预知用户会要求什么让模型自己决定先调哪个技能、后调哪个技能。缺点是不稳定同一个任务模型每次走的路径可能不一样后期排查链路就很费劲。DSL 写死流程适合稳定、可固化的业务路径。比如“发票报销”就是一条固定链路识别发票 → 查发票真伪 → 查预算额度 → 提交报销。这种流程没有必要让模型“思考”直接编排成固定 DAG 就好。我现在的主流做法是两者结合主干流程用 DSL 固化分支细节让模型自由发挥。这样既保证了核心链路不出错又保留了灵活应变的空间。5.2 技能编排的降级策略与优先级多技能编排的复杂度体现在故障传导上。技能 B 依赖技能 A 的输出如果 A 失败B 怎么办我之前设的是“直接失败”后来发现这太粗暴了。有些场景下 A 失败只是拿不到“最优数据”但 B 用“次优数据”也能完成任务。所以在编排器里我引入了降级策略。每个技能节点可以声明自己的降级路径严格模式上游失败本节点直接失败宽松模式上游失败使用默认值继续执行补偿模式上游失败尝试调用备选技能替代举个具体例子天气查询技能失败时编排器会尝试切换到“历史同期天气数据”技能如果两者都不可用则使用“未知天气默认推荐室内活动”的补偿策略。这套降级机制让我的 Agent 在外部服务抖动时依然能有接近八成的任务完成率。5.3 技能库的评估与迭代闭环技能加得越多就越需要一套评估机制。我维护了一个名为“技能体检”的定时任务每周跑一次。它会收集所有技能的调用数据从四个维度打指标触发准确率被触发时是否真的需要触发执行成功率执行过程中是否出现异常结果有效度validator 校验不通过的比例平均耗时技能执行的时间开销这些数据汇总后会生成一张表格我每周过一遍把触发准确率低和结果有效度低的技能挑出来重新打磨描述或修复逻辑。这套闭环跑了两三个月整个技能库的三大指标都显著提升Agent 的稳定性和用户的信任度也上来了。技能名称触发准确率执行成功率结果有效度平均耗时weather_query96.2%98.7%99.1%0.8steam_building88.5%91.3%89.6%2.3sinvoice_ocr99.1%95.8%94.2%1.5s6. 如果你也想引入 agent-skills这几条经验请收好6.1 先固化 5 个核心技能再谈扩展很多人都想把技能库一步建到位结果是技能设计粗糙、互相重叠、维护成本爆表。我的建议是第一个版本只挑 5 个最核心、最常用、最容易见效的业务场景做技能化。先把这 5 个技能的触发准确率和执行成功率磨到 95% 以上再继续扩展。技能化是一个“越用越准”的过程过早追求覆盖面只会让你被噪音淹没。6.2 技能描述的每一句话都要可测试“流畅地回答用户问题”这种描述是不可测试的。技能描述必须写成可以被验证的断言。比如“当用户请求中包含城市名和日期时触发”就是可测的“用户可能需要的场景”就是不可测的。我在评审每个技能的 SKILL.yaml 时都会问一个问题你能不能找个用户来试试这行描述会被错误触发吗如果不能测试就重写。6.3 日志是最值钱的资产千万别省技能化改造期间我最后悔的是前期没好好记录技能决策日志。那段时间排查问题全靠翻代码、看调用栈效率极低。后来我下定决心把全链路的日志补完整用户输入原文、匹配到的技能候选、最终决策结果、参数处理后的值、执行过程中的每步耗时、返回结果、验证结果全部落日志。有了这些每次翻车都能在几分钟内定位到根因。一段典型的技能日志长这样15:32:01.204 [decision] input北京明天天气怎么样 candidates[weather_query:0.94, clothing_advice:0.62, outdoor_activity:0.58] 15:32:01.210 [decision] selectedweather_query reasontop_score 15:32:01.318 [execute] skillweather_query params{city:北京,date:2025-06-15} statussuccess cost_ms107 15:32:01.325 [validate] skillweather_query result_statusvalid光看这几行日志你就能还原一次完整的调用过程。我强烈建议所有做 Agent 项目的人把“可观测性”当成和“功能”一样重要的事。聊到这里agent-skills 的基本玩法就说透了。从最开始纯提示词堆砌的脆弱 Agent到后来技能化改造后的稳定系统我最大的体会是技能化的本质是把大模型从“什么都要亲自做”变成“什么都知道该交给谁做”。这个思路的适用范围远不止 AI 应用任何需要“智能决策 确定性执行”的系统设计都可以借鉴这套方法。如果你也在做 Agent 相关的项目不妨从手头最痛的那个场景开始试着把它定义成一个技能。