
Agent技能工程实战从“能对话”到“能干活”的完整链路这几年做 AI 应用我最大的感受是很多人把 Agent 当成一个更聪明的聊天机器人结果搭出来的东西要么一问就瞎编要么一调用工具就断片。真正让 Agent 发挥价值的地方恰恰在“技能”这两个字上——也就是 agent-skills面向智能体的技能库它决定了一个 Agent 是只会说还是真能办事。如果你正在做智能客服、自动化运维、内容生产助手或者准备给 Agent 接入各种内部工具这篇文章就是写给你的。我会从技能划分、封装规范、编排流程、问题排查几个方面把一套我自己在项目里跑通的套路拆开来讲。内容偏实操不搞玄学。1. Agent技能体系的设计别一上来就堆Prompt很多人拿到 Agent 开发框架第一件事就是写一个超长的 System Prompt把希望 AI 干的事全塞进去。这个思路在 demo 阶段没问题一旦进入真实业务你会发现 Prompt 越长模型越容易迷路。我更推荐的方式是把能力拆成一个个独立的技能让 Agent 按需调用。1.1 技能的粒度从“写周报”到“读取Git提交记录”技能粒度是个特别容易走极端的事。太粗比如“写周报”Agent 接到指令后依然不知道从哪拿数据太细比如“读取某个文件的第三行”又会让技能库膨胀到没法维护。我自己的经验是一个技能只做一件可以被独立验证的事。以“写周报”为例。很多人会设计成一个大技能让 Agent 自己决定怎么搜集信息、怎么排版、怎么输出。实际上拆成下面几个小技能整体反而更稳定获取指定日期范围的Git提交记录汇总任务管理系统中的已完成事项读取团队成员备注的周报素材按既定模板生成周报文本每个小技能可以单独测试单独调优。如果某一步出问题直接定位到那一个技能去修不用把整个 Prompt 翻出来重写。这个思路跟微服务有点像把一个单体应用拆成多个可独立部署的服务复杂度从横向摊开了反而更好控制。1.2 技能的三要素触发条件、运行逻辑、退出机制一个设计良好的技能至少要包含三个部分触发条件、运行逻辑、退出机制。这三个部分分别回答三个问题什么时候用这个技能执行的时候具体做什么做完之后怎么收尾触发条件不是写在代码里的 if 判断而是写在技能的描述信息里。模型会通过你的描述去判断当前任务跟哪个技能匹配。描述写得不清楚再好的技能也调用不起来。运行逻辑是技能的主体接收参数、调用外部系统、处理数据、生成结果。这里有一个很容易被忽略的点技能内部不要做太多“智能”的事比如根据心情换个输出风格。技能做的应该是确定性的操作把不确定性留给Agent主模型去处理。退出机制很多人会漏掉。一个技能执行完需要明确返回什么结构的数据以及在什么情况下算失败失败后应该给 Agent 返回怎样的提示。没有这个Agent 就会在某个分支上重复打转浪费大量 token。2. 技能封装的实操要点把“能跑”做成“好用”把技能从“能跑通”提升到“好调用”中间隔着不少细节。这个环节最容易出问题的地方反而不是代码逻辑而是 Agent 与技能之间的接口契约。模型不像程序一样严格拘泥于类型它靠语义理解所以你的接口设计要考虑的是让“模糊的理解”也能找到“确定的路径”。2.1 上下文窗口的约束技能代码要“拆得碎、接得住”大模型上下文窗口再大也不是无限使用的。一个技能如果一次性拉回几十页日志后续对话基本就废了Agent 会把注意力全花在无关内容上甚至开始胡编乱造。我处理这个问题的方式是“分段拉取摘要前置”。比如查询日志先让技能支持时间范围、关键字、条数上限这些参数再返回时强制只给摘要同步提供“是否需要查看原始详情”的选项。这样一来Agent 的上下文不会被冲爆也能在需要深挖时精准地调用详细查询接口。这里有一个实际测试过的参数参考单次技能调用返回结构化内容时控制在2000个token以内比较安全如果超过5000个token模型理解准确度就会明显下降。设计技能时可以把“分页参数”作为标配哪怕第一版只有简单查询也提前留好 max_results 和 page 字段后面大概率用得上。2.2 技能之间的依赖管理与冲突避免技能多了以后不可避免会遇到两个问题技能之间互相依赖、技能之间功能重叠。这两个问题处理不好Agent 就会表现出“明明有这个能力但就是不用”的奇怪状态。先看依赖。技能A需要技能B的结果才能继续这没问题但你要有意识地控制依赖链的长度。我给自己定的规矩是一个技能最多依赖一个上游技能且上游失败时当前技能必须能给出降级方案。比如获取用户订单详情这个技能依赖上游的登录态校验。登录态过期时订单详情技能可以返回特定错误码让 Agent 引导用户重新授权而不是卡死在原地。再看冲突。两个技能描述相似、功能接近会导致模型随机选择输出质量不稳定。我之前就遇到过“发送系统通知”和“创建系统消息”两个技能并存模型有时走前者有时走后者最后消息格式都不一样。解决办法是合并同类项或者明确划分适用场景在描述里写清楚“当XX时使用本技能当YY时使用另一个技能”。边界越清晰模型的选择就越稳定。2.3 工具描述的艺术写给模型看的“说明书”技能注册到 Agent 框架里通常会有一份描述文本。别小看这段描述它就是给模型看的说明书。写法上有一个关键原则描述的是“什么时候用”和“用来干什么”而不是“技术实现细节”。错误示例“调用 /api/v1/orders/get 这个接口获取订单参数为 order_id类型为 string返回字段包括 amount、status、create_time。” 这样写模型也能用但使用意愿和准确率都不高。更好的写法“获取用户订单的核心信息包括金额、状态、创建时间。当用户询问订单详情、查询购买记录时使用当用户需要取消订单时不要使用本技能改用取消订单技能。注意order_id 必须是完整订单编号包含区域前缀。” 这种写法有一个显著优势模型可以通过语义快速匹配而且比纯参数描述更加不容易用错。3. 一次完整的技能编排实现从需求到可复用纸上谈兵聊完了我拿一个自己实际做过的场景当例子完整走一遍技能编排自动生成跨仓库代码变更周报。这个需求看起来简单实际牵扯多个数据源Git提交、合并请求、任务状态、团队成员备注。如果让 Agent 直接去问系统拿数据它连去哪问都不知道。3.1 场景拆解把模糊需求变成可执行节点我处理这个需求时没有急着写代码先列了一张图需求要什么信息这些信息从哪里来拿到之后怎么组装。拆到最后得到五个节点获取指定时间范围内的Git提交记录按仓库分组提取提交人和提交时间获取指定仓库的合并请求列表提取标题、状态、review人获取当前迭代的任务看板提取“已完成”和“进行中”的任务卡片汇总以上数据按“变更摘要—关键任务—风险点”结构生成报告正文把报告发送到指定讨论频道并附带原数据链接这五个步骤每个对应一个技能。第4步比较特殊它不是跟外部系统交互而是纯粹做文本生成但这个生成必须是结构化的不能自由发挥。所以命名为“生成周报框架”参数为变更条目数组输出为初步组装的报告Markdown文本。3.2 技能注册与调用描述、参数、返回值的规范把这五个技能落到工程语言需要统一注册。下面是我常用的一种注册格式用 JSON 描述技能元信息运行时由框架解析并注入 Agent 的工具列表{ name: fetch_git_commits, description: 获取指定仓库和日期范围内的提交记录按提交人分组汇总。当用户需要查看代码变更、周报数据、仓库动态时使用。, parameters: { type: object, properties: { repo: {type: string, description: 仓库名必填}, start_date: {type: string, description: 开始日期格式YYYY-MM-DD}, end_date: {type: string, description: 结束日期默认今天} }, required: [repo, start_date] }, returns: { type: array, items: { committer: string, commit_count: integer, summary: string } } }这里最值得花心思的是 description 字段。我每次写完都会自问如果我现在是一个什么都不懂的模型只看到这句话我知道什么时候调用它吗能分清它跟另一个相似技能的区别吗通常这样一问就能逼自己把描述改得更精确。3.3 状态管理与结果回传别忘了记忆技能编排过程中Agent 需要在多个技能之间穿梭中间结果怎么暂存是个问题。最简单的方案是让 Agent 把中间结果放在对话上下文里靠自然语言记住“刚才拿到了哪些仓库的提交记录”。这在技能少、数据量小的时候没问题但一旦数据多了对话上下文会变成一团乱麻。我实际的方案是在框架里维护一个轻量级的状态存储每个技能的返回值在进入下一步之前先存入一个全局变量字段比如$context.commits下一步技能直接从$context.commits读取而不用模型从对话历史里重新翻找。这个方案的好处是省 token、稳定、可调试——出问题时直接查看状态存储就知道中间链路哪一步出了问题。还有一个细节技能的超时与失败重试。我在封装技能时都会加一个独立的重试逻辑只针对外部接口的临时性故障。重试代码很简单但意义很大——没有它Agent 经常会在一次失败后就放弃整个任务表现就是“看起来不太聪明”。4. 常见问题与一份解决清单技能体系跑久了遇到的问题类型其实比较集中。这里整理几个高频问题附上我的排查思路。4.1 规避“工具描述”失效导致的乱用“技能明明存在模型却不用”是我被问最多的问题。这类问题的原因有几种可能技能描述写得像技术文档模型看完不知道什么时候用多个技能描述里出现了相同的关键词模型不知道选哪个技能的参数设计太复杂模型觉得“用起来麻烦”排查方式也很直接把模型的系统提示词和技能描述全部输出自己模拟一遍决策过程。只要照着描述走一遍通常能找到是哪里让模型迷路了。我自己的经验是命中了某个技能但效果不对时第一反应不应该是换模型而是想想这个技能的“触发条件”是否写清楚了业务出现的场景。4.2 上下文污染与历史信息干扰“Agent 第一次调用技能成功第二次就翻车”这个现象多半不是模型变笨了而是对话上下文被历史信息污染了。技能返回的大量内容残留在上下文里模型在后续推理时把旧数据当成了当前数据。我的对策有三层减少单次返回体量敏感数据在技能内部做脱敏每次调用结束时返回给模型的内容后面都带一行更新提示“以上为当前查询结果后续所有相关任务请以本次返回作为参考”。实际上还有第四层也是最重要的——在设计阶段就尽量少依赖长对话来完成复杂任务能拆成“一次性多技能并行调用”就不要让 Agent 来回对话多轮。4.3 多个技能编排时的死循环与费用失控技能编排有时会因为一个分支判断不清而陷入死循环。一个典型的场景是技能A返回失败Agent 觉得应该是数据没更新转去调技能B刷新数据数据更新后技能A继续失败因为失败根因是权限不足。这样来来回回几分钟能烧掉几百上万个 token。我现在的处理原则是每个外呼类型的技能必须自带“单一失败原因”字段。技能内部只做基础判定遇到权限、数据、网络三类错误时返回固定代码。Agent 看到代码后直接走对应分支而不是自己反复猜测全局原因。另外还有一个习惯救了我很多次任务级总预算限制在 Agent 每次调用模型前检查累计 token 消费超过阈值就强制终止再由上层模块决定是否继续另一个维度处理。4.4 盘点我的优先级排查速查表现象优先排查项次要排查项技能未被调用描述是否含场景触发词参数是否过度设计调用了但结果错技能内部逻辑分支上游传参是否被篡改多次调用后不稳上下文是否被污染返回体量是否过大任务中途放弃失败重试是否就位超时参数是否过短token费用暴增是否出现循环调用是否缺少任务级预算守护这张表是我自己在多个项目里用下来的通用排查顺序不一定每个问题都命中但基本能覆盖 80% 的故障场景。5. 技能评估与迭代别用感觉去优化技能做出来之后怎么判断它好不好用很多人的做法是把 Agent 拉出来对话几次感觉不错就说做完了。这个方式在小 demo 里可以用但在生产环境里必须有更立体的评估维度。5.1 成功率之外还要看什么“成功率”是第一个想到的指标但它混淆了很多因素——有可能是技能本身没问题只是触发的时机不对也有可能是技能压根没被触发但碰巧外部系统返回了正确数据。我通常会把成功率拆开看触发准确率该用这个技能的场景模型是否正确地选中了它执行成功率触发后技能本身是否正常完成输出适配率技能返回的数据是否符合期待的结构和调用方需求这三个指标分开统计才能真正定位瓶颈。之前我遇到过一个“请求用户信息”的技能执行成功率很高但输出适配率只有六成一查发现是模型把一次对话里的多个条件当成了一组导致查询结果只覆盖了其中一半。分开统计后问题立刻变得清晰。5.2 结合回归测试进行技能迭代技能迭代和普通的软件迭代一样需要防止“修了A坏了B”。我在项目里会维护一套“典型问题集”收集线上用户提问、人工标注“理想答案路径”、每次修改完技能后跑一遍。如果某次改动让理想路径被破坏立即回滚。这里有一条很重要的心得技能库的测试成本不比普通程序低甚至更高因为模型调用技能的路径有概率性。同一个技能模型可能走不同的参数组装方式造成输出不稳定。因此我的测试集是“多次采样、多数通过”的模式每个用例跑三到五次看是否稳定通过而不是单次过就放行。代价是烧 token 比较多但比起线上翻车这笔钱花得值得。6. 一点真实的体会做得越久我越觉得 agent-skills 的核心难点不在“写一个会调用工具的Agent”而在“让这个Agent知道什么时候该用哪个工具、用完怎么把结果组织回去”。前者是工程能力后者更像是一套接口语义设计。我现在接手一个 Agent 项目第一步永远是盘点要接入的技能而不是先调模型参数。技能清单列清楚、描述写好、边界划明白后面再微调模型会顺手很多。反过来如果技能设计一团乱麻换再强的模型也救不回来。一个具体的建议如果你的技能库还处于早期阶段先控制数量不超过20个。每增加一个都要同步检查是否跟现有技能功能重叠描述是否需要差异化。把时间花在打磨技能的接口描述上是性价比最高的优化。最后再分享一个小技巧在线上的技能调用日志里把模型每次调用技能前的“思考文本”打出来。这玩意儿是排查问题的金矿——模型为什么不选这个技能、为什么给了错误的参数、为什么中途放弃看一眼思考文本基本就明白了。这比读任何框架文档都管用。