技能
SKILL.md 从哪些目录被发现、同名时谁赢、内置技能怎样打包进产物,技能目录怎样按预算进入系统提示词,以及 skill 工具与斜杠命令两条调用路径。
技能是一个目录加一份 SKILL.md。模型平时只看到由名字和描述组成的一份目录,放在系统提示词末尾;需要时用 skill 工具把某个技能的全文读进上下文,再照着做。用户也可以在 TUI 里输入 /名字 直接点名。
实现分在三层:扫描与同名裁决在 packages/agent-modules/skills(包名 @mavis/skills),根目录、内置技能、目录渲染与读取在第一代运行时的 packages/local-runtime/src/skills/,提示词拼装、斜杠提醒和插件技能在 local-runtime-v2。
怎么用
- 看清单:
/skills [filter]打开只读面板,分“Built-in”“User”“Other”三组列出技能、来源及是否可用(packages/tui/src/tui/features/inspection/capabilities.ts:15)。 - 点名调用:每个启用的技能都会变成一条斜杠命令
/<名字> [instructions],补全列表里的说明带[Skill]前缀(packages/tui/src/tui/controller/run/active-run-flow.ts:82)。 - 初始化项目:
mcode init(可带目录参数,默认当前目录)就是在该目录打开 TUI 并提交/init,由内置的init技能生成根目录的AGENTS.md(packages/tui/src/cli/program.ts:84)。 - 放自己的技能:个人技能放
<数据目录>/skills/<名字>/SKILL.md,只给某个 Agent 用的放<数据目录>/agents/<Agent>/skills/,项目技能放仓库里的.minimax/skills/<名字>/SKILL.md,内置的skill-creator也是这样引导的(packages/local-runtime/assets/skills/skill-creator/SKILL.md:62);Claude Code、Codex 等工具的技能目录也会被扫描,见下表。数据目录默认是~/.minimax-code,可用MINIMAX_DATA_DIR或MAVIS_DATA_DIR改(packages/tui/src/runtime/data-dir.ts:20)。 - 调整外部来源:
<数据目录>/config.yaml里skills下的external段有enabled(总开关)、walkUp(是否逐级向上找到 Git 根)、duplicateWarn,以及按来源的sources.<来源>.enabled与priority(packages/config/src/skills-config.ts:47)。 - 改了就生效:技能根目录与各技能目录都挂了文件监听,变更后防抖 200 毫秒、最长等 1 秒就重新扫描(
packages/agent-modules/skills/src/registry.ts:311)。
TUI 的 /skills 面板不能启用或禁用技能,命令表里也没有技能安装命令。第一代运行时里有从技能市场与 GitHub 安装技能的代码(packages/local-runtime/src/skills/hub-api.ts:113),命令行里用不上。
发现:十一个来源与同名裁决
readConfiguredSkillRoots 列出全部根目录(packages/local-runtime/src/skills/roots.ts:22),外部来源的默认优先级来自 DEFAULT_SKILLS_CONFIG(skills-config.ts:62):
| 来源 | 路径 | 种类 | 优先级 |
|---|---|---|---|
workspace-minimax | 工作区的 .minimax/skills | workspace | 65 |
workspace-cc | 工作区的 .claude/skills | workspace | 60 |
workspace-agents | 工作区的 .agents/skills | workspace | 55 |
user-cc | ~/.claude/skills | user | 40 |
user-codex | ~/.codex/skills | user | 35 |
user-agents | ~/.agents/skills | user | 30 |
| Agent 目录 | <数据目录>/agents/<Agent>/skills | agent | 100,兼容的旧 Agent 名为 90 |
| 用户全局 | <数据目录>/skills | global | 未设,按 0 计 |
| 内置落盘 | <数据目录>/.builtin-skills | builtin | 20 |
| 内置 Agent 专属 | 内置 Agent 资源目录下当前 Agent 的 skills 子目录 | builtin | 10 |
| 内置全局 | 产物里的 assets/skills | builtin | 未设,按 0 计 |
工作区的三个目录从当前目录开始扫描;walkUp 打开时(默认)逐级向上,直到遇到含 .git 的目录(roots.ts:140),离当前目录越近的层级优先级加成越大,每层差千分之一(roots.ts:83)。
同名时先比种类,再比优先级(registry.ts:1042)。种类的先后是 project、workspace、agent、global 与 user 并列、最后 builtin(registry.ts:72):
const SOURCE_RANK: Record<SkillSourceKind, number> = {
project: 0,
workspace: 1,
agent: 2,
global: 3,
user: 3,
builtin: 4,
};project 这一档在根目录列表里没有用到,实际排第一的是工作区目录,内置技能永远排在最后;Agent 目录的优先级数字虽然最大,也排在工作区之后。一个不太直观的结果:<数据目录>/skills 与 ~/.claude/skills 同属第 3 档,前者没有设优先级按 0 计,后者是 40,两处有同名技能时胜出的是 ~/.claude/skills 那一份。落败的条目不会消失,会连同“被哪个来源遮蔽”的原因留在快照里,供诊断查看(registry.ts:1033)。
扫描只看每个根目录下一层的 <目录>/SKILL.md(registry.ts:528),嵌在技能内部的 SKILL.md(例如 lark-tools 里那一批)不算独立技能。SKILL.md 本身不许是符号链接;技能目录可以是符号链接,但只有 agent、global、user、builtin 四种根允许它指向根目录之外(registry.ts:474),工作区根不行。
SKILL.md 的格式
独立技能的格式很宽松(registry.ts:678):名字取 frontmatter 的 name,没有就用目录名;标题取 title 或正文第一个标题;描述取 description 或正文第一段。没有 frontmatter、YAML 写错、缺描述都只记诊断,不影响加载。frontmatter 里起作用的键:
| 键 | 作用 |
|---|---|
name、description、title | 名字、目录里的描述、标题 |
displayNames、descriptions | 按语言区分的显示名与描述,如 zh-Hans(registry.ts:914) |
requires_beta 或 requiresBeta | 只有 config.yaml 的 beta 里对应开关为真才可见(packages/local-runtime/src/skills/registry-family.ts:171) |
listed | 写成 false 就不进目录(registry-family.ts:201) |
正文里的 {{DATA_DIR}} 会替换成当前数据目录(registry.ts:64)。一个完整的例子是内置的 resume-codex(packages/local-runtime/assets/skills/resume-codex/SKILL.md:1):
---
name: resume-codex
description:
Safely continue the task from a Codex CLI or VS Code session in the current workspace. Use when
invoked as /resume-codex with an optional Codex session id, or when the user asks to pick up their
latest Codex work.
descriptions:
zh-Hans: '安全接续当前工作区中的 Codex CLI 或 VS Code session。'
displayNames:
zh-Hans: '接续 Codex Session'
---插件带来的技能由 v2 的插件系统解析,规则更严:文件必须以 YAML frontmatter 开头,必须有 description,名字缺省时退回目录名(packages/local-runtime-v2/src/service/plugin-system/skill/reader.ts:18);开了 agentSkills 选项时名字必须显式声明,还要检查 license、allowed-tools 为字符串,compatibility 长 1 到 500 个字符,metadata 只能是字符串到字符串的映射(reader.ts:43)。
内置技能:打包、落盘与清单
内置技能的源文件有两处:packages/local-runtime/assets/skills/ 下 18 个目录,以及主 Agent 专属的 packages/local-runtime-v2/assets/agents/mavis/skills/ 下 2 个。pnpm build 把前者复制到产物目录 dist 下的 assets,把 v2 的 Agent 资源树复制到其中的 agents(scripts/build.mjs:113),同时按名单排除 6 个技能(scripts/lib/builtin-skills.mjs:1):create-agent、mavis、mavis-doctor、mavis-team、miniapp-creator、plugin-creator。这 6 个在公开仓库里本来就没有目录。
运行时启动时,再把打包的内置技能复制到 <数据目录>/.builtin-skills。是否需要重写由一个组合指纹决定,指纹不变就一次也不写;一旦重写,这个目录会被整个清空,注释明说放进去的用户文件也会被删掉(packages/local-runtime/src/skills/seed-builtin.ts:44)。想改内置技能,应该在优先级更高的目录放一份同名技能,而不是直接改这里。
在命令行里各技能能不能用,取决于 resolveFeatureAwareBuiltinSkillNames 的门控(packages/local-runtime/src/agent/feature-owned-skills.ts:11):普通内置技能要求当前 Agent 有它依赖的工具,归属某项能力的技能跟着那项能力开关。
| 技能 | 用途 | 命令行里的条件 |
|---|---|---|
init | 生成根目录 AGENTS.md | 需要 read、write、bash |
code-review | 按用户指定的范围审查代码缺陷 | 需要 read |
deep-research | 分五步做需要外部核实的深度研究 | 需要 read、write、todowrite,外加联网搜索、web_fetch、bash 之一 |
docx、pdf、pptx、xlsx | 读写办公文档 | 需要 read、write、bash |
skill-creator、skill-refiner | 新建技能、按证据修补已有技能 | 前者需要 read、write、bash,后者需要 read、edit |
deploy-website、edit-deployed-website | 部署静态网站、修改已部署的网站 | 需要 website_deploy 等工具 |
lark-tools | 通过官方 lark-cli 使用飞书 | 需要 bash |
llm-call | 直接调用已配置的模型 | 需要 bash |
mcode-tools-master | mcode-tools 命令行与连接器的总入口 | 需要 bash,并且 requiresBeta: mcodeTools |
visual-page | 生成可视化 HTML 页面 | 需要 write |
x-link-reader | 读取 X 或 Twitter 链接 | 需要 web_fetch;中国区关闭(packages/local-runtime/src/skills/builtin.ts:15) |
resume-codex | 接续 Codex CLI 或 VS Code 会话 | 只在 CLI 能力档打开,且需要 bash |
cu-desktop | Computer Use 桌面操作指南 | 需要 CU 模式,命令行关闭 |
minimax-code-product | 回答关于产品本身的问题 | 需要 features.mavis,命令行关闭 |
control-in-app-browser | 控制内置浏览器 | 由浏览器服务单独提供(packages/local-runtime-v2/src/service/browser-use/browser-skill-asset.ts:13),见浏览器工具 |
resume-codex 的开关只出现在 CLI 能力档里(packages/local-runtime/src/api/hosted-agent-capability-factory.ts:39),从代码看,这是命令行有而桌面宿主没有的内置技能。同一处还把 mavis-doctor、plugin-creator 列为禁用;这两个名字在内置技能 id 表里(packages/config/src/agent-capabilities.ts:50),但公开仓库里没有它们的目录。
进提示词:<available_skills> 与预算
每个回合组装系统提示词时,v2 先取可见技能,把插件技能作为附加条目并进来,再让第一代运行时渲染目录(packages/local-runtime-v2/src/service/turn-system/agent-host/preparation/config/local-agent-config-builder.ts:522)。目录是系统提示词的最后一层,排在记忆之后(local-agent-config-builder.ts:752)。
进目录之前还要过几道筛子:禁用的技能、listed 为 false 的技能、requires_beta 未开的技能、Agent 配置里没选中的技能都去掉;名字形如 mcp-<服务器名> 且对应 MCP 服务器已配置的技能也去掉(registry-family.ts:181)。渲染格式很简单(packages/local-runtime/src/skills/catalog.ts:347):
function renderCatalog(
skills: readonly PreparedSkill[],
descriptionLengths: readonly number[],
notice?: string,
): string {
const entries = skills.map((skill, index) => {
const description = skill.description.slice(0, descriptionLengths[index] ?? 0).join('');
return description ? `- ${skill.name}: ${description}` : `- ${skill.name}`;
});
return `<available_skills>\n${[...(notice ? [notice] : []), ...entries].join('\n')}\n</available_skills>`;
}目录有预算:模型上下文窗口的 2%,封顶 5000 token,拿不到窗口大小时直接用 5000(catalog.ts:41)。相关常量与两句提示语在文件开头(catalog.ts:3):
export const LOCAL_SKILL_CATALOG_MAX_BUDGET_TOKENS = 5_000;
export const LOCAL_SKILL_DESCRIPTION_MAX_CODE_POINTS = 1_024;
const PROTECTED_SKILL_NAMES = new Set(['mcode-tools-master', 'minimax-code-product']);
const PARTIAL_DESCRIPTION_NOTICE =
'Skill descriptions were shortened to fit the catalog budget.';
const PARTIAL_SKILL_NOTICE =
'Some skills were omitted because not every skill name fits within the skills context budget.';每条描述先压成一行、截到 1024 个码点,受保护的两个技能除外。整份目录放不下时按顺序让步:
- 受保护技能排在最前,描述保持完整。
- 其余描述统一缩短:先给内置技能分配长度,内置技能的描述全部放完整了,非内置技能才分得到;放得下第一句提示语就把它加在目录开头。
- 连只列名字都放不下时,从排序靠前的开始保留,其余整条丢掉,放得下就附上第二句提示语;留下的非受保护技能也只剩名字。
除受保护技能外,目录按名字排序,所以超预算时被丢掉的是字母序靠后的技能。
packages/agent-extension/src/skills.ts 里还有一个 skillsExtension,打算把目录渲染搬到 Agent 扩展接口上(packages/agent-extension/src/skills.ts:95),用的是 @mavis/skills 按字符计的预算,默认 20000 字符(registry.ts:81)。代码里找不到它的调用方,头注释提到的 packages/local-runtime/src/tools/skill-tool.ts 在仓库里也不存在(skills.ts:21);实际生效的是上面按 token 计的这一套。
调用:skill 工具与斜杠命令
模型这一侧只有一个 skill 工具,参数只有 name(packages/agent-tools/src/desktop/builtin-defs.ts:268)。工具说明要求:用户点名(包括 /名字)或目录里的名字与描述明显匹配时才加载;名字要与目录一字不差,插件技能保留前面的插件名前缀;加载时它必须是该步唯一的工具调用,读完全文再按说明做(builtin-defs.ts:275)。
执行时先按名字精确查找独立技能,查不到再到插件技能里按规范化后的名字找(packages/local-runtime/src/api/local-native-tools.ts:132)。找到后返回整份文件(含 frontmatter),前面加两行头(packages/agent-tools/src/desktop/local-skill.ts:50):
function formatSkillContent(name: string, content: string, location?: string): string {
const header = [`# Skill: ${name}`];
if (location) header.push(`Location: ${location}`);
return `${header.join('\n')}\n\n${content}`;
}Location 用的是以 files: 开头的地址(registry.ts:1066),技能目录里的附属文件要由模型按这个位置自己去读。找不到时返回以 Local skill not found: 开头的一行文本,工具说明要求模型这时不许声称用过该技能。
用户这一侧,TUI 用 /skills 同一份清单生成斜杠命令:跳过已禁用的、与内置命令或别名重名的、含空白或斜杠的、超过 128 个字符的名字(active-run-flow.ts:82)。选中技能命令后,TUI 并不自己读 SKILL.md,而是把 /名字 参数 原样作为用户消息提交。v2 在准备这条输入时识别开头的斜杠名(允许带一个冒号分隔的前缀),对上当前回合配置里的技能名,就在消息前面加一段提醒(packages/local-runtime-v2/src/service/turn-system/agent-host/assembly/local-turn-input-preparation.ts:831):
function buildSlashSkillReminder(
canonical: AgentHostCanonicalUserInput,
agentConfig: Readonly<Record<string, unknown>>,
): string {
const requested = canonical.messages.reduce<string | undefined>((found, message) => {
if (found) return found;
return /^\/([A-Za-z0-9][A-Za-z0-9._-]*(?::[A-Za-z0-9][A-Za-z0-9._-]*)?)(?:\s|$)/u.exec(
message.text.trimStart(),
)?.[1];
}, undefined);
if (!requested) return '';
const skills = Array.isArray(agentConfig.skills) ? agentConfig.skills : [];
const match = skills.find((skill) => {
const name = readString(readRecord(skill), 'name');
return name?.toLowerCase() === requested.toLowerCase();
});
const name = readString(readRecord(match), 'name');
return name
? [
'<system-reminder>',
`The user explicitly requested the \`${name}\` skill. Load it with the \`skill\` tool and follow it.`,
'</system-reminder>',
].join('\n')
: '';
}这段提醒拼在用户消息正文之前(local-turn-input-preparation.ts:361)。技能正文依旧由模型调用 skill 工具读取,不会被直接塞进消息。匹配不分大小写,只认消息开头的第一个斜杠名。
启用状态与插件技能
独立技能可以按文件位置单独禁用。禁用集合由 v2 保存在偏好表里,键为 standalone-skill-disabled-state(packages/local-runtime-v2/src/service/plugin-system/skill/enabled-state.ts:8),再注入第一代的发现引擎;被禁用的技能既不进目录,也不能被 skill 工具读取。
插件技能的运行时名字是 插件名:技能名(packages/shared/src/plugin-skill-name.ts:9)。/skills 与斜杠命令用的清单里,独立技能在前,插件技能按名字排好附在后面,与独立技能同名的插件技能被跳过(packages/local-runtime-v2/src/application/session/runtime-skill-application.ts:154)。插件怎样安装、启用,它的技能怎样进入回合能力快照,见插件系统。
下一篇:插件系统——插件包的格式、官方与本地来源,以及它怎样把技能、MCP 服务器与钩子带进回合。