Skip to main content
Skip to content

自定义技能

技能是用于扩展 Copilot 功能的可复用提示模块。 从目录中加载技能,为特定域或工作流提供Copilot专用功能。

概述

技能是一个具有名称的目录,其中包含一个 SKILL.md 文件——这是一份向 Copilot 提供指令的 Markdown 文档。 加载后,技能的内容将注入到会话上下文中。

技能允许您:

  • 将域专业知识打包到可重用模块中
  • 跨项目共享特定行为
  • 整理复杂的代理配置
  • 每个会话启用/禁用功能

加载技能

创建会话时,指定包含技能的目录:

代码语言 navigation

TypeScript
import { CopilotClient } from "@github/copilot-sdk";

const client = new CopilotClient();
const session = await client.createSession({
    model: "gpt-5.4",
    skillDirectories: [
        "./skills/code-review",
        "./skills/documentation",
    ],
    onPermissionRequest: async () => ({ kind: "approve-once" }),
});

// Copilot now has access to skills in those directories
await session.sendAndWait({ prompt: "Review this code for security issues" });

禁用技能

禁用特定技能,同时使其他人保持活动状态:

代码语言 navigation

TypeScript
const session = await client.createSession({
    skillDirectories: ["./skills"],
    disabledSkills: ["experimental-feature", "deprecated-tool"],
});

技能目录结构

每个技能都是一个包含 SKILL.md 文件的命名子目录:

skills/
├── code-review/
│   └── SKILL.md
└── documentation/
    └── SKILL.md

选项 skillDirectories 指向父目录(例如 ./skills)。 CLI 可发现即时子目录中的所有 SKILL.md 文件。

SKILL.md 格式

SKILL.md文件是一个具有可选 YAML 头部信息的 Markdown 文件:

---
name: code-review
description: Specialized code review capabilities
---

# Code Review Guidelines

When reviewing code, always check for:

1. **Security vulnerabilities** - SQL injection, XSS, etc.
2. **Performance issues** - N+1 queries, memory leaks
3. **Code style** - Consistent formatting, naming conventions
4. **Test coverage** - Are critical paths tested?

Provide specific line-number references and suggested fixes.

"前置信息字段:"

  • name:技能的标识符(与 disabledSkills 一起使用,用于选择性地禁用该技能)。 如果省略,则使用目录名称。
  • description:简要说明技能的作用。

Markdown 正文包含加载技能时注入到会话上下文的说明。

技能提供程序(实验版)

注意

技能提供方是实验性的。 API 可以在将来的 SDK 版本中更改。

技能提供程序提供来自应用程序自身存储中的技能,例如多租户服务中的数据库,而不是磁盘上的 SKILL.md 文件。 提供方技能与基于文件的技能一起加入会话的技能目录。 模型通过 skill 工具按需加载它们,用户可以像任何其他技能一样调用它们。

提供程序实现两个操作:

  • 列出技能:返回每个技能的目录元数据:name、description,以及可选的 userInvocable、disableModelInvocation 和 argumentHint。 运行时在加载会话的技能时调用它。
  • 读取技能:返回一个技能的 markdown;如果技能不再存在,则返回未找到的结果。 运行时仅在加载技能时调用它。

创建或恢复会话时传递提供程序:

代码语言 navigation

TypeScript
import { approveAll, CopilotClient, type SkillProvider } from "@github/copilot-sdk";

const releaseSkills = new Map([
    [
        "release-notes",
        {
            description: "Writes release notes in the team's format.",
            markdown: "Group changes by feature area and link each pull request.",
        },
    ],
]);

const skillProvider: SkillProvider = {
    listSkills: () =>
        [...releaseSkills].map(([name, skill]) => ({ name, description: skill.description })),
    readSkill: (name) => releaseSkills.get(name)?.markdown ?? null,
};

const client = new CopilotClient();
const session = await client.createSession({
    onPermissionRequest: approveAll,
    skillProvider,
});

提供方技能内容

“Read skill”技能返回该技能的 SKILL.md 文本。 YAML frontmatter 是可选的:

  • 如果没有 frontmatter,则整个文本都是技能正文,所有元数据都来自列出的技能。
  • 使用 frontmatter 时,省略的字段将继承列出的值。 包含的字段必须与列出的值匹配,否则技能将无法加载。
  • allowed-tools 仅可从前置元数据中读取。
  • 如果文本的第一行是 ---,运行时会将其分析为 frontmatter,因此不要使用 Markdown 水平分隔线启动仅正文技能。

默认情况下,提供程序技能是纯文本的:它们没有基本目录,因此无法引用捆绑的脚本、模板或其他文件。 若要为提供程序技能提供此类文件,请参阅 支持文件。 skills.list 使用源 sdk 和空的 path 报告提供程序功能,有或没有支持文件均可。

