用 NuGet 包分发技能

Microsoft .NET Blog 2026-11-03T21:55:21.746089

Anthropic 在 2025 年推出了智能体技能(Agent skills),如今它已成为 AI 驱动开发中的常见做法。dotnet/skills 和 Impeccable skills 就是不错的例子。智能体技能通常通过 git 仓库分发。我们一直在尝试用 NuGet 包来分发技能,发现这种方式确实有效。两者的共同点在于:仓库和包都是开发的基本单元,都可以作为技能的分发载体。技能主要解决两个问题:一是补上模型知识的空白,二是规定“我们的”任务处理方式。新的 NuGet 包总是走在模型训练前面,因此可以受益于按需下发的技能。下面是一个使用 markout 包的快速演示,这个包里包含一组技能:

$ dotnet-inspect package markout -S Skills

path    size

skills/markout-built-in-shapes/SKILL.md 9457

skills/markout-composite-cells-cards/SKILL.md   6764

skills/markout-conditional-composition/SKILL.md 4978

skills/markout-output-formats/SKILL.md  7744

skills/markout/SKILL.md 8551

基线

我们做了大量实验。我们发现,智能体(作为一种基线行为)能够在 NuGet 缓存中找到内容来了解一个包。这包括读取 README.md 文件,甚至编写一次性的临时程序,通过反射查看程序集元数据。使用前沿模型的智能体创造力惊人,能得出令人意外的洞见,但代价未必低廉。根本问题在于:它们会把这些创造力和能力用在从元数据逆向推导文档上,还是用在解决你实际的业务问题上?我们有时会说“为什么不能两者兼顾?”可当你的用量必须控制在每月的 token 预算之内时,这就很难了。这正是我们要解决的问题。

SKILL.md

技能是带 YAML 头的 Markdown 文件,文件名为 SKILL.md。agentskills.io 是该格式的官方规范站点。通过 NuGet 分发的技能也采用同样的格式。下面这个示例技能可以用来指示智能体对项目依赖运行漏洞检查。

---
name: vulnerable-nuget-package-check

description: Check a .NET project for vulnerable NuGet packages dependencies.

.NET SDK 提供了一个 dotnet package 命令,可以列出某个 .NET 项目中存在漏洞的直接或间接依赖包。

对项目运行下面的命令:

dotnet package list --vulnerable --include-transitive

然后把有漏洞的包引用升级到已修复的版本。

传统分发方式

skills 通过 git 仓库分发,主要有两种做法。

最常见的一种是把 skills 放进普通的「代码」仓库。这种做法很适合作为起点,因为参与该仓库的每个人都能获得一致的体验。skills 组成的变化走 PR 流程,团队可以评审,甚至回退。dotnet/runtime 仓库就采用这种方式。在那个仓库里开发体验很好,能看到 Copilot 根据提示词自动调用各种 skills。

另一种主要方式是 skill 市场仓库,例如 dotnet/skills。这类仓库通过 marketplace.json 展示自己的 skills。用户执行 /plugin marketplace add dotnet/skills(或类似命令)来安装市场。add 本质上只是对 git clone 的封装,因此 skill 市场仓库应当保持体积小。

把 skills 直接放进自己仓库的好处是:它们始终都在。一个好例子是 Copilot Web Agent。本地安装的市场 skills 离开你的机器就不存在了,除非你额外把它们提交到仓库。两种方式没有优劣,只是选项不同。

通过 NuGet 包分发

NuGet 包对 agent 来说既是挑战也是机会。挑战在于,五分钟前刚发布的带新 API 的包版本并不在模型的知识范围内。机会在于,更新后的 skills 可以和更新后的二进制文件放在一起。这意味着 NuGet 包的每个版本都能带上为它量身定制的 skills,用法很像包内的 README.md,只是用途和目标读者不同。

这个挑战不止限于最近五分钟发布的包。包注册表还有很长的长尾,模型很可能对大多数包一无所知。

dotnet restore 本来就会把包拉到你的机器上以获取库。这意味着包内的 SKILL.md 文件也会出现在你的机器上。

你可以在我的机器上看到 markout 包的 skills。


$ pwd
/Users/rich/git/dotnet-inspect/src/DotnetInspect.Cli

$ dotnet build

