以下内容选自我精心打造的《.NET+AI | 智能体开发进阶》课程,如需系统学习,不妨阅读原文了解详情。
前两篇我们分别聊了 Inline Skill 和 File-based Skill。

如果说 Inline Skill 解决的是“先把能力接进来”,File-based Skill 解决的是“怎么把能力沉淀成资产”,那么到了 Class-based Skill,问题就变成了:
当一个 Skill 已经足够稳定,甚至准备进入系统内部长期维护时,能不能用更清晰、更可治理的方式来组织它?
这正是 AgentClassSkill<T> 出现的意义。
它的重点,不是再换一种写法,而是把 Skill 从“可复用资产”进一步推进到“可治理模块”。
一、为什么 File-based 之后,还需要 Class-based Skill?
File-based Skill 已经解决了很多问题,比如结构化、分发、共享、跨项目复用。
但它更偏“外部化能力资产”。
而在真实项目里,总有一些 Skill 会逐步稳定下来,变成系统内部的核心能力。比如订单查询、物流换算、售后规则解释、风控校验。这类能力往往不只是“能共享”就够了,还会进一步要求:
• 更清晰的类型约束 • 更自然的单元测试 • 更稳定的重构支持 • 更容易接入 DI、日志、安全、审批
这时候,Class-based Skill 的价值就体现出来了。
它不是把 Skill 做成一个文件目录,而是直接把 Skill 做成一个强类型 C# 类。
二、AgentClassSkill<T> 到底带来了什么?
Class-based Skill 最核心的变化,是把 Skill 的关键组成重新收拢到一个类型里。
比如在物流换算场景里,一个最小但有业务意义的 Skill 可以这样写:
#pragma warning disable MAAI001using System.ComponentModel;using System.Text.Json;internal sealed class UnitConverterClassSkill : AgentClassSkill<UnitConverterClassSkill>{ public override AgentSkillFrontmatter Frontmatter { get; } = new( "unit-converter", "Convert between common logistics units."); protected override string Instructions => """ 当用户询问距离或重量换算时: 1. 先读取 conversion-table 2. 再调用 convert 3. 回复中包含原始值、系数和结果 """; [AgentSkillResource("conversion-table")] [Description("距离与重量换算系数表")] public string ConversionTable => """ miles -> kilometers = 1.60934 kilograms -> pounds = 2.20462 """; [AgentSkillScript("convert")] [Description("按 value × factor 执行换算")]private static string Convert(double value, double factor) { return JsonSerializer.Serialize(new { value, factor, result = Math.Round(value * factor, 4) }); }}这段代码已经把 Frontmatter、Instructions、资源和脚本收进了同一个类里。
这件事的意义,不只是“结构更整齐”,而是 Skill 的边界终于变得清晰了。
你不再需要把说明写在一处、资源放在一处、动作散在另一处,而是可以把这些能力收敛成一个完整对象。
从工程角度看,这种写法真正带来的不是优雅感,而是可控感。
三、它是怎么进入 Agent 运行链路的?
Class-based Skill 的底层思路其实并不复杂。
框架会通过 Attribute 去扫描这个 Skill 类的成员:
• 哪些是资源 • 哪些是脚本 • 它们分别叫什么 • 该怎么向模型描述
然后,再把这个 Skill 实例交给 AgentSkillsProvider,作为 AIContextProvider 挂到 Agent 上下文中。
例如:
var classSkill = new UnitConverterClassSkill();var provider = new AgentSkillsProvider(classSkill);var agentOptions = new ChatClientAgentOptions{ Name = "ClassSkillUnitConverterAgent", AIContextProviders = [provider]};这意味着,AgentClassSkill<T> 并不是一个孤立的类型设计,而是和整个 Agent 上下文装配机制连在一起的。
模型最终看到的,也不是“一个 C# 类”,而是这个类经过扫描、注册和注入之后形成的 Skill 语义。
所以,Class-based Skill 的本质不是“代码式 Skill”,而是“通过强类型类来声明 Skill”。
四、为什么它更适合工程化?
这一点,才是 Class-based Skill 最值得关注的地方。
因为一旦 Skill 变成标准 C# 类,你就自然拥有了很多 File-based 很难直接获得的能力:
• 编译期检查 • IDE 重构支持 • 更自然的单元测试 • 更方便接入依赖注入 • 更容易挂接日志、安全、审批等治理机制
换句话说,Class-based Skill 的优势,不在“分发”,而在“治理”。
它特别适合那些已经不再频繁变化、但需要长期演进和稳定维护的核心能力。
所以从定位上看:
• File-based Skill 更像外部化能力资产 • Class-based Skill 更像系统内部能力模块
前者强调可共享,后者强调可治理。
五、它和 File-based Skill 是替代关系吗?
不是。
这两者最容易被误解成“一个新版本替代一个旧版本”,但其实不是这个关系。
更准确地说,它们解决的是不同层面的问题:
• File-based Skill 解决“怎么把能力做成资产” • Class-based Skill 解决“怎么把能力做成模块”
一个偏外部化,一个偏工程化。
所以在真实项目里,它们很可能会长期共存:
• 可分发、可热更新、可共享的能力,适合 File-based • 稳定、核心、需要测试和治理的能力,适合 Class-based
真正成熟的思路,不是争论哪一种更高级,而是根据能力属性选择更合适的载体。
六、总结
Class-based Skill 真正补齐的,不是另一种写法,而是一条更适合长期治理的 Skill 工程路径。
它让 Skill 不再只是“能被 Agent 调用”,也开始具备“能被系统长期维护”的结构基础。
如果说 Inline Skill 代表“先接入”,File-based Skill 代表“再沉淀”,那么 Class-based Skill 代表的,就是“进一步治理”。
下一篇,我们就把这三种写法放在一起看:Inline、File-based、Class-based,到底该怎么选?
夜雨聆风