尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
AI Agent开发实战:基于Genkit与GKE的skills能力封装与编排
1. 从“skills”这个词说起为什么它突然成了 Agent 圈子的高频词如果你最近在折腾 AI Agent大概率会在各种技术社区、开源仓库、甚至招聘 JD 里反复撞见skills这个词。它不再只是简历上“熟练掌握某某技能”的泛泛表述而是变成了一个具体的、可被工程化封装的技术单元。我最早注意到这个趋势是在研究 Google Cloud 上几个 Agent 落地案例的时候——无论是 GKE 上跑的推理服务还是 Genkit 里编排的对话流程大家都在讨论怎么把“能力”拆成一个个独立的 skill然后像搭积木一样组合起来。这件事的本质其实不复杂。早期的 Agent 开发基本是把所有逻辑塞进一个巨大的 prompt 或者一个超级函数里模型既要理解意图又要决定调用哪个工具还要处理返回结果。这种“一锅炖”的做法在 demo 阶段很爽一旦要接入真实业务、要多人协作、要反复迭代就会立刻崩盘。skills 这个概念的出现就是为了解决“能力复用”和“职责边界”这两个核心痛点。你可以把它理解成给 Agent 准备的“技能卡片”每张卡片定义了一件事怎么做、需要什么输入、会产出什么输出Agent 只需要根据当前任务去“抽卡”就行。这篇文章适合谁看如果你正在用 Gemini、Genkit 或者类似框架做 Agent 开发或者你手头有 GKE 集群想跑一些智能化的任务编排再或者你只是好奇“agent skills 测试”到底在测什么那接下来的内容应该能帮你省下不少翻文档的时间。我会从设计思路、核心细节、实操过程到踩坑记录把 skills 这套东西拆开揉碎讲一遍。文中涉及的具体参数和代码都是基于常见实践补全的你可以直接拿去改改用。2. 整体设计思路为什么要把能力拆成 skill2.1 从“单体 Agent”到“技能编排”的演进逻辑我刚开始做 Agent 的时候习惯写一个巨大的handle_request函数里面用 if-else 判断用户意图然后调用对应的 API。这种写法在只有三五个功能的时候还能忍一旦功能上到二十个代码就变成了意大利面条——改一个地方三个地方报错。更麻烦的是当你想让模型自己决定用哪个功能时你得把所有功能的描述都塞进 prompttoken 消耗巨大不说模型还经常选错。skills 的思路是把每个能力独立成一个模块每个模块有自己的元数据描述叫什么、干什么、什么时候用、输入输出契约需要什么参数、返回什么结构和执行逻辑具体怎么调 API、怎么处理异常。Agent 在运行时先根据用户请求检索出最相关的几个 skill再把它们的描述注入到上下文里让模型做最终选择。这样做的好处非常明显上下文长度可控、职责清晰、单个 skill 可以独立测试和迭代。提示不要一上来就把所有 skill 都注册给 Agent。我试过一次性挂 30 个 skill结果模型的选择准确率反而下降了。后来改成先做一层粗筛只把 Top-5 相关的 skill 给模型看准确率立刻回升。2.2 选型考量为什么是 Genkit Gemini GKE 这套组合市面上做 Agent 编排的框架不少我最终倾向 Genkit 的原因有几个。第一它和 Gemini 的集成是原生的定义 skill 的时候可以直接用 Gemini 做意图识别和参数抽取省掉很多胶水代码。第二Genkit 的 flow 概念天然适合封装 skill——一个 flow 就是一个可独立部署、可独立测试的单元和 skill 的粒度刚好匹配。第三GKE 提供了稳定的运行时环境skill 多了之后可以按需扩缩容不会因为某个 skill 调用量突增把整个 Agent 拖垮。当然这套组合也不是没有代价。Genkit 的生态相对年轻有些第三方库的适配还不完善GKE 的运维成本对于小团队来说偏高。如果你的场景比较简单其实用 Cloud Run 跑 skill 也够用等量上来了再迁到 GKE 也不迟。技术选型没有绝对的对错关键是看你的团队规模和业务增速。2.3 skill 的粒度怎么定一个容易被忽视的关键决策这是我在实际项目里踩过最大的坑。一开始我把“查询天气”和“根据天气推荐穿搭”放在同一个 skill 里结果发现前者是纯数据获取后者需要推理两者的测试用例、失败模式、调用频率完全不一样混在一起非常难维护。后来我总结了一个判断标准如果一个 skill 的描述里出现了“然后”“接着”“根据……再……”这类词大概率应该拆成两个。另一个极端是拆得太细。我见过有人把“发送邮件”拆成“填写收件人”“填写主题”“填写正文”“点击发送”四个 skill这就过度了。skill 的粒度应该对齐“一个完整的业务动作”而不是“一个 UI 操作”。判断方法很简单问自己“这个 skill 能不能独立完成一件对用户有意义的事”能就是合适的粒度。3. 核心细节解析一个 skill 到底由什么组成3.1 元数据设计让模型和人都能看懂每个 skill 的元数据至少包含四个字段name、description、when_to_use、examples。name用英文小写加下划线保持全局唯一description用一句话说清楚这个 skill 做什么不要超过 30 个字when_to_use是关键要写清楚在什么场景下应该调用它这是模型做选择时最主要的依据examples给两到三个典型请求的示例帮助模型理解边界。我实测下来when_to_use写得好不好直接决定了 skill 的调用准确率。举个例子一个“查询订单状态”的 skill如果when_to_use只写“用户想查订单”模型经常在用户说“我买的东西到哪了”的时候选不中。改成“用户询问已购买商品的物流进度、配送状态、预计送达时间时使用”命中率明显提升。写元数据的时候要想象你是在给一个刚入职的实习生写操作手册越具体越好。3.2 输入输出契约用 schema 把边界锁死Genkit 里用 Zod 来定义输入输出的 schema这是保证 skill 稳定性的关键。输入 schema 要明确每个字段的类型、是否必填、取值范围输出 schema 要定义成功和失败两种结构。我习惯在输出里统一加一个status字段取值success或error再加一个message字段放人类可读的说明这样上层 Agent 处理起来不用做各种兼容判断。import { z } from genkit; const OrderQueryInput z.object({ orderId: z.string().describe(订单编号通常是 16 位数字), userId: z.string().optional().describe(用户 ID用于权限校验), }); const OrderQueryOutput z.object({ status: z.enum([success, error]), message: z.string(), data: z.object({ orderStatus: z.string(), estimatedDelivery: z.string().optional(), }).optional(), });注意schema 里的describe不是写给人看的注释它会进入模型的上下文直接影响参数抽取的准确率。所以描述要写得像给模型看的提示词而不是给同事看的文档。3.3 执行逻辑异常处理和超时控制是生命线skill 的执行逻辑里最容易被忽视的就是异常处理。我见过太多 skill 在 API 返回 500 的时候直接抛异常导致整个 Agent 流程中断。正确的做法是在 skill 内部捕获所有异常转换成统一的错误输出返回给上层。另外每个 skill 都要设置超时时间我一般设 10 秒超过就返回超时错误避免一个慢 skill 拖死整个对话。还有一个细节是幂等性。如果 skill 涉及写操作比如下单、发消息一定要考虑重复调用的问题。我的做法是在输入里加一个requestIdskill 内部记录最近处理过的 requestId重复的直接返回上次的结果。这个设计在 Agent 重试的时候能救命。4. 实操过程从零搭建一个可用的 skill 系统4.1 环境准备与依赖安装先确保你的开发机上有 Node.js 20 以上版本然后初始化项目并安装 Genkit 相关依赖。如果你打算部署到 GKE还需要提前装好 gcloud CLI 和 kubectl并且确保你的账号有 GKE 集群的操作权限。npm init -y npm install genkit genkit-ai/googleai zod npm install -D typescript tsx types/node配置 Gemini 的 API key 时我建议用环境变量而不是硬编码。在项目根目录建一个.env文件写入GEMINI_API_KEY你的密钥然后在代码里用dotenv加载。这样本地开发和 GKE 部署可以用同一套代码只是环境变量不同。4.2 定义第一个 skill以“查询订单状态”为例我们从头写一个完整的 skill。先定义 schema再写执行函数最后用 Genkit 的defineTool把它注册成模型可调用的工具。import { genkit, z } from genkit; import { googleAI } from genkit-ai/googleai; const ai genkit({ plugins: [googleAI()], model: googleai/gemini-1.5-flash, }); const orderQueryTool ai.defineTool( { name: query_order_status, description: 查询指定订单的当前状态和预计送达时间, inputSchema: OrderQueryInput, outputSchema: OrderQueryOutput, }, async (input) { try { const result await fetchOrderFromAPI(input.orderId); return { status: success, message: 查询成功, data: { orderStatus: result.status, estimatedDelivery: result.eta, }, }; } catch (err) { return { status: error, message: 查询失败${err.message}, }; } } );写完这个 tool 之后你可以用 Genkit 自带的开发者界面做单测。运行npx genkit start在浏览器里打开调试面板直接输入参数调用这个 tool看返回是否符合预期。这一步千万别跳过我见过太多人直接集成到 Agent 里才发现 schema 写错了。4.3 把 skill 接入 Agent 流程定义好 tool 之后创建一个 flow 来编排整个对话。核心逻辑是接收用户输入让 Gemini 决定调用哪个 tool执行 tool把结果返回给模型生成最终回复。export const agentFlow ai.defineFlow( { name: agentFlow, inputSchema: z.string(), outputSchema: z.string(), }, async (userInput) { const response await ai.generate({ prompt: userInput, tools: [orderQueryTool], system: 你是一个客服助手根据用户问题调用合适的工具。, }); return response.text; } );这里有个细节tools数组里放的是所有可用的 skill。前面说过skill 多了之后要做粗筛但初期只有几个的时候直接全量传入就行。等 skill 数量超过 10 个再考虑加一层检索逻辑。4.4 部署到 GKE容器化和扩缩容配置本地跑通之后下一步是部署到 GKE。先写 Dockerfile基于node:20-slim镜像把代码复制进去安装依赖暴露端口。然后构建镜像推送到 Artifact Registry再写 Kubernetes Deployment 和 Service。apiVersion: apps/v1 kind: Deployment metadata: name: agent-skills spec: replicas: 2 selector: matchLabels: app: agent-skills template: metadata: labels: app: agent-skills spec: containers: - name: agent image: us-central1-docker.pkg.dev/your-project/agent/skills:v1 ports: - containerPort: 3400 env: - name: GEMINI_API_KEY valueFrom: secretKeyRef: name: gemini-secret key: api-key resources: requests: memory: 512Mi cpu: 250m limits: memory: 1Gi cpu: 500m副本数我一般从 2 开始配合 Horizontal Pod Autoscaler 根据 CPU 使用率自动扩缩。注意把 API key 放在 Secret 里不要写在 Deployment 的 env 里明文暴露。这个坑我踩过后来被安全扫描扫出来改了半天。5. 常见问题与排查技巧实录5.1 skill 调用准确率低怎么办这是最高频的问题。排查顺序我一般是这样的先看when_to_use写得够不够具体再看examples有没有覆盖边界情况最后看是不是 skill 数量太多导致模型选择困难。如果这三步都排查了还是不行可以试试在 system prompt 里加一句“如果不确定用哪个工具先向用户确认”让模型有退路。还有一个隐藏原因是 skill 的name起得太像。比如get_user_info和query_user_profile模型很容易混。命名要尽量差异化动词和名词都拉开距离。5.2 参数抽取错误怎么定位参数抽取错误通常表现为模型把用户说的“昨天”填到了orderId字段里。这种情况先检查 schema 里的describe是不是写得太模糊。如果orderId的描述只写“订单号”模型可能不知道格式要求。改成“16 位纯数字的订单编号例如 2024010112345678”准确率会好很多。另外Gemini 对中文的理解整体不错但遇到口语化表达时偶尔会抽错。我的做法是在 skill 内部加一层校验如果参数格式不对直接返回错误让模型重新问用户而不是硬着头皮去调 API。5.3 GKE 上 skill 响应慢的排查思路响应慢的原因可能出在三个地方模型推理慢、skill 执行慢、网络传输慢。排查方法是加日志打点记录每个阶段的耗时。如果模型推理占了大头可以考虑换更小的模型或者做 prompt 压缩如果是 skill 执行慢看看是不是 API 调用没有设超时如果是网络问题检查 GKE 集群和 API 服务是不是在同一个区域。我遇到过一次是因为 skill 里调用的外部 API 没有连接池每次请求都新建连接导致延迟很高。加上连接复用之后P99 延迟从 3 秒降到了 400 毫秒。这种问题不看日志根本发现不了。5.4 常见问题速查表问题现象可能原因排查方法解决建议skill 不被调用when_to_use 描述模糊检查元数据描述补充具体场景和示例参数抽取错误schema describe 不清晰查看模型输入上下文细化字段描述和格式响应超时外部 API 无超时设置加日志打点计时设置 10 秒超时并降级重复执行写操作缺少幂等设计检查 requestId 逻辑加 requestId 去重部署后报权限错误Secret 未正确挂载检查 env 引用确认 Secret 名称和 key提示这张表建议打印出来贴在工位上出问题的时候按顺序过一遍能省下大量瞎猜的时间。6. 关于 agent skills 测试的一些经验6.1 单 skill 测试mock 掉外部依赖每个 skill 都应该有独立的单元测试测试的时候把外部 API 调用 mock 掉只验证输入输出契约和异常处理逻辑。我用的是 Vitest写起来很快。重点测三种情况正常输入返回成功、非法输入返回错误、外部 API 抛异常时 skill 不崩溃。import { describe, it, expect, vi } from vitest; describe(query_order_status, () { it(正常订单返回 success, async () { vi.mock(./api, () ({ fetchOrderFromAPI: vi.fn().mockResolvedValue({ status: 已发货, eta: 2024-01-05, }), })); const result await orderQueryTool.run({ orderId: 2024010112345678 }); expect(result.status).toBe(success); }); });6.2 集成测试模拟多轮对话单测过了不代表集成没问题。我一般会写几个多轮对话的测试用例模拟用户先问订单、再问退货、再问物流的场景看 Agent 能不能正确切换 skill。这一步能发现很多单测发现不了的问题比如上下文污染、skill 选择冲突等。6.3 线上灰度先放 5% 流量观察新 skill 上线不要一次性全量。我的做法是先放 5% 的流量观察一周的调用成功率、平均延迟、错误分布。如果指标正常再逐步放大。灰度期间要重点关注“模型选了不该选的 skill”这种情况这往往意味着元数据描述有歧义。7. 我踩过的几个坑和对应的解法第一个坑是过度依赖模型做参数抽取。早期我让模型直接从用户原话里抽orderId结果用户说“我上周买的那个东西”的时候模型完全抽不出来。后来改成先用一个轻量的正则做预处理能匹配到订单号就直接用匹配不到再让模型抽准确率和速度都上来了。第二个坑是skill 之间共享状态。我一开始把用户会话信息存在全局变量里结果并发请求的时候串了数据。后来改成每个请求带一个context对象skill 从 context 里读用户信息彻底解决了这个问题。Agent 系统里千万不要用全局可变状态这是铁律。第三个坑是忽略 token 成本。skill 的元数据、schema 描述、示例都会进入模型上下文skill 多了之后 token 消耗非常可观。我后来做了一次精简把examples从五个减到两个description控制在 20 字以内整体 token 消耗降了 40%准确率几乎没有下降。8. 后续可以怎么扩展这套 skill 体系跑通基础流程之后有几个方向可以继续深挖。一是skill 的自动发现和注册现在我是手动把 skill 加到 tools 数组里skill 多了之后可以做一个注册中心Agent 启动时自动拉取可用的 skill 列表。二是skill 的版本管理不同版本的 skill 可以并行存在Agent 根据场景选择用哪个版本。三是skill 的性能监控给每个 skill 加埋点统计调用次数、成功率、平均延迟用数据驱动优化。还有一个我觉得很有价值的方向是skill 的组合编排。现在 Agent 一次只能调一个 skill但有些任务需要多个 skill 串行执行。可以定义一个“复合 skill”内部按顺序调用多个原子 skill对外暴露成一个接口。这样既保持了原子 skill 的复用性又能处理复杂任务。最后分享一个小技巧在开发阶段我会给每个 skill 加一个debug模式开启后 skill 会把输入输出、执行耗时、异常堆栈都打到日志里。上线前把 debug 关掉避免日志量过大。这个开关在排查线上问题的时候特别有用不用改代码就能拿到详细信息。
RELATED

