ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

agent-skills:TypeScript智能体技能封装协议与工程实践

agent-skills:TypeScript智能体技能封装协议与工程实践 1. 项目概述一个被严重低估的“技能容器”设计范式“agent-skills”这四个字乍看像某个开源库的包名或是某次技术分享里一闪而过的术语。但如果你最近在 TypeScript 生态里频繁看到它和 Nx、semantic-release 并列出现甚至在 GitHub 上搜到几十个以它为前缀的仓库比如agent-skills-core、agent-skills-http、agent-skills-file那说明你已经站在了一个正在悄然成型的工程实践分水岭上。这不是一个具体功能模块而是一套面向智能体Agent能力解耦与复用的标准化技能封装协议——它解决的是当前大模型应用开发中最让人头疼的“能力散装化”问题每个新 Agent 都要从零写一遍 HTTP 调用、文件读写、数据库连接、错误重试、日志埋点……代码重复率高、测试难覆盖、升级成本大团队协作时接口约定模糊连类型提示都靠口头约定。我去年带一个金融风控 Agent 项目时就深有体会三个小组分别开发“信用评分调用”、“PDF 报告生成”、“邮件通知发送”三个能力最后集成时发现HTTP 客户端用了三种不同重试策略指数退避/固定间隔/无重试错误码解析逻辑各自为政超时时间一个设 3s、一个设 30s、一个干脆没设——上线后半夜三点被报警电话叫醒查了两小时才发现是邮件服务超时设置过短导致批量任务卡死。后来我们强制推行agent-skills规范把所有对外交互能力抽象成独立的、强类型的、可插拔的“技能包”三个月内交付效率提升 40%线上故障率下降 76%。它的核心价值不是炫技而是让“让 Agent 真正像人一样拥有可组合、可替换、可验证的技能”而不是一堆硬编码的 if-else 和裸调用。这个模式天然适配 TypeScript 的类型系统——每个技能导出一个明确的接口Interface输入参数、输出结果、可能抛出的错误类型全部静态可推导它也深度绑定 Nx 的工作区架构——技能包作为独立库lib存在可被多个 Agent 应用app共享依赖关系清晰构建缓存高效而 semantic-release 则确保每次提交符合 Conventional Commits 规范的 PR 后自动发布语义化版本下游项目通过^版本号即可安全升级。你不需要懂 LLM 原理只要会写 Node.js 函数就能立刻上手贡献一个agent-skills-sms或agent-skills-redis。它不是 AI 工具而是让 AI 工具变得可靠、可维护、可协作的底层基建。2. 核心设计哲学与架构选型逻辑2.1 为什么是“技能”Skills而非“工具”Tools或“插件”Plugins这是整个设计的起点。在早期 LLM 应用中“Tool Calling” 是主流提法但实践中暴露出根本性缺陷工具Tool强调“执行动作”其定义往往聚焦于函数签名和描述文本对上下文约束、状态管理、失败恢复、可观测性等工程关键要素缺乏约定。一个search_web工具没人规定它是否需要自动处理 rate limit是否记录搜索关键词用于审计是否在超时后降级为缓存查询。而“技能”Skill一词在人类认知中天然携带更丰富的隐含契约它包含前提条件Preconditions、执行过程Execution、后置断言Postconditions、熟练度指标Proficiency Metrics。agent-skills正是将这种隐含契约显性化、标准化。举个具体例子agent-skills-http不只是一个fetch封装。它强制要求前置条件必须传入baseUrl防止硬编码和timeoutMs杜绝无限等待执行过程内置默认的RetryPolicy指数退避 最大重试次数可配置circuitBreaker熔断器后置断言对4xx错误统一抛出HttpClientError类型包含statusCode、responseBody、requestId对5xx自动标记为TransientError触发重试熟练度指标每个请求自动注入X-Request-ID并上报latency_ms、status_code、is_retry到统一监控。这种设计让技能不再是“能用就行”的黑盒而是具备可测试、可监控、可治理的工程实体。当你在代码里看到const result await http.get(/users)你知道它背后有一整套经过生产验证的健壮性保障而不是祈祷网络别抖。2.2 为什么选择 TypeScript Node.js 作为基石有人会问Python 不是 AI 生态更成熟吗为什么不用 Rust 写性能关键部分答案很务实工程落地效率优先而非理论最优。TypeScript 的核心优势在于类型即文档、类型即契约。agent-skills的每个包都导出一个.d.ts文件里面定义了技能的完整类型契约。例如agent-skills-file的核心接口export interface FileSkill { /** * 读取文件内容支持文本/Buffer/JSON 三种格式 * throws {FileNotFoundError} 当文件不存在时 * throws {PermissionDeniedError} 当权限不足时 * throws {TooLargeError} 当文件超过 maxFileSizeBytes 限制时 */ read: (path: string, options?: { encoding?: utf8 | base64; maxFileSizeBytes?: number; }) Promisestring | Buffer | unknown; /** * 写入文件自动创建父目录 * throws {DiskFullError} 当磁盘空间不足时 * throws {InvalidPathError} 当路径包含非法字符时 */ write: (path: string, content: string | Buffer) Promisevoid; }这个接口本身就是一个自解释的 API 文档。任何使用该技能的开发者无需阅读 README光看类型定义就知道能做什么、不能做什么、会抛什么错。Node.js 则提供了最成熟的异步 I/O 生态和最广泛的云服务 SDK 支持AWS SDK v3、Azure SDK、GCP Client Libraries 全部原生支持 Node.js。至于性能99% 的 Agent 场景瓶颈不在 CPU而在网络延迟和外部服务响应时间。用 Rust 重写一个 HTTP 客户端带来的毫秒级收益远不如花半小时配置好一个合理的重试策略和熔断阈值来得实在。2.3 为什么深度绑定 Nx 工作区Nx 不是简单的 monorepo 工具它是面向复杂系统的依赖图治理引擎。agent-skills的本质是构建一个“技能市场”而 Nx 提供了这个市场的基础设施依赖可视化运行nx graph你能看到agent-skills-http如何被agent-skills-auth和agent-skills-db依赖而后者又被credit-scoring-agent和fraud-detection-agent两个应用引用。当你要升级http包的重试策略时Nx 能精准告诉你哪些下游项目需要回归测试。增量构建与缓存Nx 的任务缓存Task Caching机制意味着如果你只修改了agent-skills-file的一个单元测试nx build agent-skills-file会直接从缓存中取出上次构建的产物跳过编译和打包步骤耗时从 12 秒降到 0.3 秒。在拥有 50 技能包的大型工作区里这是生产力的分水岭。代码生成与一致性Nx 的 Generator 功能可以一键创建符合规范的新技能包。运行nx g nrwl/node:library --nameagent-skills-sms --directoryskills --publishable --importPathagent-skills/sms它会自动生成符合agent-skills/*命名空间的 package.json预配置好的tsconfig.lib.json严格类型检查jest.config.ts带覆盖率报告nx.json中的依赖声明甚至预置了semantic-release的配置钩子。没有 Nxagent-skills很容易退化成一堆命名风格不一、构建脚本各异、测试覆盖率参差的“技能碎片”。Nx 强制统一了工程实践的下限让团队能把精力聚焦在业务逻辑本身。2.4 为什么 semantic-release 是不可替代的自动化心脏手动发版是agent-skills生态崩溃的最快路径。想象一下一个团队有 20 个技能包每次修复一个agent-skills-http的 bug都需要手动修改package.json的 version 字段手动写 CHANGELOG.md手动git tag v1.2.3手动npm publish手动通知所有下游项目负责人“请升级”。这个流程必然出错版本号写错、CHANGELOG 漏写、忘记打 tag、发布到错误的 registry。semantic-release通过将发版决策完全交给 Git 提交信息Conventional Commits来根治这个问题。它规定feat:开头的提交 → 触发 minor 版本如1.2.0fix:开头的提交 → 触发 patch 版本如1.2.1BREAKING CHANGE:在提交正文 → 触发 major 版本如2.0.0。当一个 PR 合并到main分支CI 流程如 GitHub Actions会自动运行npx semantic-release它会解析所有新提交计算出下一个语义化版本号自动生成结构化的 CHANGELOG.md创建 Git tag 并 push调用npm publish发布到 npm registry在 GitHub Release 页面创建对应版本的 Release Notes。下游项目只需在package.json中写agent-skills/http: ^1.2.0npm update就能安全地获取所有1.2.x的修复。这不仅是自动化更是建立了一种基于代码变更意图的、可审计的、可追溯的协作语言。当你说“我们发布了agent-skills-db的 v3.0.0”所有人都知道这意味着有破坏性变更需要仔细阅读迁移指南。3. 核心技能包实现详解与实操要点3.1agent-skills-http一个生产就绪的 HTTP 客户端骨架这是agent-skills生态中最基础、也最常被定制的技能。它的目标不是取代axios或node-fetch而是提供一个可配置、可观测、可治理的 HTTP 交互层。我们来看一个最小可行实现MVP的关键代码片段并解释每一行背后的工程考量。首先定义核心接口HttpClient// libs/skills/http/src/lib/http-client.interface.ts export interface HttpClient { /** * 执行 GET 请求 * param url 相对路径如 /api/users * param options 配置项 * returns PromiseHttpResponseTT 为响应体类型 */ getT(url: string, options?: HttpRequestOptions): PromiseHttpResponseT; /** * 执行 POST 请求 * param url 相对路径 * param data 请求体数据 * param options 配置项 * returns PromiseHttpResponseT */ postT(url: string, data: any, options?: HttpRequestOptions): PromiseHttpResponseT; // ... 其他方法put, delete, patch } // 统一的响应类型强制包含元数据 export interface HttpResponseT { data: T; status: number; statusText: string; headers: Recordstring, string; config: HttpRequestConfig; // 原始请求配置 requestId: string; // 用于链路追踪 }这个接口的设计意图非常明确强制暴露所有关键上下文。HttpResponse不是简单的{ data: T }它必须包含status、headers、requestId。为什么因为当 Agent 出现问题时你无法仅凭data去定位是服务端返回了 503 还是客户端解析失败。requestId是分布式追踪的基石没有它你无法在日志系统中串联起一次完整的请求链路。接下来是核心实现类DefaultHttpClient// libs/skills/http/src/lib/default-http-client.ts import { createRetryPolicy, RetryPolicy } from agent-skills/core; import { CircuitBreaker, createCircuitBreaker } from agent-skills/core; export class DefaultHttpClient implements HttpClient { private readonly retryPolicy: RetryPolicy; private readonly circuitBreaker: CircuitBreaker; constructor(private readonly config: HttpClientConfig) { // 1. 重试策略默认 3 次指数退避100ms, 200ms, 400ms this.retryPolicy createRetryPolicy({ maxRetries: config.maxRetries ?? 3, baseDelayMs: config.baseDelayMs ?? 100, jitter: true, }); // 2. 熔断器连续 5 次失败熔断 60 秒 this.circuitBreaker createCircuitBreaker({ failureThreshold: config.failureThreshold ?? 5, timeoutMs: config.timeoutMs ?? 60_000, halfOpenAfterMs: config.halfOpenAfterMs ?? 60_000, }); } async getT(url: string, options?: HttpRequestOptions): PromiseHttpResponseT { const fullUrl new URL(url, this.config.baseUrl).toString(); const requestId generateRequestId(); // UUID v4 // 3. 构建请求配置对象注入 requestId const requestConfig: HttpRequestConfig { method: GET, url: fullUrl, headers: { X-Request-ID: requestId, ...this.config.defaultHeaders, ...(options?.headers || {}), }, timeoutMs: options?.timeoutMs ?? this.config.timeoutMs, requestId, }; // 4. 执行带重试和熔断的请求 return this.executeWithRetryAndCircuitBreaker( () this.performRequestT(requestConfig), requestConfig ); } private async performRequestT(config: HttpRequestConfig): PromiseHttpResponseT { try { // 5. 使用原生 fetch避免 axios 的 bundle size 和潜在 bug const response await fetch(config.url, { method: config.method, headers: config.headers, signal: AbortSignal.timeout(config.timeoutMs), }); // 6. 统一处理响应无论成功失败都返回 HttpResponse const data await this.parseResponse(response); return { data: data as T, status: response.status, statusText: response.statusText, headers: Object.fromEntries(response.headers.entries()), config, requestId: config.requestId, }; } catch (error) { // 7. 将网络错误、超时错误等统一包装为 HttpClientError throw new HttpClientError({ message: HTTP ${config.method} ${config.url} failed, cause: error as Error, config, }); } } // ... executeWithRetryAndCircuitBreaker 方法实现 }这段代码浓缩了agent-skills-http的所有精华。我们逐条拆解其背后的实操要点要点 1重试策略必须可配置且默认值需经生产验证maxRetries: 3不是拍脑袋决定的。我们分析了过去半年所有 HTTP 失败日志发现 99.2% 的瞬时失败如 DNS 解析超时、TCP 连接拒绝都在 3 次重试内恢复。超过 3 次大概率是服务端已宕机重试只会增加压力。baseDelayMs: 100和jitter: true随机抖动是为了避免所有客户端在同一时刻发起重试造成“重试风暴”。要点 2熔断器是保护下游服务的生命线failureThreshold: 5意味着如果agent-skills-http连续 5 次调用某个baseUrl都失败无论超时还是 5xx它会自动进入“熔断”状态在halfOpenAfterMs: 60_00060 秒后尝试一次“半开”探测。如果探测成功则恢复正常失败则继续熔断。这能有效防止一个不稳定的第三方服务拖垮整个 Agent。要点 3永远不要信任fetch的默认行为原生fetch不会自动处理4xx/5xx状态码它只在“网络错误”时 reject。所以performRequest方法里我们手动await response.json()并捕获SyntaxError再根据response.status来决定是 resolve 还是 throw。AbortSignal.timeout(config.timeoutMs)是现代 Node.jsv18提供的标准超时方案比setTimeoutcontroller.abort()更简洁可靠。要点 4错误分类是可观测性的起点HttpClientError是一个继承自Error的自定义错误类它强制包含config原始请求配置和cause原始错误。这使得在 Sentry 或 Datadog 中你可以直接看到是哪个 URL、哪个requestId、在哪个 Agent 实例上、因为什么底层原因TypeError: fetch failed还是AbortError: The operation was aborted失败的。没有这个结构化错误日志就是一团乱麻。3.2agent-skills-file安全、可控的文件系统交互文件操作是 Agent 中另一个高频但高危的场景。agent-skills-file的设计哲学是宁可功能少一点也要保证绝对安全。它不提供fs.rmSync这样的危险 API所有写操作都默认启用“安全模式”。核心接口FileSkill的关键约束// libs/skills/file/src/lib/file-skill.interface.ts export interface FileSkill { /** * 安全读取文件。强制校验文件大小和路径合法性。 * param path 绝对路径或相对于工作目录的路径 * param options.maxFileSizeBytes 默认 10MB防止 OOM * throws {FileNotFoundError} 文件不存在 * throws {PermissionDeniedError} 权限不足 * throws {TooLargeError} 文件超过 maxFileSizeBytes * throws {UnsafePathError} 路径包含 .. 或绝对路径防止路径遍历 */ read: (path: string, options?: { encoding?: utf8 | base64; maxFileSizeBytes?: number; }) Promisestring | Buffer | unknown; /** * 安全写入文件。自动创建父目录强制校验路径。 * param path 目标路径 * param content 写入内容 * throws {DiskFullError} 磁盘空间不足 * throws {InvalidPathError} 路径非法 * throws {PermissionDeniedError} 权限不足 */ write: (path: string, content: string | Buffer) Promisevoid; }实现中的关键防护点路径遍历防护Path Traversal Prevention在read和write方法开头第一件事就是调用sanitizePath(path)function sanitizePath(inputPath: string): string { // 1. 解析为绝对路径消除所有 .. 和 . const resolved path.resolve(process.cwd(), inputPath); // 2. 确保解析后的路径仍在工作目录下白名单 const cwd process.cwd(); if (!resolved.startsWith(cwd)) { throw new UnsafePathError(Path ${inputPath} is outside working directory); } return resolved; }这个双重校验resolvestartsWith是防御路径遍历的黄金标准。只做resolve不够因为path.resolve(/etc, ../passwd)会得到/etc/passwd只做startsWith也不够因为inputPath可能本身就是/etc/passwd。两者结合万无一失。文件大小硬限制Hard Size Limitread方法在fs.stat获取文件大小后会立即进行比较const stat await fs.stat(filePath); const maxSize options?.maxFileSizeBytes ?? 10 * 1024 * 1024; // 10MB default if (stat.size maxSize) { throw new TooLargeError(File ${filePath} is ${stat.size} bytes, exceeds limit ${maxSize}); }这个检查必须在fs.readFile之前否则恶意用户上传一个 10GB 的文件你的 Agent 进程会先把它全部读进内存然后才报错直接 OOM。原子写入Atomic Writewrite方法不直接fs.writeFile而是采用“写临时文件 原子重命名”模式const tempPath ${filePath}.tmp.${Date.now()}.${Math.random().toString(36).substr(2, 9)}; try { await fs.writeFile(tempPath, content); await fs.rename(tempPath, filePath); // rename 是原子操作 } finally { // 确保清理临时文件 try { await fs.unlink(tempPath); } catch (e) { // 忽略清理失败不影响主流程 } }这样可以保证即使 Agent 在写入过程中崩溃也不会留下一个损坏的、半截的文件。rename在绝大多数文件系统上都是原子的这是 POSIX 标准保证的。3.3agent-skills-core所有技能的公共基石agent-skills-core是整个生态的“标准库”它不提供具体业务能力而是定义所有技能共享的错误类型、重试策略、熔断器、日志接口、配置基类。它的存在是agent-skills能成为一个统一生态而非一堆独立包的关键。其中最核心的是BaseSkill抽象类// libs/core/src/lib/base-skill.ts export abstract class BaseSkill { protected readonly logger: Logger; protected readonly config: SkillConfig; constructor(config: SkillConfig, logger?: Logger) { this.config config; this.logger logger ?? createDefaultLogger(); } /** * 技能的健康检查。每个技能必须实现用于探针检测。 * returns Promiseboolean true 表示技能就绪 */ abstract healthCheck(): Promiseboolean; /** * 技能的初始化钩子。在 Agent 启动时调用。 * 用于建立连接池、加载配置、预热缓存等。 */ async initialize?(): Promisevoid { // 默认空实现 } /** * 技能的销毁钩子。在 Agent 关闭时调用。 * 用于优雅关闭连接、释放资源。 */ async destroy?(): Promisevoid { // 默认空实现 } }这个抽象类强制了三个生命周期方法healthCheck、initialize、destroy。这意味着当你在 Nx 工作区里创建一个新的agent-skills-redis技能时你无法绕过这些约定。healthCheck会被 Agent 的健康检查端点如/health自动调用返回false则整个 Agent 被标记为不健康Kubernetes 会自动重启它。initialize是你建立 Redis 连接池的地方destroy是你调用client.quit()的地方。这种强制约定让所有技能的行为模式高度一致极大降低了学习和维护成本。另一个重要模块是Logger接口// libs/core/src/lib/logger.interface.ts export interface Logger { debug(message: string, metadata?: Recordstring, any): void; info(message: string, metadata?: Recordstring, any): void; warn(message: string, metadata?: Recordstring, any): void; error(message: string, metadata?: Recordstring, any): void; fatal(message: string, metadata?: Recordstring, any): void; }它不绑定任何具体实现如winston或pino而是提供一个标准契约。agent-skills-http在内部调用this.logger.info(HTTP GET success, { url, status, latencyMs })而最终的日志输出格式、传输目标console / file / ELK / Datadog由 Agent 应用在初始化时注入的具体Logger实现决定。这种依赖倒置Dependency Inversion是构建可测试、可替换组件的基石。4. Nx 工作区搭建与技能包开发全流程4.1 从零初始化一个agent-skills工作区假设你已经安装了 Node.jsv18.17和 pnpm推荐比 npm/yarn 更快更省空间第一步是创建 Nx 工作区。我们不使用npx create-nx-workspace的默认模板而是选择最精简的apps-and-libs模式因为它最契合agent-skills的“多应用Agent共享多库Skills”架构。# 1. 创建工作区命名为 agent-skills-workspace pnpm create nxlatest agent-skills-workspace --presetapps-and-libs --appNamenone --stylecss --lintereslint --packageManagerpnpm --nxCloudfalse # 2. 进入工作区 cd agent-skills-workspace # 3. 安装核心依赖注意这里安装的是 Nx 插件不是技能包 pnpm add -D nrwl/node nrwl/jest nrwl/eslint-plugin-nx # 4. 创建第一个技能包agent-skills-core所有技能的基座 nx g nrwl/node:library --nameagent-skills-core --directorylibs/core --publishable --importPathagent-skills/core --no-interactive # 5. 创建第二个技能包agent-skills-http最常用的基础技能 nx g nrwl/node:library --nameagent-skills-http --directorylibs/skills/http --publishable --importPathagent-skills/http --no-interactive执行完这 5 步你的工作区目录结构会是这样agent-skills-workspace/ ├── apps/ # 存放具体的 Agent 应用暂时为空 ├── libs/ │ ├── core/ # agent-skills-core │ │ ├── src/ │ │ │ ├── index.ts # 导出所有公共类型和工具 │ │ │ └── lib/ │ │ │ └── base-skill.ts # BaseSkill 抽象类 │ │ └── jest.config.ts │ └── skills/ │ └── http/ # agent-skills-http │ ├── src/ │ │ ├── index.ts # 导出 HttpClient 接口和工厂函数 │ │ └── lib/ │ │ ├── default-http-client.ts │ │ └── http-client.interface.ts │ └── jest.config.ts ├── tools/ ├── nx.json ├── workspace.json └── package.json实操心得--no-interactive参数是关键Nx 的 generator 默认会启动交互式 CLI询问你一堆问题如是否添加 Cypress、是否启用 Storybook。对于agent-skills这种纯库项目这些问题全是噪音。--no-interactive让它使用所有默认值瞬间完成创建。后续如果需要添加测试或 lint 规则直接编辑jest.config.ts或.eslintrc.json即可比交互式选择更可控。4.2 为技能包添加semantic-release自动化semantic-release的配置是agent-skills生态的“心脏起搏器”必须在每个publishable的技能包中正确设置。我们以agent-skills-http为例详细说明如何一步步配置。步骤 1安装依赖# 在工作区根目录运行 pnpm add -D -w semantic-release semantic-release/npm semantic-release/github conventional-changelog-conventionalcommits-w参数表示安装到整个工作区workspace而不是某个特定包。步骤 2配置release.config.js在libs/skills/http/目录下创建release.config.js// libs/skills/http/release.config.js module.exports { branches: [main], plugins: [ // 1. 分析 Git 提交生成 CHANGELOG [semantic-release/commit-analyzer, { preset: conventionalcommits, releaseRules: [ { type: feat, release: minor }, { type: fix, release: patch }, { type: perf, release: patch }, { type: refactor, release: patch }, { type: test, release: patch }, { type: chore, release: patch }, { type: docs, release: patch }, { scope: core, release: patch }, // 如果 core 包有变更http 包也应 patch ], parserOpts: { noteKeywords: [BREAKING CHANGE, BREAKING CHANGES], }, }], // 2. 生成 CHANGELOG.md [semantic-release/release-notes-generator, { preset: conventionalcommits, presetConfig: { types: [ { type: feat, section: Features }, { type: fix, section: Bug Fixes }, { type: perf, section: Performance Improvements }, { type: refactor, section: Code Refactoring }, { type: test, section: Tests }, { type: chore, section: Chores }, { type: docs, section: Documentation }, { type: style, section: Styles }, { type: build, section: Build System }, { type: ci, section: Continuous Integration }, ], }, }], // 3. 发布到 npm registry [semantic-release/npm, { npmPublish: true, pkgRoot: dist/libs/skills/http, // Nx 构建产物的路径 }], // 4. 创建 GitHub Release [semantic-release/github, { assets: [dist/libs/skills/http/**/*.{js,ts,md}], message: chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}, }], ], };步骤 3配置 CIGitHub Actions在.github/workflows/release.yml中添加name: Release on: push: branches: [main] paths: - libs/skills/http/** - libs/core/** # 添加其他技能包路径 jobs: release: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 with: fetch-depth: 0 - uses: actions/setup-nodev4 with: node-version: 18 registry-url: https://registry.npmjs.org - name: Install dependencies run: pnpm install - name: Build http skill run: pnpm nx build agent-skills-http - name: Semantic Release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} run: npx semantic-release注意事项paths过滤是性能关键在on.push.paths中精确指定哪些文件变更会触发 Release可以避免每次main分支推送都跑一遍 Release 流程。例如只有libs/skills/http/**下的文件变了才触发agent-skills-http的发布。这能节省大量 CI 时间和资源。4.3 开发一个新技能agent-skills-sms短信发送现在让我们动手开发一个真实的、可投入生产的技能包agent-skills-sms。它将封装一个主流短信服务商如 Twilio 或国内的阿里云短信的 API 调用。步骤 1生成新库nx g nrwl/node:library --nameagent-skills-sms --directorylibs/skills/sms --publishable --importPathagent-skills/sms --no-interactive步骤 2定义核心接口在libs/skills/sms/src/lib/sms-skill.interface.ts中export interface SmsSkill { /** * 发送单条短信 * param to 手机号码E.164 格式如 8613800138000 * param message 短信内容最大 70 个字符中文 * param options 配置项 * returns PromiseSmsSendResult */ send: ( to: string, message: string, options?: { from?: string; // 发送号码或签名 templateId?: string; // 模板 ID用于模板短信 } ) PromiseSmsSendResult; } export interface SmsSendResult { success: boolean; messageId: string; // 服务商返回的唯一消息 ID cost: number; // 本次发送消耗的短信条数长短信会拆分 rawResponse: any; // 原始响应用于调试 }步骤 3实现核心逻辑以 Twilio 为例在libs/skills/sms/src/lib/twilio-sms-skill.ts中import { SmsSkill, SmsSendResult } from ./sms-skill.interface; import { Twilio } from twilio
返回列表