支持文件(实验性)

提供程序技能可以附带支持文件,如脚本、引用、模板和资产,就像基于文件的技能文件夹可以附带这些文件一样。 应用程序将每个技能的文件保存在它控制的文件夹中,并将列出的技能上的 directory 设置为该文件夹的绝对路径。

当模型激活技能时,运行时会以列出基于文件的技能文件夹的方式列出文件夹。 该模型会获得与基于文件的技能相同的“此技能的基本目录”行和相关文件列表,用以取代仅含文本的提示,并使用其常规文件工具读取这些文件。 运行时本身绝不会读取、复制或写入这些文件。

import { approveAll, CopilotClient, type SkillProvider } from "@github/copilot-sdk";

const skillProvider: SkillProvider = {
    listSkills: () => [
        {
            name: "release-notes",
            description: "Writes release notes in the team's format.",
            directory: "/srv/skills/release-notes",
        },
    ],
    readSkill: (name) =>
        name === "release-notes"
            ? "Run `python3 scripts/changelog.py` and fill in `templates/notes.md`."
            : null,
};

const client = new CopilotClient();
const session = await client.createSession({
    onPermissionRequest: approveAll,
    skillProvider,
});

每个 SDK 在其所列技能类型对应的文件夹上进行设置:

SDK领域
Node.jsdirectory: "/srv/skills/release-notes"
Pythondirectory="/srv/skills/release-notes"
GoDirectory: copilot.String("/srv/skills/release-notes")
.NETDirectory = "/srv/skills/release-notes"
Java第六个 SkillProviderDescriptor 构造函数参数
Rustdirectory: Some("/srv/skills/release-notes".into())

文件夹所在的位置

将技能的所有文件放在其 directory下,每个技能对应一个文件夹。 运行时所在的文件夹必须可访问:

  • 本地磁盘。 在没有会话文件系统时,directory 则为运行运行时的计算机或容器上的路径。 模型可以使用任何文件工具读取文件,并且可以运行脚本。
  • 会话文件系统(多租户与服务器部署)。 使用 sessionFs 处理程序时,directory 使用处理程序的路径约定,并且处理程序提供文件;运行时会通过该处理程序列出文件夹。 view、create、edit和apply_patch可访问这些文件。 grep、glob 和 shell 命令都不支持运行,因此脚本无法运行。 Java SDK 没有sessionFs,因此Java提供程序使用本地磁盘。

运行时在读取技能返回后列出文件夹,因此你可以在读取技能中延迟创建文件夹。 如果文件夹缺失或为空,或者在运行时调用读取技能后的 15 秒内无法列出,则模型仍会获取基本目录行,而没有相关文件列表,与基于文件的技能一样。 如果运行时列出技能时 sessionFs 处理程序断开连接,则这些技能将仅显示为文本,直到下一次列出时。

Access

  • 本地磁盘。 从技能被列出时起,运行时会将每个已启用的提供程序技能 directory 视为像已配置的 skillDirectories 文件夹一样:对其下内容的读取操作会跳过外部工作区路径提示,沙盒进程获得对它的只读访问权限。 读取操作仍会转到你的权限处理程序,与基于文件的技能一样。 不要使用宽泛目录,例如主文件夹或保存每个租户文件的文件夹。
  • 会话文件系统。 运行时不会授予对该文件夹的任何访问权限。 让权限处理程序批准对其下内容的读取,当沙盒启用时,请将该文件夹添加到沙盒的readonlyPaths中。

应用程序拥有该文件夹:它所在的位置、存在的时间、清理该文件夹以及防止模型更改它。 只读访问权限仅涵盖沙盒进程。 在沙盒之外,或者当文件夹位于工作区内时,模型可以写入它,除非你的权限处理程序或你的 sessionFs 处理程序拒绝。

编写该技能

  • 请通过相对于该技能文件夹的路径来引用支持文件,例如 references/guide.md。 模型被告知文件夹的绝对路径。
  • 如技能中使用脚本所述,通过解释器(如 python3 scripts/changelog.py 或 bash scripts/build.sh)运行脚本。 脚本仅在文件夹位于本地磁盘上时才运行。

