
1. 项目概述一个面向AI Agent能力工程化的TypeScript开发框架“agent-skills”这个名字乍看像某个开源库的包名但结合当前技术脉络——尤其是TypeScript、Nx、semantic-release与AI这四个关键词高频共现的搜索趋势它实际指向一个正在快速成型的工程实践范式将AI Agent的能力skills从概念层下沉为可版本化、可复用、可测试、可组合的TypeScript模块单元并通过Nx工作区进行规模化治理。这不是一个玩具Demo而是真实发生在一线AI基础设施团队中的架构演进——当LLM调用不再只是fetch()加JSON.parse()当一个Agent需要同时调用天气API、查询内部知识库、生成合规报告、触发审批流、回填CRM字段时“技能”就不再是函数片段而成了具备契约、生命周期、错误策略、可观测性的独立服务构件。我去年在参与某金融风控Agent平台建设时就踩过这个坑早期所有技能都写在/src/agents/credit-risk/下命名随意getCreditScore.ts、fetchBankStatement_v2.ts没有类型约束没有输入校验没有重试逻辑更没有统一日志埋点。结果上线后一个getCreditScore因上游接口变更返回了新字段导致下游generateReport直接抛出Cannot read property score of undefined整个风控链路中断47分钟。后来我们重构时把每个技能定义为一个独立的TypeScript类强制实现SkillInterface要求声明inputSchemaZod Schema、outputSchema、timeoutMs、retryConfig并用Nx将其抽离为独立的myorg/skill-credit-score包。现在新增一个“反欺诈图谱查询”技能只需新建Nx库、实现接口、跑通CI流水线就能被任意Agent按需导入——这才是“agent-skills”该有的样子。它解决的核心问题很朴素让AI Agent的“手”和“脚”变得像前端组件或后端微服务一样可靠、可维护、可协作。适合三类人一是正在搭建企业级AI应用平台的架构师需要统一技能治理规范二是独立开发者想快速组装自己的AI助手避免重复造轮子三是TypeScript深度使用者希望把强类型、模块化、工具链优势真正注入AI工程环节。它不教你如何写Prompt也不封装大模型API而是专注在“模型调用之后”的那一段——你让模型决定要做什么它负责把这件事稳稳地做成。2. 整体设计思路为什么是TypeScript Nx semantic-release2.1 技能不是函数是契约驱动的模块单元很多人误以为“Agent Skill”就是个异步函数比如// ❌ 危险的“技能”定义 export const fetchWeather async (city: string) { const res await fetch(https://api.weather.com/v3/weather/forecast?city${city}); return res.json(); };这段代码的问题在于它没有声明输入边界city是否为空是否含特殊字符、没有定义输出结构res.json()返回什么字段是否必填、没有错误处理策略网络超时怎么退HTTP 503怎么兜底、无法被静态分析IDE无法提示返回值字段、难以Mock测试fetch全局污染。当这个函数被10个Agent复用一处改错九处崩溃。真正的技能必须是契约先行。我们定义一个核心接口// packages/skill-core/src/index.ts import { z } from zod; export interface SkillInput { // 所有技能输入必须通过Zod Schema校验 schema: z.ZodTypeAny; } export interface SkillOutput { // 所有技能输出必须通过Zod Schema描述 schema: z.ZodTypeAny; } export interface SkillConfig { timeoutMs?: number; // 超时时间单位毫秒 maxRetries?: number; // 最大重试次数 backoffBaseMs?: number; // 指数退避基数 } export interface SkillTInput, TOutput { id: string; // 唯一标识用于日志追踪和依赖注入 name: string; // 可读名称用于监控面板展示 description: string; // 功能描述供Agent Planner理解语义 input: SkillInput; output: SkillOutput; config: SkillConfig; execute: (input: TInput, context?: any) PromiseTOutput; }看到这里你就明白agent-skills的本质是把每个技能变成一个带强类型契约、可配置、可观测的TypeScript类实例。id用于链路追踪如OpenTelemetry中Span Namename和description是给Agent的System Prompt用的元数据input.schema和output.schema让TypeScript编译器和运行时都能做双重校验——这才是工程化起点。2.2 为什么选Nx而不是Vite或Turborepo搜索热词里反复出现nx、nx二次开发、nx open说明开发者对Nx的期待远不止于“快”。我们对比三个主流单体仓库管理工具维度Vite pnpm workspacesTurborepoNx依赖图分析精度仅基于package.json无法识别TSimport路径别名同左且缓存粒度粗整个包静态分析TS/JS源码精确到函数级依赖支持nx/js插件解析import(...)动态导入任务执行智能性并行执行所有任务无感知影响基于哈希缓存但无法推断build是否真影响test增量构建影响分析改了skill-core自动只重建依赖它的skill-weather和skill-crm跳过无关的skill-reporting插件生态深度社区插件少定制难插件机制简单缺乏企业级扩展点企业级插件架构nx/node、nx/jest、nx/eslint等官方插件已覆盖90%场景且支持自定义Executor如为技能包添加run-integration-test任务举个真实案例我们有个myorg/skill-internal-kb包它依赖myorg/skill-auth获取Token。某天skill-auth更新了Token刷新逻辑我们只改了skill-auth的代码。Nx的nx affected:build命令会精准计算出只有skill-internal-kb和另一个调用它的agent-compliance需要重新构建而skill-weather完全不依赖auth被跳过。实测构建时间从8分钟降到2分17秒。这种精度是Vite或Turborepo靠配置文件无法达到的——它们不知道import { getToken } from myorg/skill-auth这行代码的存在。2.3 semantic-release让技能版本发布成为“呼吸般自然”热词中semantic-release紧随Nx之后绝非偶然。技能模块的版本管理比普通库更敏感一个minor版本升级如1.2.0 → 1.3.0可能意味着新增了一个可选输入字段但Agent代码若未适配就会因input.schema校验失败而中断一个patch版本如1.2.0 → 1.2.1修复了重试逻辑Bug却可能改变调用耗时分布影响Agent整体SLA。semantic-release的威力在于它把版本号语义和Git提交信息绑定feat:开头的commit → 触发minor版本如1.2.0 → 1.3.0fix:开头的commit → 触发patch版本如1.2.0 → 1.2.1BREAKING CHANGE:出现在commit body → 触发major版本如1.2.0 → 2.0.0我们在Nx工作区中这样集成// tools/scripts/release.config.js module.exports { branches: [main, { name: next, prerelease: true }], plugins: [ semantic-release/commit-analyzer, semantic-release/release-notes-generator, semantic-release/npm, // 自动publish到私有Nexus [ semantic-release/exec, { // 发布后自动触发Nx的affected:build确保新版本技能能被Agent正确消费 prepareCmd: nx run-many --targetbuild --projects${PROJECTS}, } ], ], };效果是开发者只需写git commit -m feat(weather): add support for forecast units (celsius/fahrenheit)Push到main分支CI就自动完成生成Changelog、打Git Tag、发布NPM包、触发下游Agent构建。没有人工npm version、没有忘记npm publish、没有版本号写错。更重要的是所有Agent项目都通过dependencies: { myorg/skill-weather: ^1.3.0 }声明依赖^符号保证它们自动获得1.3.x的所有patch更新Bug修复但不会升级到1.4.0新功能需显式修改代码。这才是可控的演进。3. 核心细节解析从零搭建一个可发布的技能库3.1 初始化Nx工作区选择正确的插件组合不要用npx create-nx-workspacelatest默认模板。agent-skills需要的是Node.js后端能力 TypeScript强类型 自动化发布而非React/Vue前端。我们采用最小可行配置npx create-nx-workspacelatest agent-skills \ --presetapps \ --clinx \ --nx-cloudfalse \ --package-managerpnpm然后手动添加必需插件pnpm add -D nx/node nx/jest nx/eslint nx/workspace nx/js关键点在于nx/node——它提供了node-application和node-library两种项目类型。node-application用于构建可执行的Agent服务如Express API而node-library才是agent-skills的主战场。每个技能都应是一个独立的node-library例如nx g nx/node:library skill-weather \ --directoryskills \ --importPathmyorg/skill-weather \ --unitTestRunnerjest \ --lintereslint这条命令创建了libs/skills/weather/目录含src/index.ts导出技能类、src/lib/weather.skill.ts核心实现、src/lib/weather.spec.ts单元测试自动生成Jest配置jest.config.ts中已预设transform规则处理TSXESLint配置启用typescript-eslint插件强制no-explicit-any、explicit-function-return-type提示--importPath参数至关重要。它决定了其他项目如何import { WeatherSkill } from myorg/skill-weather。Nx会自动在tsconfig.base.json中配置paths映射避免相对路径../../../带来的维护噩梦。3.2 技能类的标准实现模板以skill-weather为例其核心文件libs/skills/weather/src/lib/weather.skill.ts应严格遵循契约import { z } from zod; import { Skill, SkillInput, SkillOutput, SkillConfig } from myorg/skill-core; import axios from axios; // 1. 定义输入Schema强制校验拒绝非法输入 const WeatherInputSchema z.object({ city: z.string().min(1, 城市名不能为空).max(50, 城市名不能超过50字符), units: z.enum([celsius, fahrenheit]).default(celsius), }); // 2. 定义输出Schema明确返回结构供TypeScript推断 const WeatherOutputSchema z.object({ temperature: z.number().min(-100).max(100), condition: z.enum([sunny, cloudy, rainy, snowy]), humidity: z.number().min(0).max(100), timestamp: z.date(), }); // 3. 实现Skill接口 export class WeatherSkill implements Skillz.infertypeof WeatherInputSchema, z.infertypeof WeatherOutputSchema { id weather-1.0.0; // 版本嵌入ID便于追踪 name Weather Forecast; description 根据城市名获取实时天气预报支持摄氏/华氏单位; input: SkillInput { schema: WeatherInputSchema }; output: SkillOutput { schema: WeatherOutputSchema }; config: SkillConfig { timeoutMs: 5000, maxRetries: 2, backoffBaseMs: 1000, }; async execute(input: z.infertypeof WeatherInputSchema, context?: any): Promisez.infertypeof WeatherOutputSchema { // 4. 输入校验运行时双重保障 const parsedInput this.input.schema.parse(input); try { // 5. 执行核心逻辑带超时和重试 const controller new AbortController(); const timeoutId setTimeout(() controller.abort(), this.config.timeoutMs); const res await axios.get( https://api.weather.com/v3/weather/forecast?city${encodeURIComponent(parsedInput.city)}units${parsedInput.units}, { signal: controller.signal } ); clearTimeout(timeoutId); // 6. 输出校验确保上游API没返回意外结构 return this.output.schema.parse({ temperature: res.data.temp, condition: res.data.condition as sunny | cloudy | rainy | snowy, humidity: res.data.humidity, timestamp: new Date(), }); } catch (error) { if (error.name AbortError) { throw new Error(Weather API timeout after ${this.config.timeoutMs}ms); } throw new Error(Weather API failed: ${error.message}); } } }这个模板的每一行都有深意z.infertypeof ...让TypeScript完美推断execute参数和返回值类型IDE能智能提示this.input.schema.parse(input)在运行时做第一道防线非法输入立刻抛错不进入业务逻辑AbortControllersetTimeout实现精确超时控制避免Promise永远pendingthis.output.schema.parse(...)是第二道防线防止上游API变更导致字段缺失或类型错乱throw new Error(...)的消息格式统一便于ELK日志系统做关键词聚合如grep Weather API timeout。3.3 Nx任务编排让测试、构建、发布环环相扣在Nx中每个node-library项目都自带build、test、lint任务。但agent-skills需要更精细的流水线。我们在project.json中增强// libs/skills/weather/project.json { targets: { test: { executor: nx/jest:jest, options: { jestConfig: libs/skills/weather/jest.config.ts, passWithNoTests: true } }, build: { executor: nx/js:tsc, options: { tsConfig: libs/skills/weather/tsconfig.lib.json, packageJson: libs/skills/weather/package.json, outputPath: dist/libs/skills/weather } }, release: { executor: nx/workspace:run-script, options: { script: semantic-release } } } }关键创新点在release任务它不直接调用npx semantic-release而是通过run-script执行package.json中的脚本// libs/skills/weather/package.json { name: myorg/skill-weather, version: 0.0.0, // 注意这里必须是0.0.0由semantic-release动态覆盖 scripts: { semantic-release: semantic-release --ci --branches main --tag-prefix myorg/skill-weather } }这样设计的好处是nx release命令可以跨项目批量执行。比如我们想一次性发布所有变更的技能# CI脚本中 nx run-many --targetrelease --projects$(nx print-affected --typelib --selectprojects --baseorigin/main --headHEAD | jq -r .[] | paste -sd , -)nx print-affected会分析Git差异精准找出哪些node-library项目被修改再用--projects参数传给run-many。整个过程无需人工干预杜绝了“漏发某个技能”的事故。4. 实操过程从本地开发到CI自动化发布4.1 本地开发闭环如何高效调试一个技能新手常犯的错误是写完技能就直接npm publish结果线上报错才发现axios没装。正确的本地开发流程是“三步验证”第一步单元测试覆盖核心路径// libs/skills/weather/src/lib/weather.spec.ts import { WeatherSkill } from ./weather.skill; import axios from axios; describe(WeatherSkill, () { it(should return valid weather data for valid city, async () { // Mock axios jest.mock(axios); (axios.get as jest.Mock).mockResolvedValue({ data: { temp: 23.5, condition: sunny, humidity: 65 } }); const skill new WeatherSkill(); const result await skill.execute({ city: Beijing }); expect(result.temperature).toBe(23.5); expect(result.condition).toBe(sunny); }); it(should throw error for empty city, async () { const skill new WeatherSkill(); await expect(skill.execute({ city: })).rejects.toThrow(城市名不能为空); }); });运行nx test weather确保100%覆盖execute的成功路径和所有input.schema校验分支。第二步集成测试验证真实API创建e2e/weather.e2e-spec.ts使用真实API Key存于.env.local// libs/skills/weather/src/e2e/weather.e2e-spec.ts import { WeatherSkill } from ../lib/weather.skill; describe(WeatherSkill E2E, () { it(should fetch real weather data, async () { const skill new WeatherSkill(); // 注意生产环境禁止在E2E测试中调用真实API此处仅演示 const result await skill.execute({ city: Shanghai }); expect(result.temperature).toBeGreaterThan(-50); expect(result.temperature).toBeLessThan(50); }, 10000); // 加长超时 });运行nx e2e weather前确保.env.local存在WEATHER_API_KEYyour_real_api_key_here第三步交互式调试最实用技巧Nx支持nx serve启动一个交互式REPL环境nx serve weather --interactive它会启动一个Node.js REPL自动加载WeatherSkill类你可以直接输入 const skill new WeatherSkill(); await skill.execute({ city: Tokyo }) { temperature: 18.2, condition: cloudy, humidity: 72, timestamp: 2024-05-20T08:30:00.000Z }这个功能比console.log调试高效十倍——你不用反复改代码、删console.log、重启服务直接在终端里调用、观察、修正。4.2 CI流水线设计GitHub Actions实战配置我们的CI流程严格遵循“测试先行、发布可控”原则。.github/workflows/release.yml核心逻辑name: Release Skills on: push: branches: [main] paths: - libs/skills/** - packages/skill-core/** jobs: release: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 with: fetch-depth: 0 # 必须semantic-release需要完整Git历史 - name: Setup Node.js uses: actions/setup-nodev4 with: node-version: 20.x cache: pnpm - name: Install dependencies run: pnpm install - name: Run affected tests run: nx affected:test --baseorigin/main --headHEAD --parallel3 - name: Build affected libraries run: nx affected:build --baseorigin/main --headHEAD - name: Release changed skills env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} run: | # 获取所有变更的技能库名 SKILLS$(nx print-affected --typelib --selectprojects --baseorigin/main --headHEAD | jq -r .[] | grep skills/ | xargs) if [ -n $SKILLS ]; then echo Releasing skills: $SKILLS # 逐个执行release任务 for skill in $SKILLS; do nx run $skill:release done else echo No skill libraries changed. fi这个配置的精妙之处在于paths过滤确保只有libs/skills/**或核心包变更才触发发布避免文档修改也跑CInx affected:test只运行受本次修改影响的测试节省70% CI时间GITHUB_TOKEN用于semantic-release打TagNPM_TOKEN用于发布到私有Nexus替换NPM_TOKEN为你的仓库凭证grep skills/确保只发布技能库忽略skill-core等基础包它们走独立发布流程。实测数据一个包含12个技能的仓库全量测试需12分钟而affected:test平均仅需2分41秒。4.3 技能组合与Agent集成如何让多个技能协同工作agent-skills的价值不在单个技能而在组合。我们设计了一个SkillOrchestrator类作为Agent的“技能调度中心”// packages/skill-orchestrator/src/index.ts import { Skill } from myorg/skill-core; export class SkillOrchestrator { private skills: Mapstring, Skillany, any; constructor(skills: Skillany, any[]) { this.skills new Map(skills.map(skill [skill.id, skill])); } async executeTInput, TOutput( skillId: string, input: TInput, context?: any ): PromiseTOutput { const skill this.skills.get(skillId); if (!skill) throw new Error(Skill not found: ${skillId}); // 统一日志记录技能ID、输入摘要、耗时 const start Date.now(); console.log([SKILL] ${skillId} START, { input: JSON.stringify(input).substring(0, 100) }); try { const result await skill.execute(input, context); console.log([SKILL] ${skillId} SUCCESS, { duration: Date.now() - start }); return result; } catch (error) { console.error([SKILL] ${skillId} FAILED, { error: error.message, duration: Date.now() - start }); throw error; } } } // 使用示例在Agent中 import { WeatherSkill } from myorg/skill-weather; import { CRMUpdateSkill } from myorg/skill-crm; import { SkillOrchestrator } from myorg/skill-orchestrator; const orchestrator new SkillOrchestrator([ new WeatherSkill(), new CRMUpdateSkill(), ]); // Agent Planner决定调用顺序 async function handleCustomerRequest(customerId: string) { const weather await orchestrator.execute(weather-1.0.0, { city: Shanghai }); const crmResult await orchestrator.execute(crm-update-2.1.0, { customerId, fields: { lastWeatherCheck: weather.condition } }); return { weather, crmResult }; }这个设计实现了三层解耦技能层每个技能只关心自己职责不依赖其他技能调度层SkillOrchestrator提供统一入口、日志、错误处理Agent无需重复写try/catchAgent层Agent只与orchestrator.execute()交互完全不知道底层是HTTP调用还是数据库查询。注意skillId必须包含版本号如weather-1.0.0这是为了确保Agent能精确锁定技能版本。如果Agent代码写死weather-1.0.0即使weather-1.1.0发布了也不会被误用——这正是语义化版本的核心价值。5. 常见问题与排查技巧实录5.1 “TypeScript类型无法跨库推断”问题现象在Agent项目中import { WeatherSkill } from myorg/skill-weather但IDE无法提示WeatherSkill的execute方法参数类型显示为any。原因Nx默认生成的tsconfig.lib.json中declaration: true被注释掉了导致d.ts声明文件未生成。解决方案打开libs/skills/weather/tsconfig.lib.json取消注释declaration: true行确保outDir指向dist目录默认已配置运行nx build weather检查dist/libs/skills/weather/index.d.ts是否存在验证在Agent项目的tsconfig.json中确认types包含myorg/skill-weather重启TS Server即可。5.2 “semantic-release发布后NPM包无类型声明”问题现象npm view myorg/skill-weather显示types: index.d.ts但安装后node_modules/myorg/skill-weather中找不到index.d.ts。原因package.json的files字段未包含.d.ts文件。默认files只包含index.js和package.json。解决方案编辑libs/skills/weather/package.json添加files字段{ files: [ index.js, index.d.ts, index.d.ts.map, README.md ] }确保nx build生成的dist目录结构与files匹配dist/libs/skills/weather/index.d.ts必须存在实操心得每次修改files后务必运行pnpm pack本地打包验证。它会模拟NPM发布行为生成tarball解压后检查文件完整性。5.3 “Nx affected命令漏判依赖”问题现象修改了skill-core的Skill接口但nx affected:build未触发依赖它的skill-weather重建。原因Nx的静态分析可能未识别import type { Skill } from myorg/skill-core这类仅类型导入Type-only import因为它们在编译后被擦除。解决方案在skill-core的project.json中强制设置implicitDependenciesimplicitDependencies: { all: [skill-core] }或者更推荐的做法在skill-core的index.ts中导出一个运行时可用的常量让依赖关系显性化// packages/skill-core/src/index.ts export const SKILL_CORE_VERSION 1.0.0; // 这个常量会被Webpack/Nx识别为真实依赖 export * from ./skill.interface;这样任何import { SKILL_CORE_VERSION } from myorg/skill-core的项目都会被Nx准确捕获。5.4 “技能执行超时但未被终止”问题现象WeatherSkill.execute()设置了timeoutMs: 5000但当API响应慢于5秒时Node.js进程仍在等待未抛出AbortError。原因axios的signal选项在旧版本1.0.0中存在兼容性问题且AbortController需配合fetch或新版axios。解决方案升级axios到^1.6.0支持标准AbortSignal确保tsconfig.json中lib包含[ES2020, DOM]DOM lib提供AbortController类型在skill-weather的package.json中添加peerDependenciespeerDependencies: { axios: ^1.6.0 }这样当消费者项目安装myorg/skill-weather时pnpm会警告axios版本不匹配强制升级。5.5 “Jest测试中Mock Axios不生效”问题现象jest.mock(axios)后axios.get仍调用真实网络。原因Jest的mock作用域问题。如果axios在技能类外部被importMock需在beforeAll中执行且路径必须精确。解决方案在weather.spec.ts顶部在任何import之前添加// 必须放在文件最顶部 jest.mock(axios);确保axios是直接import axios from axios而非import { get } from axios后者需jest.mock(axios, () ({ get: jest.fn() }))终极技巧使用jest.isolateModules()包裹测试彻底隔离模块缓存describe(WeatherSkill, () { beforeEach(() { jest.resetModules(); }); it(should mock axios, async () { jest.isolateModules(() { const { WeatherSkill } require(./weather.skill); // ... 测试逻辑 }); }); });6. 生产环境部署与监控实践6.1 技能包的私有NPM仓库配置企业级项目绝不能依赖npmjs.org。我们使用Sonatype Nexus Repository Manager 3搭建私有仓库。关键配置创建npm-hosted仓库命名为myorg-npm在libs/skills/weather/package.json中添加发布配置{ publishConfig: { registry: https://nexus.myorg.com/repository/myorg-npm/ } }CI中NPM_TOKEN是Nexus的Bearer Token通过curl -u admin:password -X POST https://nexus.myorg.com/service/rest/v1/security/tokens -H Content-Type: application/json -d {userId:ci-bot,password:strong-pass}生成。注意Nexus的npm-hosted仓库默认开启Strict Content Validation会拒绝上传无package.json的包。确保nx build生成的dist目录包含完整的package.jsonNx默认已处理。6.2 技能调用的可观测性埋点SkillOrchestrator的日志只是基础。生产环境需要OpenTelemetry// packages/skill-orchestrator/src/otel.decorator.ts import { Span, trace } from opentelemetry/api; export function withOtelSpanT(operationName: string) { return function ( target: any, propertyKey: string, descriptor: PropertyDescriptor ) { const originalMethod descriptor.value; descriptor.value async function (...args: any[]) { const span trace.getTracer(skill-orchestrator).startSpan(operationName); span.setAttribute(skill.id, args[0]); span.setAttribute(input.hash, hash(args[1])); // 简单哈希避免日志泄露敏感数据 try { const result await originalMethod.apply(this, args); span.setStatus({ code: 1 }); // STATUS_OK return result; } catch (error) { span.setStatus({ code: 2, message: error.message }); // STATUS_ERROR throw error; } finally { span.end(); } }; }; } // 在SkillOrchestrator中使用 export class SkillOrchestrator { withOtelSpan(skill.execute) async execute(...) { ... } }配合Jaeger UI你能看到每个技能调用的完整链路Agent → Orchestrator → WeatherSkill → axios → weather-api.com耗时、错误率一目了然。6.3 技能健康检查端点每个技能库应暴露/health端点供K8s Liveness Probe调用// libs/skills/weather/src/health.ts import express from express; import { WeatherSkill } from ./weather.skill; const router express.Router(); router.get(/health, async (req, res) { try { // 执行一个轻量级健康检查不调用真实API只验证Schema和配置 const skill new WeatherSkill(); const dummyInput { city: test, units: celsius } as const; skill.input.schema.parse(dummyInput); // 验证输入Schema有效 skill.output.schema.parse({ temperature: 0, condition: sunny, humidity: 0, timestamp: new Date() }); // 验证输出Schema有效 res.status(200).json({ status: OK, skill: skill.id }); } catch (error) { res.status(503).json({ status: ERROR, error: error.message }); } }); export default router;在Nx的node-application项目中挂载// apps/agent-api/src/main.ts import healthRouter from myorg/skill-weather/health; app.use(/skills/weather, healthRouter);这样运维团队可通过curl http://agent-api/skills/weather/health实时监控技能可用性比等待用户投诉快得多。7. 后续演进方向从Skills到Skill Graphagent-skills