$ cat ../../artifacts/obj/dotnet-inspect/project.assets.json | grep SKILL.md
"skills/markout-built-in-shapes/SKILL.md",
"skills/markout-composite-cells-cards/SKILL.md",
"skills/markout-conditional-composition/SKILL.md",
"skills/markout-output-formats/SKILL.md",
"skills/markout/SKILL.md",

$ find ~/.nuget/packages/markout/0.40.0/ | grep SKILL.md
/Users/rich/.nuget/packages/markout/0.40.0/skills/markout-conditional-composition/SKILL.md
/Users/rich/.nuget/packages/markout/0.40.0/skills/markout/SKILL.md
/Users/rich/.nuget/packages/markout/0.40.0/skills/markout-composite-cells-cards/SKILL.md
/Users/rich/.nuget/packages/markout/0.40.0/skills/markout-built-in-shapes/SKILL.md
/Users/rich/.nuget/packages/markout/0.40.0/skills/markout-output-formats/SKILL.md

$ head -6 /Users/rich/.nuget/packages/markout/0.40.0/skills/markout/SKILL.md

name: markout
version: 0.40.0
description: >-
Use when generating Markdown or other structured output (plain text, ANSI, pretty tables,
TSV/JSONL) from C# objects, or when diagnosing Markout source-generator errors such as


这是个不错的开端,对智能体来说完全可用。包里的技能会登记在 `project.assets.json` 中,所以很容易判断某个包依赖图里有没有可用的技能。不过,我们想提供比这更好的体验。这段时间我们一直在做几个用于发现 SKILL.md 文件的工具。

## dotnet-inspect

下面是 `dotnet-inspect` 的几个示例工作流。

先看如何查询某个包依赖图里包含的技能:

$ dotnet-inspect package markout -S Skills

path size
skills/markout-built-in-shapes/SKILL.md 9457
skills/markout-composite-cells-cards/SKILL.md 6764
skills/markout-conditional-composition/SKILL.md 4978
skills/markout-output-formats/SKILL.md 7744
skills/markout/SKILL.md 8551


再按行号定位并打印某个具体技能的内容:

$ dotnet-inspect package markout -S Skills --row 4 --print -12


name: markout-output-formats
version: 0.40.1
description: >-
Use when you need output other than default Markdown — plain text / Unicode, ANSI terminal
(Spectre), pretty aligned tables, or TSV/JSONL exports — or when one model must serve several


从单一渲染路径输出多种格式(用 CLI 的 `--format` 开关)。它还涵盖语义内联代码:渲染成 Markdown 的代码段(code span),但在 TSV/JSONL 中解码为纯文本。选择格式时用 formatter 加 MarkoutWriterOptions,不要手工拼 TSV/JSONL。

不用反编译程序集,也不用上网搜 API——多格式的惯用写法都在这里。

---

它也可以把项目文件作为输入:

$ dotnet new console
$ dotnet package add Mockly
$ ls *.csproj
scratch.csproj
$ dotnet-inspect project scratch.csproj -S Skills


## Skills

来自直接包依赖的有效 Agent Skills 文档。

| 包名 | 版本 | 路径 | 大小 | 名称 | 描述 |
| ------- | ------- | ---- | ---- | ---- | ----------- |
| Mockly | 1.13.0 | skills/mockly/SKILL.md | 7409 | mockly | 帮助使用 Mockly 编写测试——Mockly 是一个 .NET 的流畅式 HTTP mock 库,可拦截 HttpClient 调用。当编写单元测试或集成测试,需要 mock HTTP 请求、配置响应(状态码、JSON、OData、原始内容)、匹配请求头、捕获并检查请求、限制 mock 调用次数,或用 FluentAssertions 断言 HTTP 交互时,使用这个 skill。 |

dotnet-inspect 设想的使用方式是:agent(智能体)可以通过项目文件或直接从注册表确定有哪些可用的 skill。它可以把这些 skill 直接打印到自己的上下文里,或写入文件。这个文件随后可以被搜索,甚至存进仓库。

dotnet-package-skills 是另一个工具,专注把 skill 存进仓库。