规则和限制

  • directory在会话的路径约定中必须是绝对的;在Windows样式的会话文件系统上,它需要驱动器或 UNC 前缀。 它会像文件工具解析路径那样进行解析,因此会移除 . 和 .. 段以及重复或尾随分隔符,并且无法解析为文件系统根目录。 它计入目录的 1 MiB 元数据限制,并且不会显示在技能目录中。 无效的 directory 会导致整个目录被拒绝,就像任何其他列出的无效技能一样。
  • 相关文件列表遵循基于文件的规则:生成和依赖项文件夹(如 node_modules、 bin、 obj和 dist)被隐藏;汇总了包含 50 个以上的条目的文件夹;未列出嵌套 10 多个文件夹的文件;从不列出 SKILL.md ;最多显示 100 个文件。
  • 压缩对话后,模型会保留技能的说明,但不保留其文件夹路径。 再次调用技能会显示它。 基于文件的能力保留其路径。
  • 支持文件至少需要与添加它们的 SDK 版本一样新的Copilot CLI。 当任何列出的技能包含 directory 时,较旧的 CLI 将拒绝整个技能目录。 未设置 directory 的提供程序与较旧的 CLI 兼容。

处理现有的 SKILL.md 文件

若要为已存储的 SKILL.md 文件(如存储在数据库中的文件)提供服务,请分析每个文件的 YAML frontmatter 一次以生成其目录条目,然后从 read skill 原样返回文件:

  • 将 name 和 description 映射到所列技能的 name 和 description。 目录清单中需要两者;运行时不会回退到文件夹名称或技能正文。
  • 当前言设置了它们时,将 disable-model-invocation、user-invocable 和 argument-hint 映射到匹配的可选字段。
  • 从 read skill 返回原始文本(包括 frontmatter)。 由于列出的值来自同一个 frontmatter,因此它们匹配,运行时仍从中读取 allowed-tools 。

受信任的提供程序内容

处理提供程序技能(如技能目录):其内容是受信任的输入。 模型遵循技能的说明,frontmatter 控制技能的调用方式。 allowed-tools 不会在 SDK 会话中授予权限。 运行时在 skill.invoked 事件的 allowedTools 字段中报告它,你的权限处理程序仍决定每个工具请求。

请勿呈现最终用户或其他租户可以编辑的内容,除非你允许他们创作技能文件。 如果你从用户输入中构建技能,请自行生成 frontmatter,而不是传递用户提供的 frontmatter。

提供程序限制和错误

运行时验证提供程序的目录和内容:

  • 名称必须匹配 ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$ 且唯一,忽略大小写。

  • 目录最多可以包含 1,024 个技能和 1 MiB 元数据。 每个说明和参数提示最多可以有 1,024 个字符。

  • 每个技能的 Markdown 最多可以是 UTF-8 的 1 MiB。

  • 每个调用必须在 30 秒内完成。 当运行时停止等待调用时,由于超时、会话已结束,或恢复操作替换了提供程序,它会取消请求并忽略任何后续结果。 每个 SDK 以通常的方式将取消传递给提供程序:

    SDK取消信号
    Node.js传递给readSkill和AbortSignal的选项中的signal(listSkills)已中止。
    Python提供程序的 asyncio 任务已通过 asyncio.CancelledError 取消。 同步提供程序运行直至完成。
    Go呼叫的 context.Context 已取消。
    .NET呼叫的 CancellationToken 已取消。
    Java返回的 CompletableFuture 项通过 cancel(true) 取消,不会中断正在运行的工作。
    Rust提供程序 Future 被删除。

    这些信号适用于被运行时取消的调用。 连接关闭或客户端强制停止时,.NET、Java和 Rust SDK 也会取消仍在运行的调用。 Node.js、Python 和 Go SDK 则不会这样做:正在运行的调用会持续到返回为止,而 SDK 会丢弃其结果。

如果列出操作失败或返回无效的目录,运行时将在 errors 返回的 skills.reload 列表中报告该问题。 重新加载失败后,将保留上次成功列表中的提供程序技能。 如果读取技能失败,该工具会 skill 向模型报告通用加载失败。 SDK 永远不会转发提供程序引发或返回的错误文本,因此这些文本不会泄露到对话中。

提供程序生命周期

一个提供程序只能绑定到一个会话:

  • 提供程序会启用技能,除非将 enableSkills 设置为 false,这将保持提供程序处于绑定状态,但该提供程序永远不会被调用。 在mode: "empty",技能保持禁用状态,直到你将true设置为enableSkills。
  • 提供方永远不会被持久化。 恢复会话时再次传递它。 在没有提供方的情况下恢复会将其从会话中删除。 加入会话的扩展不会更改它。
  • 与基于文件的技能同名的提供程序技能将替换基于文件的技能,并且 skills.reload 会发出警告。
  • 子代理使用其父会话的提供程序。
  • 运行时可以同时调用提供程序,例如,从 skill 工具、用户调用和预加载技能的自定义代理,因此这两个操作都必须安全供并发使用。
  • 云会话不支持技能提供程序。 SDK 在创建会话之前拒绝配置。

配置选项

SessionConfig 技能字段

