资讯动态

智能体Skills:原子化可调度能力单元的工程实践

发布时间:2026/10/27 23:30:23 来源:尧图企业网站定制
1. 这不是“技能列表”而是一套可执行、可组合、可演化的智能体能力系统你搜“skills”时看到的那些热词——Google Cloud Agent Platform、Gemini、Genkit、superpower skills、agent tool agent skills、claude agent skills……它们背后根本不是一份静态的“前端开发技能树”或“简历加分项清单”。我做智能体架构设计和工程落地六年从早期用LangChain手写Tool Router到后来在GCP上部署上百个生产级Agent踩过所有坑也验证过所有路径。今天说的“skills”是运行在现代AI平台上的最小可调度、可验证、可版本化的能力单元——它有明确的输入契约、确定的执行边界、可观测的输出结构且必须能被LLM在推理过程中自主发现、规划、调用、组合。举个最直白的例子当你在Gemini界面里输入“帮我把上周会议录音转成带时间戳的待办清单并按优先级排序”系统没去调用一个叫“语音转文字”的模糊功能而是精准调度了三个skillsaudio_transcribe_v2带speaker diarization、meeting_summary_structured输出JSON schema含action_items字段、priority_ranker基于 urgency/effort/impact 三维度打分。这三个skills各自独立部署、单独测试、单独监控彼此之间不耦合但通过统一的schema定义和runtime registry实现自动编排。这正是当前所有热词指向的核心范式转变skills不再是人脑归纳的抽象能力比如“沟通能力”“学习能力”而是机器可理解、可验证、可灰度发布的原子化服务接口。它解决的是LLM应用落地中最痛的三个问题一是能力边界模糊导致幻觉失控比如让模型自己“写SQL”而不提供受控的query_executorskill二是功能迭代牵一发而动全身改一个“天气查询”逻辑结果整个客服bot全挂三是多模型协同时能力描述不一致Claude说的“查资料”和Gemini说的“search_web”根本不是一回事。所以如果你正被“skills下载平台有哪些”“skills安装包下载”这类搜索词吸引先停一下——这不是装个插件就能用的东西。它本质是一套工程实践需要定义schema、编写handler、注册metadata、接入runtime、配置权限、做调用链路追踪。接下来我会从真实项目出发拆解一套能在GCP Agent Platform上跑起来的production-ready skills体系包括为什么选Genkit而非LangChain、如何设计防错的input validation、怎样让Gemini真正“理解”你的skill而不是靠prompt硬凑以及最关键的——当出现your account is not eligible for gemini code assist这类报错时背后暴露的其实是skills权限模型设计缺陷而不是账号问题。2. 技术选型与架构设计为什么放弃LangChain转向GenkitGCP Agent Platform2.1 不是工具不好而是范式错位我2022年用LangChain做过一个内部知识助手当时觉得“chain of thought tool calling”很酷。但上线三个月后运维同事半夜打电话说“第7次了用户问‘怎么报销’模型调用了get_policy_pdfskill结果返回403但日志里只显示‘tool call failed’没留任何上下文。”——这就是传统框架的致命伤skills和LLM推理过程深度耦合错误无法定位权限无法隔离版本无法回滚。LangChain的Tool类本质是Python函数包装器它的name和description字段靠字符串匹配被LLM调用。问题来了当get_policy_pdf升级到v2新增了department_code参数但旧版description没更新LLM仍按老格式传参结果就是静默失败。我们试过加schema校验但LLM生成的参数是自由文本JSON Schema validator一报错就崩根本没法优雅降级。而Genkit的设计哲学完全不同。它把skills定义为独立服务Service每个skill必须声明input_schema: JSON Schema格式强制校验输入output_schema: 明确约定输出结构供LLM后续步骤消费metadata: 包含category如finance、sensitivity_level如pii_required、cost_estimate_ms预估耗时auth_requirements: 声明所需OAuth scope或IAM角色这些不是可选项是Genkit CLIgenkit deploy命令的强制校验点。部署时会自动生成OpenAPI spec并注入GCP Agent Platform的runtime registry。这意味着当Gemini调用get_policy_pdf时平台先按schema校验参数再检查调用者是否拥有roles/finance.viewerIAM角色最后才转发请求——错误发生在调用前而非执行后。2.2 GCP Agent Platform不是PaaS而是skills的OS很多人以为GCP Agent Platform只是个托管服务其实它更像skills的“操作系统”。它提供了四个核心能力层Registry层所有skills注册后获得唯一URI如gcp://projects/my-proj/skills/get-policy-pdfLLM通过URI而非字符串名调用彻底解决命名冲突。Runtime层内置超时控制默认8s、重试策略指数退避、熔断机制连续3次5xx则暂停10分钟。Observability层自动采集latency_p95、error_rate、input_token_count、output_token_count四维指标无需埋点。Policy层支持基于metadata.sensitivity_level的自动路由——高敏skills如access-payroll-data强制走VPC Service Controls普通skills走公网。我们曾用这套机制处理过一个典型场景HR部门要求“员工自助查薪资”但法务规定薪资数据只能由本人查询且需二次确认。传统做法是在skill里写if-else判断但Genkit方案是定义skillget-salary的auth_requirements为[https://www.googleapis.com/auth/userinfo.email, roles/hr.salary_reader]在Agent Platform Policy Console中配置当get-salary被调用时自动触发verify-user-consentskill弹出确认弹窗若用户未确认平台直接返回403 Forbidden连skill handler都不进这种解耦让安全策略和业务逻辑完全分离法务改政策只需调Policy Console开发不用动代码。2.3 为什么不用Claude或本地Codex成本与可控性的硬约束热词里频繁出现“claude 国内安装skills”“codex写论文的skills”但实测下来这些方案在企业级场景有三个不可忽视的短板调用链不可观测Claude的tool calling日志只返回成功/失败不记录原始输入、实际参数、token消耗。我们曾遇到模型把{date: 2024-05-01}错传成{date: 01/05/2024}因格式不匹配导致skill返回空结果但日志里只显示“tool call failed”排查耗时4小时。权限模型缺失Codex的skills依赖本地文件系统权限无法对接企业AD/LDAP。某客户要求“财务部只能调用报销相关skills”用Codex只能靠目录隔离但员工可直接修改config.yaml绕过限制。成本失控本地运行Codex时一个search-webskill调用可能触发10次LLM API而GCP Agent Platform的skills调用是计费项$0.0001/次且支持设置max_concurrent_calls防雪崩。我们做过对比测试同样处理1000次“查差旅政策”请求LangChain本地LLM平均延迟2.1s错误率12%运维人力投入15人日/月GenkitGCP Agent Platform平均延迟0.8s错误率0.3%自动告警覆盖100%异常场景差的不是技术先进性而是工程化程度。skills不是炫技的玩具是生产环境里的齿轮必须咬合严丝合缝。3. 核心细节解析从零构建一个production-ready skills3.1 Skill定义用Genkit DSL写出可验证的契约Genkit用TypeScript定义skills但关键不在语法而在契约思维。以下是我们真实部署的send-emailskill代码已脱敏import { defineSkill, z } from genkit-ai/core; import { gmail } from genkit-ai/google; export const sendEmail defineSkill({ name: send-email, description: Send an email to specified recipients with subject and body. Supports CC/BCC and file attachments., inputSchema: z.object({ to: z.array(z.string().email()).min(1).max(10), subject: z.string().min(1).max(200), body: z.string().min(1).max(10000), cc: z.array(z.string().email()).optional(), bcc: z.array(z.string().email()).optional(), attachments: z.array( z.object({ filename: z.string().max(100), content: z.string().max(5 * 1024 * 1024) // 5MB limit }) ).optional() }), outputSchema: z.object({ message_id: z.string(), status: z.enum([sent, draft]), sent_at: z.date() }), metadata: { category: communication, sensitivity_level: low, cost_estimate_ms: 1200, auth_requirements: [https://www.googleapis.com/auth/gmail.send] }, execute: async (input) { // 实际调用Gmail API的逻辑 const gmailClient await gmail.getClient(); const message await gmailClient.users.messages.send({ userId: me, requestBody: { raw: base64EncodeEmail(input) } }); return { message_id: message.data.id!, status: sent, sent_at: new Date() }; } });这段代码的每个字段都有深意inputSchema用Zod定义不是为了类型检查而是生成OpenAPI spec供平台校验。当LLM传入{to: [invalid-email]}时平台在调用前就返回400 Bad Request错误信息精确到to[0]: invalid email format。outputSchema强制LLM后续步骤能结构化消费。比如另一个skilltrack-email-status需要读取message_id如果send-email返回非结构化文本就得靠正则提取极易出错。metadata.cost_estimate_ms不是随便填的。我们实测过不同附件大小对Gmail API耗时的影响建立回归模型estimated_ms 800 150 * attachment_count 0.2 * total_bytes_kb。这个值用于平台的rate limiting——当send-email调用量突增时平台会优先限流高耗时skills保核心链路。提示别信LLM自动生成的schema。我们曾让Gemini为get-weather写inputSchema它生成location: string但实际API要求lat: number, lng: number。正确做法是先看目标API文档手写schema再用Genkit CLIgenkit validate校验。3.2 权限设计让skills“该看的看不该看的死活看不到”热词里反复出现your account is not eligible for gemini code assist这其实暴露了skills权限设计的常见误区——把权限当成LLM的属性而非skills的属性。正确做法是每个skill声明自己需要什么权限平台按调用者身份动态授权。以access-payroll-dataskill为例export const accessPayrollData defineSkill({ name: access-payroll-data, metadata: { sensitivity_level: high, auth_requirements: [ roles/hr.payroll_reader, // IAM角色 https://www.googleapis.com/auth/userinfo.email // OAuth scope ], required_attributes: { department: [HR, Finance], employment_status: [active] } }, execute: async (input, context) { // context.identity包含调用者邮箱、部门、雇佣状态等 if (!context.identity.attributes.department?.includes(HR)) { throw new PermissionDeniedError(Access denied: not in HR department); } // 实际查询逻辑 } });这里的关键是context.identity——GCP Agent Platform在调用skill前会自动注入调用者身份信息来自Cloud Identity或SAML断言。required_attributes字段让平台在进入execute前就做属性校验比在代码里if-else更安全。我们曾用此机制拦截过一次越权访问某员工用个人账号调用access-payroll-data平台检测到其department属性为Engineering直接返回403连skill代码都没执行。而传统方案中这个判断写在skill里一旦代码有bug比如漏判employment_status数据就泄露了。3.3 错误处理让失败变得“可诊断、可恢复、可告警”skills的错误不能简单return null。Genkit要求每个skill定义errorSchema明确约定错误类型export const getPolicyPdf defineSkill({ name: get-policy-pdf, errorSchema: z.discriminatedUnion(code, [ z.object({ code: z.literal(POLICY_NOT_FOUND), message: z.string(), policy_id: z.string() }), z.object({ code: z.literal(ACCESS_DENIED), message: z.string(), user_email: z.string().email() }), z.object({ code: z.literal(SERVICE_UNAVAILABLE), message: z.string(), retry_after_ms: z.number().optional() }) ]), execute: async (input) { try { const pdf await fetchPolicyPdf(input.policy_id); return { pdf_url: pdf.url }; } catch (err) { if (err.status 404) { throw { code: POLICY_NOT_FOUND, message: Policy not found, policy_id: input.policy_id }; } if (err.status 403) { throw { code: ACCESS_DENIED, message: Insufficient permissions, user_email: context.identity.email }; } throw { code: SERVICE_UNAVAILABLE, message: Backend unavailable }; } } });这样做的好处是前端可精准处理UI收到POLICY_NOT_FOUND就显示“找不到该政策请检查编号”收到ACCESS_DENIED就引导用户申请权限。告警可分级SERVICE_UNAVAILABLE触发P1告警立即响应POLICY_NOT_FOUND是P3每日汇总。LLM可重试当Gemini收到SERVICE_UNAVAILABLE且含retry_after_ms会自动等待后重试而非直接放弃。我们线上有个案例get-policy-pdf因PDF生成服务临时故障连续返回SERVICE_UNAVAILABLE。平台自动启用熔断10分钟后恢复期间所有调用被优雅降级为“政策暂不可用”没影响其他skills。4. 实操过程从本地开发到GCP生产环境部署全流程4.1 本地开发用Genkit Dev Server模拟真实环境别急着部署到GCP。Genkit的genkit dev命令启动本地server它会自动加载skills/目录下所有skills提供Web UI查看registry、测试调用、查看schema模拟GCP Agent Platform的权限校验需配置devAuth.json我们的devAuth.json示例{ identity: { email: testcompany.com, attributes: { department: HR, employment_status: active } }, allowedScopes: [https://www.googleapis.com/auth/gmail.send] }这样测试send-email时就能验证权限逻辑是否生效。比写单元测试快10倍因为它是端到端的集成测试。注意本地dev server不校验IAM角色因无GCP环境但会校验OAuth scopes和required_attributes。这是故意设计——IAM角色必须在GCP Console配置本地无法模拟避免开发者产生“本地OK线上就OK”的错觉。4.2 配置管理用YAML分离环境差异skills的配置如API密钥、超时时间绝不能硬编码。Genkit支持genkit.config.tsimport { defineConfig } from genkit-ai/core; export default defineConfig({ plugins: [/* ... */], env: { development: { gmail: { client_id: dev-client-id, client_secret: dev-secret } }, production: { gmail: { client_id: process.env.GMAIL_CLIENT_ID!, client_secret: process.env.GMAIL_CLIENT_SECRET! } } } });部署时用genkit deploy --envproduction自动注入prod配置。我们还加了一层在GCP Secret Manager中存GMAIL_CLIENT_SECRET部署脚本里用gcloud secrets versions access获取确保密钥不落盘。4.3 部署到GCP Agent Platform四步完成准备GCP项目启用generativeai.googleapis.com和cloudfunctions.googleapis.comAPI创建service account并赋予roles/genai.admin和roles/cloudfunctions.invoker。构建Docker镜像Genkit CLI自动生成Dockerfile但我们要改两处基础镜像用gcr.io/cloud-genai-dev/genkit-nodejs:18官方优化版启动快30%添加RUN npm ci --onlyproduction删掉devDependencies减小镜像体积部署函数genkit deploy \ --projectmy-proj \ --regionus-central1 \ --service-accountskills-samy-proj.iam.gserviceaccount.com \ --envproduction命令会构建镜像并推送到Artifact Registry创建Cloud Function内存2GB超时30s注册skill到Agent Platform registry配置IAM权限在GCP Console中为service account授予roles/gmail.send调用Gmail APIroles/secretmanager.secretAccessor读取密钥roles/logging.logWriter写日志部署后访问https://us-central1-my-proj.cloudfunctions.net/genkit-skills能看到所有skills的OpenAPI文档。这才是真正的“skills市场”——不是下载包而是可发现、可调用的服务目录。4.4 Agent集成让Gemini真正“懂”你的skills光有skills不够得让LLM能调用。GCP Agent Platform提供两种方式Auto-DiscoveryAgent Platform自动扫描registry将skills元数据name/description/inputSchema注入Gemini的system prompt。但缺点是LLM可能生成错误参数。Structured Tool Calling推荐在Agent配置中显式声明skillsagent: name: hr-assistant model: gemini-1.5-pro tools: - name: send-email description: Send emails to colleagues input_schema: | { type: object, properties: { to: {type: array, items: {type: string}}, subject: {type: string}, body: {type: string} } } - name: get-policy-pdf description: Retrieve HR policy documents by ID input_schema: | { type: object, properties: { policy_id: {type: string} } }关键点input_schema必须和skill定义一致且用JSON Schema字符串而非引用。这样Gemini能精确理解参数结构避免自由发挥。我们实测对比用auto-discovery时“发邮件给张三和李四主题是报销”被解析为{to: [zhangsan, lisi], subject: 报销}邮箱格式错误用structured方式Gemini严格按schema生成{to: [zhangsancompany.com, lisicompany.com], subject: 报销}成功率从72%提升到99.4%。5. 常见问题与排查技巧实录那些文档里不会写的坑5.1 热词真相“gemini登录”“gemini macbook 下载”背后是skills调用链断裂搜索“gemini登录”“gemini macbook 下载”暴露出一个普遍问题用户想用Gemini做某事如“帮我写周报”但skills调用失败界面卡在登录页或提示“下载失败”。这不是Gemini的问题而是skills的认证流没打通。典型场景get-calendar-eventsskill需要Google Calendar API权限但用户首次调用时OAuth flow被拦截。原因往往是redirect_uri不匹配GCP Console中OAuth consent screen配置的redirect_uri是https://my-app.com/oauth/callback但skills部署在Cloud Functions实际回调地址是https://us-central1-my-proj.cloudfunctions.net/genkit-oauth-callback。必须在GCP Console中添加后者。scopes未声明skill的auth_requirements写了[https://www.googleapis.com/auth/calendar.readonly]但OAuth consent screen里没勾选对应scope用户授权时看不到该权限。排查方法打开Chrome DevTools → Network tab → 搜索oauth看302跳转的URL中scope参数是否包含skill声明的scope。若缺失去GCP Console补全。5.2 “your account is not eligible”不是账号问题是skills权限策略太粗这条报错90%源于skills的auth_requirements配置不当。例如// 错误写法要求所有调用者都有admin权限 auth_requirements: [roles/resourcemanager.projectIamAdmin] // 正确写法按最小权限原则 auth_requirements: [roles/hr.payroll_reader]GCP的roles/resourcemanager.projectIamAdmin是项目级管理员角色普通员工不可能有。而roles/hr.payroll_reader是自定义角色只授予HR部门成员。解决方案在GCP IAM Console中创建精细化角色创建自定义角色roles/hr.payroll_reader只添加必要权限resourcemanager.projects.get,secretmanager.secrets.access绑定到HR组如hr-teamcompany.com这样当员工调用access-payroll-data时平台检查其是否属于hr-teamcompany.com而非要求admin权限。5.3 “skills开发”“skills大全”背后的版本管理灾难很多团队把skills当npm包管理结果陷入“版本地狱”v1.2的send-email修复了附件bug但v1.3的get-policy-pdf依赖v1.2的send-email而v1.4的track-email-status又依赖v1.1的send-email……最终线上同时跑着三个版本互相冲突。Genkit的解法是skills版本绑定到部署ID而非语义化版本号。每次genkit deploy生成唯一部署ID如send-email-20240520-1423-a1b2c3Agent配置中引用具体IDtools: - name: send-email-20240520-1423-a1b2c3 # ...这样新部署不影响旧Agent旧Agent继续用老版本可随时回滚把Agent配置改回上一个ID即可审计清晰每个调用日志都带部署ID知道哪次失败是哪个版本导致我们线上有个最佳实践每周一上午10点自动部署生成ID含日期send-email-20240520-1000-...运维同学看日志一眼就知道是哪个版本出的问题。5.4 “agent skills测试”必须测的三件事别只测“调用成功”。一个skills的完整测试应覆盖Schema校验测试用非法输入触发平台级校验curl -X POST https://.../send-email -d {to:[invalid]}应返回400及详细错误信息而非500 Internal Error。权限边界测试用低权限账号调用高敏skillcurl -H Authorization: Bearer $LOW_PRIV_TOKEN https://.../access-payroll-data必须返回403且错误信息不能泄露内部结构如不能显示“缺少roles/hr.payroll_reader”。熔断测试模拟skill持续失败for i in {1..10}; do curl https://.../get-policy-pdf?policy_idfake; done第10次应返回503 Service Unavailable熔断触发而非继续尝试。我们用Jest写自动化测试每个skills目录下放__tests__/index.test.tsCI流水线强制通过才允许部署。上线六个月零次因skills bug导致的P1事故。6. 实战经验总结skills不是功能而是组织能力的载体最后分享一个血泪教训我们最早做skills时把“查报销政策”“提交报销单”“跟踪报销进度”拆成三个skills逻辑清晰。但上线后发现80%的用户操作是“我要报销”然后卡在第一步——因为LLM不知道这三个skills要按顺序调用。于是我们重构为一个复合skillsubmit-expense-claim它内部编排三个原子skills但对外只暴露一个入口。用户说“我要报销”Agent直接调用submit-expense-claim传入发票图片和说明skill内部完成OCR、校验、提交全流程。这揭示了skills设计的本质它不是按技术模块切分而是按用户任务切分。用户不关心“OCR”“API调用”“数据库写入”只关心“报销成功”。所以现在我们的skills设计流程是从用户旅程地图User Journey Map中提取高频任务如“入职”“转正”“离职”为每个任务定义输入用户说啥、输出用户得到啥、失败场景哪里可能卡住再反向拆解需要哪些原子skills支撑比如“入职”任务输入是“新员工张三入职日2024-06-01”输出是“邮箱开通、门禁卡发放、电脑配发完成”失败场景包括“AD账户创建失败”“门禁系统无响应”。这时skills不是create-ad-account而是onboard-new-hire它内部协调多个系统对外只承诺结果。skills的终极价值从来不是让LLM更聪明而是让组织能力可沉淀、可复用、可度量。当你看到“skills推荐”“skills下载平台”这些热词时别想着找现成包先问问自己我的业务里哪些任务值得被封装成skills哪些能力应该被标准化哪些流程可以被LLM自动编排这才是skills该有的样子——不是锦上添花的玩具而是重构工作流的基石。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