恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Agent Skills 实战:从工具调用到技能封装的设计与部署
首页
资讯中心
/
Agent Skills 实战:从工具调用到技能封装的设计与部署
Agent Skills 实战:从工具调用到技能封装的设计与部署
发布时间:2026/10/7 12:04:50
1. Agent Skills 到底是什么从工具调用到技能封装的认知升级第一次看到 Agent Skills 这个词很多人会下意识地把它和 Function Calling、Tool Use 画等号。我一开始也这么理解直到真正动手把一个多步骤业务流程塞进 Agent 里跑通才发现这两者根本不在一个抽象层级上。Function Calling 解决的是模型能不能调用某个函数的问题而 Agent Skills 解决的是模型能不能稳定、可复用、可组合地完成一类任务的问题。前者是能力接口后者是能力封装。打个比方Function Calling 像是给 Agent 一把螺丝刀它知道怎么拧Agent Skills 则是给 Agent 一本装配手册里面写清楚了什么场景用哪把螺丝刀、拧几圈、拧完检查什么。手册可以反复用、可以拆成子章节、可以组合成更复杂的装配流程。这就是为什么最近 Agent Skills 会成为热词——大家发现光有工具不够工具背后的技能才是让 Agent 真正干活的关键。从技术演进的角度看这个概念的走红有几个推手。一是 Google Cloud 在 AI agents 方向上的持续投入尤其是 GKE 和 Genkit 这套组合让 Agent 的部署和编排有了工程化的落脚点二是 Claude 在 Agent Skills 上的实践被社区反复拆解那篇 a first principles deep dive 之所以传播广是因为它把技能的定义、边界、组合方式讲透了三是大量开发者在做 agent skills 测试时发现技能设计的好坏直接决定了 Agent 的成败而不是模型本身。这篇文章适合谁看如果你正在用 Genkit 或者类似框架搭 Agent如果你在 GKE 上部署过 AI agents 但总觉得行为不稳定如果你对 Agent Skills 的理解还停留在写个 prompt 让模型调工具的阶段那这篇内容应该能帮你把认知拉齐。我会从设计思路、核心细节、实操过程、问题排查四个维度展开尽量把踩过的坑和验证过的方案都摊开讲。2. 内容整体设计与思路拆解为什么要把技能单独抽出来2.1 从单体 Prompt 到技能模块的演进逻辑早期做 Agent最常见的做法是把所有指令塞进一个巨大的 system prompt 里你是一个客服助手你可以查订单、可以退款、可以查物流、可以转人工……然后挂上一堆工具定义。这种做法在 demo 阶段没问题一旦业务变复杂就崩了。崩的原因不是模型不行而是指令之间互相干扰。查订单的指令和退款的指令在同一个上下文里模型很容易在用户说我那个订单有问题的时候同时触发两个工具或者该走退款流程的时候走了查询流程。Agent Skills 的核心思路就是分而治之。把查订单封装成一个技能把退款封装成另一个技能每个技能有自己的触发条件、执行步骤、输入输出定义、异常处理逻辑。Agent 在运行时先做技能路由再在技能内部执行具体步骤。这样一来每个技能的上下文是干净的模型不需要在一个 prompt 里同时理解十件事。这个思路背后的工程考量很实际可测试性。单体 prompt 你没法单元测试改一个字可能影响所有流程。技能模块化之后每个技能可以单独测试、单独版本管理、单独灰度发布。我在实际项目里做过对比同样一个退款流程单体 prompt 方案的回归测试要跑全量用例技能化之后只需要跑退款技能相关的用例测试时间从 40 分钟降到 6 分钟。2.2 技能粒度怎么定三个判断标准技能粒度是设计时最纠结的问题。拆得太细技能之间调用关系复杂Agent 路由负担重拆得太粗又退化成单体 prompt。我总结下来有三个判断标准触发条件是否独立如果两个操作的触发条件高度重叠比如查订单和查物流都是用户问我的东西在哪那可以考虑合并成一个订单状态查询技能内部再分支。执行步骤是否可复用如果某个步骤在多个流程里都要用比如验证用户身份那就应该抽成独立技能被其他技能调用。失败处理是否一致如果两个操作的异常处理逻辑完全不同比如退款失败要人工介入、查询失败只需重试那就应该分开。注意技能粒度不是一次定死的建议先用粗粒度跑通主流程再根据实际运行数据做拆分。我见过太多人一开始就追求完美拆分结果卡在设计阶段两周没写出可运行的代码。2.3 与 GKE、Genkit 的配合关系Genkit 提供的是技能的定义和编排能力你可以用它的 flow 概念来定义技能用 tool 来定义技能内部的原子操作。GKE 提供的是运行环境Agent 作为一个服务跑在 GKE 上技能的路由、执行、日志、监控都在这个环境里完成。这个组合的好处是技能的生命周期管理和 Agent 的服务治理是打通的。技能更新可以走 GKE 的滚动发布技能调用链路可以走 GKE 的可观测性体系。我在 GKE 上部署过一个包含 12 个技能的 Agent通过 Cloud Logging 能看到每个技能的调用次数、成功率、平均耗时哪个技能是瓶颈一目了然。如果不用这套你得自己搭一套监控成本高很多。3. 核心细节解析与实操要点技能定义的关键字段3.1 技能描述怎么写才能让 Agent 正确路由技能描述是 Agent 做路由决策的唯一依据写得好不好直接决定路由准确率。我见过很多技能描述写成处理订单相关操作这种描述等于没写Agent 根本不知道什么时候该用。好的技能描述应该包含四个要素做什么、什么时候用、输入是什么、输出是什么。举个例子技能名称order_status_query 技能描述当用户询问订单的当前状态、配送进度、预计送达时间时使用此技能。输入为用户提供的订单号或手机号输出为订单状态、物流节点、预计送达时间。不适用于退款、修改地址等操作。注意最后那句不适用于这是负向描述能显著降低误路由。我在测试中发现加上负向描述后路由准确率从 78% 提升到 93%。原因是模型在做选择时不仅需要知道这个技能能干什么还需要知道这个技能不干什么才能和相邻技能区分开。3.2 输入输出的结构化定义技能之间的调用靠的是结构化数据不是自然语言。输入输出定义得越清晰技能组合时的摩擦越小。Genkit 里用 schema 来定义我建议至少包含这几个字段字段类型是否必填说明intentstring是技能意图标识用于日志和监控paramsobject是技能参数内部结构按技能自定义contextobject否上下文信息如用户 ID、会话 IDconstraintsobject否约束条件如超时时间、重试次数输出侧我习惯加一个status字段取值success、partial、failed再加一个next_action字段告诉 Agent 下一步该干什么。这个设计让技能之间可以形成链式调用而不需要 Agent 每次都重新做路由决策。3.3 技能内部的步骤编排一个技能内部通常有多个步骤比如退款技能可能是验证订单可退 → 计算退款金额 → 调用支付网关 → 更新订单状态 → 通知用户。这些步骤怎么编排我的经验是能用确定性代码就不用模型。验证订单可退、计算退款金额这种有明确规则的步骤直接写代码不要交给模型判断。模型只用在真正需要理解自然语言的地方比如解析用户说的退款原因。这样做的原因是确定性和成本代码步骤零幻觉、零 token 消耗模型步骤有幻觉风险、有成本。Genkit 的 flow 支持这种混合编排你可以在 flow 里穿插代码节点和模型节点。我一般把模型节点控制在技能总步骤的 30% 以内超过这个比例就要审视是不是设计有问题。提示技能内部的步骤要有明确的超时和重试策略。我遇到过支付网关偶发超时导致整个技能卡死的情况后来给每个外部调用都加了 3 秒超时和 2 次重试稳定性明显提升。4. 实操过程与核心环节实现从零搭一个可用的技能4.1 环境准备与项目初始化先假设你已经在 Google Cloud 上有了项目并且本地装了 Node.js 20 以上版本。第一步是初始化 Genkit 项目npm init -y npm install genkit genkit-ai/googleai genkit-ai/vertexai如果你打算部署到 GKE还需要装 Google Cloud CLI 并配置好 kubectl。我建议本地开发阶段先用 Genkit 的开发者 UI 调试那个界面能实时看到技能调用链路比看日志高效得多。初始化完成后创建技能目录结构。我习惯这样组织skills/ order-status/ index.ts schema.ts steps/ refund/ index.ts schema.ts steps/ router.ts每个技能一个目录schema 单独放steps 放内部步骤。router.ts 负责技能注册和路由。这个结构在技能数量增长到几十个的时候依然清晰。4.2 定义一个技能以订单状态查询为例先写 schemaimport { z } from genkit; export const OrderStatusInputSchema z.object({ orderId: z.string().optional(), phone: z.string().optional(), userId: z.string(), }); export const OrderStatusOutputSchema z.object({ status: z.enum([success, partial, failed]), orderState: z.string(), logisticsNodes: z.array(z.object({ time: z.string(), description: z.string(), })), estimatedDelivery: z.string().optional(), nextAction: z.string().optional(), });然后写技能主体import { ai } from ../genkit-config; import { OrderStatusInputSchema, OrderStatusOutputSchema } from ./schema; export const orderStatusSkill ai.defineFlow( { name: orderStatusSkill, inputSchema: OrderStatusInputSchema, outputSchema: OrderStatusOutputSchema, }, async (input) { // 步骤1定位订单纯代码 const order await locateOrder(input); if (!order) { return { status: failed, orderState: not_found, logisticsNodes: [], nextAction: ask_user_for_correct_info, }; } // 步骤2查询物流纯代码 const logistics await queryLogistics(order.id); // 步骤3生成自然语言摘要用模型 const summary await ai.generate({ prompt: 根据以下物流信息用一句话总结订单当前状态${JSON.stringify(logistics)}, }); return { status: success, orderState: order.state, logisticsNodes: logistics.nodes, estimatedDelivery: logistics.eta, nextAction: reply_to_user, }; } );注意步骤 1 和 2 是纯代码只有步骤 3 用了模型。这个比例是合理的。4.3 技能路由的实现路由是 Agent 的入口它接收用户输入决定调用哪个技能。最简单的实现是用模型做分类export const router ai.defineFlow( { name: router, inputSchema: z.object({ userInput: z.string(), userId: z.string() }), outputSchema: z.object({ skillName: z.string(), params: z.any() }), }, async (input) { const skills [ { name: orderStatusSkill, description: 查询订单状态、物流进度... }, { name: refundSkill, description: 处理退款申请... }, ]; const result await ai.generate({ prompt: 用户说${input.userInput}。可选技能${JSON.stringify(skills)}。请选择最合适的技能并提取参数以 JSON 返回。, output: { schema: z.object({ skillName: z.string(), params: z.any() }) }, }); return result.output!; } );这个路由方案在技能数量少于 20 个时表现不错。超过 20 个之后prompt 会变长路由准确率下降。这时候需要做分层路由先按业务域分大类再在大类内选具体技能。4.4 部署到 GKE 的关键配置本地跑通之后部署到 GKE 需要几个关键配置。首先是容器化Dockerfile 里注意把 Genkit 的运行时依赖打进去。然后是 GKE 的 Deployment 配置我一般这样设resources: requests: memory: 512Mi cpu: 250m limits: memory: 1Gi cpu: 1000m livenessProbe: httpGet: path: /health port: 3400 initialDelaySeconds: 10 readinessProbe: httpGet: path: /ready port: 3400 initialDelaySeconds: 5内存给到 1Gi 是因为模型调用的响应体可能比较大尤其是物流节点多的时候。CPU 限制 1000m 是因为技能执行大部分时间在等 IO不需要太多 CPU。注意GKE 上的 Agent 服务要配置好 HPA我一般按 CPU 70% 和自定义指标技能队列长度双指标扩缩容。纯 CPU 指标在 IO 密集场景下反应太慢。5. 常见问题与排查技巧实录那些文档里不会写的事5.1 技能路由错误的排查思路路由错误是最常见的问题表现是用户问 AAgent 走了 B 技能。排查分三步第一步看路由日志。Genkit 的开发者 UI 会记录每次路由的输入、候选技能、模型输出。先确认是模型选错了还是技能描述本身有歧义。第二步检查技能描述的重叠度。我遇到过一次查订单和查物流两个技能描述高度相似模型在用户说我的快递到哪了时随机选。解决办法是在查订单的描述里明确写不适用于查询物流进度。第三步如果描述没问题还是选错考虑加 few-shot 示例。在路由 prompt 里放几个典型输入和正确技能的对应关系准确率能提升 10 到 15 个百分点。5.2 技能执行超时与重试策略技能执行超时通常发生在外部依赖上比如支付网关、物流接口。我的处理原则是分层超时层级超时时间重试次数说明单次外部调用3s2快速失败避免拖累整体技能整体15s0超时直接返回 partialAgent 整体30s0超时返回兜底话术注意技能整体不重试因为重试可能导致重复扣款之类的副作用。如果技能是幂等的可以适当重试但要有幂等键。5.3 模型幻觉在技能内的表现与抑制即使技能内模型节点很少幻觉依然可能发生。最常见的幻觉是编造物流节点——模型在总结物流信息时如果输入数据不完整会自己补全。抑制方法有两个一是给模型的 prompt 里明确写只使用提供的数据不要补充任何未提供的信息二是在输出 schema 里加校验物流节点数量必须和输入一致不一致就降级为纯代码输出。我实测下来加了输出校验之后幻觉导致的错误回复从每周 3 到 5 次降到几乎为零。5.4 技能版本管理与灰度发布技能更新是高频操作没有版本管理会乱套。我的做法是每个技能带一个版本号路由时可以根据用户分组走不同版本。GKE 的 Service 可以配两个 Deployment一个稳定版一个灰度版通过 Istio 或者 Gateway 做流量切分。灰度期间重点看三个指标技能成功率、平均耗时、用户负反馈率。三个指标都稳定后再全量。我一般灰度 10% 流量跑 24 小时没问题再扩到 50%再 24 小时后全量。5.5 常见问题速查表问题现象可能原因排查方法解决方案路由到错误技能技能描述重叠看路由日志加负向描述或 few-shot技能执行卡住外部调用无超时看技能耗时分布加分层超时输出包含编造信息模型幻觉对比输入输出加输出校验和 prompt 约束技能更新后行为异常版本混乱看部署版本引入版本管理和灰度高并发下技能失败率上升资源不足看 GKE 指标调 HPA 和资源限制6. 技能组合与进阶玩法从单技能到技能网络6.1 技能链式调用的实现单个技能能做的事有限真正的价值在于技能组合。比如退款技能执行完后可能需要触发通知用户技能。实现方式有两种一种是在技能输出的nextAction里指定下一个技能由 Agent 编排层执行另一种是在技能内部直接调用另一个技能。我倾向于第一种因为编排逻辑集中在 Agent 层技能本身保持无状态和可复用。如果技能内部直接调其他技能技能之间就耦合了改一个影响一片。链式调用的关键是上下文传递。前一个技能的输出要能作为后一个技能的输入这要求技能之间的 schema 有兼容性。我的做法是定义一个通用的SkillContext所有技能都接收和返回这个上下文具体参数放在context.data里。6.2 技能的条件分支与循环有些业务流程需要条件分支比如如果退款金额大于 1000 元走人工审核技能否则走自动退款技能。这种逻辑放在 Agent 编排层用代码判断不要交给模型。循环场景比较少见但确实存在比如批量查询多个订单状态。这时候要注意循环次数上限我一般设 10 次超过就返回 partial 并提示用户分批处理。没有上限的循环在 Agent 里是灾难可能烧掉大量 token 还返回不了结果。6.3 技能的可观测性建设技能多了之后没有可观测性就是黑盒。我在 GKE 上搭了一套基于 Cloud Logging 和 Cloud Monitoring 的观测体系每个技能调用都打三个日志开始、结束、异常。日志里带技能名、版本、用户 ID、耗时、状态。基于这些日志可以做出几个关键看板技能调用量趋势、技能成功率、技能 P99 耗时、技能错误分布。这几个看板能覆盖 90% 的日常运维需求。我建议在技能数量超过 5 个之后就搭起来不要等到出问题才补。6.4 技能测试的自动化技能测试分三层单元测试测技能内部步骤集成测试测技能整体端到端测试测 Agent 路由加技能执行。单元测试用 mock 外部依赖集成测试用测试环境的外部服务端到端测试用真实用户场景的用例集。我维护了一个包含 200 多条用例的端到端测试集每次技能更新都跑一遍。这个测试集是逐步积累的每次线上出问题就补一条用例。跑一次大概 8 分钟能拦住大部分回归问题。提示端到端测试的用例要覆盖边界情况比如空输入、超长输入、特殊字符、并发调用。我遇到过用户输入里带 emoji 导致技能解析失败的 case补了用例之后再没出现过。7. 我在实际项目中的几点体会技能设计这件事最难的从来不是技术实现而是边界划分。我做过一个包含 30 多个技能的客服 Agent前期因为技能划分不合理路由准确率一直在 80% 左右徘徊。后来花了两周时间重新梳理技能边界把一些高频共现的技能合并把一些职责不清的技能拆分路由准确率提到了 95% 以上。这两周的投入比之前两个月的调 prompt 都值。另一个体会是不要过度依赖模型。Agent Skills 的魅力在于把确定性的部分用代码固化把不确定性的部分交给模型。我见过太多项目把本该用代码做的事交给模型结果就是不稳定、成本高、难调试。每次设计技能时问自己一句这一步真的需要模型吗如果答案是其实规则很明确那就写代码。最后分享一个小技巧技能描述写完后让团队里不熟悉这个业务的人读一遍问他你觉得这个技能什么时候用。如果他的理解和你的设计意图一致说明描述合格如果不一致说明描述有歧义需要改。这个土办法比任何自动化测试都管用因为 Agent 路由本质上就是在模拟一个不熟悉业务的人做判断。