语言领域类型Description
Node.jsskillDirectoriesstring[]要从中加载技能的目录
Node.jsdisabledSkillsstring[]禁用技能
Node.jsskillProviderSkillProviderSDK 提供的技能的实验性提供程序
Pythonskill_directorieslist[str]要从中加载技能的目录
Pythondisabled_skillslist[str]禁用技能
Pythonskill_providerSkillProviderSDK 提供的技能实验性提供程序
GoSkillDirectories[]string要从中加载技能的目录
GoDisabledSkills[]string禁用技能
GoSkillProviderSkillProviderSDK 提供的技能实验性提供程序
.NETSkillDirectoriesList<string>要从中加载技能的目录
.NETDisabledSkillsList<string>禁用技能
.NETSkillProviderISkillProvider用于 SDK 提供技能的实验性提供程序
JavasetSkillProviderSkillProvider用于 SDK 提供技能的实验性提供程序
Rustwith_skill_providerArc<dyn SkillProvider>SDK 提供技能的实验性提供程序

内置技能和 mode: "empty"

运行时附带一组默认可用的随附 内置 技能。 当您在 中运行客户端时(这是 mode: "empty" 的推荐基线),SDK 会排除所有在运行时捆绑的内置技能:它会在 post-create 和 post-resume 选项补丁中发送一个空的 includedBuiltinSkills 列表,以及一个空的 installedPlugins 列表。

此排除项是默认的,而不是永久性限制。 若要允许选定的运行时捆绑技能,请将 includedBuiltinSkills(或特定于语言的大小写形式)设置为这些技能的名称。 你还可以在 mode: "empty" 下选择启用自己的自定义技能——启用技能并传入你自己的 skillDirectories——而且这些技能仍然完全可用,包括与内置技能同名的自定义技能。 在 mode: "copilot-cli" 下,除非设置此选项,否则该字段将被省略。

首次加载技能时会扫描自定义技能目录,包括此时缺少或为空的目录。 这些目录中的文件系统更改不会在后续轮次中自动刷新目录。 添加、编辑或删除技能后,调用会话的 skills.reload RPC:

SDK重新发起调用
Node.jsawait session.rpc.skills.reload()
Pythonawait session.rpc.skills.reload()
Gosession.RPC.Skills.Reload(ctx)
.NETawait session.Rpc.Skills.ReloadAsync()
Javasession.getRpc().skills.reload().join()
Rustsession.rpc().skills().reload().await?

更改配置的技能目录也会刷新目录清单。

最佳做法

  1. 按域组织 - 将相关技能组合在一起(例如, skills/security/``skills/testing/)

  2. 使用前置元数据 - 在 YAML 前置元数据中包含 name 和 description,以提高清晰度

  3. 文档依赖项 - 记下技能所需的任何工具或 MCP 服务器

  4. 单独测试技能 - 在将技能组合起来之前,先确认各项技能可正常运行

  5. 使用相对路径 - 使技能在环境中可移植

与其他功能结合使用

技能 + 自定义代理

代理 skills 字段中列出的技能 是预先加载的—其完整内容在启动时注入到代理的上下文中,因此代理可以立即访问技能说明,而无需调用技能工具。 技能名称从会话级 skillDirectories 中解析。

const session = await client.createSession({
    skillDirectories: ["./skills/security"],
    customAgents: [{
        name: "security-auditor",
        description: "Security-focused code reviewer",
        prompt: "Focus on OWASP Top 10 vulnerabilities",
        skills: ["security-scan", "dependency-check"],
    }],
    onPermissionRequest: async () => ({ kind: "approve-once" }),
});

注意

技能为可选启用项——省略 skills 时,不会注入任何技能内容。 子代理不从父级继承技能;必须为每个代理显式列出它们。

技能 + MCP 服务器

技能可以补充 MCP 服务器功能:

const session = await client.createSession({
    skillDirectories: ["./skills/database"],
    mcpServers: {
        postgres: {
            type: "local",
            command: "npx",
            args: ["-y", "@modelcontextprotocol/server-postgres"],
            tools: ["*"],
        },
    },
    onPermissionRequest: async () => ({ kind: "approve-once" }),
});

故障排除

技能未加载

  1. 检查路径是否存在 - 验证技能目录路径是否正确,并且包含包含 SKILL.md 文件的子目录
  2. 检查权限 - 确保 SDK 可以读取目录
  3. 检查 SKILL.md 格式 - 验证 markdown 格式正确,并且任何 YAML frontmatter 都使用有效的语法
  4. 启用调试日志 - 将 logLevel: "debug" 设为开启以查看技能加载日志

技能冲突

如果多个技能提供冲突的说明:

  • 使用 disabledSkills 来排除冲突技能
  • 重新组织技能目录以避免重叠

另见