
1. 从skills这个热词说起它到底在解决什么问题最近一段时间skills这个词在技术社区里出现的频率高得离谱。不管是在讨论 Google Cloud 上的 Agent 构建还是在聊 GKE 集群里的自动化任务编排甚至是在 Genkit 这类 AI 应用框架的语境下大家都在反复提到一个词——Agent Skills。如果你只是偶尔刷到这些讨论可能会觉得这又是一个新造的概念跟之前那些插件工具调用函数注册没什么本质区别。但真正上手用过之后你会发现它解决的问题其实非常具体让一个 Agent 知道自己在什么场景下该做什么事并且能把这件事做对。我最初接触这个概念是在一个 GKE 上的运维自动化项目里。当时的诉求很简单希望有一个 Agent 能根据集群的实时状态自动判断是该扩容、该重启某个 Pod还是该触发一次滚动更新。最开始的做法是把所有逻辑写在一个巨大的 prompt 里把所有可能的操作都列出来让模型自己去选。结果就是模型经常在不需要扩容的时候扩容在该重启的时候去改配置整个系统的行为极其不稳定。后来换成了 Agent Skills 的思路把每一种能力拆成独立的 skill每个 skill 有自己的触发条件、执行逻辑和边界约束整个系统的可靠性立刻上了一个台阶。这就是 skills 的核心价值所在它不是简单的工具注册而是一种带有语义约束的能力封装。一个 skill 不仅仅告诉 Agent 你能调用这个函数更重要的是告诉它 你在什么情况下应该调用这个函数调用时需要注意什么调用之后应该期待什么结果。这个区别听起来很细微但在实际项目里它直接决定了 Agent 是能用还是不能用。从 Google Cloud 的生态来看Agent Skills 这个概念和 Genkit 框架的结合尤其紧密。Genkit 本身是一个用于构建 AI 应用的开发框架它提供了工具定义、流程编排、模型调用等基础能力。而 Agent Skills 则是在这个基础上进一步抽象出了一层能力语义层。你可以把 Genkit 理解成一套工具箱而 Agent Skills 则是告诉 Agent 什么时候该用哪把工具、怎么用、用完怎么判断结果对不对的那套说明书。对于正在做 Agent 开发的团队来说理解 skills 的设计理念比学会某个具体 API 更重要。因为 skills 的本质是一种架构模式它解决的是如何让 Agent 的行为可控、可预测、可维护这个根本问题。不管你用的是 Google Cloud 的 Agent Builder还是自己在 Genkit 上搭一套甚至是用其他框架这套思路都是通用的。接下来的内容我会从实际项目的角度出发把 Agent Skills 的设计思路、落地步骤、常见坑点、以及和 GKE、Genkit 这些具体技术的结合方式完整地拆一遍。不管你是刚接触 Agent 开发的新手还是已经在做相关项目但遇到了瓶颈的工程师应该都能从中找到可以直接用的东西。2. Agent Skills 的本质不是工具调用而是能力语义封装2.1 为什么函数注册式的做法在真实项目里会崩很多人第一次接触 Agent 开发时理解是这样的我有一堆函数把它们注册给模型模型根据用户输入决定调用哪个函数然后我执行函数并把结果返回给模型模型再生成最终回复。这个流程在 demo 里跑得很好但一到真实项目就会出问题。问题出在哪儿出在模型对函数的理解是扁平的。在模型眼里所有注册的函数都是平等的选项它只能根据函数名和描述来猜测什么时候该用哪个。当函数数量少、场景简单时这种猜测还能凑合。但一旦函数数量超过十个或者多个函数的功能有重叠模型就开始乱选了。我见过一个典型的案例一个客服 Agent 注册了查询订单修改地址取消订单发起退款四个函数。用户说我昨天买的东西不想要了模型有时候调取消订单有时候调发起退款有时候甚至先调查询订单再调修改地址。原因很简单这四个函数在模型看来都是处理用户对订单的不满它分不清哪个才是正确的第一步。Agent Skills 要解决的就是这个问题。它不是在函数层面做文章而是在能力语义层面做文章。一个 skill 不仅仅是一个可调用的函数它包含了几个关键要素触发条件什么情况下这个 skill 应该被激活前置约束执行这个 skill 之前需要满足什么条件执行逻辑具体做什么后置验证执行完之后怎么判断结果是否符合预期失败处理如果执行失败或者结果不对应该怎么回退这五个要素合在一起才构成一个完整的 skill。而普通的函数注册只覆盖了第三个要素。2.2 Skill 的语义边界让 Agent 知道不该做什么这一点是我在实际项目里体会最深的。一个好的 skill 定义不仅要告诉 Agent 能做什么更要告诉它不能做什么。举个例子在一个 GKE 运维 Agent 里我们有一个 skill 叫scale_workload用于调整工作负载的副本数。如果只写这个 skill 可以调整副本数模型可能会在流量低谷时把副本数调到 0导致服务完全不可用。但如果在 skill 定义里加上约束副本数不得低于 2且调整幅度单次不得超过 50%模型的行为就会稳定得多。这种约束不是写在代码里的硬校验而是写在 skill 描述里的语义约束。模型在决定是否调用这个 skill 时会参考这些约束。当然代码层面的硬校验也必须有两者是互补关系。语义约束减少模型犯错的可能性硬校验兜住模型犯错后的后果。在 Genkit 里你可以通过 tool 的 description 字段来承载这些语义约束。但要注意description 不是越长越好。我试过写一个 500 字的 description结果模型反而抓不住重点。后来总结出来的经验是核心约束用短句列出来每条不超过 20 个字最多 5 条。比如调整工作负载副本数。 约束 - 副本数范围 2-20 - 单次调整幅度不超过 50% - 调整前需确认当前无进行中的发布 - 流量高峰期禁止缩容这种格式模型理解起来最稳。2.3 Skill 和 Tool 的区别一个类比如果用生活化的类比来解释Tool 就像是给你一把锤子告诉你这是锤子可以敲钉子。而 Skill 则是告诉你当你要把钉子钉进木板时用这把锤子敲的时候要垂直力度要适中如果钉子歪了要先拔出来重敲。Tool 是能力本身Skill 是能力的正确使用方式。在 Agent 开发里模型不缺能力缺的是什么时候该用什么能力、怎么用才对的判断力。Agent Skills 补的就是这块。从架构上看一个 Skill 通常包含要素作用在 Genkit 中的对应触发条件决定何时激活tool description 路由逻辑前置约束执行前的检查代码中的 guard clause执行逻辑具体操作tool 的 handler 函数后置验证结果校验handler 返回后的验证步骤失败处理异常回退错误处理和重试逻辑这张表是我在实际项目中总结出来的映射关系。你会发现Genkit 的 tool 机制只直接覆盖了执行逻辑这一块其他四块都需要你自己在设计层面补上。这就是为什么很多人用 Genkit 做出来的 Agent 不稳定——他们只做了 tool 注册没做 skill 设计。3. 在 Google Cloud 上落地 Agent Skills 的完整路径3.1 环境准备GKE 集群和 Genkit 项目的初始化如果你打算在 Google Cloud 上做 Agent Skills 的落地最顺手的组合是 GKE Genkit。GKE 提供运行环境Genkit 提供开发框架。下面是我实际用下来比较稳的一套初始化流程。首先是 GKE 集群的创建。如果你只是做开发测试用 Autopilot 模式最省心不用管节点池的配置。但如果是生产环境建议用 Standard 模式因为 Agent 类工作负载对资源的要求比较特殊需要精细控制。gcloud container clusters create agent-skills-cluster \ --region us-central1 \ --num-nodes 3 \ --machine-type e2-standard-4 \ --enable-autoscaling \ --min-nodes 2 \ --max-nodes 10这里有几个参数值得说明。machine-type选 e2-standard-4 是因为 Agent 工作负载通常是内存敏感型的模型调用和上下文管理会占用较多内存4GB 是起步配置。enable-autoscaling是必须的因为 Agent 的负载波动很大有时候一批任务进来需要快速扩容有时候长时间空闲需要缩容。集群建好之后接下来是 Genkit 项目的初始化。Genkit 支持 Node.js 和 Go 两种语言我这边用 Node.js 比较多因为生态更成熟。npm install -g genkit-cli mkdir agent-skills-demo cd agent-skills-demo npm init -y npm install genkit genkit-ai/googleai初始化完成之后你需要配置模型访问。Genkit 支持多种模型后端在 Google Cloud 环境下用 Vertex AI 是最自然的选择。import { configureGenkit } from genkit; import { vertexAI } from genkit-ai/vertexai; configureGenkit({ plugins: [ vertexAI({ projectId: your-project-id, location: us-central1, }), ], logLevel: debug, enableTracingAndMetrics: true, });enableTracingAndMetrics这个选项我强烈建议打开。Agent 的行为调试非常依赖 trace没有 trace 你根本不知道模型为什么选了某个 skill、为什么没选另一个。这个后面会详细讲。3.2 Skill 的定义从能做什么到该怎么做环境准备好之后核心工作就是定义 skill。我以 GKE 运维场景为例完整走一遍 skill 的定义过程。假设我们要定义一个检查 Pod 健康状态的 skill。最粗糙的做法是直接写一个函数export const checkPodHealth ai.defineTool( { name: checkPodHealth, description: 检查 Pod 的健康状态, inputSchema: z.object({ namespace: z.string(), podName: z.string(), }), outputSchema: z.object({ status: z.string(), restarts: z.number(), lastRestart: z.string(), }), }, async (input) { // 调用 K8s API 获取 Pod 状态 const pod await k8sApi.readNamespacedPod(input.podName, input.namespace); return { status: pod.status.phase, restarts: pod.status.containerStatuses[0].restartCount, lastRestart: pod.status.containerStatuses[0].lastState.terminated?.finishedAt || never, }; } );这个定义能用但它只是一个 tool不是一个 skill。它没有告诉模型什么时候该检查 Pod 健康状态检查出来异常之后该怎么办哪些情况下不应该检查。一个完整的 skill 定义应该是这样的export const checkPodHealth ai.defineTool( { name: checkPodHealth, description: 检查指定 Pod 的健康状态。 使用场景 - 用户报告服务异常时 - 例行巡检时 - 发布后验证时 约束 - 单次只检查一个 Pod - 命名空间必须存在 - 如果 Pod 不存在返回明确错误而非重试 输出解读 - statusRunning 且 restarts5 视为健康 - restarts5 需要进一步检查日志 - status!Running 需要触发告警, inputSchema: z.object({ namespace: z.string().describe(Pod 所在的命名空间), podName: z.string().describe(Pod 的名称), }), outputSchema: z.object({ status: z.string(), restarts: z.number(), lastRestart: z.string(), healthy: z.boolean(), }), }, async (input) { try { const pod await k8sApi.readNamespacedPod(input.podName, input.namespace); const restarts pod.status.containerStatuses[0].restartCount; const status pod.status.phase; return { status, restarts, lastRestart: pod.status.containerStatuses[0].lastState.terminated?.finishedAt || never, healthy: status Running restarts 5, }; } catch (error) { if (error.response?.statusCode 404) { throw new Error(Pod ${input.podName} 在命名空间 ${input.namespace} 中不存在); } throw error; } } );对比一下就能看出区别。第二个版本里description 承载了大量的语义信息使用场景、约束、输出解读。这些信息会直接影响模型的行为。而 handler 里增加了错误处理确保 Pod 不存在时返回明确错误而不是让模型反复重试。3.3 Skill 的编排多个 skill 如何协同单个 skill 定义好之后接下来的问题是怎么让多个 skill 协同工作。在 GKE 运维场景里通常需要这样一条链路检查 Pod 状态 → 如果异常检查日志 → 如果日志显示是配置问题检查 ConfigMap → 如果确认是配置问题触发配置更新。这条链路如果让模型自己串很容易串错。我的做法是定义一个编排 skill把这条链路固化下来export const diagnosePod ai.defineFlow( { name: diagnosePod, inputSchema: z.object({ namespace: z.string(), podName: z.string(), }), outputSchema: z.object({ diagnosis: z.string(), recommendedAction: z.string(), confidence: z.number(), }), }, async (input) { // 第一步检查 Pod 健康状态 const health await checkPodHealth(input); if (health.healthy) { return { diagnosis: Pod 健康无需处理, recommendedAction: none, confidence: 0.95, }; } // 第二步检查日志 const logs await checkPodLogs({ namespace: input.namespace, podName: input.podName, tailLines: 100, }); // 第三步根据日志内容判断问题类型 const analysis await ai.generate({ prompt: 根据以下 Pod 日志判断问题类型 ${logs.content} 可能的类型配置错误、资源不足、依赖服务不可用、代码 bug。 只返回类型名称。, }); // 第四步根据问题类型给出建议 const actionMap { 配置错误: 检查 ConfigMap 并更新配置, 资源不足: 调整资源限制或扩容, 依赖服务不可用: 检查依赖服务状态, 代码 bug: 回滚到上一个稳定版本, }; return { diagnosis: analysis.text, recommendedAction: actionMap[analysis.text] || 人工介入, confidence: 0.8, }; } );这个 flow 把诊断链路固化下来了模型只在判断问题类型这一步参与其他步骤都是确定性的代码逻辑。这样做的好处是整个诊断过程可控、可复现不会因为模型的随机性导致诊断结果不稳定。3.4 部署到 GKE容器化和资源配置Skill 定义好之后最后一步是部署到 GKE。Agent 应用的容器化有几个特殊点需要注意。首先是 Dockerfile。Agent 应用通常是长时间运行的服务需要处理并发请求所以基础镜像要选对FROM node:20-slim WORKDIR /app COPY package*.json ./ RUN npm ci --onlyproduction COPY . . # Agent 应用通常需要较大的内存 ENV NODE_OPTIONS--max-old-space-size3072 EXPOSE 8080 CMD [node, dist/server.js]NODE_OPTIONS这个环境变量很关键。Agent 应用在处理长上下文时内存占用会飙升默认的 Node.js 内存限制经常不够用。3GB 是我实测下来比较稳的配置再低就容易 OOM。接下来是 K8s 的 Deployment 配置apiVersion: apps/v1 kind: Deployment metadata: name: agent-skills-service spec: replicas: 3 selector: matchLabels: app: agent-skills template: metadata: labels: app: agent-skills spec: containers: - name: agent image: gcr.io/your-project/agent-skills:latest resources: requests: memory: 2Gi cpu: 500m limits: memory: 4Gi cpu: 2000m env: - name: GOOGLE_CLOUD_PROJECT value: your-project-id - name: NODE_ENV value: production readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 10 periodSeconds: 5 livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 30 periodSeconds: 10资源限制这块requests和limits的差距不要设太大。我见过有人把 requests 设成 512Mi、limits 设成 8Gi结果 Pod 被调度到资源紧张的节点上运行时频繁 OOM。比较合理的做法是 requests 设为 limits 的 50%-70%。4. 踩坑实录Agent Skills 落地过程中最容易翻车的几个地方4.1 Skill 描述写得太聪明模型反而不会用这是我踩过的第一个坑也是最隐蔽的一个。当时我为了让 skill 描述看起来更专业用了很多行业术语和缩写。比如把检查 Pod 的健康状态写成执行 Pod 健康探针校验。结果模型经常不调用这个 skill因为它不理解探针校验是什么意思。后来我把所有 skill 描述都改成大白话调用率立刻上来了。模型对自然语言的理解是字面意义上的它不会像人一样根据上下文推断术语的含义。你写探针校验它就真的以为是在检查某种叫探针的东西而不是在检查 Pod 状态。这个坑的教训是skill 描述要用最直白的语言宁可啰嗦也不要简洁。如果一定要用术语在描述里加一句解释。比如执行 Pod 健康探针校验即检查 Pod 是否正常运行。4.2 多个 skill 功能重叠模型选择困难第二个坑是 skill 之间的功能边界不清晰。在一个项目里我定义了三个 skillgetPodStatus、checkPodHealth、inspectPod。这三个 skill 的功能其实高度重叠都是获取 Pod 信息只是返回的字段略有不同。结果模型在这三个之间反复横跳有时候调这个有时候调那个行为完全不可预测。解决方法是合并重叠的 skill或者明确划分边界。我最后的做法是把三个合并成一个getPodInfo通过参数控制返回哪些信息。这样模型只有一个选择行为就稳定了。如果确实需要多个 skill那就要在描述里明确写出什么时候用这个、什么时候用那个。比如getPodStatus: 仅获取 Pod 的基本状态Running/Pending/Failed用于快速判断。 checkPodHealth: 获取 Pod 的详细健康信息包括重启次数、最后重启时间用于深入诊断。这种明确的边界划分模型是能理解的。4.3 忽略了 skill 执行的幂等性第三个坑比较技术性但影响很大。Agent 在执行任务时可能会因为各种原因重试同一个 skill。如果这个 skill 不是幂等的重试就会导致副作用。我遇到过一个案例一个 skill 用于重启 Pod实现方式是先删除 Pod 再让 Deployment 重建。这个操作本身是幂等的但问题是如果模型连续调用两次第二次调用时 Pod 可能正在重建中删除操作会失败然后模型会认为重启失败触发告警。解决方法是在 skill 层面做幂等性保护。具体做法是在执行前检查当前状态如果状态已经是目标状态直接返回成功而不执行操作。async (input) { const pod await k8sApi.readNamespacedPod(input.podName, input.namespace); // 如果 Pod 已经在重启中直接返回 if (pod.metadata.deletionTimestamp) { return { status: restarting, message: Pod 正在重启中无需重复操作 }; } // 执行重启 await k8sApi.deleteNamespacedPod(input.podName, input.namespace); return { status: restarted, message: Pod 已触发重启 }; }这个检查看起来简单但能避免大量的误报和重复操作。4.4 Trace 没开出问题只能靠猜第四个坑是调试相关的。Agent 的行为不像传统程序那样有明确的调用栈它的决策过程是黑盒的。如果不开 trace出了问题你根本不知道模型为什么选了某个 skill、为什么没选另一个。Genkit 的 trace 功能可以记录每一次模型调用、每一次 skill 执行、每一次决策的输入输出。开启方式很简单在 configureGenkit 里设置enableTracingAndMetrics: true就行。但要注意trace 数据量很大生产环境需要配置采样率不然存储成本会很高。我通常的做法是开发环境 100% 采样预发环境 50%生产环境 10%。出问题时可以临时调高采样率问题解决后再调回去。4.5 排查链路一次典型的 skill 误调用最后分享一个完整的排查案例。有一次运维 Agent 在流量高峰期错误地触发了一次缩容操作导致服务短暂不可用。排查过程是这样的第一步查 trace。找到那次缩容操作的 trace看模型是在什么上下文下决定调用scaleWorkload的。发现触发原因是检测到 CPU 使用率下降。第二步查 skill 描述。发现scaleWorkload的描述里只写了调整工作负载副本数没有写流量高峰期禁止缩容这条约束。第三步查上下文。发现模型在决策时上下文中确实包含了当前是流量高峰期的信息但因为 skill 描述里没有这条约束模型没有把这个信息用上。第四步修复。在 skill 描述里加上流量高峰期工作日 9:00-18:00禁止缩容这条约束同时在 handler 里加上硬校验。第五步验证。在预发环境模拟流量高峰期场景确认模型不再触发缩容。这个案例的教训是skill 描述里的约束不是装饰是模型决策的依据。你写了它就会用不写它就不会用。不要指望模型自己聪明到能推断出你没写的约束。5. 从 Genkit 到生产性能优化和成本控制5.1 上下文管理别让 skill 描述撑爆 tokenSkill 描述虽然重要但也不是越多越好。每个 skill 的描述都会占用上下文 tokenskill 数量多了之后光描述就能占掉几千 token。这不仅增加成本还会稀释模型的注意力导致它抓不住重点。我的做法是分层管理 skill 描述。核心 skill高频使用、关键路径上的写详细描述边缘 skill低频使用、辅助性的写简短描述。具体标准是核心 skill描述 100-200 字包含使用场景、约束、输出解读边缘 skill描述 20-50 字只写基本功能另外如果 skill 数量超过 20 个建议做动态加载。根据当前上下文只加载相关的 skill而不是一次性把所有 skill 都塞给模型。Genkit 本身不直接支持动态加载但可以通过自定义路由逻辑实现。5.2 模型选择不是所有 skill 都需要大模型Agent 的决策过程可以拆成两部分路由决策选哪个 skill和参数生成skill 的输入参数是什么。这两部分对模型能力的要求不一样。路由决策通常比较简单只需要理解用户意图和 skill 描述小模型就能胜任。参数生成则可能需要理解复杂的上下文需要大模型。我的做法是混合使用路由用轻量模型如 Gemini Flash参数生成用大模型如 Gemini Pro。这样能在保证效果的前提下显著降低成本。在 Genkit 里你可以为不同的 generate 调用指定不同的模型// 路由决策用轻量模型 const routeDecision await ai.generate({ model: vertexai/gemini-1.5-flash, prompt: 根据用户输入选择合适的 skill${userInput}, }); // 参数生成用大模型 const params await ai.generate({ model: vertexai/gemini-1.5-pro, prompt: 根据用户输入生成 skill 参数${userInput}, });实测下来这种混合方案能降低 40%-60% 的模型调用成本而效果几乎没有损失。5.3 缓存重复的 skill 调用没必要每次都走模型Agent 在处理相似请求时经常会做出相同的决策。比如检查 Pod 健康状态这个 skill如果用户连续问了三个 Pod 的状态模型的路由决策其实是一样的。这种重复决策完全可以缓存。我的做法是在路由层加一个简单的缓存把用户输入做归一化处理去掉具体名称、时间等变量然后查缓存。如果命中直接返回上次的 skill 选择。const cacheKey normalizeInput(userInput); const cached await cache.get(cacheKey); if (cached) { return cached; } const decision await routeDecision(userInput); await cache.set(cacheKey, decision, { ttl: 300 }); return decision;这个缓存不需要很复杂一个内存级的 LRU 就够了。关键是归一化逻辑要设计好既要保证相似输入能命中又要避免不同输入误命中。5.4 监控哪些 skill 在被用哪些是摆设上线之后一定要监控 skill 的使用情况。我见过很多项目定义了几十个 skill实际上只有五六个在被调用其他的要么是描述写得不好模型不会用要么是场景根本不存在。监控指标至少包括指标含义关注点调用次数skill 被调用的总次数长期为 0 的 skill 考虑下线成功率调用成功返回的比例低于 90% 需要排查平均耗时从调用到返回的时间超过 5s 需要优化误调用率被调用但结果不符合预期的比例高于 10% 需要改描述这些指标可以通过 Genkit 的 tracing 数据聚合出来。我通常会在 Grafana 上做一个看板每周 review 一次。6. 一些实战中总结出来的经验6.1 Skill 的粒度一个 skill 只做一件事这是最重要的原则。一个 skill 如果做了太多事模型就很难判断什么时候该用它。比如检查并修复 Pod 问题这个 skill它既检查又修复模型在只需要检查的时候也会调用它导致不必要的修复操作。正确的做法是拆成两个 skill检查 Pod 问题和修复 Pod 问题。检查是只读的可以随便调用修复是有副作用的需要谨慎调用。拆开之后模型的行为就清晰多了。6.2 错误信息要写给模型看不是写给人看Skill 执行失败时返回的错误信息模型是会读的。所以错误信息要写得让模型能理解并且能指导它下一步该怎么做。比如不要写Error: ENOENT要写Pod 不存在请检查 Pod 名称是否正确或先调用 listPods 获取可用 Pod 列表。后者模型能理解并且知道下一步该调用 listPods。前者模型只能看到一堆乱码然后随机重试。6.3 定期 review skill 描述Skill 描述不是写完就完了需要定期 review。因为业务在变模型的能力也在变之前写得好的描述可能过一段时间就不适用了。我的做法是每个月 review 一次所有 skill 的描述重点看三个地方一是调用率低的 skill看是不是描述有问题二是误调用率高的 skill看是不是约束不够三是业务已经变化的 skill看描述是否需要更新。6.4 不要过度依赖模型能固化的逻辑就固化Agent 的优势是灵活但灵活也意味着不确定。对于确定性的逻辑不要交给模型直接写成代码。比如如果 Pod 状态是 Failed就触发告警这种逻辑完全没必要让模型判断直接写 if-else 就行。模型应该用在真正需要判断力的地方比如根据日志内容判断问题类型这种模糊匹配的场景。把确定性的逻辑固化下来既能提高可靠性又能降低成本。6.5 测试skill 的测试和普通函数不一样Skill 的测试不能只测 handler 的逻辑还要测模型的行为。具体来说要测三个方面一是模型在给定输入下是否会选择正确的 skill二是模型生成的参数是否正确三是 skill 执行失败时模型是否能正确处理。前两个测试需要构造大量的输入样本然后观察模型的选择。这个工作量不小但很值得。我通常会用历史数据构造测试集覆盖常见的用户输入模式。第三个测试相对简单可以手动构造失败场景观察模型的行为。重点看模型是否会无限重试、是否会给出合理的错误提示。7. 后续可以继续深入的方向Agent Skills 这个领域还在快速演进有几个方向我觉得值得继续深入。一个是skill 的自动发现和组合。现在的做法是人工定义 skill然后人工编排。未来如果能让 Agent 根据任务自动发现需要的 skill 并组合成 flow那灵活性会大大提升。Google Cloud 的一些新功能已经在往这个方向走了。另一个是skill 的版本管理和灰度发布。Skill 描述改了之后模型的行为会变这本质上是一次行为变更。怎么安全地发布这种变更怎么在出问题时快速回滚这些都是需要解决的问题。还有一个是跨 Agent 的 skill 共享。现在每个 Agent 的 skill 都是独立定义的但其实很多 skill 是通用的比如查询数据库发送通知。如果能有一套标准的 skill 注册和发现机制让不同 Agent 共享 skill开发效率会高很多。我在实际项目里已经尝试了一些简单的共享机制比如把通用 skill 抽成一个独立的包各个 Agent 引用。但这种方式还是偏手工不够自动化。期待后续有更成熟的方案出来。最后说一个我自己的体会Agent Skills 的核心不是技术而是设计思维。你要站在模型的角度去想它在什么情况下需要什么信息、会做什么判断、可能犯什么错然后据此设计 skill 的描述和约束。这个思维方式的转变比学会某个具体 API 重要得多。技术会变框架会换但这种为模型设计的思维方式是通用的。