$ dotnet-package-skills install
Source: /Users/rich/git/dotnet-inspect/dotnet-inspect.slnx
NuGet global-packages directory: /Users/rich/.nuget/packages/
Destination: /Users/rich/git/dotnet-inspect/.agents/skills
Scanned 23 packages (direct).
Copied 5 skills:
markout (Markout 0.40.1)
markout-built-in-shapes (Markout 0.40.1)
markout-composite-cells-cards (Markout 0.40.1)
markout-conditional-composition (Markout 0.40.1)
markout-output-formats (Markout 0.40.1)
Ignored 22 packages without valid bundled skills


$ git status
On branch main
Your branch is up to date with 'origin/main'.

Untracked files:


(use "git add ..." to include in what will be committed)

.agents/

nothing added to commit but untracked files present (use "git add" to track)

$ git add .

$ git status

On branch main

Your branch is up to date with 'origin/main'.

Changes to be committed:

(use "git restore --staged ..." to unstage)

new file: .agents/skills/.dotnet-package-skills.json

new file: .agents/skills/markout-built-in-shapes/SKILL.md

new file: .agents/skills/markout-composite-cells-cards/SKILL.md

new file: .agents/skills/markout-conditional-composition/SKILL.md

new file: .agents/skills/markout-output-formats/SKILL.md


## 通过 NuGet 包交付技能

new file: .agents/skills/markout/SKILL.md
```

这是往仓库里添加技能、并确保技能始终可用的一种简单做法。你需要自己保证:当对应的 PackageRef 条目更新时,技能也跟着更新。写一个技能来同时完成这两件事,可能会很有用。

NuGet MCP

NuGet MCP 是个好用的工具,能让智能体(agent)对 NuGet 形成深入而全面的理解,无论针对本地项目还是你的包仓库。它目前还没有针对技能做更新。我们打算很快着手这个项目。

经验教训

包交付的技能不是什么特殊技能。前面已经说过,技能本来就可以通过多种方式交付。包只是同一套格式下的一种变体。也就是说,常规的技能评估方法同样适用。

智能体技能归根结底能带来两方面的好处:有效性(能给出正确答案)和效率(以更低的成本给出答案)。但这并不意味着智能体技能一定能带来有效性和效率上的好处。它们有可能带来这些好处,但如果写得不好或测试不到位,也可能适得其反。

模型面临的一个挑战是,成本是个重要因素。如果我在包里放一个 SKILL.md,结果你的智能体成本上升了,那就很糟糕。这意味着维护者需要做技能评估。如果用户反馈某个技能无效或成本高,维护者就应当改进或删除它。

在前沿模型上,受限于模型的创造能力,我们没发现多少提升有效性的空间。而在较弱的模型上,由于技能遵从度较低,我们观察到了明显的有效性不足和波动。我们把帕累托效率当作改进工作的纠偏工具。

基本思路是:“不造成伤害”。我们早期的想法是用技能把“mini”级别的模型提升到前沿水平。这个想法不错,但根据我们的经验,通常并不现实。难点在于,同一套技能要交付给所有模型。把 Anthropic Haiku 的效率提升 50%,却让 Opus 的效率下降 10%,这是一笔亏本买卖。你让 Opus 用户的智能体变得更贵了,而 Opus 的 token 价格高得多。Opus 用户抱怨是完全合理的。

因此,我们的主要精力一直放在用前沿模型做测试上。只用便宜模型来测试(虽然这看起来很有吸引力)——按照我们的发现——是一种反模式,不建议这样做。

下一步

我们正在为包维护者编写指导。已经熟悉技能评估的维护者会领先一步。通过 NuGet 包交付技能的方式变化不大。我们的重点是搞清楚如何在有限的预算下交付高质量技能,包括判断技能是否真的带来了价值。

维护者长期以来一直在包里附带 markdown 文档。这些文档一直是静态资源,而代理改变了这一点。我们需要更新面向使用者的安全指导。原则和以前一样:“只用你信任的维护者发布的包。”但代理读取 markdown 也带来了新的威胁面,需要纳入考虑。

技能交付是 NuGet 包的一个重要新选项。我们认为,它能让包实现自文档化,也更容易集成到应用中。

文章《在 NuGet 包中交付技能》最早发表于 .NET Blog。

智能体技能(agent skills)可以打包进 NuGet 包分发,借此向模型提供最新的库文档。

本文原载于 .NET Blog。

查看原文