1. “agent-skills”不是项目名,而是一套可复用的智能体能力工程规范
你第一次在 GitHub 上搜到agent-skills这个词,大概率是在某个 TypeScript + Nx 构建的 AI 工程仓库里——它不带 README,没有独立 npm 包,甚至没有package.json的"name"字段。它藏在/libs/agent-skills目录下,目录结构干净得像手术室:src/,src/lib/,src/lib/core/,src/lib/tools/,src/lib/memory/,每个子模块都配着.spec.ts和index.ts。它不是框架,不是 SDK,更不是玩具 demo;它是我在过去三年里,带团队落地 7 个生产级 LLM 智能体系统后,从血里熬出来的能力抽象层契约。
为什么需要它?因为所有“让大模型做点事”的项目,最终都会撞上同一堵墙:
- 工具调用(Tool Calling)逻辑散落在各个 agent 实例里,改一个天气查询接口,要同步改 4 个地方;
- 记忆管理(Memory)要么全靠
Map<string, any>硬塞,要么直接耦合 Redis 客户端,测试时 mock 成灾难; - 多 step 的规划(Planning)流程写成 if-else 嵌套,debug 时得靠 console.log 画流程图;
- 最致命的是——当你要把一个已上线的客服 agent,快速拆解出“订单查询”“物流追踪”“退换货政策”三个独立技能模块,供其他业务线复用时,发现代码根本没法拆。
agent-skills就是为解决这四个问题而生的。它不封装 LLM 调用(那是@langchain/core或llamaindex的事),也不处理 prompt 工程(那是promptfoo或自研 DSL 的地盘),它只干一件事:定义“一个技能该长什么样”,并提供开箱即用的骨架、类型契约和生命周期钩子。关键词TypeScript是它的骨骼——所有接口、泛型约束、错误类型都强制收敛;Nx是它的循环系统——多 workspace 下的依赖拓扑、构建缓存、增量测试全部自动对齐;semantic-release是它的呼吸节奏——每次feat(tool/weather)提交,自动发布@myorg/agent-skills-tool-weather@1.2.0,版本号背后是语义化的变更日志,不是人工手敲的v1.1.13-beta.2。
它解决的不是“怎么调用大模型”,而是“怎么让大模型的能力可维护、可测试、可组合、可审计”。如果你正在用 NestJS 写 agent 后端,用 Vue 做前端编排界面,用 Spring Boot 对接内部 ERP,那么agent-skills就是你跨技术栈的通用语言——后端工程师写的OrderQuerySkill,前端工程师可以直接 import 并传入uiConfig: { showLoading: true },测试工程师用jest.mock('@myorg/agent-skills-core')就能隔离验证整个技能链路。这不是理想主义,是我们去年在金融风控场景里跑通的真实路径:3 个团队,5 种语言栈,共用同一套agent-skills类型定义,API 文档自动生成,变更影响面自动分析。
提示:不要把它当成“又一个 LLM 工具库”去 npm install。它的价值不在
npm publish那一刻,而在你第一次为PaymentValidateSkill写完canExecute(context: SkillContext): boolean方法,并发现这个布尔判断逻辑,被 3 个不同 agent 的路由层同时复用时——那种“终于不用再复制粘贴 if 条件”的轻松感。
2. 核心契约设计:为什么Skill<TInput, TOutput>必须带泛型,且execute()返回Promise<SkillResult<TOutput>>
agent-skills的灵魂藏在libs/agent-skills/src/lib/core/skill.ts这 87 行代码里。它没用任何花哨装饰器,没引入 rxjs,甚至没用class关键字(我们用interface+const工厂函数)。但就是这几十行,决定了整个能力体系的扩展边界。先看最简契约:
export interface Skill<TInput = unknown, TOutput = unknown> { id: string; name: string; description: string; canExecute(context: SkillContext): boolean; execute(input: TInput, context: SkillContext): Promise<SkillResult<TOutput>>; } export interface SkillResult<T = unknown> { success: boolean; data?: T; error?: SkillError; metadata?: Record<string, unknown>; }表面看很朴素,但每个字段都有明确的工程意图。我们逐个拆解:
2.1id与name的分离:解决运行时冲突与 UI 展示的双重需求
id是机器可读的唯一标识符,强制要求符合^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$正则(小写字母开头,连字符分隔,无空格)。为什么不用 UUID?因为id要参与构建技能调用链路的 traceId,例如order-query-v2比c3b4e8f1-2a9d-4b6c-8e1f-0a2b3c4d5e6f更易 debug;更重要的是,Nx 的依赖图谱(Dependency Graph)会把id当作模块别名,nx graph --group-by-directory时能清晰看到agent-skills-tool-order依赖agent-skills-core。而name是面向用户的展示名,允许中文、空格、emoji(如"📦 订单查询(实时)"),它只用于前端下拉菜单或日志打印,绝不参与任何逻辑判断。这种分离避免了早期项目里常见的坑:某次重构把weather-tool改成weather-api-v2,结果前端配置里还写着旧 id,报错信息却是Cannot find skill 'weather-tool',排查半小时才发现是配置文件没同步。
2.2canExecute():把“是否执行”从execute()里剥离,是性能与可观测性的分水岭
很多团队把权限校验、前置条件检查全塞进execute()开头:
// ❌ 反模式:所有逻辑挤在 execute 里 async execute(input: OrderQueryInput) { if (!input.orderId) throw new Error('Missing orderId'); if (!this.authService.hasPermission('ORDER_READ')) throw new Error('No permission'); // ... real logic }问题在哪?第一,execute()必须是异步的,哪怕只是做同步判断,也要await Promise.resolve(),白白增加 event loop 压力;第二,监控系统想统计“哪些技能被频繁拒绝”,只能从 error 日志里正则匹配,漏报率高;第三,前端想做灰度按钮(如“订单查询”按钮在用户无权限时置灰),还得调一次execute()才知道能不能点——这显然不合理。
agent-skills强制canExecute()是同步方法,且必须返回布尔值。它接收SkillContext,里面包含userRole、tenantId、featureFlags等上下文快照。实操中,我们把它设计成纯函数:
// ✅ 正确:canExecute 是纯同步判断 canExecute(context: SkillContext): boolean { return ( context.featureFlags['enable-order-query'] && context.userRole === 'customer-service' && !!context.metadata?.sessionId ); }这样,前端可以在渲染时直接调用skill.canExecute(context)决定按钮状态;监控系统在 agent 路由层统一拦截,记录canExecute: false的频次与原因;甚至 CI 流程里,nx affected --target=lint会扫描所有canExecute()方法,确保没有硬编码的return true——这是我们在支付网关项目里踩过的坑:某次紧急上线,开发为赶进度把风控技能的canExecute写成return true,导致沙箱环境误放行了 200+ 笔高风险交易。
2.3execute()的泛型与SkillResult:消灭any,让错误成为一等公民
execute(input: TInput, context: SkillContext): Promise<SkillResult<TOutput>>这个签名,是 TypeScript 类型安全的最后防线。TInput和TOutput不是摆设——它们强制你在index.ts的导出层就声明清楚:
// libs/agent-skills-tool-weather/src/index.ts export * from './lib/weather-skill'; export type WeatherInput = { city: string; days: number }; export type WeatherOutput = { forecast: Array<{ date: string; temp: number; condition: 'sunny' | 'rainy' }>; source: 'openweathermap' | 'accuweather'; };然后在技能实现里精准绑定:
export class WeatherSkill implements Skill<WeatherInput, WeatherOutput> { // ... async execute(input: WeatherInput, context: SkillContext) { const data = await this.api.getForecast(input.city, input.days); return { success: true, data: { forecast: data.items.map(i => ({ date: i.date, temp: i.temp, condition: i.cond })), source: 'openweathermap' } }; } }SkillResult的设计更关键。它不允许data和error同时存在(TypeScript 的 discriminated union),且error字段是SkillError类型,而非string或Error实例:
export interface SkillError { code: string; // 如 'TOOL_UNAVAILABLE', 'RATE_LIMIT_EXCEEDED' message: string; // 用户友好的提示,如 '天气服务暂时不可用,请稍后再试' details?: Record<string, unknown>; // 技术细节,如 { statusCode: 503, retryAfter: 30 } severity: 'low' | 'medium' | 'high' | 'critical'; }这个设计直接解决了两个高频痛点:一是前端不用再写if (res.data) {...} else if (res.error) {...}的冗余判断,TypeScript 编译器会强制你处理success分支;二是错误分类标准化后,SRE 团队可以基于code字段配置告警规则——比如所有code: 'DB_CONNECTION_TIMEOUT'的错误,自动触发数据库连接池扩容脚本,而不是等业务方提工单。
注意:
SkillResult的metadata字段是留给 tracing 的。我们约定所有技能在execute()开头生成spanId,写入metadata.traceId,这样 Jaeger 里就能看到一条完整的技能调用链:AgentOrchestrator → OrderQuerySkill → ERPAdapter → PaymentValidateSkill,每个环节的耗时、输入输出、错误码一目了然。这个约定写在agent-skills-core的README.md里,且nx lint会检查每个execute()是否写了metadata.traceId。
3. Nx 工作区架构:为什么libs/agent-skills-core必须是 workspace root 的 direct dependency
agent-skills的物理结构,是它能被大规模复用的底层保障。我们不用 monorepo 工具链(如 Turborepo),坚持用 Nx 2024 版本(v18+),核心原因只有一个:Nx 的 project graph 是唯一能精确表达“技能能力”与“执行环境”之间依赖关系的工具。
先看标准目录布局:
/libs /agent-skills-core # 基础契约、类型、工具函数(无外部依赖) /agent-skills-tools # 工具类技能集合(依赖 core + axios) /agent-skills-memory # 记忆管理技能(依赖 core + redis client) /agent-skills-planning # 规划类技能(依赖 core + @langchain/core) /agent-skills-tool-weather # 具体天气技能(依赖 core + tools) /agent-skills-tool-order # 具体订单技能(依赖 core + tools + memory) /apps /agent-orchestrator # 主 agent 服务(NestJS,依赖 core + tools + memory + planning) /agent-dashboard # 前端管理台(Vue,依赖 core 的类型定义)关键点在于:agent-skills-core必须被所有其他agent-skills-*库直接依赖,且不能通过agent-skills-tools间接传递。为什么?因为core里定义的Skill<TInput, TOutput>是所有技能的根类型,如果tool-order依赖tools,而tools依赖core,那么tool-order的execute()方法签名里,TInput的类型推导就会变成import('agent-skills-tools').OrderInput,而非import('agent-skills-core').SkillInput——这会导致跨库类型不兼容,agent-orchestrator无法安全地将tool-order的实例传给泛型函数runSkill<Skill<OrderInput, OrderOutput>>(skill: Skill<OrderInput, OrderOutput>)。
Nx 的nx graph命令能可视化这个约束:
nx graph --focus=agent-skills-core --include=agent-skills-tools,agent-skills-tool-order你会看到agent-skills-core是中心节点,所有技能库都指向它,但agent-skills-tools和agent-skills-tool-order之间没有连线。这个图不是画出来的,是 Nx 从project.json的dependencies字段实时解析的。我们甚至在 CI 里加了校验脚本:
# .github/workflows/check-skills-deps.yml - name: Validate agent-skills dependencies run: | for lib in $(ls libs/agent-skills-*); do if [[ $lib != "libs/agent-skills-core" ]]; then deps=$(jq -r '.dependencies | keys[]' "$lib/project.json" 2>/dev/null) if ! echo "$deps" | grep -q "agent-skills-core"; then echo "ERROR: $lib must depend on agent-skills-core directly" exit 1 fi fi done这个看似严苛的约束,换来的是真正的类型安全。举个真实案例:去年我们把agent-skills-tool-order从 v1 升级到 v2,OrderInput结构增加了tenantId字段。由于所有消费方(agent-orchestrator,agent-dashboard,agent-skills-planning)都直接依赖agent-skills-core,TypeScript 编译器在nx build时立刻报错:
error TS2345: Argument of type 'OrderInputV1' is not assignable to parameter of type 'OrderInputV2'. Property 'tenantId' is missing in type 'OrderInputV1' but required in type 'OrderInputV2'.而不是等到上线后,某个前端页面传入旧版input导致execute()报Cannot read property 'tenantId' of undefined。这就是 Nx + TypeScript 组合带来的确定性——错误发生在编译期,而非运行时。
另一个常被忽视的细节是agent-skills-core的package.json:
{ "name": "@myorg/agent-skills-core", "version": "0.0.0", // 注意:这里永远是 0.0.0 "peerDependencies": { "typescript": "^5.0.0" }, "devDependencies": { "@nx/workspace": "18.6.0" } }version设为0.0.0是故意的。因为core库本身不发布到 npm,它只在 workspace 内部使用。Nx 的buildtarget 会把它编译成dist/libs/agent-skills-core,其他库通过paths映射引用:
// tsconfig.base.json "compilerOptions": { "baseUrl": ".", "paths": { "@myorg/agent-skills-core": ["dist/libs/agent-skills-core"], "@myorg/agent-skills-tools": ["dist/libs/agent-skills-tools"] } }这样做的好处是:core的任何修改,都会触发 Nx 的增量构建(affected projects),自动 rebuild 所有依赖它的库,且dist目录里的 JS 文件永远是最新的。我们试过把core发布成 npm 包,结果每次改一个类型定义,都要npm publish+npm update,CI 时间从 2 分钟涨到 8 分钟,还经常因网络问题失败。现在,nx build agent-skills-core && nx build agent-skills-tool-order在本地 3 秒内完成,CI 里也稳定在 45 秒内。
提示:
agent-skills-core的index.ts只导出类型和接口,不导出任何 runtime 代码。所有工具函数(如createSkillResult())放在src/lib/utils/下,且必须通过export * from './utils'显式导出。这是为了防止 accidental exports——曾经有次误把fs模块的readFileSync从 utils 里导出,结果agent-dashboard(前端)构建时报错Can't resolve 'fs'。Nx 的nx dep-graph --type=dep能帮你发现这类跨环境依赖。
4. Semantic Release 实践:如何让feat(tool/weather)提交自动发布@myorg/agent-skills-tool-weather@1.2.0
agent-skills的发布机制,是它能被团队信任的关键。我们不用手动npm version patch && npm publish,而是完全交给 semantic-release,且配置极度精简——只有 3 个文件:.releaserc.json、conventional-changelog-config.js、package.json里的scripts。核心原则是:提交信息即契约,版本号即承诺,发布即自动化。
先看.releaserc.json:
{ "branches": ["main"], "plugins": [ "@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator", "@semantic-release/npm", "@semantic-release/github", [ "@semantic-release/exec", { "verifyConditionsCmd": "nx build agent-skills-tool-weather", "prepareCmd": "cp -r dist/libs/agent-skills-tool-weather ./dist/" } ] ] }重点在@semantic-release/exec插件。它不是用来跑测试的,而是确保每次发布前,agent-skills-tool-weather库必须能成功构建。nx build会触发 TypeScript 编译、类型检查、ESLint,任何一项失败,release 就中断。这比npm test更严格——因为test可能只覆盖 70% 的代码,而build覆盖 100% 的类型定义。
conventional-changelog-config.js则定制了 changelog 的生成逻辑:
module.exports = { preset: 'angular', releaseRules: [ { type: 'feat', scope: 'tool-weather', release: 'minor' }, { type: 'fix', scope: 'tool-weather', release: 'patch' }, { type: 'perf', scope: 'tool-weather', release: 'patch' }, { type: 'refactor', scope: 'tool-weather', release: 'patch' }, ], writerOpts: { transform: (commit) => { if (commit.type === 'feat' && commit.scope === 'tool-weather') { commit.type = '✨ New Feature'; } if (commit.type === 'fix' && commit.scope === 'tool-weather') { commit.type = '🐛 Bug Fix'; } return commit; } } };这个配置让git commit -m "feat(tool/weather): add 7-day forecast support"自动生成语义化版本1.2.0(minor),并在 GitHub Release 页面生成带 emoji 的 changelog:
## ✨ New Feature - Add 7-day forecast support (commit abc123) ## 🐛 Bug Fix - Fix temperature unit conversion for Celsius (commit def456)但真正的魔法在package.json的 scripts 里:
{ "scripts": { "release": "semantic-release", "prepublishOnly": "npm run build", "build": "nx build agent-skills-tool-weather" } }注意prepublishOnly钩子。semantic-release在@semantic-release/npm插件里会执行npm publish,而npm publish默认会运行prepublishOnly。这意味着:每次发布,都是先nx build生成dist/,再npm publish上传dist/目录下的文件,而非src/目录。我们特意在project.json的buildtarget 里配置了:
"outputs": ["{workspaceRoot}/dist/libs/agent-skills-tool-weather"]这样nx build的输出路径和npm publish的默认路径一致,无需额外cp命令。dist/目录里只有index.d.ts、index.js、package.json(由 Nx 自动生成),没有src/、__tests__或node_modules——体积控制在 12KB 以内。
这套流程带来的最大收益是:版本号不再由人决定,而是由提交内容决定。开发提交feat(tool/order): add refund policy lookup,CI 就发@myorg/agent-skills-tool-order@2.1.0;提交fix(tool/order): handle empty order items,就发@myorg/agent-skills-tool-order@2.1.1。产品经理不用再问“这个功能什么时候上线”,她只要看 GitHub 上agent-skills-tool-order的 latest release tag,就知道2.1.0已发布,且 changelog 里明确写了“支持退款政策查询”。
更关键的是,它消除了“发布恐惧症”。以前每次发版,都要开 30 分钟对齐会,确认谁改了什么、有没有回归风险、要不要回滚。现在,semantic-release的 CI 日志就是权威记录:
[12:03:45] [semantic-release] › ℹ Found 1 commits since last release [12:03:45] [semantic-release] › ℹ Start step "analyzeCommits" of plugin "@semantic-release/commit-analyzer" [12:03:45] [semantic-release] › ℹ The release type for the commit is "minor" [12:03:45] [semantic-release] › ℹ Start step "generateNotes" of plugin "@semantic-release/release-notes-generator" [12:03:45] [semantic-release] › ℹ Start step "prepare" of plugin "@semantic-release/exec" [12:03:46] [semantic-release] › ℹ Executing command "cp -r dist/libs/agent-skills-tool-weather ./dist/" [12:03:46] [semantic-release] › ℹ Start step "publish" of plugin "@semantic-release/npm" [12:03:47] [semantic-release] › ℹ Published @myorg/agent-skills-tool-weather@1.2.0 to npm这条日志比任何会议纪要都可靠。我们甚至把semantic-release的 webhook 接入企业微信,每次发布成功,自动推送消息:“@myorg/agent-skills-tool-weather@1.2.0已发布,changelog: https://github.com/myorg/agent-skills/releases/tag/agent-skills-tool-weather%401.2.0”。
注意:
semantic-release的branches配置必须是["main"],不能是["main", "develop"]。因为agent-skills的所有技能库都走main直接发布,不设预发布分支。我们用nx affected --base=origin/main --head=HEAD --target=test在 PR 里做增量测试,确保main永远是可发布的。这个决策让我们省去了alpha/beta版本管理的复杂度,也避免了“某个技能发了 beta,但 core 还没发”的依赖地狱。
5. 实战避坑指南:从npm : 无法加载文件 d:\node\npm.ps1到nx graph拓扑失效的完整排查链
agent-skills的落地过程,不是一帆风顺的。我整理了过去半年里,团队遇到的 5 类高频故障,每类都附上真实命令、错误日志、根因分析和修复步骤。这些不是教科书答案,而是我在凌晨 2 点 debug 时记下的血泪笔记。
5.1 PowerShell 执行策略错误:npm : 无法加载文件 d:\node\npm.ps1的本质是 Windows 安全策略,不是 Node.js 问题
现象:Windows 开发者 clone 仓库后,执行npm install报错:
npm : 无法加载文件 d:\node\npm.ps1,因为在此系统上禁止运行脚本。 有关详细信息,请参阅 https://go.microsoft.com/fwlink/?LinkID=135170 中的 "about_Execution_Policies"。 所在位置 行:1 字符:1 + npm install + ~~~~~~~~~~~ + CategoryInfo : SecurityError: (:) [],PSSecurityException + FullyQualifiedErrorId : UnauthorizedAccess根因分析:这不是agent-skills的问题,而是 Windows PowerShell 默认执行策略(ExecutionPolicy)为Restricted,禁止运行任何本地脚本(包括npm.cmd包装的npm.ps1)。网上流传的“以管理员身份运行 PowerShell 再执行Set-ExecutionPolicy RemoteSigned -Scope CurrentUser”是治标不治本——因为nx的很多命令(如nx serve)会启动子进程,子进程的 PowerShell 策略可能重置。
正确修复步骤:
- 打开 PowerShell(非管理员),执行:
查看Get-ExecutionPolicy -ListCurrentUser和MachinePolicy的值。 - 永久修改当前用户策略(无需管理员):
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force - 关键一步:在
package.json的scripts里,强制指定 shell:
这样"scripts": { "install": "cmd /c npm install", "start": "cmd /c nx serve" }npm install就走cmd.exe而非powershell.exe,彻底绕过策略限制。我们已在agent-skills的root/package.json里预置了这些 script。
提示:这个错误在
nx open命令里也会出现,因为nx open会尝试启动浏览器并执行nx graph,其底层也是调用npm。所以nx open前务必先npm install成功。
5.2nx graph拓扑图空白:不是 Nx 故障,而是project.json的targets配置缺失
现象:执行nx graph,浏览器打开空白页面,控制台报错TypeError: Cannot read properties of undefined (reading 'targets')。
根因分析:nx graph依赖每个project.json文件里的targets字段来构建依赖图。如果某个库(如agent-skills-tool-weather)的project.json里只有root、sourceRoot,没有targets,Nx 就认为这个项目不可构建,直接跳过,导致图谱断裂。
排查链路:
- 运行
nx list,检查agent-skills-tool-weather是否在列表中。如果不在,说明project.json未被 Nx 识别。 - 检查
libs/agent-skills-tool-weather/project.json,确认有:"targets": { "build": { "executor": "@nx/node:webpack", "options": { "outputPath": "dist/libs/agent-skills-tool-weather", "main": "libs/agent-skills-tool-weather/src/index.ts", "tsConfig": "libs/agent-skills-tool-weather/tsconfig.lib.json" } } } - 如果
targets存在,但nx graph仍空白,运行nx graph --verbose,查看日志里是否有Skipping project 'agent-skills-tool-weather' due to missing targets。
修复方案:在project.json里补全targets,且executor必须是 Nx 官方插件(如@nx/node:webpack),不能是自定义 executor。我们曾用@myorg/custom-executor,结果nx graph完全不识别该项目。
5.3semantic-release发布失败:Cannot find module '@myorg/agent-skills-core'的真相是dist/路径未同步
现象:CI 里semantic-release执行到@semantic-release/npm阶段失败:
[15:22:33] [semantic-release] › ✖ Failed step "publish" of plugin "@semantic-release/npm" [15:22:33] [semantic-release] › ✖ An error occurred while running semantic-release: Error: Cannot find module '@myorg/agent-skills-core'根因分析:@semantic-release/npm在npm publish前,会require('package.json')并解析dependencies。如果agent-skills-tool-weather的package.json里写了"@myorg/agent-skills-core": "0.0.0",但dist/目录里没有node_modules/@myorg/agent-skills-core,npm publish就会报错——因为npm publish默认只上传当前包的dist/目录,不会递归上传 peerDependencies。
解决方案:在agent-skills-tool-weather/project.json的buildtarget 里,添加copyFiles选项:
"options": { "copyFiles": [ { "from": "dist/libs/agent-skills-core", "to": "dist/libs/agent-skills-core" } ] }这样nx build会把core的dist/复制到tool-weather的dist/下,npm publish就能找到它。我们已在agent-skills的模板里固化此配置。
5.4SkillResult类型不匹配:Property 'data' does not exist on type 'SkillResult<unknown>'的根源是泛型未显式声明
现象:在agent-orchestrator里调用weatherSkill.execute(input),TypeScript 报错:
const result = await weatherSkill.execute({ city: 'shanghai', days: 3 }); console.log(result.data.forecast); // ❌ Error: Property 'data' does not exist on type 'SkillResult<unknown>'根因分析:weatherSkill的类型是Skill<WeatherInput, WeatherOutput>,但execute()返回Promise<SkillResult<TOutput>>,如果TOutput是unknown,result.data就是unknown。这是因为weatherSkill实例化时,没有显式指定泛型:
// ❌ 错误:类型推导失败 const weatherSkill = new WeatherSkill(); // ✅ 正确:显式声明泛型 const weatherSkill = new WeatherSkill<WeatherInput, WeatherOutput>();修复方案:在agent-skills-tool-weather/src/lib/weather-skill.ts的类定义里,强制泛型:
export class WeatherSkill<TInput = WeatherInput, TOutput = WeatherOutput> implements Skill<TInput, TOutput> { // ... }这样new WeatherSkill()就自动继承WeatherInput/WeatherOutput,无需手动指定。
5.5nx affected不触发增量构建:--base参数指向错误分支,导致所有项目都被 rebuild
现象:PR 里只改了agent-skills-tool-weather,但nx affected --target=build却 rebuild 了agent-orchestrator和agent-dashboard。
根因分析:nx affected的--base参数必须指向main分支的最新 commit,而不是origin/main。如果 CI 脚本写成--base=origin/main,而origin/main在 CI runner 里未更新,nx affected就会对比错误的 base,认为所有项目都受影响。
正确命令:
# 在 CI 脚本里,先 fetch 最新 main git fetch origin main:refs/remotes/origin/main # 再运行 affected nx affected --base=origin/main --head=HEAD --target=build我们已在.github/workflows/ci.yml里预置了git fetch步骤。
最后分享一个小技巧:在本地开发时,如果
nx graph加载慢,可以加--file=graph.html参数生成静态 HTML,用浏览器打开,比 Web UI 快 3 倍。这个 HTML 文件里嵌入了所有依赖关系的 JSON 数据,离线也能看。