
1. 从“skills”这个标题说起一个被低估的Agent能力单元第一次看到“skills”这个标题配合热搜词里的 Agent Skills、Google Cloud、AI agents、GKE、Genkit我大概能猜到这背后想聊的是什么——不是某个具体框架的 API 文档而是AI Agent 的能力封装单元这件事本身。这个词在 2024 年下半年开始被反复提及尤其是 Claude 的 Agent Skills 概念出来之后整个行业对“Agent 到底该怎么组织能力”这件事有了新的讨论。我先说结论skills 的本质是把“一段可复用的专业能力”从 Agent 的主循环里剥离出来做成独立、可组合、可版本管理的模块。这件事听起来简单但它解决的是过去两年 Agent 开发里最让人头疼的问题——prompt 越写越长、工具越挂越多、上下文越来越乱、换个场景就得重写一遍。如果你正在做 AI Agent 相关的开发不管是用 Google 的 Genkit、跑在 GKE 上的服务化 Agent还是自己手搓一套 tool-calling 逻辑这篇文章都值得你看完。我会从 skills 的核心设计逻辑讲起拆解它和传统 function calling、tool use 的区别然后给出一套可以落地的工程化方案最后聊聊我在实际项目里踩过的坑。提示本文讨论的 skills 是通用意义上的“Agent 能力封装模式”不绑定任何特定厂商的实现细节但会结合 Google Cloud 生态Genkit、GKE给出参考路径。2. skills 到底解决了什么问题从 prompt 膨胀说起2.1 一个真实的 Agent 项目是怎么失控的我拿一个实际场景举例。假设你要做一个“企业文档助手 Agent”它需要查内部知识库、读 PDF、做摘要、发邮件、查日历、生成周报。最开始你可能用一个 system prompt 加五六个 tool 定义就搞定了。但三个月后需求变成二十个工具prompt 从 800 token 涨到 6000 token每次调用都要把全部工具描述塞进上下文。这时候问题就来了上下文成本爆炸每次请求都要带上所有工具的 schematoken 消耗翻倍。工具选择准确率下降模型面对 20 个工具选错的概率明显上升。维护困难改一个工具的 description可能影响其他场景的表现。无法复用另一个项目也需要“读 PDF 摘要”这套能力但你只能复制粘贴。这就是 skills 要解决的核心矛盾Agent 的能力在增长但上下文窗口和模型注意力是有限资源。2.2 skills 的核心思路能力按需加载skills 的设计哲学其实很像我们写代码时的模块化。你不会把所有函数都写在一个文件里而是按职责拆成模块用到哪个 import 哪个。skills 对 Agent 做的事是一样的每个 skill 是一个独立的能力单元有自己的名称、描述、执行逻辑。Agent 主循环只持有 skill 的索引名称 一句话描述不持有完整实现。当模型判断需要某个 skill 时才把它的完整定义加载进上下文。skill 可以组合一个复杂任务可以调用多个 skill 串联完成。这个模式和传统的“把所有 tool 一次性塞给模型”有本质区别。前者是惰性加载后者是全量暴露。在工具数量少的时候差别不大但工具一多差距就是数量级的。2.3 和 function calling、tool use 的关系很多人会问这不就是 function calling 吗我的理解是function calling 是机制skills 是组织方式。维度传统 tool useskills 模式工具暴露方式全量塞进上下文索引 按需加载能力粒度单个函数可组合的能力包复用性复制 schema独立模块跨项目引用版本管理无可独立版本化上下文成本随工具数线性增长基本恒定function calling 解决的是“模型怎么调用外部函数”skills 解决的是“这些函数怎么组织才不失控”。两者是互补的不是替代。3. 拆解一个 skill 的解剖结构不只是 prompt 加函数3.1 skill 的四个核心组成部分我在实际项目里总结下来一个设计良好的 skill 通常包含四部分元信息metadata名称、一句话描述、适用场景标签。这部分是常驻上下文的必须极度精简。触发条件when to use什么情况下该用这个 skill。这是模型做路由判断的依据。执行逻辑how to execute具体的 prompt 模板、工具调用链、后处理逻辑。输入输出契约I/O schema明确的参数定义和返回格式保证可组合。很多人做 skill 只做了第 3 部分结果就是模型不知道该什么时候用它或者用完了不知道怎么接下一步。元信息和触发条件才是 skills 模式真正的价值所在。3.2 元信息为什么要“极度精简”这里有个反直觉的点skill 的描述不是越详细越好。因为元信息是常驻上下文的你有 30 个 skill每个描述 100 token那就是 3000 token 的固定开销。所以描述要控制在一句话、20 token 以内把详细信息放到触发后加载的部分。我试过两种写法对比很明显写法 A差这个 skill 用于处理 PDF 文档可以提取文本、识别表格、生成摘要支持中英文适用于合同、报告、论文等多种场景……80 token写法 B好提取 PDF 文本并生成结构化摘要15 token写法 B 在 30 个 skill 的场景下光元信息就省了将近 2000 token。而且实测下来模型对简短描述的路由准确率反而更高因为干扰信息少了。3.3 触发条件的设计技巧触发条件是 skill 路由的关键。我的经验是不要写“什么时候用”而要写“什么信号出现时用”。比如差当用户需要处理文档时好当输入包含 .pdf 路径或用户提到这份文件时前者太模糊模型没法判断后者给了明确的信号词和模式路由准确率能提升一大截。这个技巧是我在调试了几十个 skill 之后才悟出来的本质上是在帮模型做特征匹配而不是让它做语义推理。4. 在 Google Cloud 生态里落地 skillsGenkit 与 GKE 的配合4.1 为什么选 Genkit 做 skill 编排层Genkit 是 Google 推出的 AI 应用开发框架它最大的价值在于把 prompt、tool、flow 这三件事统一到了一套抽象里。用它来做 skills 编排有几个实际好处Flow 天然适合封装 skill一个 flow 就是一个可组合的执行单元输入输出都有 schema 约束。内置 tool calling 支持不用自己处理 function calling 的协议细节。可观测性Genkit 自带 tracing调试 skill 调用链的时候非常省事。我一般的做法是一个 skill 对应一个 Genkit flowflow 的 input schema 就是 skill 的 I/O 契约flow 内部的 prompt 和 tool 调用就是 skill 的执行逻辑。// 一个 PDF 摘要 skill 的 Genkit flow 示例 import { genkit, z } from genkit; import { googleAI } from genkit-ai/googleai; const ai genkit({ plugins: [googleAI()] }); export const pdfSummarySkill ai.defineFlow( { name: pdfSummary, inputSchema: z.object({ filePath: z.string() }), outputSchema: z.object({ summary: z.string(), keywords: z.array(z.string()) }), }, async ({ filePath }) { const text await extractPdfText(filePath); const { output } await ai.generate({ model: googleAI.model(gemini-2.0-flash), prompt: 请对以下文档生成结构化摘要并提取5个关键词\n${text}, output: { schema: z.object({ summary: z.string(), keywords: z.array(z.string()) }) }, }); return output; } );这段代码的关键点在于input 和 output 都有严格的 schema。这是 skill 可组合的前提——上一个 skill 的输出能直接喂给下一个 skill。4.2 用 GKE 做 skill 的服务化部署当 skill 数量多起来之后把它们全部打包在一个进程里就不合适了。我的做法是把高频、重资源的 skill 拆成独立服务部署到 GKE 上主 Agent 通过内部 API 调用。这么做的理由有三个独立扩缩容PDF 解析这种 CPU 密集型的 skill和纯 prompt 的 skill资源需求完全不同混在一起浪费。故障隔离一个 skill 挂了不影响主 Agent 的其他能力。版本独立发布改一个 skill 不用重新部署整个 Agent。在 GKE 上部署时我一般用Cloud Run 处理无状态 skill用 GKE 处理需要 GPU 或长连接的 skill。这个分工是根据实际负载特征来的不是拍脑袋定的。4.3 skill 注册中心的实现思路主 Agent 怎么知道有哪些 skill 可用这就需要一