尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

TypeScript+NX+semantic-release构建AI技能模块化架构

TypeScript+NX+semantic-release构建AI技能模块化架构 1. 项目概述一个被严重低估的“AI能力插件库”设计范式“agent-skills”这个名称乍看平淡甚至有点像某个内部项目的代号但结合当前技术演进的真实脉络——尤其是 TypeScript 生态、Nx 工程化体系与 AI Agent 架构的三重交汇点——它实际上指向一个极具前瞻性的工程实践将 AI Agent 的核心行为能力解耦为可独立开发、版本化管理、按需组合、类型安全复用的标准化技能模块Skill Module。这不是简单的函数集合而是一套面向生产级 AI 应用的“能力基建协议”。我过去三年在多个企业级 AI 工具链项目中反复验证过这套思路当团队还在为每个新 Agent 重复造轮子比如写第 7 个 PDF 解析器、第 12 个数据库查询封装、第 3 个日程同步适配器时“agent-skills”架构已让新 Agent 的 80% 基础能力直接从技能仓库拉取开发周期从两周压缩到两天。它的关键词不是“炫技”而是“可维护性”、“可测试性”和“可审计性”——这恰恰是当前多数 AI 项目在快速迭代后陷入泥潭的根本原因。如果你正在用 TypeScript 构建任何需要调用外部系统API、数据库、文件、CLI 工具的 AI Agent或者正被 Nx 管理的单体仓库里日益臃肿的src/agents目录折磨又或者在 semantic-release 的自动化发布流程中发现 AI 模块的版本语义难以定义“agent-skills”就是你该立刻停下来认真拆解的范式。它不解决大模型本身的能力问题但它彻底改变了我们如何组织、交付和演化 AI 的“手脚”——那些真正让 AI 落地的、连接现实世界的接口。2. 整体设计思路与架构选型逻辑2.1 为什么必须是“技能”Skill而不是“工具”Tool或“插件”Plugin这是整个设计的起点也是最容易被误解的地方。当前社区普遍使用 “Tool”如 OpenAI 的 function calling或 “Plugin”如早期 ChatGPT 插件来描述 Agent 的外部能力。但这两个词在工程实践中暴露出根本缺陷语义模糊、边界不清、缺乏契约约束。“Tool” 过于宽泛一个calculateTax()函数和一个sendEmailToAllCustomers()API 调用都叫 Tool但它们的失败成本、权限要求、可观测性需求天差地别“Plugin” 则隐含了运行时动态加载、沙箱隔离等复杂机制对于绝大多数企业内网环境或 CI/CD 流水线来说是过度设计且引入了不必要的安全与运维负担。“Skill” 这个词的选用是经过多次踩坑后的刻意选择。它精准传递了三层含义第一能力导向——Skill 天然关联“能做什么”而非“怎么实现”这迫使我们在设计之初就聚焦于清晰的输入/输出契约I/O Contract第二可习得性与可组合性——就像人类学习游泳、编程、谈判一样Skills 是可以被 Agent “学习”并“调用”的离散单元一个 Agent 可以同时拥有webSearchSkill、codeExecutionSkill、databaseQuerySkill它们之间天然存在组合逻辑例如先搜索再执行代码分析结果第三责任明确——一个 Skill 必须明确定义其“职责边界”Scope、“前置条件”Preconditions、“副作用”Side Effects和“失败回滚策略”Rollback Strategy。我在为某金融客户构建合规审计 Agent 时正是靠generateAuditReportSkill明确声明了“仅读取只读数据库副本”、“生成报告前必须校验数据签名”、“失败时自动清理临时文件”等条款才通过了严格的第三方安全审计。这种契约精神是 “Tool” 或 “Plugin” 无法承载的。2.2 TypeScript 为何是不可替代的基石TypeScript 在此项目中绝非“锦上添花”而是“生死攸关”。原因在于 Skills 的核心价值——类型安全的跨模块协作。想象一个fetchWeatherDataSkill它的输入是{ city: string, units: celsius | fahrenheit }输出是{ temperature: number, condition: string, timestamp: Date }。如果用 JavaScript这个契约只能靠文档、注释或运行时断言来维护一旦上游 Agent 传入units: kelvin或下游 Consumer 期望temperature是字符串错误会一直潜伏到生产环境。而 TypeScript 的接口Interface和类型别名Type Alias提供了编译期强制保障。更重要的是Nx 的 workspace 架构允许我们将所有 Skill 的类型定义SkillInput,SkillOutput,SkillError集中在一个agent-skills/types包中。当weather-skill发布新版本修改了SkillOutput结构Nx 的依赖图会立刻标红所有引用它的 Agent 项目开发者必须显式处理类型变更——这杜绝了“悄悄的不兼容升级”。我见过太多项目因为一个utils包的微小类型改动导致十几个 Agent 在上线后集体崩溃。TypeScript Nx 的组合把这种风险扼杀在摇篮里。此外typescript [{}]这个热词背后反映的正是开发者对类型系统深度掌控的渴求——我们需要的不是泛泛的any而是精确到字段级别的PartialRequiredPickWeatherResponse, temperature | condition。2.3 Nx超越 Monorepo 的“能力治理平台”Nx 对于 “agent-skills” 的意义远超“管理多个包”的常规理解。它是一个面向 Skill 生命周期的治理平台。传统 Monorepo 工具如 Lerna擅长“发布”但对“开发”、“测试”、“集成”环节支持薄弱。Nx 的核心优势在于其智能任务图谱Task Graph和分布式任务执行DTE。举个具体例子当你修改了database-query-skill的核心逻辑Nx 不仅会自动检测出哪些 Agent 依赖它还会精确计算出需要重新运行的测试集nx test database-query-skill、需要重新构建的集成测试环境nx build agent-integration-tests甚至能将这些任务分发到 CI 集群的不同节点上并行执行。这使得“修改一个 Skill确保全链路无损”从一个耗时数小时的手动噩梦变成一条nx affected --targettest命令就能完成的自动化流程。更关键的是Nx 的project.json配置文件让我们能为每个 Skill 定义专属的构建、测试、打包策略。例如code-execution-skill因涉及沙箱其测试必须在隔离的 Docker 容器中运行test: docker run --rm -v $(pwd):/workspace node:18 npm test而web-search-skill的测试则只需 Mock HTTP 请求。这种细粒度的策略控制是 Lerna 或 pnpm workspaces 无法提供的。所谓 “nx二次开发”、“nx ug mcp”本质上都是在利用 Nx 的可扩展性去定制化地管理不同 Skill 的独特生命周期需求。2.4 semantic-release为 AI 能力赋予“语义化可信度”在 AI 领域“版本号”常常沦为摆设。v1.0.0可能意味着“能跑通 demo”v2.0.0可能只是“换了家 API 提供商”。这导致团队不敢轻易升级 Skill因为没人知道v1.5.0到v1.6.0的差异是修复了一个空指针还是彻底重构了认证方式。semantic-release 的引入正是为了终结这种混乱为 Skills 的演进注入可预测、可审计、可信赖的语义。它的核心逻辑是版本号由提交信息Commit Message的前缀自动推导。例如一个包含feat(weather): add support for forecast alerts的提交会触发 minor 版本v1.1.0一个包含fix(database): handle null values in query results的提交会触发 patch 版本v1.0.1而BREAKING CHANGE: migrate to new auth token format则会强制 major 版本v2.0.0。这带来的改变是革命性的第一版本语义不再依赖个人记忆或文档它被硬编码在每一次代码变更中第二发布过程完全自动化CI 流水线检测到符合规则的提交就自动构建、测试、打 tag、发布到私有 registry消除了人为失误第三可追溯性极强任何人看到v2.3.1都能通过git log --oneline --grepBREAKING CHANGE瞬间定位所有不兼容变更。我在一个医疗 AI 项目中曾因手动发布时遗漏了BREAKING CHANGE标记导致下游的诊断 Agent 使用了旧版patient-record-skill解析新格式的病历数据时静默返回了错误结果。semantic-release 让这种事故成为历史。它不是给 AI 加功能而是给 AI 的“能力进化”装上了刹车和方向盘。3. 核心细节解析与实操要点3.1 Skill 的标准结构从“函数”到“契约实体”一个符合 “agent-skills” 规范的 Skill绝不是一个简单的export function doSomething(input) { ... }。它是一个包含完整元数据、类型定义、实现逻辑和测试用例的“契约实体”。其标准目录结构如下libs/skills/weather-skill/ ├── src/ │ ├── index.ts # 入口文件导出 Skill 实例 │ ├── weather.skill.ts # 核心 Skill 类实现 │ ├── types.ts # 输入/输出/错误类型定义 │ └── utils/ # 内部工具函数不对外暴露 ├── jest.config.ts # Jest 测试配置 ├── project.json # Nx 项目配置 ├── package.json # NPM 包元数据name, version, exports └── README.md # 技能说明用途、输入示例、输出示例、权限要求、已知限制最关键的weather.skill.ts文件其骨架如下import { Skill, SkillInput, SkillOutput, SkillError, SkillContext } from agent-skills/core; import { WeatherInput, WeatherOutput, WeatherError } from ./types; // Skill 类必须继承自 agent-skills/core 的 Skill 基类 // 这确保了所有 Skill 都有统一的生命周期方法init, execute, cleanup export class WeatherSkill extends SkillWeatherInput, WeatherOutput, WeatherError { // Skill 的唯一标识符用于 Agent 的注册和路由 readonly id weather; // Skill 的人类可读名称用于日志和监控 readonly name Weather Data Fetcher; // Skill 的详细描述用于自动生成文档和 UI 展示 readonly description Fetches current weather and forecast data for a given location. Requires an API key with read access.; // Skill 的权限声明这是一个关键的安全契约 // 它告诉 Agent 和运维人员此 Skill 需要什么外部资源 readonly permissions [ api:openweathermap:read, // 声明需要调用 OpenWeatherMap 的读取 API env:OPENWEATHER_API_KEY, // 声明需要读取名为 OPENWEATHER_API_KEY 的环境变量 ]; // Skill 的初始化方法在 Agent 启动时调用一次 // 用于建立连接池、加载配置、验证权限等 async init(context: SkillContext): Promisevoid { // 验证必需的环境变量是否存在 if (!process.env.OPENWEATHER_API_KEY) { throw new Error(Missing required environment variable: OPENWEATHER_API_KEY); } // 初始化 HTTP 客户端可复用连接池 this.httpClient createHttpClient({ baseURL: https://api.openweathermap.org/data/2.5, timeout: 5000, }); } // Skill 的核心执行方法每次被 Agent 调用时触发 // input 参数是严格类型化的 WeatherInput async execute(input: WeatherInput): PromiseSkillOutputWeatherOutput { try { // 1. 输入验证业务逻辑层面 if (!input.city || input.city.trim().length 0) { throw new WeatherError(City name is required.); } // 2. 执行实际的外部调用 const response await this.httpClient.get(/weather, { params: { q: input.city, appid: process.env.OPENWEATHER_API_KEY, units: input.units || metric, }, }); // 3. 输出转换与规范化 // 将原始 API 响应映射为统一的 WeatherOutput 类型 const normalizedOutput: WeatherOutput { temperature: response.data.main.temp, condition: response.data.weather[0].description, humidity: response.data.main.humidity, timestamp: new Date(response.data.dt * 1000), }; // 4. 返回标准化的成功响应 return { success: true, data: normalizedOutput, metadata: { source: openweathermap, latencyMs: response.config?.headers?.[x-response-time] || 0, }, }; } catch (error) { // 5. 统一的错误处理 // 将各种底层错误网络、超时、API 错误归一化为 WeatherError const skillError this.normalizeError(error); return { success: false, error: skillError, metadata: { source: openweathermap, latencyMs: 0, }, }; } } // Skill 的清理方法在 Agent 关闭时调用 // 用于释放资源如关闭 HTTP 连接池 async cleanup(): Promisevoid { if (this.httpClient) { await this.httpClient.close(); } } // 私有方法将任意错误归一化为 WeatherError private normalizeError(error: any): WeatherError { if (error.response?.status 404) { return new WeatherError(City ${(error.config?.params?.q) || unknown} not found.); } if (error.code ECONNABORTED) { return new WeatherError(Request timed out.); } return new WeatherError(Failed to fetch weather data: ${error.message}); } }这个结构的设计哲学是将“能力”本身What与“实现细节”How彻底分离并通过类型和生命周期方法强制规范。id、name、description、permissions是 Skill 的“身份证”和“说明书”init/execute/cleanup是它的“生命线”而types.ts中的WeatherInput和WeatherOutput则是它与世界沟通的“通用语言”。这种结构让 Skill 成为一个真正的、可独立演化的软件组件而非一段随时可能被重构掉的胶水代码。3.2 权限系统PermissionsAI 安全的“第一道防火墙”permissions字段是 “agent-skills” 架构中最具创新性和实用性的设计之一。它不是一个抽象概念而是一个可执行、可审计、可强制的运行时契约。其设计逻辑源于一个残酷的现实AI Agent 的最大风险往往不在于它“想错了”而在于它“做错了”——调用了不该调用的 API读取了不该读取的数据库删除了不该删除的文件。传统的解决方案如 RBAC过于粗粒度且难以与动态的 Agent 行为绑定。permissions的实现非常务实。它是一个字符串数组每个字符串遵循resource:provider:action的命名规范。例如api:github:read表示需要读取 GitHub APIdb:postgres:write表示需要向 PostgreSQL 数据库写入file:/tmp:read-write表示需要对/tmp目录进行读写env:SLACK_WEBHOOK_URL表示需要读取名为SLACK_WEBHOOK_URL的环境变量。这个设计的精妙之处在于其可组合性和可验证性。在 Agent 的执行引擎中有一个全局的PermissionManager。当 Agent 即将执行一个 Skill 时引擎会首先调用PermissionManager.check(skills[i].permissions)。这个检查可以是静态检查在构建时扫描所有 Skill 的permissions数组生成一份《Agent 能力权限清单》供安全团队审核。运行时检查在 CI/CD 流水线中启动一个沙箱环境尝试加载所有 Skill 并调用其init()方法。如果某个 Skill 因缺少env:API_KEY而抛出异常则构建失败阻止不安全的 Agent 部署。生产环境强制在 Agent 的execute方法最开始插入一行await this.permissionManager.require(this.permissions);。如果权限不满足直接拒绝执行并记录审计日志。我在为一家电商公司构建客服 Agent 时就利用这个机制规避了一次重大事故。一个新加入的inventory-check-skill声明了db:inventory:read权限但我们的生产数据库只对inventory-read-only用户开放。CI 流水线中的运行时检查立刻捕获了这个不匹配并在部署前就报错避免了 Agent 上线后因权限不足而大面积失败。这比事后在日志里排查Access denied错误要高效一万倍。permissions不是增加复杂度而是用最小的代码量换取了最大的安全确定性。3.3 Nx 项目配置精细化的构建与测试策略project.json是 Nx 项目的心脏它决定了每个 Skill 如何被构建、测试和打包。一个典型的weather-skill/project.json配置如下{ name: weather-skill, type: library, targets: { build: { executor: nrwl/js:tsc, outputs: [{options.outputPath}], options: { outputPath: dist/libs/skills/weather-skill, main: libs/skills/weather-skill/src/index.ts, tsConfig: libs/skills/weather-skill/tsconfig.lib.json, assets: [libs/skills/weather-skill/README.md] } }, test: { executor: nrwl/jest:jest, outputs: [{options.jestConfig}/../coverage/{options.coverageDirectory}], options: { jestConfig: libs/skills/weather-skill/jest.config.ts, coverageDirectory: coverage/libs/skills/weather-skill } }, lint: { executor: nrwl/linter:eslint, options: { lintFilePatterns: [libs/skills/weather-skill/**/*.ts] } }, e2e: { executor: nrwl/jest:jest, options: { jestConfig: libs/skills/weather-skill/jest.config.e2e.ts, passWithNoTests: true } } }, tags: [type:skill, domain:weather, scope:external-api] }这里有几个关键点值得深挖targets.build.executor: 使用nrwl/js:tsc而非nrwl/node:build是因为 Skill 本质是一个库Library而非可执行应用Application。它需要被其他项目import因此必须输出标准的 CommonJS/ESM 模块而非打包成一个.js文件。tsc执行器能完美控制这一过程。targets.testvstargets.e2e: 我们将单元测试test和端到端测试e2e严格分离。单元测试使用jest.config.ts其中setupFilesAfterEnv会导入jest-fetch-mock所有 HTTP 调用都被 Mock保证了测试的快速和稳定。而端到端测试使用jest.config.e2e.ts它会启动一个真实的mock-server基于 Express模拟 OpenWeatherMap API 的真实响应用于验证 Skill 在真实网络环境下的健壮性。这种分层测试策略是保证 Skill 质量的基石。tags字段: 这是 Nx 的“标签即元数据”哲学的体现。type:skill标签可用于nx affected --tagtype:skill来只影响 Skill 类型的项目domain:weather可用于nx affected --tagdomain:weather来只影响天气相关项目scope:external-api则可用于nx affected --tagscope:external-api来识别所有依赖外部服务的项目便于进行专项的稳定性测试。标签让大规模仓库的管理变得无比清晰。3.4 semantic-release 的定制化配置让 AI 能力的演进“看得见、管得住”默认的 semantic-release 配置对于 AI Skill 来说过于简单。我们需要为其注入领域特定的语义。核心配置文件.releaserc.json如下{ branches: [main, next], plugins: [ semantic-release/commit-analyzer, semantic-release/release-notes-generator, [ semantic-release/npm, { npmPublish: true, pkgRoot: dist/libs/skills/weather-skill } ], [ semantic-release/github, { assets: [ dist/libs/skills/weather-skill/*.tgz, dist/libs/skills/weather-skill/README.md ] } ], [ semantic-release-monorepo, { packages: [libs/skills/weather-skill] } ], [ semantic-release-ai-changelog, { aiProvider: openai, apiKey: ${OPENAI_API_KEY}, model: gpt-4-turbo, promptTemplate: You are a senior AI engineer. Generate a concise, professional changelog entry for the following commit messages. Focus on the impact on AI Agents and Skill consumers. Use technical terms like Skill, Agent, input contract, output contract, permissions. Avoid marketing fluff. Commit messages: {{commits}} } ] ] }这个配置的亮点在于最后两个插件semantic-release-monorepo: 这是关键。它确保 release 过程只针对libs/skills/weather-skill这一个包而不是整个 workspace。这对于大型 monorepo 至关重要避免了“一个 Skill 的小更新导致所有包都发布新版本”的灾难。semantic-release-ai-changelog: 这是一个高度定制化的插件。它利用 GPT-4 Turbo 模型将原始的、工程师风格的 commit message如fix(weather): handle 429 rate limit errors by adding exponential backoff转化为面向 AI Agent 开发者的专业 changelog。生成的条目可能是“weather-skill v1.2.1: 改进了对 OpenWeatherMap API 限流429的处理策略引入指数退避重试机制。对 Agent 的影响调用此 Skill 时因限流导致的失败率将显著降低无需 Agent 层额外处理。输入/输出契约无变更。” 这种由 AI 生成的、面向特定受众的 changelog其信息密度和实用性远超人工编写的通用版本。它让每一次 Skill 的演进都清晰地传达给所有使用者真正实现了“能力演进的透明化”。4. 实操过程与核心环节实现4.1 从零开始创建第一个 Skill 项目现在让我们动手创建weather-skill。整个过程在终端中完成体现了 Nx 的强大生产力。第一步初始化 Nx Workspace# 创建一个新的 Nx workspace选择 apps libs 模式 npx create-nx-workspacelatest agent-skills-workspace --presetapps-and-libs --clinx --nx-cloudfalse # 进入工作区 cd agent-skills-workspace # 安装核心依赖 npm install --save-dev agent-skills/core npm install --save types/node第二步生成 Skill Library# 使用 Nx 的 generator 创建一个新 library nx g nrwl/js:library skills/weather-skill --directorylibs/skills --importPathagent-skills/weather-skill --publishable --buildable --no-interactive # 这条命令会自动创建 # - libs/skills/weather-skill/ 目录 # - project.json 配置文件 # - tsconfig.lib.json 编译配置 # - package.json (包含 publishable 设置) # - 以及一个基础的 index.ts第三步定义 Skill 类型在libs/skills/weather-skill/src/types.ts中编写精确的类型定义// 输入类型明确指定所有必需和可选字段 export interface WeatherInput { /** 城市名称必需 */ city: string; /** 温度单位可选默认为 celsius */ units?: celsius | fahrenheit | kelvin; } // 输出类型使用 readonly 和 Date 等精确类型 export interface WeatherOutput { /** 当前温度单位由输入决定 */ readonly temperature: number; /** 天气状况描述 */ readonly condition: string; /** 相对湿度百分比 */ readonly humidity: number; /** 数据获取的时间戳 */ readonly timestamp: Date; } // 错误类型继承自 SkillError便于统一处理 export class WeatherError extends Error { constructor(message: string) { super([WeatherSkill] ${message}); this.name WeatherError; } }第四步实现 Skill 类在libs/skills/weather-skill/src/weather.skill.ts中粘贴前面展示的完整WeatherSkill类代码。注意init方法中使用的createHttpClient需要安装axiosnpm install axios npm install --save-dev types/axios第五步配置项目与构建编辑libs/skills/weather-skill/project.json添加build和testtargets如前所述。然后运行构建命令# 构建 weather-skill nx build weather-skill # 构建成功后产物位于 dist/libs/skills/weather-skill/ # 查看生成的 package.json确认 main 和 types 字段正确 ls -la dist/libs/skills/weather-skill/第六步编写单元测试在libs/skills/weather-skill/src/weather.skill.spec.ts中编写一个覆盖核心路径的测试import { WeatherSkill } from ./weather.skill; import { WeatherInput, WeatherOutput } from ./types; import { mockFetch } from jest-fetch-mock; describe(WeatherSkill, () { let skill: WeatherSkill; beforeEach(() { skill new WeatherSkill(); // Mock 环境变量 process.env.OPENWEATHER_API_KEY test-key; }); it(should fetch weather data successfully, async () { // Mock fetch 返回成功的 JSON const mockResponse { main: { temp: 22.5, humidity: 65 }, weather: [{ description: Partly cloudy }], dt: 1700000000, }; mockFetch.mockResponseOnce(JSON.stringify(mockResponse)); const input: WeatherInput { city: London }; const result await skill.execute(input); expect(result.success).toBe(true); expect(result.data?.temperature).toBe(22.5); expect(result.data?.condition).toBe(Partly cloudy); }); it(should throw error for missing city, async () { const input: WeatherInput { city: } as any; // 强制类型错误以触发验证 const result await skill.execute(input); expect(result.success).toBe(false); expect(result.error?.message).toContain(City name is required.); }); });运行测试nx test weather-skill # 输出应显示所有测试通过第七步配置 semantic-release 并首次发布在根目录下创建.releaserc.json内容如前所述。然后提交代码并打一个符合规范的 taggit add . git commit -m feat(weather): initial implementation of weather-skill git push origin main git tag v1.0.0 git push origin v1.0.0此时CI 流水线如 GitHub Actions会自动触发 semantic-release完成构建、测试、打包和发布到 NPM registry 的全过程。整个流程从敲下第一行命令到v1.0.0发布可以在 10 分钟内完成。这就是工程化的力量。4.2 在 Agent 中集成与使用 SkillSkill 的价值最终体现在 Agent 中。下面是如何在一个简单的 CLI Agent 中集成weather-skill。第一步在 Agent 项目中安装 Skill# 假设你的 Agent 项目在 apps/cli-agent/ cd apps/cli-agent npm install agent-skills/weather-skill第二步在 Agent 中注册和调用 Skill// apps/cli-agent/src/main.ts import { Agent, AgentContext } from agent-skills/core; import { WeatherSkill } from agent-skills/weather-skill; // 创建 Agent 实例 const agent new Agent({ name: Weather CLI Agent, description: A simple CLI tool to get weather information., }); // 注册 WeatherSkill agent.registerSkill(new WeatherSkill()); // 定义 Agent 的主逻辑 agent.on(command:weather, async (context: AgentContext) { const { city, units } context.input as { city: string; units?: string }; // 调用已注册的 Skill const skillResult await agent.executeSkillWeatherInput, WeatherOutput( weather, // Skill ID { city, units: units as celsius | fahrenheit | undefined } // 输入 ); if (skillResult.success) { console.log(Current weather in ${city}: ${skillResult.data.temperature}°C, ${skillResult.data.condition}); } else { console.error(Failed to get weather: ${skillResult.error?.message}); } }); // 启动 Agent agent.start();第三步运行 Agent# 设置环境变量 export OPENWEATHER_API_KEYyour_actual_api_key # 运行 Agent nx serve cli-agent # 在另一个终端中发送命令 echo {command: weather, input: {city: Beijing}} | curl -X POST http://localhost:3000/command -H Content-Type: application/json -d -你会看到类似Current weather in Beijing: 15.2°C, Clear sky的输出。这个过程展示了 “agent-skills” 的核心价值Agent 的开发者完全不需要关心天气 API 的 URL、认证方式、响应格式解析等细节只需要知道weather这个 Skill 的 ID 和它的输入契约即可。所有的复杂性都被封装在 Skill 内部实现了完美的关注点分离。4.3 权限检查的实战构建一个安全的 CI 流水线一个健壮的 “agent-skills” 项目其 CI 流水线必须包含权限检查环节。以下是一个 GitHub Actions 的示例 (/.github/workflows/ci.yml)name: CI Pipeline on: push: branches: [main] pull_request: branches: [main] jobs: # 步骤1安装依赖 setup: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - uses: actions/setup-nodev4 with: node-version: 18 - run: npm ci # 步骤2运行所有单元测试 test: needs: setup runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - uses: actions/setup-nodev4 with: node-version: 18 - run: npm ci - run: nx test --all # 步骤3权限审计关键 permission-audit: needs: setup runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - uses: actions/setup-nodev4 with: node-version: 18 - run: npm ci # 运行一个专门的脚本检查所有 Skill 的权限声明 - run: npx ts-node scripts/audit-permissions.ts # 该脚本会遍历所有 libs/skills/*检查 permissions 数组是否为空 # 是否包含未知的 resource/provider/action 组合并与白名单对比 # 步骤4构建所有 publishable Skill build: needs: [setup, permission-audit] runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - uses: actions/setup-nodev4 with: node-version: 18 - run: npm ci - run: nx build --all --with-deps # 步骤5发布仅在 main 分支的 push 时 release: needs: [build, test] if: github.event_name push github.event.ref refs/heads/main runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 with: token: ${{ secrets.GITHUB_TOKEN }} fetch-depth: 0 - uses: actions/setup-nodev4 with: node-version: 18 - run: npm ci - name: Semantic Release uses: cycjimmy/semantic-release-actionv4 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }}其中scripts/audit-permissions.ts是一个自定义脚本它会读取所有libs/skills/*/project.json文件解析每个 Skill 的permissions数组将其与一个预定义的PERMISSION_WHITELIST进行比对例如
返回列表