从 MCP 到 Agent Skills,AI Ready 的 .NET 10 正当时

使用 .NET File-Based Apps 编写高效 Agent Skills 脚本指南

前言

AI 工具生态正在经历一场深刻的变革。从 Anthropic 推出的 Model Context Protocol (MCP) 到最新发布的 Agent Skills,我们见证了 AI 能力扩展方式的演进:MCP 为 AI 提供了访问外部数据和工具的标准化接口,而 Agent Skills 则更进一步,专注于将人类的专业知识和工作流程封装为 AI 可理解、可执行的格式。

Agent Skills (https://agentskills.io) 中可以包含可执行脚本(位于 scripts/ 目录),官方规范支持 Python、Bash、JavaScript 等语言。而 .NET 10 引入的 File-Based Apps 特性为这些脚本的编写提供了一个强大的新选择:单个 .cs 文件即可作为完整应用运行,内联依赖声明,支持跨平台部署和 Native AOT 编译。

本文将带你了解 Agent Skills 的规范,以及如何使用 .NET File-Based Apps 编写高效、可靠的 Skill 脚本。你会发现,.NET 为 Agent Skills 的脚本开发提供了类型安全、高性能和优秀跨平台支持的独特优势。

一、什么是 Agent Skills?

1.1 核心概念

Agent Skills 是一种轻量级、开放的格式,用于扩展 AI Agent 的能力和专业知识。本质上,一个 Skill 就是一个包含 SKILL.md 文件的文件夹

该文件包含:

  • 元数据namedescription(最少必需)
  • 指令:告诉 Agent 如何执行特定任务的 Markdown 文档
  • 可选资源:脚本、模板、参考文档等

Skills 的核心价值在于:

  • 专业知识封装:将特定领域的程序化知识(procedural knowledge)和公司/团队/用户特定的上下文打包
  • 按需加载:Agent 启动时只加载 Skill 的 namedescription,任务匹配时才加载完整指令
  • 可执行能力:可以包含脚本、工具,扩展 Agent 的实际操作能力
  • 版本化管理:Skills 就是文件,易于编辑、版本控制和分享

1.2 Agent Skills 能做什么?

根据官方文档,Agent Skills 的主要应用场景包括:

领域专长(Domain Expertise)

将专业知识打包为可复用的指令:

  • 法律审查流程:标准化的合同审查检查清单和审批流程
  • 数据分析管道:统一的数据清洗、转换、可视化工作流
  • 代码审查规范:团队的编码标准、安全检查、性能优化指南
  • 医疗诊断协议:基于症状的诊断路径和治疗建议流程

新能力(New Capabilities)

赋予 Agent 原本不具备的操作能力:

  • 创建演示文稿:根据内容自动生成 PPT/Keynote
  • 构建 MCP 服务器:生成 Model Context Protocol 服务器代码
  • 分析数据集:执行统计分析、生成可视化图表
  • 处理 PDF 文档:拆分、合并、提取文本、填写表单

可重复工作流(Repeatable Workflows)

将多步骤任务转化为一致且可审计的流程:

  • CI/CD 流水线:标准化的构建、测试、部署步骤
  • 客户入职流程:账户创建、权限配置、培训材料发送
  • 月度报告生成:数据收集、分析、报告编写、分发
  • 代码重构任务:识别问题、建议改进、执行修改、验证测试

跨工具互操作(Interoperability)

一次编写,多处使用:

  • GitHub Copilot 中辅助编码
  • Cursor 中进行项目重构
  • Claude 中进行文档写作
  • Goose 中执行自动化任务

这种标准化使得组织知识可以在不同工具间无缝流转。

1.3 谁在支持 Agent Skills?

目前已有多个主流 AI 开发工具支持该标准:

二、Agent Skills 规范解读

2.1 基本目录结构

一个最简单的 Skill 只需要包含 SKILL.md 文件:

skill-name/
└── SKILL.md          # 必需

可选的支持目录:

skill-name/
├── SKILL.md          # 必需:技能描述和使用说明
├── scripts/          # 可选:可执行脚本
│   └── tool.py
├── references/       # 可选:详细参考文档
│   └── REFERENCE.md
└── assets/          # 可选:静态资源
    └── template.json

2.2 SKILL.md 格式规范

SKILL.md 文件包含两部分:YAML frontmatterMarkdown 正文

Frontmatter 必需字段

---
name: skill-name
description: 描述技能功能和使用场景的文字,应包含帮助 Agent 识别相关任务的关键词
---

name 字段规则

  • 长度:1-64 字符
  • 字符:仅允许小写字母、数字和连字符 (a-z, -)
  • 不能以连字符开头或结尾
  • 不能包含连续的连字符 (--)
  • 必须与父目录名完全一致

description 字段规则

  • 长度:1-1024 字符
  • 应说明技能的功能和使用时机
  • 包含关键词帮助 Agent 识别适用场景

可选字段

---
name: pdf-processing
description: Extract text from PDFs and merge multiple documents
license: MIT
compatibility: Requires Python 3.8+ and poppler-utils
metadata:
  author: your-org
  version: "1.0.0"
---

2.3 渐进式信息披露

Agent Skills 采用渐进式加载策略来优化 token 使用:

  1. 元数据阶段 (~100 tokens):启动时加载所有 Skills 的 namedescription
  2. 指令阶段 (<5000 tokens 推荐):激活 Skill 时加载完整的 SKILL.md
  3. 资源阶段 (按需):仅在需要时加载 scripts/references/assets/ 中的文件

最佳实践

  • 保持 SKILL.md 在 500 行以内
  • 将详细参考资料移至 references/ 目录
  • 避免深层嵌套的文件引用

三、使用 .NET File-Based Apps 编写 Agent Skills 脚本

在进入具体实现之前,我们先来看看为什么 .NET File-Based Apps 是编写 Agent Skills 中 scripts/ 目录下可执行脚本的优秀选择。

3.0 .NET 作为脚本语言的独特优势

根据 Agent Skills 规范,scripts/ 目录中的可执行代码应该自包含或明确记录依赖、包含有用的错误消息、优雅处理边界情况。常见的脚本语言包括 Python、Bash、JavaScript,而 .NET File-Based Apps 为此提供了一个强大的替代方案:

对比其他脚本语言方案

在为 Agent Skills 编写 scripts/ 目录下的可执行脚本时,.NET File-Based Apps 与其他常用语言的对比:

特性 .NET File-Based Apps Python 脚本 Node.js 脚本
单文件运行 dotnet file.cs python file.py node file.js
依赖声明 ✅ 文件内声明 ⚠️ 需 requirements.txt ⚠️ 需 package.json
类型安全 ✅ 编译时检查 ❌ 运行时错误 ⚠️ 需 TypeScript
性能 ✅ Native AOT 编译 ⚠️ 解释执行 ⚠️ JIT 编译
跨平台部署 ✅ 单个可执行文件 ⚠️ 需 Python 运行时 ⚠️ 需 Node.js 运行时
企业级库支持 ✅ 丰富的 NuGet 生态 ✅ PyPI 生态 ✅ npm 生态
AI Agent 可读性 ✅ 结构清晰、自文档化 ✅ 简洁 ✅ 简洁

.NET 的三大杀手锏

1. 真正的自包含
依赖声明直接写在代码文件头部,Agent 一眼就能看懂需要什么包、什么版本,无需查找外部配置文件。

#:package PdfSharpCore@1.3.65
#:package Spectre.Console@0.49.1

2. 从开发到生产无缝过渡
开发时直接运行 .cs 文件,生产时一键发布为 Native AOT 可执行文件,启动速度可达毫秒级,内存占用极小。

3. AI 友好的代码结构
.NET 的强类型和清晰的语法结构,让 AI Agent 更容易理解代码意图、发现潜在问题、提出改进建议。

3.1 什么是 .NET File-Based Apps?

.NET 10 引入的 File-Based Apps 特性允许将单个 .cs 文件作为完整的应用程序运行,无需传统的项目文件(.csproj)。关键特性包括:

  • 单文件即应用:一个 .cs 文件包含完整程序
  • 内联依赖声明:通过特殊注释声明 NuGet 包
  • 直接运行dotnet file.cs 即可执行
  • 支持发布:可以发布为独立可执行文件或 Native AOT
  • 零配置:无需 csprojsln 等项目文件

3.2 File-Based Apps 如何适配 Agent Skills 需求?

Agent Skills 规范强调「简洁」、「自包含」、「可理解」,.NET File-Based Apps 的设计理念与之完美契合:

适配点 1:渐进式复杂度

// 入门:10 行代码的简单工具
#!/usr/bin/env dotnet
if (args.Length == 0) { Console.WriteLine("Hello, Agent!"); return; }
Console.WriteLine($"Processing: {args[0]}");

// 进阶:添加依赖和错误处理
#:package Newtonsoft.Json@13.0.3
using Newtonsoft.Json;
try { /* 处理逻辑 */ }
catch (Exception ex) { Console.Error.WriteLine(ex.Message); return 1; }

// 生产:发布为高性能可执行文件
#:property PublishAot=true
// 一行命令:dotnet publish -r win-x64

从原型到生产,同一个文件,逐步迭代,没有项目结构的重构成本。

适配点 2:AI Agent 的理解成本

Python 方案:Agent 需要找到并理解多个文件

my-skill/
├── tool.py          # Agent 需要读取
├── requirements.txt # Agent 需要读取
└── README.md        # Agent 需要读取

.NET 方案:一个文件包含所有信息

my-skill/
└── scripts/
    └── tool.cs      # 依赖、逻辑、配置全在这里

Agent 只需读取一个文件,就能了解:

  • 需要什么依赖(#:package
  • 如何运行(#!/usr/bin/env dotnet
  • 做什么事(代码逻辑)
  • 如何部署(#:property

适配点 3:企业级可靠性

.NET 的类型系统在 Agent 驱动的开发中尤为重要:

// 编译时就能发现错误,而不是运行时崩溃
string pdfPath = args[0];  // Agent 知道这是字符串
int pageCount = GetPageCount(pdfPath);  // Agent 知道返回值是整数

// Python 中相同的错误可能运行时才暴露
# pdf_path = args[0]  # 类型不明确
# page_count = get_page_count(pdf_path)  # 返回值类型不明确

这意味着 Agent 在生成或修改代码时,有更多的安全护栏。

适配点 4:性能与资源效率

AI Agent 可能频繁调用 Skills,启动性能至关重要:

# Python 脚本启动
$ time python tool.py input.pdf
real    0m0.234s  # 需要加载解释器

# .NET File-Based App 启动
$ time dotnet tool.cs input.pdf
real    0m0.089s  # JIT 编译

# Native AOT 编译后
$ time ./tool input.pdf
real    0m0.012s  # 接近原生 C++ 性能

对于 Agent 执行的自动化任务,这种性能差异会累积成显著的时间节省。

3.3 实战案例:split-pdf Skill

让我们以一个实际的 PDF 拆分工具为例,演示如何开发符合规范的 Agent Skill。

步骤 1:创建目录结构

mkdir -p split-pdf/scripts
cd split-pdf

步骤 2:编写 SKILL.md

SKILL.md 是 Agent Skill 的核心,包含元数据和使用说明。创建 SKILL.md 文件:

---
name: split-pdf
description: Split PDF files into separate single-page documents or extract specific page ranges. Use when you need to divide a PDF into multiple files, extract particular pages, or process PDF pages individually. Works with multi-page PDF documents.
license: MIT
---

# Split PDF

将 PDF 文件拆分为多个单页文件或提取指定页面范围。

## 使用场景

- 将多页 PDF 拆分为独立的单页文件
- 提取 PDF 的特定页面范围
- 需要单独处理 PDF 各个页面时

## 使用方法

使用 `scripts/split-pdf.cs` 脚本进行 PDF 拆分:

### 拆分页面
# 拆分所有页面
dotnet scripts/split-pdf.cs input.pdf output-dir/
# 拆分第 1-5 页
dotnet scripts/split-pdf.cs input.pdf output-dir/ 1-5


## 输出格式

拆分后的文件命名格式:`{原文件名}_page_{页码}.pdf`

## 依赖项

- PdfSharpCore 1.3.65 - PDF 操作核心库
- Spectre.Console 0.49.1 - 美化的控制台输出



**注意**:
- `name` 必须与目录名 `split-pdf` 完全一致
- `description` 包含关键词 "split", "PDF", "pages" 帮助 Agent 识别场景

步骤 3:编写 File-Based App 脚本

scripts/ 目录下创建 .NET File-Based App 脚本 split-pdf.cs`:

#!/usr/bin/env dotnet
#:package PdfSharpCore@1.3.65
#:package Spectre.Console@0.49.1
#:property PublishAot=true

using PdfSharpCore.Pdf;
using PdfSharpCore.Pdf.IO;
using Spectre.Console;
using System;
using System.IO;

// ==================== 参数校验 ====================
if (args.Length < 2)
{
    AnsiConsole.MarkupLine("[red]错误: 参数不足[/]");
    AnsiConsole.MarkupLine("[yellow]用法: dotnet split-pdf.cs <PDF文件> <输出目录> [页面范围][/]");
    return 1;
}

var pdfPath = args[0];
var outputDir = args[1];
var pageRange = args.Length >= 3 ? args[2] : null;

// 验证文件
if (!File.Exists(pdfPath))
{
    AnsiConsole.MarkupLine($"[red]错误: 文件不存在: {pdfPath}[/]");
    return 1;
}

// 创建输出目录
Directory.CreateDirectory(outputDir);

// ==================== 拆分 PDF ====================
try
{
    using var inputDocument = PdfReader.Open(pdfPath, PdfDocumentOpenMode.Import);
    var totalPages = inputDocument.PageCount;
    
    // 解析页面范围
    int startPage = 1, endPage = totalPages;
    if (!string.IsNullOrEmpty(pageRange))
    {
        var parts = pageRange.Split('-');
        if (parts.Length == 2 && 
            int.TryParse(parts[0], out startPage) && 
            int.TryParse(parts[1], out endPage))
        {
            startPage = Math.Max(1, Math.Min(startPage, totalPages));
            endPage = Math.Max(startPage, Math.Min(endPage, totalPages));
        }
    }

    var baseName = Path.GetFileNameWithoutExtension(pdfPath);
    
    await AnsiConsole.Progress()
        .StartAsync(async ctx =>
        {
            var task = ctx.AddTask("拆分 PDF 页面", maxValue: endPage - startPage + 1);

            for (int i = startPage; i <= endPage; i++)
            {
                using var outputDocument = new PdfDocument();
                outputDocument.AddPage(inputDocument.Pages[i - 1]);
                
                var outputPath = Path.Combine(outputDir, $"{baseName}_page_{i:D3}.pdf");
                outputDocument.Save(outputPath);
                
                task.Increment(1);
                await Task.CompletedTask;
            }
        });

    AnsiConsole.MarkupLine($"[green]✅ 拆分完成!已生成 {endPage - startPage + 1} 个文件[/]");
    return 0;
}
catch (Exception ex)
{
    AnsiConsole.MarkupLine($"[red]❌ 错误: {ex.Message}[/]");
    return 1;
}

关键要素解析

  1. Shebang 行#!/usr/bin/env dotnet - 使脚本可在 Unix 系统直接执行
  2. 依赖声明#:package 指令声明 NuGet 包及版本
  3. 发布配置#:property PublishAot=true - 支持 Native AOT 编译
  4. 顶层语句:无需 Main 方法,直接编写逻辑
  5. 返回值:使用 return 返回退出码

四、测试与验证

4.1 本地测试

直接运行测试

# 准备测试 PDF 文件
cd split-pdf

# 测试拆分所有页面
dotnet scripts/split-pdf.cs test.pdf ./output/

# 测试拆分指定范围
dotnet scripts/split-pdf.cs test.pdf ./output/ 1-3

# 验证输出
ls ./output/
# 应该看到:test_page_001.pdf, test_page_002.pdf, test_page_003.pdf

错误处理测试

# 测试文件不存在
dotnet scripts/split-pdf.cs nonexistent.pdf ./output/

# 测试参数不足
dotnet scripts/split-pdf.cs

# 测试无效页面范围
dotnet scripts/split-pdf.cs test.pdf ./output/ 100-200

4.2 Agent 集成测试

在支持 Agent Skills 的环境中测试(如 GitHub Copilot):

  1. 将 Skill 放置在 .github/skills/ 目录下
  2. 重启或刷新 Agent
  3. 测试 Agent 是否能发现和使用该 Skill
用户提问:请帮我把这个 PDF 文件拆分成单独的页面

Agent 行为:
1. 识别任务涉及 PDF 拆分
2. 查找并激活 split-pdf skill
3. 读取 SKILL.md 了解使用方法
4. 执行:dotnet scripts/split-pdf.cs document.pdf ./pages/
5. 向用户报告结果

五、最佳实践

5.1 脚本设计原则

清晰的参数设计

// ❌ 不好:参数含义不明
dotnet tool.cs input output 1

// ✅ 好:参数含义清晰
dotnet split-pdf.cs document.pdf ./pages/ 1-10

友好的错误提示

if (args.Length < 2)
{
    AnsiConsole.MarkupLine("[red]错误: 参数不足[/]");
    AnsiConsole.MarkupLine("[yellow]用法: dotnet split-pdf.cs <PDF文件> <输出目录> [页面范围][/]");
    AnsiConsole.MarkupLine("[gray]示例: dotnet split-pdf.cs input.pdf ./output/ 1-5[/]");
    return 1;
}

进度反馈

使用 Spectre.Console 提供直观的进度显示:

await AnsiConsole.Progress()
    .StartAsync(async ctx =>
    {
        var task = ctx.AddTask("处理中", maxValue: totalItems);
        foreach (var item in items)
        {
            // 处理逻辑
            task.Increment(1);
        }
    });

5.2 依赖管理

选择稳定的包版本

// ✅ 指定明确版本
#:package PdfSharpCore@1.3.65
#:package Spectre.Console@0.49.1

// ❌ 避免使用不稳定版本
#:package SomePackage@*
#:package BetaPackage@2.0.0-beta

最小化依赖

只引入必需的包,减少潜在的兼容性问题。

5.3 文档编写

Description 关键词策略

description 中包含:

  • 动作关键词:split, extract, convert, merge
  • 领域关键词:PDF, document, pages
  • 场景关键词:when you need to divide, when working with
# ✅ 好的 description
description: Split PDF files into separate single-page documents or extract specific page ranges. Use when you need to divide a PDF into multiple files, extract particular pages, or process PDF pages individually.

# ❌ 不够好
description: PDF tool for splitting.

提供清晰的示例

SKILL.md 正文中提供:

  • 常见用例的示例
  • 不同参数组合的效果
  • 预期的输出格式

5.4 跨平台兼容性

路径处理

// ✅ 使用 Path.Combine
var outputPath = Path.Combine(outputDir, $"{baseName}_page_{i:D3}.pdf");

// ❌ 避免硬编码路径分隔符
var outputPath = outputDir + "\\" + baseName + "_page_" + i + ".pdf";

编码处理

// 确保控制台正确显示 Unicode
Console.OutputEncoding = System.Text.Encoding.UTF8;

六、进阶话题

6.1 支持多个相关工具

可以在一个 Skill 中包含多个相关脚本:

pdf-toolkit/
├── SKILL.md
└── scripts/
    ├── split.cs
    ├── merge.cs
    └── extract-text.cs

SKILL.md 中说明每个工具的用途和使用场景。

6.2 使用 References 目录

对于复杂的 Skill,将详细文档分离:

data-analysis/
├── SKILL.md           # 简要说明和快速开始
├── scripts/
│   └── analyze.cs
└── references/
    ├── REFERENCE.md   # 详细 API 参考
    ├── examples.md    # 更多示例
    └── algorithms.md  # 算法说明

SKILL.md 中引用:

详细的 API 参考请见 [REFERENCE.md](references/REFERENCE.md "REFERENCE.md")。

6.3 Native AOT 优化

对于性能敏感的工具,启用 Native AOT:

#:property PublishAot=true
#:property InvariantGlobalization=true  // 减小体积

发布时使用:

dotnet publish scripts/tool.cs -r win-x64 --property:PublishAot=true

Native AOT 优势

  • 极快的启动时间
  • 较小的内存占用
  • 无需安装 .NET 运行时
  • 单个可执行文件

6.4 团队协作

将 Skills 放入版本控制:

.github/
└── skills/
    ├── split-pdf/
    ├── data-analysis/
    └── code-review/

在团队 README 中说明:

  • 如何安装 Skills
  • 如何贡献新 Skills
  • 编码规范和测试要求

七、意义与展望

7.1 对开发者的价值

Agent Skills 为开发者带来三大核心价值:

能力复用:一次编写,在 Copilot、Cursor、Claude 等多个 Agent 产品中使用,还可跨团队共享或通过 GitHub 公开发布。

知识沉淀:将团队最佳实践固化为版本化的 Skills,如代码审查规范、部署流程、数据分析模板等,确保工作流程的一致性。

提升效率:通过明确的指导让 Agent 更准确地执行复杂任务,减少试错和修正,提供一致的输出质量。

7.2 .NET File-Based Apps 的机遇

File-Based Apps 为 .NET 带来了新的应用场景:

降低门槛:从复杂的项目结构到单文件脚本,让 Python、Node.js 开发者也能轻松尝试 .NET。

AI 协作优势:强类型系统帮助 AI 更准确理解代码、即时编译反馈加速修正、丰富的 API 文档提升 AI 可读性。

性能差异化:Native AOT 编译在批量任务中的性能优势(启动快 90%+),不仅提升用户体验,也降低云端成本。

生态拓展:.NET 开发者可以将企业实践打包为 Skills,在 AI Agent 生态中展现 .NET 价值,同时 .NET 的实践经验也能反哺 Agent Skills 标准演进。

7.3 未来展望

Agent Skills 生态正在快速发展,可以期待:

  • Skills 市场:类似 npm 的包管理器和分发平台
  • 工具链集成:IDE 内置 Agent Skills 模板和验证工具
  • 应用拓展:从个人工具到企业知识库、教育培训、行业解决方案

.NET File-Based Apps 在这个生态中的定位清晰:作为 Agent Skills 脚本的强类型、高性能选择,为开发者提供从原型到生产的无缝体验。

八、总结

通过本文的探索,我们看到 .NET File-Based Apps 作为 Agent Skills 脚本语言的独特价值:单文件自包含、强类型安全、Native AOT 高性能,完美匹配 Agent Skills 规范对脚本的要求。

核心收获

本文从 Agent Skills 规范入手,通过 split-pdf 实战案例,展示了如何用 .NET File-Based Apps 编写高质量的 Skill 脚本,并探讨了其在 AI 时代的应用机遇。

开始行动

对于 .NET 开发者:用熟悉的 C# 为 Agent Skills 编写脚本,将专业知识封装为可复用的 Skills,在 AI 时代发挥 .NET 的价值。

对于其他技术栈开发者:.NET File-Based Apps 和 Python 一样简洁,但提供了类型安全和 Native AOT 性能。值得尝试:winget install Microsoft.DotNet.SDK.10 然后创建你的第一个 .cs 脚本。

下一步:动手实现你的第一个 Agent Skill,将专业知识转化为可复用的能力,在 AI 时代发挥更大价值。

参考资源


posted @ 2025-12-22 11:58  「圣杰」  阅读(459)  评论(1)    收藏  举报