首页 / 资讯中心 / 文章详情

Agent Skills 实战:从 Genkit 定义到 GKE 部署与排查

Agent Skills 实战:从 Genkit 定义到 GKE 部署与排查 ★ FEATURED ARTICLE
1. 从“skills”这个标题说起为什么它值得单独拿出来聊“skills”这个词看起来简单到有点敷衍但如果你最近在关注 Agent 开发、Google Cloud 的 AI 工具链或者刷到过 Gemini 相关的各种讨论就会发现它其实踩在了一个非常关键的位置上。我最初注意到这个标题是因为在几个开发者社群里频繁看到有人把 Agent Skills、GKE、Genkit、Gemini 这几个词放在一起讨论而且讨论的焦点往往不是“怎么用”而是“为什么我的 Agent 跑不起来”“为什么同样的 skill 定义在本地能用、部署到云端就报错”。这说明一件事skills 已经从一个抽象概念变成了实际工程中必须落地的模块。它不再只是“让模型会做某件事”的提示词技巧而是涉及工具注册、权限边界、运行时环境、模型调用链路的一整套机制。你如果只是把它当成一段 prompt 来写大概率会在某个环节卡住而且卡住的地方往往不是模型本身而是围绕 skill 的加载、路由和执行环境。这篇文章适合几类人看第一类是在做 Agent 应用、想搞清楚 skill 到底该怎么设计的人第二类是在 Google Cloud 上折腾 GKE 和 Genkit、想把 Gemini 接进自己工作流的人第三类是被各种“account is not eligible”提示搞烦了、想弄明白这些限制背后逻辑的人。我会尽量把原理讲透同时给出可以直接参考的操作路径和排查思路不堆术语也不绕弯子。2. Agent Skills 的核心设计逻辑它到底解决什么问题2.1 从“一个万能提示词”到“一组可组合的能力单元”早期做 Agent 的人习惯把所有能力塞进一个系统提示里你既要它会查天气又要它会算账还要它会调用内部 API。结果就是提示词越写越长模型注意力被稀释稍微复杂一点的任务就开始胡编。Agent Skills 的思路正好相反把每一种能力拆成独立的、可描述、可注册的单元模型在需要的时候才去调用对应的 skill。这个转变背后的逻辑其实很朴素。你可以把 Agent 想象成一个刚入职的助理如果你一次性给他一本五百页的操作手册他大概率记不住但如果你告诉他“遇到查数据的事去找数据组遇到发邮件的事去找行政”他反而能更快上手。Skill 就是那个“找谁办什么事”的路由表而不是把所有知识都压进模型参数里。从工程角度看这种拆分带来三个直接好处。第一是可测试性每个 skill 可以单独写测试用例不用每次都跑整个 Agent。第二是可替换性某个 skill 的实现从本地函数换成远程 API只要接口不变上层逻辑不用动。第三是可观测性哪个 skill 被调用了、耗时多少、失败原因是什么都能单独打点而不是在一大坨日志里捞。2.2 Skill 的边界它不是什么比它是什么更重要很多人第一次接触 Agent Skills 时容易把它和“函数调用”或者“工具调用”混为一谈。它们确实有重叠但 skill 的范畴通常更大一点。一个 skill 可以包含多个底层工具也可以包含一段固定的处理流程甚至可以是“先查缓存、没有再调 API、最后格式化输出”这样一串动作。但 skill 也不是万能的。它不适合承载需要长时间运行的状态机也不适合做需要跨会话保持记忆的逻辑。我见过有人试图把一个完整的订单处理流程塞进一个 skill 里结果调试时根本分不清是模型选错了 skill还是 skill 内部逻辑写错了。比较稳妥的做法是skill 只负责“一件事”而且这件事的输入输出边界要非常清晰。如果一件事需要多个步骤就拆成多个 skill让 Agent 自己去编排。还有一个容易被忽略的点skill 的描述文本本身也是设计的一部分。模型选择哪个 skill很大程度上依赖你对 skill 的自然语言描述。描述写得太窄模型遇到稍微变形的请求就不敢调用写得太宽又会和别的 skill 抢活。我的经验是描述里要包含“什么时候用”和“什么时候不用”这比单纯罗列功能更有用。2.3 为什么 Google Cloud 和 Genkit 会出现在这个语境里Agent Skills 本身是一个偏框架层的概念但真正让它跑起来需要一整套运行时支持。Google Cloud 在这里扮演的是基础设施角色GKE 提供容器编排Genkit 提供 AI 工作流的编排和工具注册能力Gemini 则是背后的模型。这三者组合起来基本就是一套“从开发到部署”的完整链路。Genkit 比较有意思的地方在于它把 skill 的定义和调用做成了类似插件的东西。你可以用 TypeScript 或 Go 写一个 tool然后通过 Genkit 的接口注册进去模型在推理时就能看到这个 tool 的存在。而 GKE 负责的是把这些东西打包成容器、按需扩缩容、管理网络和密钥。换句话说Genkit 管“模型怎么用 skill”GKE 管“skill 跑在哪里”。这个分工带来的一个实际影响是你在本地开发时可能只需要 Genkit 的 dev 环境就能跑通但一旦要上线就必须考虑 GKE 上的资源限制、冷启动时间、以及服务账号权限。很多“本地能用、线上报错”的问题根源都在这个切换过程里。3. 核心细节拆解Skill 定义、注册与调用的关键环节3.1 Skill 定义的结构输入、输出、描述、执行体一个可用的 skill 定义通常包含四个部分。第一部分是名称和描述这部分是给模型看的决定了模型会不会选它。第二部分是输入参数的 schema决定了模型传进来的数据长什么样。第三部分是输出结构决定了调用方拿到什么。第四部分是执行体也就是真正干活的代码或 API 调用。这里最容易出问题的是输入 schema。如果你把参数定义得太宽松比如全部用 string模型可能会传进来一堆格式不对的东西执行体里再做校验就很被动。比较稳的做法是尽量用枚举、数字范围、必填字段这些约束让模型在生成参数时就有明确的边界。我试过把日期参数从 string 改成带格式说明的 string调用成功率明显提升因为模型知道要传YYYY-MM-DD而不是随便写个“明天”。输出结构同样重要。如果 skill 返回的是一大段自由文本模型后续处理起来会很吃力如果返回的是结构化 JSON模型更容易提取关键字段。但也要注意输出字段不宜过多否则会占用大量上下文反而影响模型判断。3.2 注册与发现模型怎么知道有哪些 skill 可用Skill 注册的本质是把 skill 的元信息名称、描述、参数 schema注入到模型的上下文中。不同框架的做法不一样有的是一次性全部注入有的是按需检索。Genkit 这类工具通常会在每次请求时把当前可用的 tool 列表附在提示里。这里有一个很实际的权衡skill 越多模型的选择空间越大但提示长度也越长成本和延迟都会上升。我见过一个项目注册了四十多个 skill结果模型经常在几个相似 skill 之间反复横跳。后来他们把 skill 按业务域分组每次只注入当前域相关的 skill准确率立刻上来了。另一个坑是 skill 名称的命名。不要用tool1、helper这种无意义的名字也不要用过于相似的名称比如getUser和getUserInfo。模型在区分这两个时很容易出错。比较好的命名是动词加名词并且能体现数据来源比如fetchOrderFromDB和fetchOrderFromCache。3.3 调用链路从模型输出到实际执行当模型决定调用某个 skill 时它通常会输出一个结构化的调用请求包含 skill 名称和参数。框架层拿到这个请求后会做几件事校验参数是否符合 schema、找到对应的执行体、执行、把结果返回给模型。这个链路里任何一环出问题都会表现为“skill 没反应”或者“模型说它调用了但实际没执行”。我排查这类问题时习惯先看框架层的日志确认模型到底输出了什么调用请求。很多时候问题出在参数格式上比如模型传了一个字符串3但 schema 要求的是数字3校验直接失败。还有一种情况是 skill 执行超时框架默认超时时间太短而实际 API 响应慢结果模型收到的是超时错误但它可能会把这个错误当成正常结果继续往下编。注意skill 执行体的错误处理一定要显式不要把异常直接抛给模型。比较稳妥的做法是返回一个包含error字段的结构化结果让模型知道这次调用失败了而不是让它误以为拿到了有效数据。4. 实操过程在 Google Cloud 上跑通一个带 Skill 的 Agent4.1 环境准备与依赖安装假设你已经在本地有一个能跑 Genkit 的环境接下来要把它部署到 GKE 上。第一步是确认本地开发环境的基础依赖。Node.js 版本建议用 20 或以上因为 Genkit 的一些新特性对运行时版本有要求。如果你用的是 Go那版本至少要 1.21。安装 Genkit 的命令行工具和核心库通常是通过包管理器完成。以 Node 为例初始化项目后安装genkit和对应的模型插件。这里要注意Gemini 的插件和 Google Cloud 的插件是分开的如果你既要调用 Gemini 又要访问 GKE 上的服务两个都要装。npm install genkit genkit-ai/googleai配置 API 密钥时不要硬编码在代码里。本地开发可以用环境变量部署到 GKE 时建议用 Secret Manager 挂载。我见过有人把密钥直接写在genkit.config.ts里然后提交到仓库这是大忌。4.2 定义一个最小可用的 Skill先从一个最简单的 skill 开始比如“根据城市名返回天气”。这个 skill 的输入是一个城市名字符串输出是温度和天气描述。定义时描述要写清楚“当用户询问某个城市的天气时使用此 skill输入必须是城市名称不要传入省份或国家。”import { defineTool } from genkit; export const getWeather defineTool( { name: getWeather, description: 根据城市名查询当前天气输入为城市名称字符串, inputSchema: { type: object, properties: { city: { type: string, description: 城市名称例如 Beijing } }, required: [city] }, outputSchema: { type: object, properties: { temperature: { type: number }, condition: { type: string } } } }, async (input) { // 实际调用天气 API 的逻辑 return { temperature: 25, condition: sunny }; } );这个定义里inputSchema和outputSchema是给模型看的约束执行体里的逻辑是给运行时看的。两者要一致否则会出现模型以为传了城市名、实际执行体收到空值的情况。4.3 在 Genkit 流程中注册并调用 Skill定义好 skill 后需要在 Genkit 的 flow 里注册。注册的方式通常是在生成请求的配置里传入 tools 数组。模型在推理时会看到这些 tool 的描述并决定是否调用。import { genkit } from genkit; import { googleAI } from genkit-ai/googleai; import { getWeather } from ./tools/getWeather; const ai genkit({ plugins: [googleAI()], model: gemini-1.5-flash }); export const weatherFlow ai.defineFlow( { name: weatherFlow, inputSchema: { type: string }, outputSchema: { type: string } }, async (input) { const response await ai.generate({ prompt: input, tools: [getWeather] }); return response.text; } );这里的关键点是tools数组。如果你有多个 skill都放进去但要注意前面提到的上下文长度问题。测试时可以先只放一个确认链路通了再加。4.4 部署到 GKE 的关键配置把上面这个 flow 部署到 GKE需要先容器化。Dockerfile 里要注意基础镜像的选择Node 项目建议用node:20-slim体积小且兼容性好。构建时把node_modules一起打进去避免运行时再安装。部署到 GKE 时有几个配置项容易出错。第一是服务账号的权限如果你的 skill 需要访问其他 Google Cloud 服务服务账号必须要有对应的 IAM 角色。第二是资源限制Genkit 的冷启动可能比较慢initialDelaySeconds要设得足够大否则 Pod 还没起来就被判定为不健康。第三是环境变量API 密钥要通过 Secret 挂载不要写在 Deployment 的明文里。apiVersion: apps/v1 kind: Deployment metadata: name: agent-skills-demo spec: replicas: 1 selector: matchLabels: app: agent-skills-demo template: metadata: labels: app: agent-skills-demo spec: containers: - name: app image: gcr.io/your-project/agent-skills-demo:latest ports: - containerPort: 3000 env: - name: GOOGLE_API_KEY valueFrom: secretKeyRef: name: gemini-secret key: api-key readinessProbe: httpGet: path: /health port: 3000 initialDelaySeconds: 15 periodSeconds: 10这个 YAML 里initialDelaySeconds: 15是我实测下来比较稳妥的值。如果你用的模型加载比较慢可以再往上调。5. 常见问题与排查技巧实录5.1 模型不调用 Skill或者调用了错误的 Skill这是最常见的问题。表现是模型直接用自己的知识回答而不是去调用你定义的 skill。原因通常有三个一是 skill 描述不够明确模型觉得不需要调用二是提示里没有明确要求“必须使用工具”三是 skill 名称和用户问题里的关键词不匹配。排查时先把 skill 的描述改得更具体加入“当用户提到 X 时使用”。然后在系统提示里加一句“优先使用可用工具来回答问题”。如果还是不行检查一下模型版本有些轻量模型对工具调用的支持不如大模型稳定。5.2 参数校验失败模型传了不符合 schema 的数据这种问题的日志里通常会出现 schema validation error。解决办法有两个方向一是放宽 schema比如把数字改成字符串再在执行体里转换二是加强描述在参数说明里写清楚格式要求。我一般优先选第二个因为放宽 schema 会让后续处理更麻烦。还有一种情况是模型传了多余字段。有些框架对多余字段是宽容的有些是严格模式会直接报错。如果你用的是严格模式可以在 schema 里加additionalProperties: false但这样模型一旦多传就会失败。比较平衡的做法是允许额外字段但在执行体里忽略它们。5.3 部署到 GKE 后 Skill 执行超时本地跑得好好的一上 GKE 就超时通常是网络问题。GKE 的 Pod 访问外部 API 需要经过 NAT 或者配置了 Cloud NAT如果没配出站请求会失败。另一个可能是 DNS 解析慢可以在 Pod 的dnsConfig里指定ndots: 1来减少解析次数。超时时间本身也要检查。Genkit 默认的超时可能只有几秒而你的 skill 调用的外部 API 响应要十几秒。这种情况下要么调大超时要么把 skill 改成异步模式先返回一个任务 ID再让模型轮询结果。5.4 关于“account is not eligible”这类提示在配置 Gemini 相关服务时偶尔会遇到账号资格相关的提示。这类提示通常和账号的结算状态、地区支持情况、或者服务开通状态有关。我的建议是先去控制台确认结算账号是否正常、所需 API 是否已启用。如果确认都没问题再检查项目层级是否有组织策略限制了某些服务。这类问题没有通用解法因为每个账号的情况不一样但排查顺序基本是结算状态、API 启用状态、IAM 权限、组织策略。5.5 常见问题速查表问题现象可能原因排查方向模型不调用 skill描述不清晰、提示未要求改描述、加系统提示参数校验失败schema 太严、模型格式不对放宽 schema 或加强描述部署后超时网络不通、超时太短检查 NAT、调大超时skill 执行报错但模型继续错误未结构化返回返回 error 字段多个 skill 冲突命名相似、描述重叠重命名、按域分组注入6. 一些实操心得和后续扩展方向我在实际项目里踩过最深的坑是把 skill 当成“万能胶”来用。一开始觉得什么都能塞进去结果 skill 越写越复杂最后连自己都说不清某个 skill 到底负责什么。后来强制自己遵守一个规则如果一个 skill 的执行体超过一百行就说明它该拆了。拆完之后不仅调试更容易模型选择 skill 的准确率也上去了。另一个心得是关于日志的。Agent 的日志一定要把模型输出、skill 调用请求、skill 执行结果分开打。我见过有人只打最终回复出了问题根本不知道是模型没调用还是调用了但执行失败。分开打之后排查时间从半小时缩短到几分钟。这个方向后续还可以往两个方向扩展。一个是 skill 的版本管理当你有几十个 skill 时怎么知道线上跑的是哪个版本、回滚时怎么操作。另一个是 skill 的权限控制不是所有用户都能调用所有 skill怎么在注册层做过滤。这两个问题在小型项目里不明显但一旦上规模就会变成刚需。
阅读完成 · 觉得有帮助?
咨询建站