
许多开发者在初次接触 Microsoft Agent Framework (MAF) 中的 Agent Skills 时,容易将其简单理解为“为大模型增加一份 SKILL.md 说明书”。
这个理解没错,但若止步于此,就很容易低估其背后真正的工程价值。深入 MAF 源码,你会发现它所做的事远不止“读取一个技能目录”那么简单,而是在进行一项更具工程意义的抽象:将 Skill 从一个静态内容包,转化为 Agent Runtime 中可发现、可加载、可读取、可执行、可治理的能力单元。
因此,本文不再从“如何编写 SKILL.md”入门,而是换个角度,通过源码分析来回答几个更关键的问题:
- MAF 是如何抽象 Agent Skills 的?
- 为何没有将 Skill 仅视为一堆 Markdown 文件?
- 相比标准 agentskills.io 协议,它在工程化上做了哪些扩展?
- 在真实业务(如多租户电商客服助理)中,这套抽象有何价值?
如果你正在从事 .NET + AI Agent 的工程落地,这几个问题值得深入探讨。
一、先看全貌:MAF Agent Skills 的五层架构
在深入细节前,把握整体架构至关重要。否则,面对 AgentSkillsSource、Builder、Provider 等类时,容易只记其名,不解其意。
上图清晰地展示了核心问题的答案:一个放在目录中的 SKILL.md,是如何一步步变为 Agent 运行时中的上下文和工具能力的?
从左至右,MAF 的处理流程大致分为五层:
- 协议与内容层:承接 agentskills.io 标准定义的目录结构与内容规范。
- 统一技能模型层:将文件、代码、内存中的 Skill 收敛为统一的对象抽象。
- 技能来源层:解决 Skill 从何处来,不再局限于文件系统。
- 组装治理层:解决 Skill 如何被组合、过滤、去重、审批和治理。
- 运行时注入层:最终将 Skill 转换为 AIContext 的 Instructions 和 Tools,注入 Agent Runtime。
下文将按这五层逐一拆解。
二、第一层:协议与内容层——对齐标准,奠定基础
首先明确:MAF 并非另起炉灶,而是明确建立在 agentskills.io 的“渐进式信息披露”(Progressive Disclosure)模型之上。
AgentSkillsProvider 的注释已明确指出,它实现了 Agent Skills 的四阶段模式:
- Advertise:先将技能的名称和描述注入系统提示。
- Load:当模型判断某技能相关时,通过
load_skill 工具加载完整内容。
- Read resources:如需更详细的补充资料,再按需读取
references / assets 中的资源。
- Run scripts:如果技能中定义了脚本,则再按需执行。
这意味着,在 MAF 的视角里,Skill 从来不是“一次性全量塞进上下文”的内容包,而是一个分阶段暴露的信息单元。这解释了为何一个 Skill 目录会被拆分为 SKILL.md、references/、assets/、scripts/,且 frontmatter 只放置最简化的发现信息。
在 AgentFileSkillsSource 中,也能看到对标准目录结构的显式承接:
- 默认脚本目录:
scripts
- 默认资源目录:
references, assets
- 默认脚本扩展名:
.py, .js, .sh, .ps1, .cs, .csx
- 默认资源扩展名:
.md, .json, .yaml, .yml, .csv, .xml, .txt
这说明 MAF 并非简单“支持 SKILL.md”,而是将标准中的目录语义直接映射到了运行时行为上。简言之,在 agentskills.io 中,目录结构是规范;在 MAF 中,目录结构开始成为运行时协议的一部分。
本层价值:解决了“Skill 如何被标准化描述和渐进式披露”的问题,为后续的统一抽象和运行时注入提供了共同基础。
三、第二层:统一技能模型层——Skill的本体是对象,而非文件
如果说第一层解决“如何描述”,那么第二层则解决“框架内部如何理解”。
MAF 在此处的设计非常关键:它没有将 Skill 绑定在文件系统上,而是先抽象出一套统一的对象模型。核心对象可概括为四个:
- AgentSkill:统一的 Skill 抽象本体。
- AgentSkillFrontmatter:Skill 的发现元数据,如 name、description、compatibility 等。
- AgentSkillResource:Skill 的资源抽象,对应
references/assets 这类按需读取的内容。
- AgentSkillScript:Skill 的脚本抽象,对应
scripts 目录或代码中的可执行能力。
这层抽象背后的核心意图是:Skill 的本体是能力对象,文件只是其载体之一。一旦 Skill 被抽象为对象,MAF 就能自然支持多种来源(文件、内存、代码、自定义远程),而无需上层运行时关心其具体来源。
从职责上看,这四个对象对应了 Skill 的三个核心维度:
- Frontmatter 负责“发现”:模型初期只需知道有哪些技能及其大致功能。
- Content 负责“完整指令”:决定使用某技能时,再获取其完整内容。
- Resources / Scripts 负责“知识扩展与动作执行”:资源提供补充资料,脚本提供实际可执行能力。
用一句话概括:在 MAF 中,一个 AgentSkill 本质上是 Prompt、Knowledge、Action 的统一容器。这与许多人理解的“Skill = 一段提示词模板”已完全不同。
本层价值:解决了“如何用统一对象模型承载不同来源、不同形态的 Skill 能力”的问题。
四、第三层:技能来源层——工程化必须有的 Source 抽象
统一模型解决了“是什么”,接下来要解决更工程化的问题:“从哪里来”?这就是 AgentSkillsSource 抽象存在的意义。
其基类非常直接,核心方法只有一个:GetSkillsAsync()。这个抽象的价值在于将 “技能的供给方式” 与 “技能的使用方式” 彻底解耦。目前,MAF 至少支持以下几类典型来源:
1. 文件来源:AgentFileSkillsSource
最贴近标准的一种。它会递归扫描目录中的 SKILL.md,解析 frontmatter,并进一步发现资源和脚本。细节上包含搜索深度限制(默认2层)、frontmatter 正则提取、扩展名校验以及路径安全校验(防止路径遍历和符号链接逃逸),体现了生产级的边界控制。
2. 内存来源:AgentInMemorySkillsSource
这意味着 Skill 可完全不依赖磁盘,直接来自运行时内存中的对象集合。这对于配置中心下发、按租户动态生成、测试环境快速注入等场景极具价值。
3. 代码技能:AgentInlineSkill / AgentClassSkill
此处最能体现 MAF 的工程思维。它允许直接用 C# 代码定义 Skill,其中 AgentInlineSkill 适合快速构建,AgentClassSkill 则更适合封装成面向对象的、可维护性更高的能力类。
至此,MAF 完成了一次重要跨越:将 agentskills.io 解决的 “技能如何描述”,进一步扩展为 “技能如何供给”。AgentSkillsSource 这个抽象为诸多企业场景预留了接口,如从数据库、Git、对象存储、配置平台或 SaaS 平台动态获取技能。因此,Source 抽象看似普通,实则是整个工程化扩展能力的起点。
本层价值:解决了“Skill 如何被供给”的问题,而 agentskills.io 标准更多解决的是“Skill 如何被描述”。
五、第四层:组装治理层——Builder 体现核心工程价值
如果问 MAF Agent Skills 设计中最具工程味道的一层,我会选 Builder。
很多框架可能止步于“扫描并加载技能”,但 MAF 继续向前,思考了一个更接近真实项目的问题:当技能来自多个来源、层级、租户和团队时,应如何装配和治理? 于是便有了 AgentSkillsProviderBuilder。
从源码看,该 Builder 提供了 UseFileSkill、UseSource、UseFilter、UseScriptApproval 等一系列链式方法。它并非仅仅为了调用优雅,而是真正将技能的装配过程 管道化(Pipeline)。
其内部的构建流水线清晰:
- 解析多个来源。
- 用
AggregatingAgentSkillsSource 聚合(如果存在多个来源)。
- 应用配置的 Filter 进行过滤。
- 用
DeduplicatingAgentSkillsSource 统一去重(遵循 first occurrence wins 原则)。
- 最终交给
AgentSkillsProvider 注入运行时。
其中最值得展开的三个点是:
1. Aggregate:技能集合是“装配结果”,非“目录扫描结果”
在真实系统中,技能往往来源多样(平台默认、团队共享、租户自定义、会话动态)。若框架没有 Aggregate 层,技能就只是孤立目录的内容集合,而非真正的、可组合的能力注册表。
2. Filter:技能暴露需要治理
Builder 支持 UseFilter,意味着并非所有被发现的技能都会暴露给 Agent。这在企业环境中至关重要,例如控制租户可见性、敏感技能审核启用、按角色过滤等。Filter 让技能暴露从“静态存在”变为“策略决策”。
3. Deduplicate:重名冲突是常态,而非异常
Builder 在最后统一去重。这在多来源系统中是一个非常实用的策略点。一旦进入企业场景,平台默认技能、客户自定义技能、环境临时补丁技能之间的重名冲突几乎是必然的。明确的去重策略保障了运行时行为的可控性。
因此,Builder 这一层的真正价值在于 “可治理”。
本层价值:解决了“多个 Skill 来源如何被组合、筛选、去重和治理”的问题,这是 MAF 相对标准最明显的工程化扩展之一。
前面几层均是“准备”,运行时的关键问题是:这些 Skill 最终如何进入 Agent? 答案在 AgentSkillsProvider。
AgentSkillsProvider 继承自 AIContextProvider,这一定位非常关键。它表明 MAF 并非将 Skill 视为外挂配置,而是将其纳入了 AIContext 的核心构建链路。它最终生成一个包含两部分的 AIContext:
- Instructions:将技能的摘要信息注入系统提示。
- Tools:将
load_skill、read_skill_resource、run_skill_script 等工具暴露给模型。
这意味着,Skill 在运行时会同时影响 Prompt 和 Tooling,这正是渐进式信息披露能落地的关键。默认的提示模板会明确教导模型如何按顺序(load_skill -> 读资源 -> 执行脚本)使用技能机制。
MAF 的高明之处在于:它不仅是提供工具,更是将 “如何以渐进方式使用 Skill”的行为规范 一同注入给了模型。整个运行链路体现了 MAF 在运行时做对的三件事:
- 只先暴露最小信息:先暴露技能名和描述,而非全文。
- 将“完整内容”和“执行动作”工具化:让技能成为可按需拉取和调用的运行时能力。
- 将 Skill 正式接入 AIContextProvider 体系:使其成为 Agent 上下文构建的一等公民。
本层价值:解决了“Skill 如何真正进入 Agent Runtime,并以 Prompt + Tool 的方式被模型按需使用”的问题。
七、安全与边界——生产级框架的考量
一套能力能否进入生产,往往取决于其“边界处理”。MAF 在 Agent Skills 上的设计已远超“玩具级支持”。在 AgentFileSkillsSource 中可见多处安全与边界控制:
- 搜索深度限制:目录递归搜索限制在最多 2 层,避免范围失控。
- Frontmatter 校验:需匹配特定结构并校验关键字段。
- 扩展名白名单:通过默认和可配置的白名单控制可加载的资源与脚本类型。
- 路径逃逸防护:显式检查 path traversal 和 symlink escape,将目录边界视为安全边界。
- Script Approval Gate:Builder 支持
UseScriptApproval,将脚本执行视为需要审批和治理的动作,而非默认放开。
这种设计体现了 AI Agent 工程化的重要原则:越是可执行能力,越需强调约束。标准定义了 scripts 能做什么,而 MAF 进一步定义了 scripts 在什么边界内才能做。
本层价值:解决了“Skill 在生产环境中如何被安全发现、受控读取和谨慎执行”的问题。
八、从 agentskills.io 到 MAF:到底扩展了什么?
回到更高层的问题:既然 agentskills.io 已定义了格式,MAF 额外做了什么?
1. agentskills.io 更关注“协议”
它解决:Skill 长什么样、SKILL.md 怎么写、目录如何组织、模型应如何渐进式获取信息。
2. MAF 更关注“运行时与工程化”
它解决:Skill 如何被统一建模、如何来自多个来源、如何被聚合/过滤/去重、如何注入 AIContext、如何通过工具按需读取和执行、如何被安全审批和边界控制。
更凝练的概括是:agentskills.io 提供的是技能协议;MAF 提供的是技能运行时。 二者是上下层关系,协议解决互通,运行时解决落地。一个标准是否有生命力,往往在于是否有框架愿意将其推进到运行时核心链路中。从这个角度看,MAF 的价值在于将 Skill 从文件格式,真正推进成了 .NET Agent Runtime 里的能力机制。
九、案例:多租户电商客服助理的落地场景
理论需结合实践。以“多租户电商客服助理”为例,平台上通常并存三类能力:
- 平台公共技能:如
customer-service-baseline, customer-risk-control,所有租户共用。
- 租户定制技能:如
tenant-a-order-query, tenant-b-refund-process。
- 动态运行时技能:如临时活动、节假日规则。
若无 Skill 抽象,系统易退化将所有规则、SOP、接口说明塞进一大段 Prompt,导致上下文冗长、租户隔离困难、难以治理、执行能力不足。
而 MAF 的 Skill 抽象可逐一拆解这些问题:
1. 用 Source 解决“分层供给”
- 平台技能来自
/skills/platform
- 租户技能来自
/skills/{tenantId}
- 动态技能来自内存或自定义 Source
2. 用 Builder 解决“多来源装配”
伪代码示例如下:
var provider = new AgentSkillsProviderBuilder()
.UseFileSkills("./skills/platform", scriptRunner: platformRunner)
.UseFileSkills($"./skills/{tenantId}", scriptRunner: tenantRunner)
.UseFilter(skill =>
skill.Frontmatter.Name.StartsWith("customer-") ||
skill.Frontmatter.Name.StartsWith($"{tenantId}-"))
.UseScriptApproval()
.Build();
关键点:平台与租户技能同时装配、Filter 控制可见性、Deduplicate 处理命名冲突、ScriptApproval 控制动作执行。
3. 用 Progressive Disclosure 解决“按需加载”
以用户咨询“订单未发货,能否补偿?”为例,Agent 无需预加载所有规则,可按需走以下路径:
load_skill("tenant-a-order-service")
read_skill_resource("tenant-a-order-service", "warehouse-sla")
run_skill_script("tenant-a-order-service", "query-order-status", args)
- 若涉及补偿,再加载
tenant-a-refund-service 相关技能与资源。
这正好是前文五层架构的完整业务落地。在此场景下,Skill 真正成为了业务能力单元:规则可封装、知识可读取、动作可执行、可见范围可治理、租户可隔离、平台能力可复用。可以说,Skill 的本质已从“给模型看的说明书”,演进为 封装了平台规则、租户知识、实时查询能力与安全边界的运行时能力模块。
十、总结
纵观 MAF 源码,其对 Agent Skills 做的最重要的事,是 将其推进到了运行时核心。
它先以 agentskills.io 标准为基础,再通过统一对象模型将 Skill 从文件提升为能力抽象,接着用 Source 解决供给、用 Builder 解决装配治理,最后通过 Provider 将 Skill 作为 AIContext 的一部分正式注入 Agent Runtime。
回到开头的问题:MAF 中的 Agent Skills 是否只是“多了一份 SKILL.md”?答案很明确:不是。它真正补齐的是一条从标准化描述,到统一建模、多来源供给、运行时注入、按需执行与安全治理的完整工程链路。
如果说 agentskills.io 解决了“Skill 应如何被标准化描述”,那么 MAF 真正推进的,是 “Skill 如何在 .NET Agent Runtime 中被工程化供给、装配、注入与治理”。这件事的意义,远比“支持一个新协议”更为深远,因为它意味着组织知识、业务规则与执行能力,开始以标准化、可组合、可治理的方式进入 人工智能 Agent 的运行时核心。对于从事 .NET Agent 工程化落地的开发者而言,这无疑是值得重点关注的方向。欢迎在云栈社区交流更多关于智能体开发的实践与思考。