
目录 第一部分什么是 Agent Skills1.1 问题引入 — Agent 如何获取领域知识1.2 Agent Skills 规范1.3 Agent Skills vs Function Calling vs RAG 第二部分SKILL.md 文件剖析2.1 目录结构2.2 SKILL.md 结构2.3 安全机制 第三部分源码架构深度解析3.1 核心类关系3.2 FileAgentSkillsProvider — 技能提供者3.3 FileAgentSkillLoader — 技能发现与加载3.4 工具调用链路分析 第四部分实战 — 费用报销 Agent场景说明步骤 1环境准备步骤 2准备 Skill 文件步骤 3加载 Skills 并创建 Agent步骤 4测试 — 费用政策问答步骤 5测试 — 多轮对话生成报销报告步骤 6多轮跟进 — 补充缺失信息 第五部分进阶用法5.1 自定义技能摘要提示词5.2 多技能注册5.3 Skills 文件夹结构最佳实践5.4 实战创建第二个 Skill 并测试多技能 Agent测试多技能智能路由 总结核心要点实际价值️ 课后练习 第一部分什么是 Agent Skills1.1 问题引入 — Agent 如何获取领域知识假设你正在开发一个企业级 AI 助手需要它精通费用报销政策——包括各类费用限额、审批流程、收据要求等。你有几种选择方案具体做法缺点❌ 写死在 Instructions 里把整个政策文档塞进系统提示Token 浪费严重所有请求都负载全部知识❌ 写成代码逻辑用 if-else 硬编码规则不灵活非开发人员无法维护⚠️ 做成 RAG建立向量索引检索过重对结构化政策文档不必要✅Agent Skills将知识打包为标准化 Skill按需加载模块化非开发者可维护1.2 Agent Skills 规范Agent Skills 是一种开放规范定义了如何将领域知识打包为模块化、可复用的知识包Skill供 AI Agent 按需使用。核心设计理念 — 渐进式披露Progressive Disclosure三阶段解释阶段触发时机内容Token 开销 摘要展示Advertise每次请求技能名称 描述~100 tokens/skill 加载LoadAgent 判断需要时完整的 SKILL.md 指令按需加载 读取资源ReadAgent 需要参考资料时FAQ、模板等补充文件按需加载❝为什么不一次性全部加载渐进式披露通过延迟加载大幅降低 Token 消耗。一个 Agent 可能注册 20 个 Skills但每次请求通常只需要 1-2 个。如果全部塞进 System Prompt可能消耗数万 Token而渐进式披露将初始开销控制在每个 Skill ~100 tokens。1.3 Agent Skills vs Function Calling vs RAG维度Agent SkillsFunction CallingRAG加载方式渐进式披露全量注册检索 注入知识类型静态领域知识动态操作能力非结构化文档维护者业务专家开发者数据工程师文件格式Markdown (SKILL.md)C# 方法 属性向量数据库典型场景政策规则、操作指南调用 API、执行操作知识库搜索注册方式AIContextProvidersChatOptions.ToolsAIContextProviders❝Skills 与 Function Calling 是互补关系Skills 提供知道什么领域知识Function Calling 提供能做什么操作能力。一个完善的 Agent 通常两者兼备。 第二部分SKILL.md 文件剖析一个 Agent Skill 就是一个文件夹核心是一个SKILL.md文件。让我们剖析它的结构2.1 目录结构skills/ └── expense-report/ # 技能目录名称与 frontmatter.name 一致 ├── SKILL.md # 技能定义文件必需 ├── references/ # 参考资料目录 │ └── POLICY_FAQ.md # 费用政策 FAQ └── assets/ # 资产目录 └── expense-report-template.md # 报销模板2.2 SKILL.md 结构SKILL.md 由两部分组成YAML Frontmatter元数据和Markdown Body指令内容。--- name: expense-report description: File and validate employee expense reports according to company policy. --- # Expense Report ## Categories and Limits | Category | Limit | Receipt | Approval | |---|---|---|---| | Meals — solo | $50/day | $25 | No | ... ## Filing Process 1. Collect receipts... 2. Use template: [assets/expense-report-template.md](assets/expense-report-template.md) ... ## Policy Rules - For policy questions, consult the FAQ: [references/POLICY_FAQ.md](references/POLICY_FAQ.md) Frontmatter 规则字段必需规则示例name✅小写字母、数字和连字符不超过 64 字符expense-reportdescription✅不超过 1024 字符用于系统提示中的技能摘要展示File and validate... Body 中的资源引用Body 中可以使用 Markdown 链接引用本地文件。FileAgentSkillLoader会自动解析这些链接将其注册为可读取的资源!-- ✅ 支持相对路径引用 -- [assets/expense-report-template.md](assets/expense-report-template.md) [references/POLICY_FAQ.md](references/POLICY_FAQ.md) !-- ✅ 支持带 ./ 前缀 -- [./references/POLICY_FAQ.md](./references/POLICY_FAQ.md) !-- ❌ 不支持URL 链接会被忽略 -- [外部链接](https://example.com/doc.md) !-- ❌ 不支持路径穿越安全防护 -- [恶意路径](../../etc/passwd)2.3 安全机制FileAgentSkillLoader内置了多重安全防护️路径穿越防护资源文件必须位于 Skill 目录内部️符号链接检查防止通过 symlink 逃逸到外部目录️XML 转义技能元数据在嵌入提示词前会进行 XML 编码️名称验证仅允许[a-z0-9-]格式的技能名称 第三部分源码架构深度解析3.1 核心类关系3.2 FileAgentSkillsProvider — 技能提供者FileAgentSkillsProvider是AIContextProvider的子类实现了完整的渐进式披露机制。构造函数中的初始化流程public FileAgentSkillsProvider(string skillPath, ...) { // 1️⃣ 创建 Loader扫描目录发现所有 Skill this._loader new FileAgentSkillLoader(this._logger); this._skills this._loader.DiscoverAndLoadSkills(skillPaths); // 2️⃣ 构建技能摘要提示词 this._skillsInstructionPrompt BuildSkillsInstructionPrompt(options, this._skills); // 3️⃣ 注册两个 AI 工具 this._tools [ AIFunctionFactory.Create(this.LoadSkill, name: load_skill, description: Loads the full instructions for a specific skill.), AIFunctionFactory.Create(this.ReadSkillResourceAsync, name: read_skill_resource, description: Reads a file associated with a skill, such as references or assets.), ]; }内置的系统提示词模板You have access to skills containing domain-specific knowledge and capabilities. Each skill provides specialized instructions, reference documents, and assets. available_skills skill nameexpense-report/name descriptionFile and validate employee expense reports.../description /skill /available_skills When a task aligns with a skills domain: 1. Use load_skill to retrieve the skills instructions 2. Follow the provided guidance 3. Use read_skill_resource to read any references or other files mentioned Only load what is needed, when it is needed.❝注意FileAgentSkillsProvider重写的是ProvideAIContextAsync方法而非InvokingAsync返回的AIContext同时包含Instructions系统提示和Tools两个工具函数。3.3 FileAgentSkillLoader — 技能发现与加载FileAgentSkillLoader负责从文件系统中发现和解析 SKILL.md 文件。发现流程关键代码片段 — YAML 解析// 正则匹配 YAML frontmatter--- 分隔符之间的内容 private static readonly Regex s_frontmatterRegex new(\A\uFEFF?^---\s*$(.?)^---\s*$, RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled); // 正则匹配 Markdown 中的本地资源链接 private static readonly Regex s_resourceLinkRegex new(\[.*?\]\((\.?\.?/?[\w][\w\-./]*\.\w)\), RegexOptions.Compiled); // 技能名称验证仅允许小写字母、数字和连字符 private static readonly Regex s_validNameRegex new(^[a-z0-9]([a-z0-9\-]*[a-z0-9])?$, RegexOptions.Compiled);3.4 工具调用链路分析当 Agent 调用load_skill或read_skill_resource时// load_skill根据名称返回 SKILL.md 的 Body 部分 private string LoadSkill(string skillName) { if (!this._skills.TryGetValue(skillName, out FileAgentSkill? skill)) return $Error: Skill {skillName} not found.; return skill.Body; // 返回 Markdown 指令内容 } // read_skill_resource从磁盘读取资源文件内容 private async Taskstring ReadSkillResourceAsync( string skillName, string resourceName, CancellationToken cancellationToken) { if (!this._skills.TryGetValue(skillName, out FileAgentSkill? skill)) return $Error: Skill {skillName} not found.; // 委托给 Loader包含路径安全检查 return await this._loader.ReadSkillResourceAsync(skill, resourceName, cancellationToken); } 第四部分实战 — 费用报销 Agent场景说明我们将创建一个具备费用报销领域知识的 AI Agent它能够 回答费用报销政策问题小费能否报销住宿限额多少 根据模板生成报销报告 按需加载 FAQ 和模板等参考资料步骤 1环境准备导入必要的依赖包和 Helper 类。// 导入 AI Client Helper #!import ../helper/AIClientHelper.cs // 导入 MAF 核心包 #!import ../helper/MafHelper.cs using Microsoft.Agents.AI; using Microsoft.Extensions.AI; Console.WriteLine(✅ 依赖包加载完成); Console.WriteLine( 核心命名空间:); new { Microsoft_Agents_AI Agent 核心功能 Skills 支持, Microsoft_Extensions_AI AI 抽象层IChatClient、AIFunctionFactory }.Display();步骤 2准备 Skill 文件我们将在 Notebook 运行目录下动态创建expense-report技能文件。这包含三个部分SKILL.md— 技能定义Frontmatter 指令references/POLICY_FAQ.md— 费用政策 FAQassets/expense-report-template.md— 报销模板using System.IO; // 在 Notebook 运行目录下创建 skills 文件结构 var skillsRootPath Path.Combine(Directory.GetCurrentDirectory(), skills); var expenseSkillPath Path.Combine(skillsRootPath, expense-report); var referencesPath Path.Combine(expenseSkillPath, references); var assetsPath Path.Combine(expenseSkillPath, assets); // 确保目录存在 Directory.CreateDirectory(referencesPath); Directory.CreateDirectory(assetsPath); // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 1️⃣ 创建 SKILL.md — 技能定义文件已中文化 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ var skillMd --- name: expense-report description: 按照 Contoso 公司政策填写和审核员工费用报销。适用于费用报销、报销规则、收据要求、支出限额或费用类别等相关问题。 --- # 费用报销Expense Report ## 费用类别与限额 | 类别 | 限额 | 收据要求 | 审批 | |---|---|---|---| | 单人用餐 | $50/天 | $25 | 无需 | | 团队/客户用餐 | $75/人 | 必须 | 总额$200需经理 | | 住宿 | $250/晚 | 必须 | 超过3晚需经理 | | 地面交通 | $100/天 | $15 | 无需 | | 机票 | 经济舱 | 必须 | $1,500需VP | | 会议/培训 | $2,000/次 | 必须 | 经理LD | | 办公用品 | $100 | 是 | 无需 | | 软件/订阅 | $50/月 | 是 | $200/年需经理 | ## 报销流程 1. 收集收据——需包含商家、日期、金额、支付方式。 2. 按上表分类。 3. 使用模板[assets/expense-report-template.md](assets/expense-report-template.md)。 4. 团队/客户用餐需列出参与人及业务目的。 5. 提交——$500自动审批$500–$2,000需经理$2,000需VP。 6. 报销10个工作日内通过银行转账。 ## 政策规则 - 需在交易后30天内提交。 - 酒精类费用不予报销。 - 外币按交易日汇率折算为美元并注明原币种及金额。 - 混合个人/商务出行仅报销商务部分需提供对比报价。 - 丢失收据$25需提交财务部的丢失收据声明每季度最多2次。 - 如有未涵盖的问题请查阅FAQ[references/POLICY_FAQ.md](references/POLICY_FAQ.md)。答案应以本文件和FAQ为准。 ; File.WriteAllText(Path.Combine(expenseSkillPath, SKILL.md), skillMd); // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 2️⃣ 创建 references/POLICY_FAQ.md — 费用政策 FAQ已中文化 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ var faqMd # 费用政策常见问答FAQ ## 用餐 **问工作日咖啡或零食可以报销吗** 答每日咖啡/零食低于$10不予报销视为个人消费。客户会议或团队协作期间购买的咖啡可作为团队用餐报销。 **问团队晚餐超出人均限额怎么办** 答$75/人限额为指导标准。超出20%以内可书面说明如“客户指定场地”超出20%需VP预先批准。 **问需要列出所有参与人吗** 答是。客户用餐需列明客户姓名及公司团队用餐需列出所有员工姓名。10人以上可单独附名单。 ## 差旅 **问可以预订高端经济舱或商务舱吗** 答经济舱为标准。6小时以上可选高端经济舱商务舱需VP预批通常仅限10小时以上或医疗原因。 **问打车Uber/Lyft和租车如何选择** 答往返30英里以内建议打车多日或超$100/天建议租车。3人以上可选更大车型。 **问小费可以报销吗** 答用餐、打车、酒店清洁小费20%以内可报销超出需说明理由。 ## 住宿 **问部分城市$250/晚不够怎么办** 答纽约、旧金山、伦敦、东京、悉尼等高消费城市自动提升至$350/晚无需额外审批。其他特殊情况请提前向经理申请。 **问住亲友家能否领取补贴** 答不可以。公司仅报销实际住宿费用不提供补贴。 ## 订阅与软件 **问个人效率工具可以报销吗** 答需与工作直接相关如IDE、设计软件、项目管理工具可报销。一般效率类应用需经理书面确认。 ## 收据与材料 **问收据模糊/损坏怎么办** 答可向商家补打如无法获取需提交丢失收据声明财务部网站下载每季度限2次。 **问停车/过路费需要收据吗** 答低于$15无需收据注明日期、地点、金额即可$15及以上需收据或银行流水。 ## 审批与报销 **问经理休假谁来审批** 答可由上级经理或系统指定的代理审批人审批。 **问能否报销上季度的费用** 答标准为30天内超期需书面说明并VP批准超90天仅特殊情况经CFO批准。 ; File.WriteAllText(Path.Combine(referencesPath, POLICY_FAQ.md), faqMd); // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 3️⃣ 创建 assets/expense-report-template.md — 报销模板无需翻译表头已通用 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ var templateMd # 费用报销模板Expense Report Template | 日期 | 费用类别 | 商家 | 描述 | 金额USD | 原币种 | 原币金额 | 参与人 | 业务目的 | 已附收据 | |------|----------|------|------|-------------|--------|----------|--------|----------|----------| | | | | | | | | | | 是 / 否 | ; File.WriteAllText(Path.Combine(assetsPath, expense-report-template.md), templateMd); // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Console.WriteLine(✅ Skill 文件创建完成); Console.WriteLine(); Console.WriteLine($ 技能根目录: {skillsRootPath}); Console.WriteLine(); // 显示文件结构 new { 文件结构 new[] { skills/, └── expense-report/, ├── SKILL.md (技能定义Frontmatter 指令), ├── references/, │ └── POLICY_FAQ.md (费用政策 FAQ), └── assets/, └── expense-report-template.md (报销模板) } }.Display();步骤 3加载 Skills 并创建 Agent使用FileAgentSkillsProvider从文件系统发现和加载技能然后将其注入到 Agent 中。核心流程FileAgentSkillsProvider扫描skills/目录发现expense-report/SKILL.md解析 Frontmatter 和 Body验证所有资源文件FAQ、模板存在且路径安全自动注册load_skill和read_skill_resource两个 AI 工具将技能列表注入系统提示词#pragma warning disable MAAI001 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 1️⃣ 创建 SkillsProvider — 从文件系统发现和加载 Skills // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ var skillsProvider new FileAgentSkillsProvider( skillPath: Path.Combine(Directory.GetCurrentDirectory(), skills) ); Console.WriteLine(✅ FileAgentSkillsProvider 创建成功); Console.WriteLine( Skills 已从文件系统加载); Console.WriteLine(); // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 2️⃣ 创建 Agent — 注入 SkillsProvider // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ var chatClient AIClientHelper.GetDefaultChatClient(); AIAgent agent chatClient.AsAIAgent(new ChatClientAgentOptions { Name SkillsAgent, ChatOptions new() { Instructions You are a helpful assistant., }, // 关键通过 AIContextProviders 注入 SkillsProvider AIContextProviders [skillsProvider], }); Console.WriteLine(✅ Agent 创建成功); Console.WriteLine(); new { Agent名称 SkillsAgent, 基础指令 You are a helpful assistant., 上下文提供者 FileAgentSkillsProvider, 自动注册的工具 new[] { load_skill, read_skill_resource }, 技能摘要开销 ~100 tokens/skill仅名称和描述 }.Display(); Console.WriteLine(); Console.WriteLine( 注意此时 Agent 的系统提示中已包含技能摘要但完整指令尚未加载。); Console.WriteLine( 只有当 Agent 判断用户问题与技能相关时才会调用 load_skill 按需加载。);步骤 4测试 — 费用政策问答第一个测试询问关于小费报销的问题。预期行为Agent 看到系统提示中的技能摘要 → 识别问题属于expense-report领域调用load_skill(expense-report)→ 获取完整的费用报销规则发现规则中提到 FAQ → 调用read_skill_resource(expense-report, references/POLICY_FAQ.md)根据 FAQ 中的小费政策回答用户问题Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine( 测试 1费用政策 FAQ 问答); Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine(); // 提出关于小费报销的问题 string question 小费可以报销吗我在一次出租车行程中给了 25% 的小费想知道这是否可以报销。; Console.WriteLine($ 用户: {question}); Console.WriteLine(); // Agent 会自动执行渐进式披露流程 // 1. 识别属于 expense-report 领域 // 2. 调用 load_skill 获取完整规则 // 3. 调用 read_skill_resource 获取 FAQ // 4. 根据 FAQ 内容回答 AgentResponse response await agent.RunAsync(question); Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine($ Agent: {response.Text}); Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine(); Console.WriteLine( 观察要点:); new { 渐进式披露 Agent 自动判断需要加载 expense-report 技能, 工具调用链 load_skill → read_skill_resource, FAQ匹配 FAQ 中明确说明小费 20% 以内可报销超过 20% 需要说明理由, 预期回答 25% 的小费超过了 20% 的标准需要额外提供理由说明 }.Display();步骤 5测试 — 多轮对话生成报销报告第二个测试通过多轮对话生成费用报告。Agent 需要加载报销模板资产assets/expense-report-template.md并引导用户补充缺失信息。预期行为Agent 加载 skill → 获取费用类别和限额规则Agent 读取报销模板 → 按照模板格式生成报告Agent 识别缺失信息 → 主动询问用户补充细节Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine( 测试 2多轮对话生成报销报告); Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine(); // 使用 AgentSession 支持多轮对话 AgentSession session await agent.CreateSessionAsync(); // 第 1 轮提供费用信息要求生成报告草稿 string request 我上周有 3 笔客户晚餐费用和一张 $1,200 的机票。请先返回一份报销报告草稿并询问我缺失的细节信息。; Console.WriteLine($ 用户: {request}); Console.WriteLine(); // Agent 会 // 1. 加载 expense-report skill如果尚未加载 // 2. 读取报销模板 assets/expense-report-template.md // 3. 根据用户提供的信息填写模板 // 4. 识别缺失字段并主动询问 AgentResponse response2 await agent.RunAsync(request, session); Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine($ Agent: {response2.Text}); Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine(); Console.WriteLine( 观察要点:); new { 模板加载 Agent 通过 read_skill_resource 获取报销模板, 智能分类 Agent 根据 Skill 中的限额表对费用进行分类, 缺失信息 Agent 会询问缺失信息如晚餐日期、具体金额、参与人等, 审批提示 $1,200 机票需要 Manager 审批 $1,500 }.Display();步骤 6多轮跟进 — 补充缺失信息根据 Agent 上一轮指出的缺失信息我们补充细节观察 Agent 如何更新报销草稿。Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine( 测试 2续补充缺失信息); Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine(); // 提供详细信息让 Agent 完善报告 string details 以下是详细信息 - 客户晚餐 1周一在 Starlight Restaurant$2804 人我、来自 Marketing 的 Alice、来自 Contoso Corp 的 Bob Chen、来自 Contoso Corp 的 Lisa Wang。业务目的Q4 合作复盘。 - 客户晚餐 2周三在 Golden Dragon$1953 人我、来自 ABC Inc 的 Tom Li、来自 ABC Inc 的 Sarah Kim。业务目的新项目启动会。 - 客户晚餐 3周五在 Café Milano$1502 人我、来自 XYZ Ltd 的 David Liu。业务目的合同续签讨论。 - 机票Delta Airlines经济舱JFK 往返 SFO通过公司差旅平台预订。 所有收据均已附上。 ; Console.WriteLine($ 用户: {details}); Console.WriteLine(); // Agent 利用 session 中的对话历史更新报告草稿 AgentResponse response3 await agent.RunAsync(details, session); Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine($ Agent: {response3.Text}); Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine(); Console.WriteLine( 观察要点:); new { 模板填充 Agent 使用报销模板格式填充所有字段, 限额检查 Agent 根据 Skill 中的限额规则检查每笔费用, Dinner1 $280/4人$70/人 → 未超过 $75/人限额 ✅ 但总额 $200 需 Manager 审批, Flight $1,200 经济舱 → 需 Manager 审批 $1,500无需 VP, 审批总额 总额可能超过 $2,000 → 需要 VP 审批 }.Display(); 第五部分进阶用法5.1 自定义技能摘要提示词FileAgentSkillsProviderOptions允许你自定义系统提示词模板。使用{0}作为技能列表的占位符var skillsProvider new FileAgentSkillsProvider( skillPath: skills, options: new FileAgentSkillsProviderOptions { SkillsInstructionPrompt 你是一名专业的企业咨询顾问。你具备以下专业技能 {0} 当用户问题与某个技能相关时 1. 使用 load_skill 加载该技能的详细指令 2. 严格按照指令中的规则回答 3. 需要时使用 read_skill_resource 读取参考资料 务必用中文回答所有问题。 } );5.2 多技能注册一个 Agent 可以同时注册多个 Skills// 方式 1通过父目录自动发现推荐 // skills/ 下的每个子目录如果包含 SKILL.md就会被注册为一个 Skill var provider new FileAgentSkillsProvider(skills); // 自动发现expense-report, travel-policy, it-support ... // 方式 2指定多个路径 var provider new FileAgentSkillsProvider([ skills/finance, // 财务相关技能 skills/hr, // 人力资源技能 skills/it-support // IT 支持技能 ]);❝性能提示渐进式披露确保了即使注册 20 个 Skills初始 Token 开销也仅为 ~2000 tokens每个 ~100 tokens 的名称描述摘要。Agent 会智能判断并仅加载相关的 Skill。5.3 Skills 文件夹结构最佳实践skills/ ├── expense-report/ # ✅ 好的清晰的领域边界 │ ├── SKILL.md │ ├── references/ │ └── assets/ ├── travel-policy/ # ✅ 好的独立的政策领域 │ ├── SKILL.md │ └── references/ ├── code-review/ # ✅ 好的操作指南 │ ├── SKILL.md │ └── assets/ └── general-hr/ # ⚠️ 避免范围过大 ├── SKILL.md # 应拆分为更细粒度的技能 └── ...命名规范✅ 使用kebab-caseexpense-report、travel-policy✅ 名称简洁且具描述性❌ 避免特殊字符~~expense_report、ExpenseReport~~❌ 避免过长名称最大 64 字符5.4 实战创建第二个 Skill 并测试多技能 Agent让我们创建一个travel-policy技能然后测试 Agent 在同时拥有两个 Skills 时的智能路由能力。#pragma warning disable MAAI001 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 创建第二个 Skilltravel-policy差旅政策已中文化 // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ var travelSkillPath Path.Combine(skillsRootPath, travel-policy); Directory.CreateDirectory(travelSkillPath); var travelSkillMd --- name: travel-policy description: 公司差旅预订与审批政策。适用于航班预订、酒店预订、差旅审批流程或差旅安全指引等相关问题。 --- # 差旅政策Travel Policy ## 预订规则 | 项目 | 政策 | 审批 | |------|--------|----------| | 国内航班 | 仅限经济舱 | $800自动审批 | | 国际航班 | 经济舱6小时以上可选高端经济舱 | 均需经理审批 | | 酒店 | 优先公司协议酒店 | ≤$250/晚自动审批 | | 租车 | 紧凑/标准车型 | ≤3天自动审批 | | 火车/高铁 | 标准座 | 自动审批 | ## 预订流程 1. 所有预订须通过公司差旅平台TravelHub完成 2. 国内提前14天国际提前21天预订 3. 总费用超$2,000需经理预先审批 4. 预订后24小时内取消可全额退款 ## 安全指引 - 所有国际行程需向差旅安全团队报备 - 超过5天的行程需购买差旅保险 - 随身携带公司应急联系方式卡 ; File.WriteAllText(Path.Combine(travelSkillPath, SKILL.md), travelSkillMd); Console.WriteLine(✅ travel-policy Skill 创建完成); Console.WriteLine(); // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ // 重新创建包含两个 Skills 的 Provider 和 Agent // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ var multiSkillProvider new FileAgentSkillsProvider( skillPath: Path.Combine(Directory.GetCurrentDirectory(), skills) ); AIAgent multiSkillAgent chatClient.AsAIAgent(new ChatClientAgentOptions { Name MultiSkillAgent, ChatOptions new() { Instructions You are a helpful corporate assistant. Answer in the same language as the user., }, AIContextProviders [multiSkillProvider], }); Console.WriteLine(✅ 多技能 Agent 创建成功); Console.WriteLine(); new { Agent名称 MultiSkillAgent, 已注册技能 new[] { expense-report费用报销, travel-policy差旅政策 }, 系统提示摘要开销 ~200 tokens2 × ~100 tokens/skill }.Display();测试多技能智能路由分别提问差旅政策和费用报销问题观察 Agent 如何智能选择加载正确的 Skill。Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine( 测试 3多技能智能路由); Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine(); // 测试 A差旅政策问题 → 应加载 travel-policy string travelQuestion 我需要预订一张从纽约到伦敦、为期两周项目的航班。我可以乘坐什么舱位需要审批吗; Console.WriteLine( 测试 A差旅政策问题); Console.WriteLine($ 用户: {travelQuestion}); Console.WriteLine(); AgentResponse travelResponse await multiSkillAgent.RunAsync(travelQuestion); Console.WriteLine($ Agent: {travelResponse.Text}); Console.WriteLine(); Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine(); // 测试 B费用报销问题 → 应加载 expense-report string expenseQuestion 我上周买了一个 $45/月的项目管理软件订阅需要什么审批流程; Console.WriteLine( 测试 B费用报销问题); Console.WriteLine($ 用户: {expenseQuestion}); Console.WriteLine(); AgentResponse expenseResponse await multiSkillAgent.RunAsync(expenseQuestion); Console.WriteLine($ Agent: {expenseResponse.Text}); Console.WriteLine(); Console.WriteLine(━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━); Console.WriteLine(); Console.WriteLine( 观察要点:); new { 智能路由 Agent 根据问题内容自动选择加载正确的 Skill, 测试A 差旅问题 → load_skill(travel-policy), 测试B 报销问题 → load_skill(expense-report), 语言适配 Agent 会根据用户语言中/英自适应回复 }.Display(); 总结通过本节课我们系统学习了Agent Skills— 一种基于AIContextProvider的模块化领域知识管理机制。核心要点Agent Skills 规范遵循开放规范支持跨框架互操作核心思想是渐进式披露摘要展示 → 加载 → 读取资源SKILL.md 文件结构YAML Frontmatternamedescription技能元数据Markdown Body完整的领域指令规则、流程、资源引用资源文件通过 Markdown 链接引用按需读取FileAgentSkillsProvider继承AIContextProvider重写ProvideAIContextAsync自动注册load_skill和read_skill_resource两个 AI 工具初始化时扫描目录、解析 SKILL.md、验证资源完整性安全机制路径穿越防护 符号链接检查 XML 转义 名称格式验证实际价值✅可维护性业务专家可以直接编辑 Markdown 文件无需修改代码✅Token 效率渐进式披露将 Token 开销降低 90%✅模块化每个 Skill 独立维护可即插即用✅安全性内置路径穿越防护和输入验证️ 课后练习创建自定义 Skill为你的业务领域创建一个 Skill如 IT 支持指南、编码规范等包含至少一个references/资源文件中文化提示词使用FileAgentSkillsProviderOptions自定义中文系统提示词模板结合 Function Calling创建一个同时具备 Skills领域知识和 Function Calling操作能力的 Agent下一篇引入地址