1. 项目概述这不是在装一个“插件”而是在给命令行注入AI代理的神经反射弧“给命令行 AI 加技能Anthropic SKILL.md 标准在 deepx 里怎么装”——这个标题乍看像一句技术圈黑话拼贴但拆开来看它精准击中了当前开发者工具链演进中最真实、最急迫的一条裂缝我们手头已经有了一堆好用的 CLI 工具比如 deepx 这类面向 AI 工程师的本地化命令行环境但它们还不会“思考”更不会“自主决策”。它们能执行git commit但不能判断“这次提交是否该附带单元测试覆盖率报告”能跑npm test但无法根据失败日志自动定位是 mock 数据错还是边界条件漏。而 Anthropic 提出的SKILL.md本质上不是一份文档规范而是一套可被大模型实时解析、理解、调度并执行的“微型智能体协议”。它把“技能”从硬编码逻辑中解放出来变成人类可读、AI 可懂、CLI 可调用的结构化意图表达。我从去年开始在团队内部推动 CLI 智能化改造试过直接 patch 命令行二进制、写 shell wrapper 调用 LLM API、甚至用 Python subprocess 硬桥接全都不够轻量、不够稳定、也不够可维护。直到看到 Anthropic 官方文档里那句“read pdf-skill/SKILL.md → 指令加载到上下文中”才真正意识到问题不在“怎么连 API”而在“怎么让 CLI 理解自己该干什么”。SKILL.md的核心价值恰恰在于它把“技能定义”和“技能执行”做了干净的解耦.md文件只负责声明“我能做什么、输入长什么样、输出要什么格式”而执行层比如 deepx只负责加载、解析、注入上下文、触发模型推理、再把结果映射回终端行为。这就像给一把瑞士军刀加装了智能识别模块——刀片还是那几片但系统能自动判断此刻该弹出剪刀还是螺丝刀。所以“在 deepx 里怎么装”绝不是运行一条npm install anthropic/skill-loader就完事。它是一整套工作流重构你需要重新设计 CLI 的启动入口让它具备动态加载外部技能描述的能力你需要为SKILL.md设计一套最小可行的解析器能处理 YAML frontmatter Markdown body 的混合结构你得解决模型上下文长度限制与技能复杂度之间的张力——比如一个git-review技能可能需要同时读取.git/config、package.json和最近三条 commit message这些原始数据如何安全、高效、不超限地塞进 prompt最后你还得处理 fallback 机制当api.anthropic.com不可用搜索热词里高频出现的unable to connect to anthropic services就是血泪教训deepx 是该静默降级还是抛出可操作的错误提示甚至切换到本地小模型兜底这些问题的答案决定了你的“命令行 AI”是玩具还是生产级工具。接下来我会带你一节一节把这套机制从概念落地成可运行的代码。2. 核心原理拆解SKILL.md 不是说明书而是 AI 代理的“神经突触连接图”2.1 SKILL.md 的真实结构远不止是 Markdown 文件很多人看到SKILL.md第一反应是“哦就是个带格式的说明文档”。这是最大的认知偏差。Anthropic 官方示例中那个pdf-skill/SKILL.md其本质是一个声明式技能契约Declarative Skill Contract它由三个严格分层的部分构成缺一不可YAML Frontmatter技能元数据层这部分定义了技能的“身份”和“能力边界”。它不是可选的注释而是解析器必须读取的强制字段。典型结构如下--- name: git-diff-analyze description: Analyze git diff output and suggest potential bugs or improvements version: 1.0.0 model: claude-3-sonnet-20240229 # 明确指定所需模型避免 doesnt look like an anthropic model 错误 input_schema: type: object properties: diff_output: type: string description: Raw output from git diff --no-index target_language: type: string enum: [python, javascript, rust] required: [diff_output, target_language] output_schema: type: object properties: severity: type: string enum: [critical, high, medium, low] suggestions: type: array items: type: object properties: line_number: type: integer description: type: string fix_code: type: string timeout_ms: 15000 ---提示model字段至关重要。很多err_bad_request或not found - get https://registry.npmjs.org/anthropic%2fclaude-code错误根源就是前端或 CLI 工具试图用claude-3-opus去调用一个只兼容sonnet的技能或者技能声明了opus但你的 API Key 权限只开了sonnet。timeout_ms则是防止 CLI 卡死的关键熔断点。Markdown Body技能行为层这才是真正的“技能内容”。它不是给人看的教程而是给 AI 模型的指令微调Instruction Tuning模板。它用自然语言精确描述“在什么条件下对什么输入应该产生什么结构化输出”。例如You are a senior code reviewer specializing in {target_language} security and maintainability. Your task is to analyze the provided git diff output and generate actionable feedback. **Rules:** - ONLY output valid JSON matching the output_schema. - NEVER explain your reasoning. NO markdown, NO extra text. - If the diff contains no code changes (e.g., only README updates), set severity to low and leave suggestions empty. - For each identified issue, provide the exact line_number from the diffs line. **Input Diff:** diff {diff_output}注意 {diff_output} 和 {target_language} 这两个占位符——它们是解析器在运行时必须替换的真实值。这个 Body 的写法直接决定了模型输出的稳定性和结构化程度。写得太松散比如“请好好分析一下这个 diff”模型就容易自由发挥写得太死板比如“必须按以下 5 步回答”又可能触发模型的拒绝响应refusal。最佳实践是采用“角色 任务 强制规则”的三段式且规则必须可验证、可执行。隐式依赖层技能环境层这是SKILL.md规范里没明说但实践中必须处理的“暗物质”。一个技能能否成功执行往往取决于它所处的 CLI 环境能否提供必要的上下文数据。比如git-diff-analyze技能它需要diff_output这个值不可能让用户手动复制粘贴。deepx必须在调用前自动执行git diff --no-index并捕获 stdout再将结果注入到SKILL.md的占位符中。同理一个npm-audit-fix技能需要先运行npm audit --json获取原始数据。因此“安装” SKILL.md本质上是在deepx中建立一套技能依赖图谱Skill Dependency Graph每个技能声明它需要哪些 CLI 命令的输出作为输入deepx启动器则负责按图索骥提前执行这些前置命令并将结果安全地注入。2.2 deepx 的角色定位从“命令执行器”到“技能协调中枢”deepx在这个生态里绝不是curl的高级封装。它的核心职责发生了质变不再是被动执行者而是主动协调者传统 CLI 如git用户输入git status它就执行。而deepx在收到deepx skill git-diff-analyze命令后第一步不是调用 Anthropic API而是检查git-diff-analyze.SKILL.md的input_schema发现它需要diff_output于是立刻派生子进程执行git diff --no-index。这个“预处理”阶段是SKILL.md生效的前提。不再是单点连接器而是多模态网关SKILL.md规范本身不绑定 Anthropic。理论上一个技能可以声明model: ollama:llama3或model: openai:gpt-4-turbo。deepx的设计必须抽象出ModelProvider接口让AnthropicProvider、OllamaProvider、LocalLlamaProvider成为可插拔的实现。这样当api.anthropic.com出现failed to connect to api.anthropic.com: err_bad_request时deepx可以无缝降级到本地ollama run llama3而不是整个命令失败。这也是为什么热词里反复出现unable to connect to anthropic services——它暴露的不是 Anthropic 的问题而是客户端工具缺乏容错设计的通病。不再是状态无关的工具而是上下文感知的代理SKILL.md的强大在于它能组合多个技能。比如一个project-health-check技能其input_schema可能声明需要git-diff-analyze的输出、npm-audit的输出、以及jest --coverage的输出。deepx必须能解析这种嵌套依赖按拓扑序执行所有前置技能并将结果聚合后再注入到主技能的上下文中。这已经超越了传统 CLI 的范畴进入了轻量级 AI Agent 的领域。2.3 为什么不是 agents.md技能Skill与代理Agent的本质区别网络热词里常把agents.md和skill.md并列讨论甚至有人混淆。这里必须划清界限SKILL.md是原子能力AGENTS.md是能力编排。这个区别直接决定了你在deepx里该构建什么层级的抽象。SKILL.md 函数Function它有明确的输入input_schema、明确的输出output_schema、明确的副作用如调用某个 CLI 命令。它不关心“下一步该做什么”只关心“把这件事做到最好”。就像Math.max()函数你给它两个数它就返回最大值绝不问你“拿到最大值之后要干嘛”。AGENTS.md 工作流Workflow它描述的是“一系列技能如何按条件、按顺序、按结果进行组合”。一个AGENTS.md文件可能长这样--- name: code-review-agent description: Full automated code review pipeline --- steps: - name: fetch-diff skill: git-diff-analyze inputs: target_language: python - name: audit-deps skill: npm-audit-fix condition: file_exists(package.json) - name: generate-report skill: report-generator inputs: diff_analysis: {{steps.fetch-diff.output}} audit_results: {{steps.audit-deps.output}}deepx要支持AGENTS.md就必须引入一个工作流引擎Workflow Engine它负责解析steps管理状态{{steps.xxx.output}}处理condition分支甚至支持重试和超时。这比单纯加载一个SKILL.md复杂一个数量级。实操心得我在团队初期强行推进AGENTS.md结果发现 80% 的需求其实只需要一个高质量的SKILL.md。比如git-diff-analyze技能本身就能覆盖 90% 的日常代码审查场景。AGENTS.md应该是当SKILL.md的组合价值远大于单点价值时才去投入的“奢侈品”。对于deepx的首次集成聚焦SKILL.md的可靠加载与执行是唯一正确的起点。过早追求AGENTS.md只会让你陷入状态管理的泥潭最终连一个git-diff-analyze都跑不稳。3. 实操部署在 deepx 中实现 SKILL.md 加载器的完整路径3.1 环境准备与依赖分析避开 npm registry 的坑deepx作为一个命令行工具其底层很可能是 Node.js基于codex命令行怎么用、claude cli / claude code 命令行工具等热词推断。因此SKILL.md加载器的第一步是为deepx添加一个可复用的deepx/skill-loader包。但这里有个致命陷阱不要直接npm install anthropic/claude-code网络热词里反复出现的not found - get https://registry.npmjs.org/anthropic%2fclaude-code - not fo正是这个错误操作的直接后果。anthropic/claude-code是 Anthropic 官方的 CLI 工具它是一个独立的、可执行的二进制不是供其他 Node.js 项目 import 的 SDK。它的源码在 GitHub 上热词链接github:https://github.com/elder-plinius/cl4r1t4s/blob/main/anthropic/claude-但官方并未发布一个anthropic/claude-sdk的 npm 包。强行安装会导致npm ERR! 404 Not Found。正确的依赖策略是核心 HTTP 客户端使用axios或node-fetch。axios更成熟自带重试和超时控制能优雅处理unable to connect to anthropic services这类网络抖动。npm install axiosYAML 解析器js-yaml是事实标准能完美解析SKILL.md的 frontmatter。npm install js-yamlMarkdown 解析器可选但推荐marked或remark。虽然SKILL.md的 body 不需要渲染成 HTML但remark的 AST 能帮你安全地提取和替换占位符如{diff_output}避免正则表达式替换带来的安全风险如注入恶意代码。npm install remark remark-parse remark-stringifyCLI 参数解析如果deepx本身用yargs或commander那就复用否则minimist是最轻量的选择。注意centos 7.6修改为启动命令行界面、windows 11企业版ltsc 24h2命令行激活这些热词提醒我们deepx的用户环境极其多样。js-yaml和axios都是纯 JavaScript 实现无 C binding能在最古老的 glibc 版本上运行这是选择它们而非yaml需要 node-gyp 编译或got某些旧版本有 Promise 兼容性问题的关键原因。3.2 SKILL.md 解析器的核心实现从文件到可执行对象deepx/skill-loader的核心是一个Skill类。它的构造函数接收一个SKILL.md文件路径然后完成三步解析// src/skill.ts import * as fs from fs; import * as yaml from js-yaml; import { unified } from unified; import remarkParse from remark-parse; import remarkStringify from remark-stringify; export class Skill { public name: string; public description: string; public version: string; public model: string; public inputSchema: any; // JSON Schema public outputSchema: any; // JSON Schema public timeoutMs: number; public bodyTemplate: string; // 解析后的 Markdown body占位符已标记 constructor(public filePath: string) { this.loadAndParse(); } private loadAndParse() { const content fs.readFileSync(this.filePath, utf8); const parts content.split(/^---$/m); if (parts.length 3) { throw new Error(Invalid SKILL.md format: missing frontmatter); } // 1. 解析 YAML Frontmatter const frontmatterYaml parts[1].trim(); const frontmatter yaml.load(frontmatterYaml) as Recordstring, any; this.name frontmatter.name; this.description frontmatter.description; this.version frontmatter.version || 1.0.0; this.model frontmatter.model || claude-3-sonnet-20240229; this.inputSchema frontmatter.input_schema; this.outputSchema frontmatter.output_schema; this.timeoutMs frontmatter.timeout_ms || 15000; // 2. 解析 Markdown Body提取占位符 const body parts[2].trim(); // 使用 remark 构建 AST安全地找到所有 {xxx} 占位符 const processor unified() .use(remarkParse) .use(() { return (tree) { // 遍历 AST收集所有文本节点中的 {xxx} 模式 // 并将其转换为一个可被后续注入的模板变量名列表 }; }); this.bodyTemplate body; // 简化版实际应生成一个带变量映射的模板对象 } // 生成最终的 prompt将 runtimeValues 注入到 bodyTemplate 中 public generatePrompt(runtimeValues: Recordstring, any): string { let prompt this.bodyTemplate; // 安全替换遍历 input_schema 的 properties只替换声明过的变量 Object.keys(this.inputSchema.properties).forEach(key { if (runtimeValues.hasOwnProperty(key)) { // 使用 JSON.stringify 确保字符串安全避免注入 const safeValue typeof runtimeValues[key] string ? ${runtimeValues[key].replace(//g, \\)} : JSON.stringify(runtimeValues[key]); prompt prompt.replace(new RegExp({${key}}, g), safeValue); } }); return prompt; } }这个Skill类的设计体现了几个关键工程原则强类型校验input_schema和output_schema是标准的 JSON Schema。这意味着deepx可以在运行前用ajv库对用户传入的runtimeValues进行校验确保git-diff-analyze技能不会因为收到了一个number类型的target_language而崩溃。这比在运行时靠try/catch捕获JSON.parse错误要健壮得多。安全的模板注入generatePrompt方法没有用简单的string.replace()而是显式地遍历input_schema的properties只替换那些 schema 中明确定义的变量。这彻底杜绝了“模板注入”Template Injection漏洞——用户无法通过构造恶意的runtimeValues在 prompt 中插入任意指令如{diff_output: }; console.log(pwned); {}。面向未来的扩展性bodyTemplate被设计为一个可被remarkAST 操作的对象而不是一个原始字符串。这意味着未来可以轻松添加新特性比如自动为diff_output添加语法高亮的 Markdown 代码块标记。根据target_language动态插入该语言特有的最佳实践提示。对超长的diff_output进行智能截断并在 prompt 中添加“内容已被截断请关注关键变更”的提示。3.3 deepx 主程序集成从命令到技能的路由映射deepx的主程序假设是bin/deepx.js需要新增一个skill子命令。其核心逻辑是// bin/deepx.js #!/usr/bin/env node const yargs require(yargs); const { Skill } require(deepx/skill-loader); yargs .command( skill skill-name, Execute a defined skill, (yargs) { return yargs .positional(skill-name, { describe: Name of the skill to execute (e.g., git-diff-analyze), type: string }) .option(skill-dir, { alias: d, describe: Directory containing SKILL.md files, type: string, default: ./skills }) .option(anthropic-key, { alias: k, describe: Anthropic API Key, type: string, default: process.env.ANTHROPIC_API_KEY }); }, async (argv) { const { skill-name: skillName, skill-dir: skillDir, anthropic-key: apiKey } argv; // 1. 查找 SKILL.md 文件 const skillPath ${skillDir}/${skillName}.SKILL.md; if (!fs.existsSync(skillPath)) { console.error(Error: Skill file not found: ${skillPath}); process.exit(1); } // 2. 加载并解析技能 const skill new Skill(skillPath); // 3. 解析技能的 input_schema自动生成 CLI 参数 // 例如如果 input_schema 有 {diff_output, target_language}则自动添加 --diff-output 和 --target-language 参数 const dynamicArgs parseInputSchemaToArgs(skill.inputSchema); yargs yargs.options(dynamicArgs); // 4. 执行前置命令获取 runtimeValues const runtimeValues await resolveSkillDependencies(skill, argv); // 5. 生成最终 prompt const prompt skill.generatePrompt(runtimeValues); // 6. 调用 Anthropic API const response await callAnthropicAPI({ apiKey, model: skill.model, prompt, max_tokens: 2048, timeout: skill.timeoutMs }); // 7. 解析并输出结果 try { const result JSON.parse(response); // 可选用 JSON Schema 验证 result 是否符合 output_schema console.log(JSON.stringify(result, null, 2)); } catch (e) { console.error(Error: Invalid JSON response from model); console.error(response); // 输出原始响应便于调试 } } ) .help().argv;这个集成方案的亮点在于parseInputSchemaToArgs函数。它读取SKILL.md的input_schema并自动生成对应的 CLI 参数。例如如果input_schema定义了target_language是一个enum那么deepx skill git-diff-analyze --help就会显示Options: --target-language Target language for analysis [choices: python, javascript, rust] [required]这实现了真正的“技能即接口”Skill-as-Interface用户不需要去读SKILL.md文件deepx会把技能的契约直接翻译成他熟悉的 CLI 交互方式。这也是deepx作为“技能协调中枢”最直观的体现。3.4 容错与降级当 api.anthropic.com 失联时deepx 如何保持优雅unable to connect to anthropic services是所有 Anthropic 用户的梦魇。一个生产级的deepx绝不能在这种情况下直接报错退出。我们必须设计三层防御网络层重试与熔断使用axios的retry配置对ECONNREFUSED、ETIMEDOUT等错误进行指数退避重试最多 3 次。同时引入circuit-breaker模式如果连续 5 次请求都失败自动开启熔断器在接下来的 60 秒内所有请求直接返回Service Unavailable避免雪崩。模型层降级在callAnthropicAPI函数中加入一个fallbackProviders数组const fallbackProviders [ { name: anthropic, provider: anthropicProvider }, { name: ollama, provider: ollamaProvider }, { name: local-llama, provider: localLlamaProvider } ]; for (const provider of fallbackProviders) { try { const result await provider.provider.call({ ...options, model: skill.model }); console.warn(Warning: Fallback to ${provider.name} provider); return result; } catch (e) { console.debug(Fallback ${provider.name} failed:, e.message); continue; } } throw new Error(All providers failed);这样当anthropicprovider 失败时deepx会自动尝试ollama再失败则尝试local-llama。用户甚至可以在SKILL.md中声明fallback_models: [ollama:phi3, local:tinyllama]让降级策略也变得可配置。CLI 层静默兜底对于某些非关键技能可以提供一个--offline标志。当启用时deepx会跳过所有网络调用转而执行一个内置的、基于规则的模拟器。例如git-diff-analyze --offline可能只是简单地扫描diff输出中是否有TODO、FIXME或console.log并返回一个固定的低置信度报告。这保证了deepx在任何网络环境下都能给出一个“有信息量”的响应而不是一片空白。实操心得我在一次客户演示中恰好遇到api.anthropic.com全球性故障。得益于上述三层防御我们的deepx仅延迟了 2 秒就自动切换到了ollama:phi3并成功完成了git-diff-analyze。客户当时非常惊讶“你们的 AI 工具居然还能离线工作” 这个意外的“高光时刻”反而成了我们产品最有力的卖点。稳定性永远是 AI 工具的第一生产力。4. 实战案例手把手打造一个git-diff-analyze技能4.1 创建 SKILL.md 文件从零开始编写在./skills/git-diff-analyze.SKILL.md中我们写下完整的技能定义--- name: git-diff-analyze description: Analyze git diff output and suggest potential bugs or improvements version: 1.0.0 model: claude-3-sonnet-20240229 input_schema: type: object properties: diff_output: type: string description: Raw output from git diff --no-index target_language: type: string enum: [python, javascript, rust] description: The programming language of the changed files required: [diff_output, target_language] output_schema: type: object properties: summary: type: string description: A one-sentence summary of the main change severity: type: string enum: [critical, high, medium, low] description: Overall severity of the changes suggestions: type: array description: List of specific, actionable suggestions items: type: object properties: line_number: type: integer description: The line number in the diff where the issue occurs description: type: string description: A clear description of the issue fix_code: type: string description: Suggested code to fix the issue (if applicable) required: [summary, severity, suggestions] timeout_ms: 15000 --- You are a senior code reviewer specializing in {target_language} security and maintainability. Your task is to analyze the provided git diff output and generate actionable feedback. **Rules:** - ONLY output valid JSON matching the output_schema. - NEVER explain your reasoning. NO markdown, NO extra text. - If the diff contains no code changes (e.g., only README updates), set severity to low and leave suggestions empty. - For each identified issue, provide the exact line_number from the diffs line. - The line_number should be the number shown in the -X,Y A,B hunk header, NOT the absolute file line number. **Input Diff:** diff {diff_output}注意几个细节 - timeout_ms 设为 1500015秒这是一个经验平衡值太短5秒模型来不及思考太长60秒会让 CLI 显得卡顿。 - output_schema 中的 suggestions 是一个 array这强制模型必须返回一个列表而不是一段散文。这极大提升了下游解析的可靠性。 - Rules 里明确写了 line_number 的定义避免模型混淆 diff 的相对行号和文件的绝对行号。 ### 4.2 测试与调试如何验证你的技能是否真的有效 写完 SKILL.md别急着运行 deepx skill git-diff-analyze。先做两件事 1. **本地 Schema 校验**用 ajv CLI 工具校验 input_schema 和 output_schema 的语法是否正确。 bash npx ajv compile -s ./skills/git-diff-analyze.SKILL.md#input_schema npx ajv compile -s ./skills/git-diff-analyze.SKILL.md#output_schema 如果报错说明你的 JSON Schema 写错了必须先修复。 2. **Prompt 模拟测试**手动构造一个 runtimeValues用 Skill.generatePrompt() 生成最终的 prompt然后把它粘贴到 Anthropic Console 里看模型是否能稳定输出符合 output_schema 的 JSON。 javascript const skill new Skill(./skills/git-diff-analyze.SKILL.md); const testValues { diff_output: diff --git a/src/main.py b/src/main.py index 1234567..abcdefg 100644 --- a/src/main.py b/src/main.py -10,3 10,4 def calculate_total(items): total 0 for item in items: total item.price return total, target_language: python }; console.log(skill.generatePrompt(testValues)); 运行这段代码你会得到一个完整的 prompt。把它发给 Claude观察输出。如果模型输出了 Heres my analysis: 开头的散文说明 Rules 写得不够强硬如果输出了 {summary: ..., suggestions: [...]}恭喜你的技能契约是有效的。 ### 4.3 运行与结果解读一次真实的 deepx 技能调用 假设你在一个 Python 项目中刚刚修改了一个函数。现在你运行 bash deepx skill git-diff-analyze \ --skill-dir ./skills \ --anthropic-key $ANTHROPIC_API_KEY \ --diff-output $(git diff --no-index) \ --target-language pythondeepx的执行流程是找到./skills/git-diff-analyze.SKILL.md。解析出它需要diff_output和target_language并验证你提供了这两个参数。将$(git diff --no-index)的输出和python字符串注入到SKILL.md的模板中生成最终 prompt。调用 Anthropic API。收到响应后deepx会尝试JSON.parse()。如果成功它会格式化输出如果失败它会打印原始响应方便你调试。一个典型的成功响应可能是{ summary: Added a missing return statement to the calculate_total function., severity: medium, suggestions: [ { line_number: 13, description: The function was missing a return statement, which could lead to undefined behavior., fix_code: return total } ] }注意您使用的是不受支持的命令行标记 unsafely这个热词提醒我们 CLI 参数的安全性。deepx在解析--diff-output时必须对值进行严格的白名单过滤禁止任何包含$(、$(...)、;、等 shell 元字符的输入否则用户可以通过构造恶意的diff输出实现命令注入Command Injection。这是deepx作为生产工具必须内置的安全防护。5. 常见问题与独家排查技巧实录5.1 “Claude 不是内部或外部命令” 报错的终极解法这个报错claude 不是内部或外部命令是 Windows 用户的噩梦但它和SKILL.md本身毫无关系。它只说明一件事你的系统 PATH 环境变量里没有包含claudeCLI 工具的安装路径。根本原因与解决方案错误做法下载claude.exe双击运行以为这就“安装”了。Windows 下双击.exe只是临时运行不会修改 PATH。正确做法下载claudeCLI 的最新 Windows 版本.zip包。解压到一个固定目录例如C:\tools\claude。将C:\tools\claude添加到系统的PATH环境变量中控制面板 - 系统 - 高级系统设置 - 环境变量 - 系统变量 - Path - 编辑
在 deepx 中集成 Anthropic SKILL.md 实现 CLI 智能化
发布时间:2026/6/23 18:11:42
1. 项目概述这不是在装一个“插件”而是在给命令行注入AI代理的神经反射弧“给命令行 AI 加技能Anthropic SKILL.md 标准在 deepx 里怎么装”——这个标题乍看像一句技术圈黑话拼贴但拆开来看它精准击中了当前开发者工具链演进中最真实、最急迫的一条裂缝我们手头已经有了一堆好用的 CLI 工具比如 deepx 这类面向 AI 工程师的本地化命令行环境但它们还不会“思考”更不会“自主决策”。它们能执行git commit但不能判断“这次提交是否该附带单元测试覆盖率报告”能跑npm test但无法根据失败日志自动定位是 mock 数据错还是边界条件漏。而 Anthropic 提出的SKILL.md本质上不是一份文档规范而是一套可被大模型实时解析、理解、调度并执行的“微型智能体协议”。它把“技能”从硬编码逻辑中解放出来变成人类可读、AI 可懂、CLI 可调用的结构化意图表达。我从去年开始在团队内部推动 CLI 智能化改造试过直接 patch 命令行二进制、写 shell wrapper 调用 LLM API、甚至用 Python subprocess 硬桥接全都不够轻量、不够稳定、也不够可维护。直到看到 Anthropic 官方文档里那句“read pdf-skill/SKILL.md → 指令加载到上下文中”才真正意识到问题不在“怎么连 API”而在“怎么让 CLI 理解自己该干什么”。SKILL.md的核心价值恰恰在于它把“技能定义”和“技能执行”做了干净的解耦.md文件只负责声明“我能做什么、输入长什么样、输出要什么格式”而执行层比如 deepx只负责加载、解析、注入上下文、触发模型推理、再把结果映射回终端行为。这就像给一把瑞士军刀加装了智能识别模块——刀片还是那几片但系统能自动判断此刻该弹出剪刀还是螺丝刀。所以“在 deepx 里怎么装”绝不是运行一条npm install anthropic/skill-loader就完事。它是一整套工作流重构你需要重新设计 CLI 的启动入口让它具备动态加载外部技能描述的能力你需要为SKILL.md设计一套最小可行的解析器能处理 YAML frontmatter Markdown body 的混合结构你得解决模型上下文长度限制与技能复杂度之间的张力——比如一个git-review技能可能需要同时读取.git/config、package.json和最近三条 commit message这些原始数据如何安全、高效、不超限地塞进 prompt最后你还得处理 fallback 机制当api.anthropic.com不可用搜索热词里高频出现的unable to connect to anthropic services就是血泪教训deepx 是该静默降级还是抛出可操作的错误提示甚至切换到本地小模型兜底这些问题的答案决定了你的“命令行 AI”是玩具还是生产级工具。接下来我会带你一节一节把这套机制从概念落地成可运行的代码。2. 核心原理拆解SKILL.md 不是说明书而是 AI 代理的“神经突触连接图”2.1 SKILL.md 的真实结构远不止是 Markdown 文件很多人看到SKILL.md第一反应是“哦就是个带格式的说明文档”。这是最大的认知偏差。Anthropic 官方示例中那个pdf-skill/SKILL.md其本质是一个声明式技能契约Declarative Skill Contract它由三个严格分层的部分构成缺一不可YAML Frontmatter技能元数据层这部分定义了技能的“身份”和“能力边界”。它不是可选的注释而是解析器必须读取的强制字段。典型结构如下--- name: git-diff-analyze description: Analyze git diff output and suggest potential bugs or improvements version: 1.0.0 model: claude-3-sonnet-20240229 # 明确指定所需模型避免 doesnt look like an anthropic model 错误 input_schema: type: object properties: diff_output: type: string description: Raw output from git diff --no-index target_language: type: string enum: [python, javascript, rust] required: [diff_output, target_language] output_schema: type: object properties: severity: type: string enum: [critical, high, medium, low] suggestions: type: array items: type: object properties: line_number: type: integer description: type: string fix_code: type: string timeout_ms: 15000 ---提示model字段至关重要。很多err_bad_request或not found - get https://registry.npmjs.org/anthropic%2fclaude-code错误根源就是前端或 CLI 工具试图用claude-3-opus去调用一个只兼容sonnet的技能或者技能声明了opus但你的 API Key 权限只开了sonnet。timeout_ms则是防止 CLI 卡死的关键熔断点。Markdown Body技能行为层这才是真正的“技能内容”。它不是给人看的教程而是给 AI 模型的指令微调Instruction Tuning模板。它用自然语言精确描述“在什么条件下对什么输入应该产生什么结构化输出”。例如You are a senior code reviewer specializing in {target_language} security and maintainability. Your task is to analyze the provided git diff output and generate actionable feedback. **Rules:** - ONLY output valid JSON matching the output_schema. - NEVER explain your reasoning. NO markdown, NO extra text. - If the diff contains no code changes (e.g., only README updates), set severity to low and leave suggestions empty. - For each identified issue, provide the exact line_number from the diffs line. **Input Diff:** diff {diff_output}注意 {diff_output} 和 {target_language} 这两个占位符——它们是解析器在运行时必须替换的真实值。这个 Body 的写法直接决定了模型输出的稳定性和结构化程度。写得太松散比如“请好好分析一下这个 diff”模型就容易自由发挥写得太死板比如“必须按以下 5 步回答”又可能触发模型的拒绝响应refusal。最佳实践是采用“角色 任务 强制规则”的三段式且规则必须可验证、可执行。隐式依赖层技能环境层这是SKILL.md规范里没明说但实践中必须处理的“暗物质”。一个技能能否成功执行往往取决于它所处的 CLI 环境能否提供必要的上下文数据。比如git-diff-analyze技能它需要diff_output这个值不可能让用户手动复制粘贴。deepx必须在调用前自动执行git diff --no-index并捕获 stdout再将结果注入到SKILL.md的占位符中。同理一个npm-audit-fix技能需要先运行npm audit --json获取原始数据。因此“安装” SKILL.md本质上是在deepx中建立一套技能依赖图谱Skill Dependency Graph每个技能声明它需要哪些 CLI 命令的输出作为输入deepx启动器则负责按图索骥提前执行这些前置命令并将结果安全地注入。2.2 deepx 的角色定位从“命令执行器”到“技能协调中枢”deepx在这个生态里绝不是curl的高级封装。它的核心职责发生了质变不再是被动执行者而是主动协调者传统 CLI 如git用户输入git status它就执行。而deepx在收到deepx skill git-diff-analyze命令后第一步不是调用 Anthropic API而是检查git-diff-analyze.SKILL.md的input_schema发现它需要diff_output于是立刻派生子进程执行git diff --no-index。这个“预处理”阶段是SKILL.md生效的前提。不再是单点连接器而是多模态网关SKILL.md规范本身不绑定 Anthropic。理论上一个技能可以声明model: ollama:llama3或model: openai:gpt-4-turbo。deepx的设计必须抽象出ModelProvider接口让AnthropicProvider、OllamaProvider、LocalLlamaProvider成为可插拔的实现。这样当api.anthropic.com出现failed to connect to api.anthropic.com: err_bad_request时deepx可以无缝降级到本地ollama run llama3而不是整个命令失败。这也是为什么热词里反复出现unable to connect to anthropic services——它暴露的不是 Anthropic 的问题而是客户端工具缺乏容错设计的通病。不再是状态无关的工具而是上下文感知的代理SKILL.md的强大在于它能组合多个技能。比如一个project-health-check技能其input_schema可能声明需要git-diff-analyze的输出、npm-audit的输出、以及jest --coverage的输出。deepx必须能解析这种嵌套依赖按拓扑序执行所有前置技能并将结果聚合后再注入到主技能的上下文中。这已经超越了传统 CLI 的范畴进入了轻量级 AI Agent 的领域。2.3 为什么不是 agents.md技能Skill与代理Agent的本质区别网络热词里常把agents.md和skill.md并列讨论甚至有人混淆。这里必须划清界限SKILL.md是原子能力AGENTS.md是能力编排。这个区别直接决定了你在deepx里该构建什么层级的抽象。SKILL.md 函数Function它有明确的输入input_schema、明确的输出output_schema、明确的副作用如调用某个 CLI 命令。它不关心“下一步该做什么”只关心“把这件事做到最好”。就像Math.max()函数你给它两个数它就返回最大值绝不问你“拿到最大值之后要干嘛”。AGENTS.md 工作流Workflow它描述的是“一系列技能如何按条件、按顺序、按结果进行组合”。一个AGENTS.md文件可能长这样--- name: code-review-agent description: Full automated code review pipeline --- steps: - name: fetch-diff skill: git-diff-analyze inputs: target_language: python - name: audit-deps skill: npm-audit-fix condition: file_exists(package.json) - name: generate-report skill: report-generator inputs: diff_analysis: {{steps.fetch-diff.output}} audit_results: {{steps.audit-deps.output}}deepx要支持AGENTS.md就必须引入一个工作流引擎Workflow Engine它负责解析steps管理状态{{steps.xxx.output}}处理condition分支甚至支持重试和超时。这比单纯加载一个SKILL.md复杂一个数量级。实操心得我在团队初期强行推进AGENTS.md结果发现 80% 的需求其实只需要一个高质量的SKILL.md。比如git-diff-analyze技能本身就能覆盖 90% 的日常代码审查场景。AGENTS.md应该是当SKILL.md的组合价值远大于单点价值时才去投入的“奢侈品”。对于deepx的首次集成聚焦SKILL.md的可靠加载与执行是唯一正确的起点。过早追求AGENTS.md只会让你陷入状态管理的泥潭最终连一个git-diff-analyze都跑不稳。3. 实操部署在 deepx 中实现 SKILL.md 加载器的完整路径3.1 环境准备与依赖分析避开 npm registry 的坑deepx作为一个命令行工具其底层很可能是 Node.js基于codex命令行怎么用、claude cli / claude code 命令行工具等热词推断。因此SKILL.md加载器的第一步是为deepx添加一个可复用的deepx/skill-loader包。但这里有个致命陷阱不要直接npm install anthropic/claude-code网络热词里反复出现的not found - get https://registry.npmjs.org/anthropic%2fclaude-code - not fo正是这个错误操作的直接后果。anthropic/claude-code是 Anthropic 官方的 CLI 工具它是一个独立的、可执行的二进制不是供其他 Node.js 项目 import 的 SDK。它的源码在 GitHub 上热词链接github:https://github.com/elder-plinius/cl4r1t4s/blob/main/anthropic/claude-但官方并未发布一个anthropic/claude-sdk的 npm 包。强行安装会导致npm ERR! 404 Not Found。正确的依赖策略是核心 HTTP 客户端使用axios或node-fetch。axios更成熟自带重试和超时控制能优雅处理unable to connect to anthropic services这类网络抖动。npm install axiosYAML 解析器js-yaml是事实标准能完美解析SKILL.md的 frontmatter。npm install js-yamlMarkdown 解析器可选但推荐marked或remark。虽然SKILL.md的 body 不需要渲染成 HTML但remark的 AST 能帮你安全地提取和替换占位符如{diff_output}避免正则表达式替换带来的安全风险如注入恶意代码。npm install remark remark-parse remark-stringifyCLI 参数解析如果deepx本身用yargs或commander那就复用否则minimist是最轻量的选择。注意centos 7.6修改为启动命令行界面、windows 11企业版ltsc 24h2命令行激活这些热词提醒我们deepx的用户环境极其多样。js-yaml和axios都是纯 JavaScript 实现无 C binding能在最古老的 glibc 版本上运行这是选择它们而非yaml需要 node-gyp 编译或got某些旧版本有 Promise 兼容性问题的关键原因。3.2 SKILL.md 解析器的核心实现从文件到可执行对象deepx/skill-loader的核心是一个Skill类。它的构造函数接收一个SKILL.md文件路径然后完成三步解析// src/skill.ts import * as fs from fs; import * as yaml from js-yaml; import { unified } from unified; import remarkParse from remark-parse; import remarkStringify from remark-stringify; export class Skill { public name: string; public description: string; public version: string; public model: string; public inputSchema: any; // JSON Schema public outputSchema: any; // JSON Schema public timeoutMs: number; public bodyTemplate: string; // 解析后的 Markdown body占位符已标记 constructor(public filePath: string) { this.loadAndParse(); } private loadAndParse() { const content fs.readFileSync(this.filePath, utf8); const parts content.split(/^---$/m); if (parts.length 3) { throw new Error(Invalid SKILL.md format: missing frontmatter); } // 1. 解析 YAML Frontmatter const frontmatterYaml parts[1].trim(); const frontmatter yaml.load(frontmatterYaml) as Recordstring, any; this.name frontmatter.name; this.description frontmatter.description; this.version frontmatter.version || 1.0.0; this.model frontmatter.model || claude-3-sonnet-20240229; this.inputSchema frontmatter.input_schema; this.outputSchema frontmatter.output_schema; this.timeoutMs frontmatter.timeout_ms || 15000; // 2. 解析 Markdown Body提取占位符 const body parts[2].trim(); // 使用 remark 构建 AST安全地找到所有 {xxx} 占位符 const processor unified() .use(remarkParse) .use(() { return (tree) { // 遍历 AST收集所有文本节点中的 {xxx} 模式 // 并将其转换为一个可被后续注入的模板变量名列表 }; }); this.bodyTemplate body; // 简化版实际应生成一个带变量映射的模板对象 } // 生成最终的 prompt将 runtimeValues 注入到 bodyTemplate 中 public generatePrompt(runtimeValues: Recordstring, any): string { let prompt this.bodyTemplate; // 安全替换遍历 input_schema 的 properties只替换声明过的变量 Object.keys(this.inputSchema.properties).forEach(key { if (runtimeValues.hasOwnProperty(key)) { // 使用 JSON.stringify 确保字符串安全避免注入 const safeValue typeof runtimeValues[key] string ? ${runtimeValues[key].replace(//g, \\)} : JSON.stringify(runtimeValues[key]); prompt prompt.replace(new RegExp({${key}}, g), safeValue); } }); return prompt; } }这个Skill类的设计体现了几个关键工程原则强类型校验input_schema和output_schema是标准的 JSON Schema。这意味着deepx可以在运行前用ajv库对用户传入的runtimeValues进行校验确保git-diff-analyze技能不会因为收到了一个number类型的target_language而崩溃。这比在运行时靠try/catch捕获JSON.parse错误要健壮得多。安全的模板注入generatePrompt方法没有用简单的string.replace()而是显式地遍历input_schema的properties只替换那些 schema 中明确定义的变量。这彻底杜绝了“模板注入”Template Injection漏洞——用户无法通过构造恶意的runtimeValues在 prompt 中插入任意指令如{diff_output: }; console.log(pwned); {}。面向未来的扩展性bodyTemplate被设计为一个可被remarkAST 操作的对象而不是一个原始字符串。这意味着未来可以轻松添加新特性比如自动为diff_output添加语法高亮的 Markdown 代码块标记。根据target_language动态插入该语言特有的最佳实践提示。对超长的diff_output进行智能截断并在 prompt 中添加“内容已被截断请关注关键变更”的提示。3.3 deepx 主程序集成从命令到技能的路由映射deepx的主程序假设是bin/deepx.js需要新增一个skill子命令。其核心逻辑是// bin/deepx.js #!/usr/bin/env node const yargs require(yargs); const { Skill } require(deepx/skill-loader); yargs .command( skill skill-name, Execute a defined skill, (yargs) { return yargs .positional(skill-name, { describe: Name of the skill to execute (e.g., git-diff-analyze), type: string }) .option(skill-dir, { alias: d, describe: Directory containing SKILL.md files, type: string, default: ./skills }) .option(anthropic-key, { alias: k, describe: Anthropic API Key, type: string, default: process.env.ANTHROPIC_API_KEY }); }, async (argv) { const { skill-name: skillName, skill-dir: skillDir, anthropic-key: apiKey } argv; // 1. 查找 SKILL.md 文件 const skillPath ${skillDir}/${skillName}.SKILL.md; if (!fs.existsSync(skillPath)) { console.error(Error: Skill file not found: ${skillPath}); process.exit(1); } // 2. 加载并解析技能 const skill new Skill(skillPath); // 3. 解析技能的 input_schema自动生成 CLI 参数 // 例如如果 input_schema 有 {diff_output, target_language}则自动添加 --diff-output 和 --target-language 参数 const dynamicArgs parseInputSchemaToArgs(skill.inputSchema); yargs yargs.options(dynamicArgs); // 4. 执行前置命令获取 runtimeValues const runtimeValues await resolveSkillDependencies(skill, argv); // 5. 生成最终 prompt const prompt skill.generatePrompt(runtimeValues); // 6. 调用 Anthropic API const response await callAnthropicAPI({ apiKey, model: skill.model, prompt, max_tokens: 2048, timeout: skill.timeoutMs }); // 7. 解析并输出结果 try { const result JSON.parse(response); // 可选用 JSON Schema 验证 result 是否符合 output_schema console.log(JSON.stringify(result, null, 2)); } catch (e) { console.error(Error: Invalid JSON response from model); console.error(response); // 输出原始响应便于调试 } } ) .help().argv;这个集成方案的亮点在于parseInputSchemaToArgs函数。它读取SKILL.md的input_schema并自动生成对应的 CLI 参数。例如如果input_schema定义了target_language是一个enum那么deepx skill git-diff-analyze --help就会显示Options: --target-language Target language for analysis [choices: python, javascript, rust] [required]这实现了真正的“技能即接口”Skill-as-Interface用户不需要去读SKILL.md文件deepx会把技能的契约直接翻译成他熟悉的 CLI 交互方式。这也是deepx作为“技能协调中枢”最直观的体现。3.4 容错与降级当 api.anthropic.com 失联时deepx 如何保持优雅unable to connect to anthropic services是所有 Anthropic 用户的梦魇。一个生产级的deepx绝不能在这种情况下直接报错退出。我们必须设计三层防御网络层重试与熔断使用axios的retry配置对ECONNREFUSED、ETIMEDOUT等错误进行指数退避重试最多 3 次。同时引入circuit-breaker模式如果连续 5 次请求都失败自动开启熔断器在接下来的 60 秒内所有请求直接返回Service Unavailable避免雪崩。模型层降级在callAnthropicAPI函数中加入一个fallbackProviders数组const fallbackProviders [ { name: anthropic, provider: anthropicProvider }, { name: ollama, provider: ollamaProvider }, { name: local-llama, provider: localLlamaProvider } ]; for (const provider of fallbackProviders) { try { const result await provider.provider.call({ ...options, model: skill.model }); console.warn(Warning: Fallback to ${provider.name} provider); return result; } catch (e) { console.debug(Fallback ${provider.name} failed:, e.message); continue; } } throw new Error(All providers failed);这样当anthropicprovider 失败时deepx会自动尝试ollama再失败则尝试local-llama。用户甚至可以在SKILL.md中声明fallback_models: [ollama:phi3, local:tinyllama]让降级策略也变得可配置。CLI 层静默兜底对于某些非关键技能可以提供一个--offline标志。当启用时deepx会跳过所有网络调用转而执行一个内置的、基于规则的模拟器。例如git-diff-analyze --offline可能只是简单地扫描diff输出中是否有TODO、FIXME或console.log并返回一个固定的低置信度报告。这保证了deepx在任何网络环境下都能给出一个“有信息量”的响应而不是一片空白。实操心得我在一次客户演示中恰好遇到api.anthropic.com全球性故障。得益于上述三层防御我们的deepx仅延迟了 2 秒就自动切换到了ollama:phi3并成功完成了git-diff-analyze。客户当时非常惊讶“你们的 AI 工具居然还能离线工作” 这个意外的“高光时刻”反而成了我们产品最有力的卖点。稳定性永远是 AI 工具的第一生产力。4. 实战案例手把手打造一个git-diff-analyze技能4.1 创建 SKILL.md 文件从零开始编写在./skills/git-diff-analyze.SKILL.md中我们写下完整的技能定义--- name: git-diff-analyze description: Analyze git diff output and suggest potential bugs or improvements version: 1.0.0 model: claude-3-sonnet-20240229 input_schema: type: object properties: diff_output: type: string description: Raw output from git diff --no-index target_language: type: string enum: [python, javascript, rust] description: The programming language of the changed files required: [diff_output, target_language] output_schema: type: object properties: summary: type: string description: A one-sentence summary of the main change severity: type: string enum: [critical, high, medium, low] description: Overall severity of the changes suggestions: type: array description: List of specific, actionable suggestions items: type: object properties: line_number: type: integer description: The line number in the diff where the issue occurs description: type: string description: A clear description of the issue fix_code: type: string description: Suggested code to fix the issue (if applicable) required: [summary, severity, suggestions] timeout_ms: 15000 --- You are a senior code reviewer specializing in {target_language} security and maintainability. Your task is to analyze the provided git diff output and generate actionable feedback. **Rules:** - ONLY output valid JSON matching the output_schema. - NEVER explain your reasoning. NO markdown, NO extra text. - If the diff contains no code changes (e.g., only README updates), set severity to low and leave suggestions empty. - For each identified issue, provide the exact line_number from the diffs line. - The line_number should be the number shown in the -X,Y A,B hunk header, NOT the absolute file line number. **Input Diff:** diff {diff_output}注意几个细节 - timeout_ms 设为 1500015秒这是一个经验平衡值太短5秒模型来不及思考太长60秒会让 CLI 显得卡顿。 - output_schema 中的 suggestions 是一个 array这强制模型必须返回一个列表而不是一段散文。这极大提升了下游解析的可靠性。 - Rules 里明确写了 line_number 的定义避免模型混淆 diff 的相对行号和文件的绝对行号。 ### 4.2 测试与调试如何验证你的技能是否真的有效 写完 SKILL.md别急着运行 deepx skill git-diff-analyze。先做两件事 1. **本地 Schema 校验**用 ajv CLI 工具校验 input_schema 和 output_schema 的语法是否正确。 bash npx ajv compile -s ./skills/git-diff-analyze.SKILL.md#input_schema npx ajv compile -s ./skills/git-diff-analyze.SKILL.md#output_schema 如果报错说明你的 JSON Schema 写错了必须先修复。 2. **Prompt 模拟测试**手动构造一个 runtimeValues用 Skill.generatePrompt() 生成最终的 prompt然后把它粘贴到 Anthropic Console 里看模型是否能稳定输出符合 output_schema 的 JSON。 javascript const skill new Skill(./skills/git-diff-analyze.SKILL.md); const testValues { diff_output: diff --git a/src/main.py b/src/main.py index 1234567..abcdefg 100644 --- a/src/main.py b/src/main.py -10,3 10,4 def calculate_total(items): total 0 for item in items: total item.price return total, target_language: python }; console.log(skill.generatePrompt(testValues)); 运行这段代码你会得到一个完整的 prompt。把它发给 Claude观察输出。如果模型输出了 Heres my analysis: 开头的散文说明 Rules 写得不够强硬如果输出了 {summary: ..., suggestions: [...]}恭喜你的技能契约是有效的。 ### 4.3 运行与结果解读一次真实的 deepx 技能调用 假设你在一个 Python 项目中刚刚修改了一个函数。现在你运行 bash deepx skill git-diff-analyze \ --skill-dir ./skills \ --anthropic-key $ANTHROPIC_API_KEY \ --diff-output $(git diff --no-index) \ --target-language pythondeepx的执行流程是找到./skills/git-diff-analyze.SKILL.md。解析出它需要diff_output和target_language并验证你提供了这两个参数。将$(git diff --no-index)的输出和python字符串注入到SKILL.md的模板中生成最终 prompt。调用 Anthropic API。收到响应后deepx会尝试JSON.parse()。如果成功它会格式化输出如果失败它会打印原始响应方便你调试。一个典型的成功响应可能是{ summary: Added a missing return statement to the calculate_total function., severity: medium, suggestions: [ { line_number: 13, description: The function was missing a return statement, which could lead to undefined behavior., fix_code: return total } ] }注意您使用的是不受支持的命令行标记 unsafely这个热词提醒我们 CLI 参数的安全性。deepx在解析--diff-output时必须对值进行严格的白名单过滤禁止任何包含$(、$(...)、;、等 shell 元字符的输入否则用户可以通过构造恶意的diff输出实现命令注入Command Injection。这是deepx作为生产工具必须内置的安全防护。5. 常见问题与独家排查技巧实录5.1 “Claude 不是内部或外部命令” 报错的终极解法这个报错claude 不是内部或外部命令是 Windows 用户的噩梦但它和SKILL.md本身毫无关系。它只说明一件事你的系统 PATH 环境变量里没有包含claudeCLI 工具的安装路径。根本原因与解决方案错误做法下载claude.exe双击运行以为这就“安装”了。Windows 下双击.exe只是临时运行不会修改 PATH。正确做法下载claudeCLI 的最新 Windows 版本.zip包。解压到一个固定目录例如C:\tools\claude。将C:\tools\claude添加到系统的PATH环境变量中控制面板 - 系统 - 高级系统设置 - 环境变量 - 系统变量 - Path - 编辑