ARTICLE DETAIL

资讯详情

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

TypeScript+Monorepo构建可发布AI技能的工程化实践

TypeScript+Monorepo构建可发布AI技能的工程化实践 1. 项目概述一个面向AI Agent能力工程的TypeScript开发框架“agent-skills”这个名字乍一听像某个开源库的包名但拆开来看——agent是当前AI工程落地最核心的抽象单元skills则直指能力封装的本质。它不是泛泛而谈的“AI助手”而是聚焦于可复用、可组合、可测试、可发布的原子级AI能力模块设计范式。我第一次在Nx monorepo里看到这个命名时就意识到这不是又一个玩具Demo而是一套试图把AI能力从“脚本逻辑”升级为“软件构件”的工程化实践。核心关键词里“TypeScript”不是装饰词而是整个架构的基石——类型即契约接口即协议泛型即能力扩展的语法糖“Nx”不是简单的构建工具而是支撑多技能协同、依赖拓扑管理、增量构建与CI/CD流水线的中枢“semantic-release”意味着每个skill的版本号不是人工拍脑袋定的而是由commit message语义自动推导让能力演进有迹可循而所有这些最终服务于一个更宏大的目标让AI能力像npm包一样被发现、安装、集成、替换和灰度发布。这正是当前大模型应用层最缺的一环——我们有千百个LLM调用示例却极少见到真正按软件工程标准组织的AI能力仓库。适合谁参考如果你正在做以下任何一件事这个项目对你就有直接价值正在搭建企业级AI中台需要统一管理Prompt工程、工具调用、记忆回溯、多步推理等能力模块带团队开发Agent应用苦于不同成员写的“技能函数”风格不一、无类型约束、无法复用在做AI垂直领域如法律、医疗、金融落地需要把领域知识封装成可审计、可验证的技能单元或者你只是个TypeScript深度用户想看看强类型系统如何约束非确定性AI行为——这本身就是一场值得记录的实验。它解决的不是“能不能跑通一个Agent”而是“能不能让一百个Agent共享同一套能力底盘”。接下来我会从设计哲学、技术选型、实操细节到踩坑现场一层层剥开这个看似简单名字背后的完整工程图谱。2. 整体架构设计与核心思路拆解2.1 为什么必须用Monorepo管理AI技能很多人第一反应是“一个skill写个ts文件不就完了何必搞Nx”——这恰恰是早期踩过的最大认知陷阱。我带过三个AI项目组初期都用独立repo管理技能结果半年后全部回归monorepo原因很现实依赖漂移问题A技能用OpenAI v4 SDKB技能用v5C技能自己封装了v3兼容层。当平台强制升级API时三处要分别改、分别测、分别发版没人能说清哪几个技能已适配。类型共享断层每个skill自己定义ToolInput、ExecutionResult结果A传给B的数据结构字段名不一致userIdvsuser_id运行时才报错。测试成本爆炸新增一个通用重试机制需手动复制到23个skill的test目录下修改全局错误码映射表要grep全量代码再逐个替换。Nx的workspace.json天然解决这些问题。它强制所有skill共享同一套agent-skills/core基础库该库定义了SkillDefinitionTInput, TOutput泛型接口约束输入输出类型、执行上下文、超时配置SkillError枚举统一错误分类TOOL_UNAVAILABLE,CONTEXT_EXPIRED,VALIDATION_FAILEDSkillRunner抽象类提供标准执行生命周期钩子beforeExecute,onRetry,afterSuccess。提示Nx的project-level dependencies graph通过nx dep-graph可视化能清晰看到skill-web-search依赖agent-skills/http-client而后者又被skill-db-query和skill-file-upload共同引用。这种拓扑关系在单repo时代靠文档维护现在由工具链自动生成并校验。2.2 TypeScript如何成为AI能力的“安全护栏”AI的不确定性常让人误以为“类型系统无用”。但实际恰恰相反——越不确定越需要确定的边界。我们用TypeScript做了三件事第一用泛型固化能力契约// 定义一个天气查询技能 export const weatherSkill defineSkill({ id: weather-lookup, inputSchema: z.object({ location: z.string().min(2), units: z.enum([celsius, fahrenheit]).default(celsius) }), outputSchema: z.object({ temperature: z.number(), condition: z.string(), humidity: z.number().min(0).max(100) }), async execute(input, context) { // 实际调用天气API... return { temperature: 23.5, condition: partly cloudy, humidity: 65 }; } });这里z.object不仅是校验更是编译期类型声明。IDE能自动推导weatherSkill.execute的参数类型为{location: string, units: celsius | fahrenheit}返回值为{temperature: number, ...}。当其他模块调用此skill时输入错误会在编码阶段报错而非运行时报Cannot read property temperature of undefined。第二用声明合并约束全局上下文Agent执行时需要访问会话ID、用户偏好、历史消息等。我们定义declare global { namespace AgentContext { interface Base { sessionId: string; userId: string; timezone: string; } interface Extended extends Base { // 各skill可扩展自己的上下文字段 weather?: { language: string }; db?: { maxRetries: number }; } } }这样weatherSkill的execute函数签名自动获得context: AgentContext.Extended类型且能安全访问context.weather?.language无需类型断言。第三用条件类型实现技能组合编排当构建复合skill如“先查天气再根据温度推荐穿衣”时我们用TS条件类型推导链式调用的类型流type ChainS1 extends Skill, S2 extends Skill S1[outputSchema] extends S2[inputSchema] ? { input: S1[inputSchema], output: S2[outputSchema] } : never;这使得chain(weatherSkill, clothingRecommendationSkill)的类型检查在编译期完成避免运行时因数据格式不匹配导致的静默失败。2.3 semantic-release为何是AI技能发布的“信任锚点”AI技能更新频繁但业务方不敢随便升级——怕新版本改了输出格式或引入新错误。semantic-release通过commit convention建立可信发布管道feat(weather): add humidity field to response→ 自动发布minor版本如1.2.0fix(weather): handle null location gracefully→ 自动发布patch版本1.2.1BREAKING CHANGE: change temperature unit from string to number→ 自动发布major版本2.0.0关键在于它强制所有变更必须关联到具体skill。Nx配合semantic-release的nx/semantic-release插件能精准识别本次commit只修改了libs/skills/weather因此只触发该skill的版本发布不影响libs/skills/db-query。我们还定制了release notes模板自动生成每版变更的影响面分析## [1.3.0](https://github.com/your-org/agent-skills/compare/v1.2.0...v1.3.0) (2024-06-15) ### ️ weather-lookup - **Added**: humidity field in output (backwards compatible) - **Changed**: Default units now celsius (no breaking change) - **Tested**: Verified with 12 location samples across 3 timezones这份notes不是给人看的而是给CI pipeline读的——下游服务可通过解析它自动判断是否需要触发回归测试。3. 核心细节解析与实操要点3.1 Nx workspace的精细化配置不只是“建个lib”Nx默认创建的lib过于通用而AI技能需要特殊约束。我们在libs/skills/tsconfig.lib.json中添加了关键配置{ compilerOptions: { strict: true, noImplicitAny: true, strictNullChecks: true, strictFunctionTypes: true, strictBindCallApply: true, strictPropertyInitialization: true, noImplicitThis: true, alwaysStrict: true, // 关键禁用any类型强制使用unknown类型守卫 noImplicitAny: true, // 关键禁止require强制ESM导入 module: node16, moduleResolution: node16, allowSyntheticDefaultImports: false, esModuleInterop: false }, include: [**/*.ts], exclude: [**/*.spec.ts, **/*.test.ts] }为什么禁用allowSyntheticDefaultImports因为AI技能常需调用Python后端或REST API若允许默认导入容易写出import axios from axios这种掩盖真实依赖的代码。我们要求显式声明import * as axios from axios; // 明确知道这是第三方库 // 或 import { default as axios } from axios; // 强制标注default导出此外在nx.json中配置了专属executortargets: { build: { executor: nx/node:package, options: { outputPath: dist/libs/skills/{projectName}, main: src/index.ts, tsConfig: tsconfig.lib.json, // 关键只打包必要文件排除test和dev依赖 assets: [ { input: src, glob: **/*.md, output: . }, { input: src, glob: **/*.schema.json, output: . } ], externalDependencies: none } } }externalDependencies: none确保打包产物包含所有runtime依赖如zod、axios避免下游项目因Node版本差异导致Cannot find module zod。3.2 技能定义DSL的设计哲学平衡表达力与约束力我们没选择YAML或JSON Schema定义skill而是用TypeScript对象字面量——因为开发者最熟悉的语法就是最好的DSL。但为防滥用我们设计了三层约束第一层编译期类型检查defineSkill()函数签名强制输入必须满足SkillDefinition接口其中inputSchema和outputSchema必须是zod schema实例export function defineSkillTInput, TOutput( config: SkillDefinitionTInput, TOutput ): SkillTInput, TOutput { // 运行时校验逻辑 return { id: config.id, inputSchema: config.inputSchema, outputSchema: config.outputSchema, execute: config.execute }; }第二层构建时静态分析Nx插件扫描所有defineSkill调用提取id字段生成skills-index.json{ weather-lookup: { path: libs/skills/weather/src/index.ts, input: { $ref: #/components/schemas/WeatherInput }, output: { $ref: #/components/schemas/WeatherOutput } } }该文件被注入到API网关供动态路由和OpenAPI文档生成使用。第三层测试时契约验证每个skill必须提供contract.test.ts验证输入输出符合schemadescribe(weatherSkill contract, () { it(should validate valid input, () { expect(() weatherSkill.inputSchema.parse({ location: Shanghai })) .not.toThrow(); }); it(should reject invalid input, () { expect(() weatherSkill.inputSchema.parse({ location: })) .toThrow(String must contain at least 2 character(s)); }); it(should validate output structure, () { const mockOutput { temperature: 25, condition: sunny, humidity: 70 }; expect(() weatherSkill.outputSchema.parse(mockOutput)).not.toThrow(); }); });这个测试不跑实际API只验证schema定义本身——它是技能交付的准入门槛。3.3 AI能力的可观测性设计不只是打日志AI技能失败时传统日志只记录Error: Request failed但业务方需要知道是网络超时API限流还是模型返回了非法JSON我们为每个skill注入统一的ObservabilityContextexport interface ObservabilityContext { traceId: string; skillId: string; attempt: number; // 重试次数 inputHash: string; // 输入数据SHA256用于去重分析 startTime: number; endTime?: number; error?: { code: string; // 如 HTTP_429, PARSE_ERROR, TIMEOUT message: string; stack?: string; }; metrics: { apiLatencyMs: number; tokensIn: number; tokensOut: number; }; } // 在SkillRunner中自动注入 async execute(input: TInput, context: ExecutionContext) { const obsCtx: ObservabilityContext { traceId: context.traceId || generateTraceId(), skillId: this.id, attempt: context.attempt || 1, inputHash: hashInput(input), startTime: Date.now() }; try { const result await this._execute(input, context); obsCtx.endTime Date.now(); obsCtx.metrics this.extractMetrics(result); this.reportSuccess(obsCtx); return result; } catch (e) { obsCtx.endTime Date.now(); obsCtx.error this.normalizeError(e); this.reportFailure(obsCtx); throw e; } }关键点在于error.code的标准化。我们定义了12类错误码覆盖所有AI技能可能失败的场景错误码触发条件业务含义HTTP_TIMEOUTAxios请求超时网络不稳定建议重试MODEL_PARSE_ERRORLLM返回非JSON或字段缺失模型不可靠需降级或换模型CONTEXT_EXPIRED会话过期无法获取用户信息需引导用户重新登录TOOL_RATE_LIMITED天气API返回429服务端限流需排队或降级这些错误码被发送到中央监控系统运营人员可设置告警当MODEL_PARSE_ERROR5分钟内超过10次自动通知算法团队检查prompt稳定性。4. 实操过程与核心环节实现4.1 从零初始化agent-skills workspace第一步不是写代码而是规划拓扑。我们按Nx最佳实践创建四类projectProject类型名称用途依赖关系Core Libraryagent-skills/core定义Skill基类、上下文、错误码所有skills依赖Skills Librarylibs/skills/*具体技能实现weather, db, file等依赖coreIntegration Testsapps/integration-tests跨skill流程测试如“查天气→订伞”依赖所有skillsDocs Siteapps/docs自动生成OpenAPI文档和技能目录依赖core和skills初始化命令npx create-nx-workspacelatest agent-skills \ --presetapps \ --clinx \ --nxCloudfalse \ --packageManagerpnpm cd agent-skills pnpm add -D nx/node nx/eslint nx/jest nx/workspace pnpm nx g nx/node:library core --directorylibs --importPathagent-skills/core pnpm nx g nx/node:library skills --directorylibs --importPathagent-skills/skills关键配置在workspace.json中启用project referencesprojects: { agent-skills/core: { tags: [type:core] }, skills-weather: { tags: [type:skill, domain:weather], implicitDependencies: [agent-skills/core] } }tags用于Nx的依赖过滤例如nx affected --targettest --tagsdomain:weather只测试天气相关skill。4.2 实现第一个技能weather-lookup含完整错误处理创建skill目录pnpm nx g nx/node:library weather --directorylibs/skills --importPathagent-skills/skills/weather编辑libs/skills/weather/src/index.tsimport { defineSkill } from agent-skills/core; import { z } from zod; import axios from axios; // 1. 定义输入输出schema编译期类型运行时校验 const WeatherInput z.object({ location: z.string().min(2, Location must be at least 2 characters), units: z.enum([celsius, fahrenheit]).default(celsius) }); const WeatherOutput z.object({ temperature: z.number().min(-100).max(100), condition: z.string().min(1), humidity: z.number().min(0).max(100), windSpeed: z.number().optional() }); // 2. 实现执行逻辑关注错误分类 export const weatherSkill defineSkill({ id: weather-lookup, inputSchema: WeatherInput, outputSchema: WeatherOutput, async execute(input, context) { // 输入校验已在调用前完成此处专注业务逻辑 try { const response await axios.get( https://api.openweathermap.org/data/2.5/weather, { params: { q: input.location, appid: process.env.OPENWEATHER_API_KEY, units: input.units celsius ? metric : imperial }, timeout: 5000 } ); // 3. 结构化提取防御性编程 const data response.data; if (!data.main || !data.weather || !Array.isArray(data.weather)) { throw new Error(Invalid API response structure: ${JSON.stringify(data)}); } // 4. 类型安全转换 const result { temperature: Math.round(data.main.temp), condition: data.weather[0].description, humidity: data.main.humidity, windSpeed: data.wind?.speed }; // 5. 输出校验确保不返回非法值 return WeatherOutput.parse(result); } catch (error: any) { // 6. 标准化错误码 if (axios.isAxiosError(error)) { switch (error.response?.status) { case 404: throw { code: LOCATION_NOT_FOUND, message: Weather data not found for ${input.location} }; case 429: throw { code: TOOL_RATE_LIMITED, message: OpenWeather API rate limit exceeded }; case 500: throw { code: TOOL_UNAVAILABLE, message: OpenWeather service unavailable }; default: throw { code: HTTP_ERROR, message: HTTP ${error.response?.status}: ${error.message} }; } } else if (error.name TimeoutError) { throw { code: HTTP_TIMEOUT, message: Request timed out after 5s }; } else { throw { code: PARSE_ERROR, message: Failed to parse weather response: ${error.message} }; } } } });注意throw { code, message }不是抛Error对象而是抛plain object——这是为了在SkillRunner中统一捕获并注入observability context。真正的Error被包装在obsCtx.error中业务方看到的是结构化错误码。4.3 构建可发布的NPM包从本地lib到registryNx默认打包产物是CommonJS但现代AI应用多用ESM。我们在libs/skills/weather/project.json中配置{ targets: { build: { executor: nx/node:package, options: { outputPath: dist/libs/skills/weather, main: src/index.ts, tsConfig: tsconfig.lib.json, assets: [ { input: libs/skills/weather/src, glob: **/*.md, output: . } ], generateExports: true, updateBuildableProjectDepsInPackageJson: true } } } }generateExports启用后dist/libs/skills/weather/package.json自动生成{ name: agent-skills/skills-weather, version: 1.0.0, types: ./index.d.ts, main: ./index.js, module: ./index.mjs, exports: { .: { import: ./index.mjs, require: ./index.js } } }发布前执行pnpm nx build weather cd dist/libs/skills/weather npm publish --access public关键点--access public确保私有registry也能正确解析。我们内部用Verdaccio搭建私有npm所有skill包都发布到agent-skills/*scope下避免命名冲突。4.4 集成测试验证技能组合的端到端行为在apps/integration-tests/src/main.ts中编写跨skill流程import { weatherSkill } from agent-skills/skills-weather; import { clothingRecommendationSkill } from agent-skills/skills-clothing; // 模拟Agent执行引擎 async function runAgentFlow() { try { // Step 1: 获取天气 const weather await weatherSkill.execute( { location: Beijing, units: celsius }, { traceId: test-123, userId: u123 } ); // Step 2: 基于天气推荐穿衣 const recommendation await clothingRecommendationSkill.execute( { temperature: weather.temperature, condition: weather.condition }, { traceId: test-123, userId: u123 } ); console.log(Final recommendation:, recommendation); return recommendation; } catch (error) { console.error(Agent flow failed:, error); throw error; } } runAgentFlow();测试脚本apps/integration-tests/src/test.flow.spec.tsdescribe(Agent Flow: Weather → Clothing, () { it(should recommend jacket when temperature 15°C, async () { // Mock weatherSkill to return cold weather jest.mock(agent-skills/skills-weather, () ({ weatherSkill: { execute: jest.fn().mockResolvedValue({ temperature: 8, condition: cloudy, humidity: 75 }) } })); const result await runAgentFlow(); expect(result.items).toContainEqual( expect.objectContaining({ type: jacket, priority: high }) ); }); });这里用jest.mock精确控制依赖行为避免真实API调用。Nx的affected命令能智能识别修改weatherSkill后自动运行所有依赖它的integration tests。5. 常见问题与排查技巧实录5.1 TypeScript类型推导失效为什么IDE不提示skill方法现象在调用weatherSkill.execute(...)时VS Code不显示参数提示Hover显示any类型。根因分析TypeScript的类型推导依赖node_modules中的.d.ts声明文件。但Nx默认构建时dist/libs/skills/weather/index.d.ts可能未正确生成。排查步骤检查libs/skills/weather/tsconfig.lib.json是否包含declaration: true默认开启运行pnpm nx build weather确认dist/libs/skills/weather/index.d.ts存在且内容非空检查dist/libs/skills/weather/package.json中types字段是否指向./index.d.ts在消费项目中删除node_modules/agent-skills/skills-weather重新pnpm install。实操心得Nx的buildtarget默认不生成d.ts到dist目录。需在project.json中显式配置options: { emitDeclaration: true, declarationDir: dist/libs/skills/weather }5.2 semantic-release发布失败Commit message格式被拒绝现象git commit -m feat: add humidity field被semantic-release拒绝提示INVALID COMMIT MSG。根因semantic-release默认只识别Angular规范而我们的commit message缺少scope模块名。解决方案在libs/skills/weather/.releaserc中配置{ plugins: [ semantic-release/commit-analyzer, semantic-release/release-notes-generator, semantic-release/npm, semantic-release/github ], branches: [main], tagFormat: v${version} }并在package.json中添加husky pre-commit hookhusky: { hooks: { commit-msg: exec /dev/tty npx --no-install commitlint --edit $1 } }配合.commitlintrc.json{ extends: [commitlint/config-conventional], rules: { scope-case: [2, always, [lower-case]], subject-case: [2, never, [sentence-case]] } }现在必须用git commit -m feat(weather): add humidity field才能通过校验。5.3 Nx依赖图混乱为什么skills-db-query显示依赖skills-weather现象nx dep-graph显示skills-db-query依赖skills-weather但代码中并无import。根因Nx的自动依赖检测基于文件导入但有时存在隐式依赖skills-db-query的test文件导入了agent-skills/core而agent-skills/core又依赖skills-weather的类型定义或者tsconfig.json中paths别名配置错误导致路径映射污染。排查命令# 查看具体依赖路径 nx dep-graph --focusskills-db-query --group-by-directory # 检查是否被test文件污染 grep -r weather libs/skills/db-query/src/**/*.spec.ts # 验证tsconfig paths pnpm nx show-project skills-db-query注意Nx的implicitDependencies配置应仅用于硬依赖。若skills-db-query确实不需要skills-weather在project.json中显式声明implicitDependencies: [agent-skills/core]并移除所有间接引用。5.4 技能执行超时为什么5秒timeout实际耗时12秒现象weatherSkill配置timeout: 5000但日志显示apiLatencyMs: 12450。根因axios的timeout只作用于连接建立和响应头接收不包括响应体下载和JSON解析时间。解决方案分层设置超时const controller new AbortController(); setTimeout(() controller.abort(), 5000); // 总超时 try { const response await axios.get(..., { signal: controller.signal, timeout: 3000 // 连接响应头超时 }); // 解析响应体单独计时 const parseStart Date.now(); const result WeatherOutput.parse(response.data); const parseTime Date.now() - parseStart; if (parseTime 2000) { throw new Error(JSON parsing took ${parseTime}ms, exceeding budget); } return result; } catch (error) { // 统一错误处理... }5.5 Zod schema校验性能瓶颈为什么1000次调用耗时800ms现象压测时weatherSkill.inputSchema.parse()成为性能热点。根因Zod的parse()方法每次调用都重建验证器。对于高频技能应缓存schema实例。优化方案// libs/skills/weather/src/schema.ts import { z } from zod; // 导出已编译的schema避免重复解析 export const WeatherInputSchema z .object({ location: z.string().min(2), units: z.enum([celsius, fahrenheit]).default(celsius) }) .parse; // 直接导出parse函数 // 在skill中使用 export const weatherSkill defineSkill({ inputSchema: WeatherInputSchema, // 不再调用z.object(...).parse // ... });实测效果单次parse从0.8ms降至0.05ms1000次调用从800ms降至50ms。6. 生产环境部署与运维实践6.1 多环境配置管理如何让skill在dev/staging/prod使用不同API密钥Nx的project.json不支持环境变量注入我们采用分层配置策略基础配置libs/skills/weather/src/config.base.tsexport const BASE_CONFIG { openWeatherApiUrl: https://api.openweathermap.org/data/2.5/weather, defaultUnits: celsius as const };环境特定配置libs/skills/weather/src/config.staging.tsimport { BASE_CONFIG } from ./config.base; export const STAGING_CONFIG { ...BASE_CONFIG, apiKey: process.env.OPENWEATHER_STAGING_KEY || };技能工厂函数libs/skills/weather/src/factory.tsimport { weatherSkill } from ./index; import { STAGING_CONFIG, PROD_CONFIG } from ./config; export function createWeatherSkill(env: staging | prod) { const config env staging ? STAGING_CONFIG : PROD_CONFIG; // 返回增强版skill注入配置 return { ...weatherSkill, execute: async (input, context) { // 在执行前注入env配置 return weatherSkill.execute(input, { ...context, config }); } }; }部署时CI pipeline根据分支自动选择配置# .github/workflows/deploy.yml - name: Build and deploy run: | if [[ $GITHUB_REF refs/heads/main ]]; then pnpm nx build weather --configurationproduction npm publish dist/libs/skills/weather --registry https://prod-registry.example.com else pnpm nx build weather --configurationstaging npm publish dist/libs/skills/weather --registry https://staging-registry.example.com fi6.2 技能灰度发布如何让10%流量走新版本weatherSkill我们不依赖Kubernetes流量切分而是在API网关层实现技能级灰度注册多版本skill// 网关初始化时 skillRegistry.register(weather-lookup, weatherSkillV1, { version: 1.0.0 }); skillRegistry.register(weather-lookup, weatherSkillV2, { version: 2.0.0 });灰度路由逻辑function selectSkillVersion(skillId: string, userId: string): string { // 基于userId哈希实现一致性哈希 const hash createHash(sha256).update(userId).digest(hex).slice(0, 8); const num parseInt(hash.substring(0, 4), 16); const percentage num % 100; return percentage 10 ? 2.0.0 : 1.0.0; } // 调用时 const version selectSkillVersion(weather-lookup, context.userId); const skill skillRegistry.get(weather-lookup, version); return skill.execute(input, context);监控对比实时统计v1.0.0和v2.0.0的successRate、avgLatency、errorCodes分布当v2.0.0的MODEL_PARSE_ERROR率高于v1.0.02%自动降级。6.3 技能健康度看板如何一眼看出哪个skill拖慢了整个Agent我们开发了轻量级健康度看板apps/health-dashboard聚合所有skill指标Skill IDSuccess RateAvg LatencyError Top3Last Deployweather-lookup99.2%420msHTTP_429(42%), PARSE_ERROR(33%)2024-06-10db-query99.8%180msCONTEXT_EXPIRED(95%)2024-06-05file-upload95.1%2100msTOOL_UNAVAILABLE(68%)2024-06-12数据来源Prometheus抓取Nx构建的/metrics端点暴露skill_execution_total{skill_id, status}ELK收集obsCtxJSON日志聚合错误码GitHub API读取各skill repo的last commit时间。实操心得不要在看板上堆砌所有指标。我们只保留三个核心维度成功率业务可用性、延迟用户体验、错误分布根因定位。第四个维度“部署频率”被刻意隐藏——因为高频发布不等于高质量反而可能增加故障率。7. 后续演进方向与经验反思这个项目运行一年后我们沉淀出几条血泪经验第一技能粒度必须足够小。早期我们定义了research-skill试图封装“查论文→总结→翻译”全流程。结果发现学术数据库API、摘要模型、翻译引擎的SLA
返回列表