相关推荐

高维数据处理不再头疼:用hyperframes实现分块加载与并行计算

高维数据处理不再头疼:用hyperframes实现分块加载与并行计算

从最早用嵌套字典管实验数据,到后来换 pandas 的 MultiIndex,再到自己手搓各种“半结构化”存储,我在高维数据处理这条路上折腾了不少时间。最近一个项目里,我需要同时按温度、电压、循环次数、采样点四个维度去查一批老化实验数据…

📅 2026/10/8 5:29:49
Java图片上传下载全解析:multipart协议、Part接口与避坑指南

Java图片上传下载全解析:multipart协议、Part接口与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/8 5:29:49
AI Agent skills 完全指南:从安装到自建,避开常见坑

AI Agent skills 完全指南:从安装到自建,避开常见坑

1. 从"skills"这个热词说起:它到底指什么最近一段时间,"skills"这个词在技术社区里出现的频率高得离谱。你随便翻翻开发者群聊、技术论坛或者代码托管平台的热门仓库,总能看到有人在讨论"这个skills怎么装"&qu…

📅 2026/10/8 5:24:48
MORE NEWS

更多资讯

📰

VScode前端插件推荐:用TaoToken统一管理AI编程助手的Key与API通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

三大主流推理框架汇总:vLLM · SGLang · FlashInfer 与 TaoToken 统一 Key 接入实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

MuMu模拟器接入AI工具,三步实现自然语言控制:TaoToken统一Key配置与mumu-control skill验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

NVMe 驱动开发入门:从 U-Boot 到 Linux 内核的实战指南

1. 为什么说 NVMe 是复杂存储驱动开发的入门首选1.1 存储驱动开发的“地狱开局”与 NVMe 的破局点做过 Linux 内核存储子系统的人都有一个共识:传统 SCSI 或 SATA 驱动栈的复杂度,足以让一个刚接触内核开发的工程师在头三个月里怀疑人生。SCSI 协议本身就…

📰

Kimi For Coding 实测:技能包安装到 README 维护的完整体验

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

龙虾AI OpenClaw Win11安装全流程:TaoToken统一Key接入本地自动化工具部署

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