首页 / 资讯中心 / 文章详情

Agent-Skills:基于 CLI 与 npx 的开发者智能工作流中枢

Agent-Skills:基于 CLI 与 npx 的开发者智能工作流中枢 ★ FEATURED ARTICLE
1. 项目概述Agent-Skills 不是插件是开发者的新工作流中枢“agent-skills”这个词最近在前端、AI 工具链和 CLI 开发者圈子里高频出现但它不是某个具体产品的名字也不是 npm 上一个待安装的包——它代表一种正在快速成型的能力组织范式。我从去年底开始系统性地把日常开发中反复写的脚本、调用的 API、处理的文件格式、对接的 LLM 模型全部按“技能skill”来建模和封装。比如“从 Markdown 提取所有代码块并执行语法检查”是一个 skill“自动读取 PR 描述生成符合团队规范的 changelog 条目”是另一个“接收一段自然语言需求输出可运行的 React 组件 Storybook 示例 Jest 测试骨架”也是一个。这些 skill 不依赖特定 IDE不绑定某家大模型厂商也不需要你改写整个开发流程——它们通过统一的 CLI 接口暴露用npx即时调用用/命令触发类似 Slack 的 slash commands像操作系统里的命令一样即装即用、即删即净。核心关键词agent-skills、CLI、npx、slash commands、skills其实指向同一个底层逻辑把开发者重复性高、上下文强、决策链长的“智能操作”从散落在 shell 脚本、VS Code 扩展、Copilot 插件、甚至手写 Python 小工具里的状态抽离成独立、可组合、可测试、可版本化的最小执行单元。它解决的不是“能不能做”而是“能不能在 3 秒内、不离开终端、不切换窗口、不打开文档”的前提下精准调用那个刚好匹配当前任务的智能动作。适合三类人一是每天要写大量胶水脚本的前端/全栈工程师二是被 Copilot 生成结果质量波动折磨的团队技术负责人三是想把内部知识沉淀为可复用能力、但又不想搞复杂低代码平台的中小团队架构师。它不替代 IDE而是让 IDE 更懂你不取代 LLM而是让 LLM 更贴合你的工程约束。我试过把团队里 17 个常用脚本全部重构成 skills最终发现真正节省时间的不是单次执行快了 200ms而是当你在 review 一个涉及 SVG 图标导出、颜色变量校验、无障碍属性补全的 PR 时只需输入/check-pr三个 skill 就自动串行执行、交叉验证、生成带链接的报告——而这个动作在过去需要你手动打开 4 个终端标签页、复制粘贴 6 次路径、确认 5 次交互提示。这才是 agent-skills 的真实价值它把“人驱动工具”变成了“任务驱动人工具协同”。你不再记住npm run lint:svg npm run check:colors node scripts/gen-a11y-report.js你只记住/check-pr——剩下的交给 skill 的注册表、执行器和错误恢复机制。2. 核心设计思路为什么必须用 CLI npx Slash Commands 构建 Skills 生态2.1 CLI 是唯一能穿透所有开发环境的通用接口很多人第一反应是“为什么不做成 VS Code 插件” 或 “直接集成到 Cursor 里不更方便” 这是个关键误判。我去年花了三个月把一套核心 skill 集成进 VS Code结果发现当同事用 WebStorm 查看 PR 时功能不可用当 CI 流水线需要复现本地检查逻辑时插件环境无法加载当新成员用 Vim 编辑器时整个技能体系对他就是黑盒。CLI 的价值在于它的环境无关性——只要系统有sh或PowerShell就能执行。它不依赖 GUI 线程、不占用编辑器内存、不与特定语言服务耦合。更重要的是CLI 天然支持管道|、重定向、后台执行和条件判断这使得 skill 之间可以像 Unix 命令一样自由组合。比如/extract-api-spec | /gen-openapi-client --langts这种链式调用在插件体系里需要定制化消息总线而在 CLI 里就是一行原生命令。提示不要把 CLI 当作“命令行界面”的缩写而要理解为Command Line Interface—— 它本质是一种协议一种约定好的输入/输出契约。一个 skill 的 CLI 接口定义就等于它的 API 文档、测试入口和部署单元。2.2 npx 是零配置分发与沙箱执行的核心引擎npx常被误解为“临时运行 npm 包的工具”但它真正的杀手级特性是按需下载、隔离执行、版本锁定。我们团队的/lint-cssskill 实际对应一个独立仓库ourorg/skill-lint-css但用户从不执行npm install ourorg/skill-lint-css。他们只输入/lint-css背后是npx ourorg/skill-lint-css1.2.4 --inputsrc/components --fix。这意味着新成员第一天入职不需要git clone任何仓库不需要yarn install整个项目只要能跑npx就能用所有 skill某个 skill 依赖postcss8.4.32另一个依赖postcss8.4.35npx会为每个 skill 创建独立 node_modules彻底避免 peer dependency 冲突当你发现ourorg/skill-lint-css1.2.4有严重 bug只需发布1.2.5所有用户下次执行时自动升级无需手动更新。我实测过在 Windows Terminal、iTerm2、VS Code 集成终端、GitHub Codespaces 里npx的启动延迟平均为 320ms含网络下载而本地全局安装的 CLI 平均延迟为 180ms。多出的 140ms 换来了零维护成本、零版本漂移风险、零环境预置要求——这笔账在团队协作场景下稳赚不赔。2.3 Slash Commands 是降低认知负荷的终极交互设计/前缀不是为了模仿 Slack而是解决一个根本问题如何让命令具备语义可发现性git commit -m xxx和npm run build这类命令你需要先记住命令名再查参数。而/check-pr、/gen-docs、/sync-i18n这类 slash command其名称本身就是动宾短语直指意图。更重要的是它天然支持 tab 补全和模糊匹配。我们在 zsh 中配置了自定义 completion输入/che Tab会列出/check-pr、/check-types、/check-deps输入/gen Tab会列出/gen-component、/gen-story、/gen-test。这种“意图优先”的交互把记忆负担从“命令语法”转移到“业务动作”极大降低了新 skill 的学习门槛。注意slash command 的实现不依赖 shell 内置功能。我们用一个轻量级 wrapper 脚本约 200 行 Bash/PowerShell拦截所有以/开头的命令解析后转发给对应的npx调用。这意味着它能在任何支持自定义 alias 的终端里工作包括 Windows Command Prompt通过 doskey 宏。2.4 Skills 必须是原子化、可组合、带上下文感知的执行单元一个合格的 skill 必须满足三个硬性标准原子性只做一件事且这件事有明确边界。/format-code只负责格式化不负责 lint/lint-js只负责静态检查不负责修复。这保证了可预测性和可测试性。可组合性skill 的输出必须是结构化数据JSON/NDJSON便于被其他 skill 消费。例如/extract-todos输出[{file:src/App.tsx,line:42,text:TODO: add error boundary}]后续可直接被/create-github-issue读取并创建 issue。上下文感知skill 必须能自动识别当前工作目录的工程类型。/gen-component在 React 项目里生成.tsx.stories.tsx在 Vue 项目里生成.vue.stories.ts在 Svelte 项目里生成.svelte.stories.ts。我们通过检测package.json中的dependencies、scripts字段和根目录文件如vite.config.ts、next.config.js来动态加载对应模板而不是让用户传--frameworkreact参数。这套设计让 skills 不再是孤立脚本而成为可编排的“智能积木”。我们用一个简单的 YAML 文件定义 workflowname: PR Validation steps: - skill: /extract-changes output: changes - skill: /check-types input: ${changes} - skill: /gen-snapshot input: ${changes} if: ${changes.length 0}这个 workflow 本身也是一个 skill通过/run-workflow pr-validation调用。这才是 agent-skills 的进化方向从单点能力走向能力网络。3. 核心细节解析Skills 的目录结构、执行协议与上下文注入机制3.1 标准 Skills 目录结构为什么必须严格遵循一个 skill 的文件结构不是随意约定而是执行器识别、缓存策略、权限控制的基础。我们强制采用以下布局以ourorg/skill-check-pr为例skill-check-pr/ ├── package.json # 必须包含 bin: { check-pr: ./bin/index.js } ├── bin/ │ └── index.js # CLI 入口必须导出 main() 函数 ├── src/ │ ├── core/ # 核心逻辑不依赖 CLI 层 │ │ ├── git.js # 封装 git 操作返回 PromiseArrayChange │ │ └── pr.js # 解析 PR 上下文返回 { title, body, diff } │ ├── rules/ # 可插拔的检查规则 │ │ ├── no-console.js # 规则 1禁止 console.log │ │ └── has-test.js # 规则 2新增文件必须有对应 test │ └── reporter/ # 输出适配器 │ ├── json.js # JSON 格式输出供其他 skill 消费 │ └── terminal.js # 彩色终端输出供人阅读 ├── config/ # 默认配置可被 --config 覆盖 │ └── default.json ├── templates/ # 模板文件如 report.md.ejs └── test/ # 必须包含 integration test └── e2e.test.js # 在真实 git repo 中测试完整流程这个结构的关键设计点在于分层隔离bin/index.js只做三件事解析 CLI 参数、调用src/core/main()、选择 reporter 输出。它不包含任何业务逻辑src/core/是纯函数式模块所有函数都接受明确输入、返回明确输出无副作用。这使得单元测试覆盖率可达 95%src/rules/是插件化设计每个规则导出{ id, name, run }对象core/main()动态加载启用的规则。新增规则无需修改主逻辑config/default.json定义规则开关、阈值、忽略路径等--config ./my-config.json可覆盖支持团队级和项目级配置。实操心得我们曾尝试让 skill 直接读取项目根目录的.checkprrc文件结果发现跨团队协作时不同项目配置差异巨大导致同一 skill 行为不一致。后来改为“skill 自带 sane defaults 显式 --config 覆盖”问题迎刃而解。默认值不是为了省事而是为了提供可预期的行为基线。3.2 Skills 执行协议输入、输出、错误码的标准化契约CLI 的强大在于其契约精神。一个 skill 必须严格遵守以下协议否则会被执行器拒绝项目要求示例输入支持--help、--version、--json强制 JSON 输出、--verbose详细日志。所有业务参数必须是--keyvalue形式禁用位置参数positional argsnpx ourorg/skill-check-pr --json --repogithub.com/ourorg/app --pr123输出成功时 stdout 输出结构化数据JSON/NDJSONstderr 输出人类可读日志失败时 stdout 为空stderr 输出错误信息exit code ≠ 0{status:passed,issues:[],summary:All checks passed}退出码0: 完全成功1: 业务失败如 lint 错误、test 失败2: 执行异常如网络超时、权限不足3: 输入错误如参数缺失、格式错误echo $?返回1表示有 warningCI 可据此设置非阻断式检查上下文注入自动注入SKILL_CONTEXT环境变量值为 JSON 字符串包含cwd,git_root,branch,commit_hash,is_ci等字段{cwd:/Users/me/project,git_root:/Users/me/project,branch:main,is_ci:false}这个协议让 skills 具备了“可编程性”。你可以写一个 shell 脚本循环调用多个 skill 并聚合结果#!/bin/bash # aggregate-checks.sh results$(mktemp) npx ourorg/skill-check-pr --json $results npx ourorg/skill-check-types --json $results npx ourorg/skill-check-security --json $results # 统计总问题数 jq -s map(select(.status failed)) | length $results rm $results3.3 上下文感知机制如何让 Skill 知道自己在哪、为谁服务Skills 不是孤立运行的它们必须理解所处的工程上下文。我们设计了一套轻量级上下文探测器Context Detector在 skill 启动时自动运行结果通过SKILL_CONTEXT注入。探测器按优先级顺序执行Git 上下文执行git rev-parse --show-toplevel 2/dev/null获取工作区根目录git branch --show-current获取当前分支git log -1 --format%H HEAD获取 commit hash。这是最可靠的上下文源。工程类型识别在 Git 根目录下查找特征文件vite.config.*→ Vite 项目next.config.*→ Next.js 项目remix.config.*→ Remix 项目package.json中type: module→ ESM 项目package.json中dependencies包含react→ React 项目框架版本探测对识别出的框架执行框架特有命令获取版本npx vite --version→ Vite 版本npx next --version→ Next.js 版本node -p require(react/package.json).version→ React 版本CI 环境标记检查CI,GITHUB_ACTIONS,GITLAB_CI等环境变量设置is_ci: true。这个机制让/gen-componentskill 能自动选择模板在 Next.js App Router 项目中它生成app/[slug]/page.tsx在 Pages Router 项目中生成pages/[slug].tsx在 Vite React 项目中生成src/pages/[slug]/index.tsx。用户无需指定--routerappskill 自己“读懂”项目。踩过的坑早期我们用fs.readdirSync(.)列出所有文件来判断框架结果在大型 monorepo 中耗时超过 2s。后来改为只检查 5 个关键文件路径vite.config.js,next.config.js,remix.config.js,astro.config.mjs,package.json耗时降至 12ms。上下文探测必须是 O(1) 操作不能成为性能瓶颈。4. 实操过程从零构建一个可复用的/gen-componentSkill4.1 初始化项目与 CLI 入口搭建第一步不是写业务逻辑而是建立可交付的 CLI 骨架。我用create-skill-app脚手架基于 pnpm TypeScript初始化pnpm create skill-app ourorg/skill-gen-component cd skill-gen-component脚手架生成的标准package.json包含{ name: ourorg/skill-gen-component, version: 0.1.0, bin: { gen-component: ./bin/index.js }, exports: { .: ./src/index.ts, ./bin: ./bin/index.js }, types: ./src/index.ts }bin/index.js是 Node.js 兼容入口避免 ESM 导入问题#!/usr/bin/env node import { main } from ../src/index.js; main(process.argv);src/index.ts是核心入口只做参数解析和调度import { parseArgs } from node:util; import { generateComponent } from ./core/generator.js; export async function main(argv: string[]) { const { values, positionals } parseArgs({ args: argv.slice(2), options: { name: { type: string, demand: true }, path: { type: string, default: src/components }, framework: { type: string, default: auto }, // auto-detect help: { type: boolean }, version: { type: boolean } } }); if (values.help) { console.log(Usage: gen-component --nameButton --pathsrc/components); process.exit(0); } if (values.version) { console.log(require(../package.json).version); process.exit(0); } try { const result await generateComponent({ name: values.name, path: values.path, framework: values.framework }); console.log(JSON.stringify(result, null, 2)); } catch (error) { console.error(Error:, error.message); process.exit(1); } }这个设计确保CLI 层薄如纸所有业务逻辑都在src/core/下便于单元测试和复用。4.2 核心生成逻辑模板引擎与框架适配策略src/core/generator.ts是 skill 的心脏。它不硬编码任何框架模板而是通过FrameworkAdapter抽象interface FrameworkAdapter { getTemplate(name: string): Promisestring; getExtension(): string; getTestTemplate(name: string): Promisestring; } class ReactAdapter implements FrameworkAdapter { async getTemplate(name: string) { return readTemplate(react/Component.tsx.ejs); } getExtension() { return .tsx; } async getTestTemplate(name: string) { return readTemplate(react/Component.test.tsx.ejs); } } class VueAdapter implements FrameworkAdapter { async getTemplate(name: string) { return readTemplate(vue/Component.vue.ejs); } getExtension() { return .vue; } async getTestTemplate(name: string) { return readTemplate(vue/Component.spec.ts.ejs); } } export async function generateComponent(options: { name: string; path: string; framework: auto | react | vue | svelte; }) { const framework await detectFramework(options.framework); const adapter getAdapter(framework); const componentPath join(options.path, ${options.name}${adapter.getExtension()}); const testPath join(options.path, ${options.name}.test${adapter.getExtension()}); // 渲染模板 const componentContent await renderTemplate( await adapter.getTemplate(options.name), { name: options.name } ); const testContent await renderTemplate( await adapter.getTestTemplate(options.name), { name: options.name } ); // 写入文件 await fs.writeFile(componentPath, componentContent); await fs.writeFile(testPath, testContent); return { component: componentPath, test: testPath, framework }; }模板文件放在templates/目录下使用 EJS 语法!-- templates/react/Component.tsx.ejs -- import React from react; export interface % name %Props { children?: React.ReactNode; } export const % name %: React.FC% name %Props ({ children }) { return div className% name.toLowerCase() %{children}/div; }; export default % name %;关键技巧模板中所有变量都用% name %而不是${name}因为 EJS 在字符串中渲染而 TypeScript 模板字面量会提前解析。这个细节让模板既能被 EJS 渲染又能被 IDE 正确识别为 TSX。4.3 框架自动探测与 fallback 机制detectFramework函数是上下文感知的关键async function detectFramework(framework: string): Promisereact | vue | svelte { if (framework ! auto) return framework as any; const pkg await readPackageJson(); const deps { ...pkg.dependencies, ...pkg.devDependencies }; if (deps.react !deps.vue !deps.svelte) return react; if (deps.vue !deps.react !deps.svelte) return vue; if (deps.svelte !deps.react !deps.vue) return svelte; // 模糊匹配检查是否有框架特有配置 const files await fs.readdir(process.cwd()); if (files.some(f f.startsWith(vite.config))) return react; // Vite 默认 React if (files.some(f f.startsWith(astro.config))) return svelte; throw new Error(Cannot detect framework. Please specify --frameworkreact/vue/svelte); }这个探测器有三层 fallback显式声明--frameworkvue优先级最高依赖分析精确匹配dependencies配置文件启发Vite/Astro 配置暗示框架倾向。我们故意不支持“混合框架”项目如 React Vue因为那违背了 skill 的原子性原则。如果项目确实混合应拆分为ourorg/skill-gen-react-component和ourorg/skill-gen-vue-component两个独立 skill。4.4 本地开发与调试如何高效迭代一个 Skill开发 skill 最大的陷阱是“在真实项目里调试”。正确做法是用pnpm link建立本地链接cd skill-gen-component pnpm link cd ~/my-project pnpm link ourorg/skill-gen-component这样npx ourorg/skill-gen-component就指向本地代码修改保存后立即生效。创建最小化测试项目 在skill-gen-component/test/fixtures/下建几个微型项目test/fixtures/ ├── react-app/ # 只有 package.json react 依赖 ├── vue-app/ # 只有 package.json vue 依赖 └── svelte-app/ # 只有 package.json svelte 依赖每个 fixture 都是git init初始化的干净仓库用于 e2e 测试。编写可复现的测试用例// test/e2e.test.ts import { execa } from execa; import { join } from node:path; test(generates React component in react-app, async () { const cwd join(__dirname, fixtures, react-app); const { stdout } await execa(npx, [ ourorg/skill-gen-component, --nameButton, --pathsrc/components ], { cwd }); const result JSON.parse(stdout); expect(result.component).toBe(src/components/Button.tsx); expect(result.framework).toBe(react); });这样每次pnpm test都在隔离环境中运行不会污染你的主项目。我实测过从修改代码到看到测试结果平均耗时 1.8s比在真实项目里手动测试快 12 倍。5. 常见问题与排查技巧实录那些文档里不会写的实战经验5.1 “Unable to locate the codex cli binary” 类错误的根源与解法这个错误信息及其变体如command not found: codex,Failed to start: cannot find binary在社区里高频出现但绝大多数人被误导去重装 CLI。真相是这不是 CLI 未安装而是 skill 的执行器找不到它期望的二进制路径。我们遇到的真实案例Case 1Windows Path 长度限制某用户在深度嵌套的路径C:\Users\John\Documents\Projects\Company\Monorepo\packages\ui\src\components下执行/gen-componentnpx下载的临时二进制路径超过 260 字符Windows 拒绝创建。解法在package.json中添加npx: {cache: C:/tmp/npx-cache}强制缩短路径。Case 2Node.js 版本不兼容codex-cli依赖node:fs/promises而用户 Node.js 14.0.0 不支持。npx下载后执行报错SyntaxError: Unexpected token export但错误被吞掉只显示“unable to locate binary”。解法在 skill 的package.json中添加engines: {node: 16.14.0}npx会提前检查并报错。Case 3权限策略拦截macOS Monterey 后Gatekeeper 默认阻止从互联网下载的未签名二进制。npx下载的 skill 二进制被 quarantine 属性标记。解法在bin/index.js开头添加if (process.platform darwin) { const { execSync } require(child_process); try { execSync(xattr -d com.apple.quarantine ${process.execPath}, { stdio: ignore }); } catch {} }独家技巧在所有 skill 的bin/index.js开头加入诊断代码console.error([DEBUG] NODE_ENV:, process.env.NODE_ENV); console.error([DEBUG] PATH:, process.env.PATH.split(:).slice(0,3).join(:) ...); console.error([DEBUG] SKILL_CONTEXT:, process.env.SKILL_CONTEXT?.substring(0,100) || undefined);这些日志默认 stderr 输出加--verbose才显示是排查环境问题的第一手线索。5.2 Slash Command Tab 补全失效的 5 种场景与修复Tab 补全是 slash command 的灵魂但极易因环境差异失效场景现象修复方案Zsh 未启用 compinit输入/gen Tab 无响应在~/.zshrc添加autoload -Uz compinit compinitBash 未安装 bash-completionUbuntu 默认无补全支持sudo apt install bash-completion source /etc/bash_completionWindows Terminal 使用 PowerShell Coredoskey宏不生效改用 PowerShell 的Register-ArgumentCompleter见 官方文档Skill 名称含特殊字符/gen-component被 shell 解析为/gen-component在 wrapper 脚本中用eval command $*替代$*直接执行CI 环境无交互终端GitHub Actions 中 Tab 补全无意义在 CI 中禁用补全if [ -t 0 ]; then source (npx ourorg/skill-completion); fi我们最终采用“渐进增强”策略在支持的 shell 中提供完整补全在不支持的环境中降级为npx ourorg/skill-list命令输出所有可用 skill。5.3 Skills 间数据传递的陷阱JSON vs NDJSON 的选型逻辑当一个 skill 的输出要被另一个 skill 消费时选择--json还是--ndjsonNewline-Delimited JSON这是个关键设计决策。JSON 适用场景输出是单一对象结构固定。如/check-pr输出{ status: passed, issues: [] }。优点易读、易 debug、IDE 友好。NDJSON 适用场景输出是流式数据或大量对象。如/extract-todos可能输出 200 个 TODO每个一行{file:src/App.tsx,line:42,text:TODO: add error boundary} {file:src/utils/api.ts,line:18,text:TODO: handle network timeout}陷阱在于jq默认解析 JSON对 NDJSON 需加-r参数。我们规定所有 skill 默认输出 JSON当 skill 明确设计为“流式处理器”如/filter-files,/transform-json才支持--ndjson在 workflow 编排中input: ${changes}自动识别 NDJSON 格式并按行解析。实操心得我们曾用 JSON 输出 500 个文件路径结果jq .files[] | select(.size 10000)耗时 1.2s改用 NDJSON 后相同查询耗时 0.08s。数据量 100 条时NDJSON 性能优势碾压 JSON。5.4 权限问题如何让 Skill 安全地访问敏感资源Skills 经常需要读取.env、调用私有 API、写入node_modules。粗暴的chmod 777或sudo npx是毒药。我们的权限模型分三层进程级沙箱所有npx执行的 skill 默认以当前用户权限运行无法访问/root或其他用户家目录。文件系统白名单skill 的package.json中声明allowedPaths: [src/, public/, .github/]执行器在fs.readFile前检查路径前缀。API Token 隔离敏感 token如 GitHub Token不通过 CLI 参数传递而是从process.env.GITHUB_TOKEN读取CI 环境从~/.config/ourorg/skills.json加密读取本地环境文件权限600从Keychain/Windows Credential Manager读取macOS/Windows例如/create-github-issueskill 的代码import { getSecret } from ourorg/skill-utils; export async function createIssue(title: string) { const token await getSecret(github_token); // 自动选择最佳存储后端 const res await fetch(https://api.github.com/repos/ourorg/app/issues, { method: POST, headers: { Authorization: token ${token} }, body: JSON.stringify({ title }) }); }这个getSecret库会按顺序尝试环境变量 → 加密配置文件 → 系统凭据管理器。用户无需关心存储位置只需配置一次。5.5 性能优化从 3.2s 到 0.4s 的 CLI 启动加速实践初始版本的/gen-component平均启动耗时 3.2sMacBook Pro M1用户抱怨“比手动创建文件还慢”。我们用console.time()逐段测量发现瓶颈在import { parseArgs } from node:utilESM 动态导入耗时 1.1sreadPackageJson()同步读取package.json耗时 0.8srenderTemplate()EJS 编译模板耗时 0.6s。优化方案替换parseArgs改用轻量minimist3KB vsnode:util120KB耗时降至 0.05s缓存package.json首次读取后存入Map后续调用直接返回耗时 0s预编译模板构建时用ejs.compile()生成 JS 函数运行时直接调用耗时 0.02s懒加载 AdaptergetAdapter()只在需要时import()避免一次性加载所有框架代码。最终启动耗时稳定在 0.38s ± 0.05s用户感知为“瞬时响应”。CLI 的性能阈值是 500ms超过这个值用户就会产生“卡顿”心理。6. Skills 生态扩展从单点工具到团队智能中枢6.1 技能市场Skills Marketplace的冷启动策略我们没有一开始就建一个中心化 marketplace而是采用“分布式注册表”模式每个 skill 在package.json中声明 skill: { category:
阅读完成 · 觉得有帮助?
咨询建站