1. 为什么 .NET 开发者需要 Agent Skill
如果你正在用 MAF(Microsoft Agent Framework)做智能体,大概率遇到过这个场景:Agent 什么都能聊,但一碰到公司内部的报销规则、差旅标准、审批流程,就开始一本正经地胡说八道。你当然可以把这些规则全塞进系统提示词,但很快就会发现提示词膨胀到几千 token,模型反而抓不住重点,改一条规则还要重新发版。
Agent Skill 解决的正是这个问题。用一句话说,它就是大模型随时翻阅的说明文档:把某个业务域的 SOP 沉淀成一个SKILL.md文件,Agent 启动时只加载技能的名称和描述(约 100 token),判断当前问题跟哪个技能相关,命中后才把完整正文加载进上下文。这个机制叫渐进式披露,分三层——元数据层固定加载、指令层按需加载、资源层按需中的按需加载。好处很直接:token 消耗大幅下降,模型判断相关性的速度也更快。
它和 MCP 不是替代关系。MCP 负责把 Agent 连到数据源,Agent Skill 负责教 Agent 拿到数据之后怎么处理。一个管连接,一个管指导,配合使用才能做出靠谱的企业级智能体。
这篇面向 .NET 开发者,从SKILL.md的写法讲到config.toml骨架配置,再演示如何通过统一 Key/API 通道接入 TaoToken,最后跑通一次技能调用验证。适合已经写过 MAF 基础 Agent、想进一步做业务知识分域的人。下面所有配置和代码都可以直接复制。
2. 前置准备:TaoToken 统一 Key 与 MAF 项目骨架
在写 Skill 之前,先把模型通道打通。MAF 里的IChatClient需要一个兼容 OpenAI 协议的端点,TaoToken 提供的就是这样一个统一入口,一个 Key 可以调用多种模型,省去在多个平台之间来回切换的麻烦。
第一步,去控制台创建一个 API Key。打开 https://taotoken.net/console ,登录后在 API Keys 页面新建一个 Key,复制出来保存好,它只会完整显示一次。如果你还没注册,先走 https://taotoken.net/ 完成账号创建。
第二步,确认你要用的模型名。不同模型在工具调用和长上下文上的表现差异不小,做 Agent Skill 这种需要读取文档再推理的场景,建议选指令遵循能力强的模型。模型列表可以在 https://taotoken.net/models 查看,也可以直接在模型对话页面试跑一段带表格的提示词,看它能不能准确引用表格里的限额数字。
第三步,准备 .NET 项目。新建一个控制台应用,通过 NuGet 引入 MAF 相关包和 OpenAI 兼容客户端。项目结构建议这样组织:
EnterpriseAssistant/ ├── Program.cs ├── appsettings.json ├── config.toml └── skills/ ├── expense-report/ │ ├── SKILL.md │ ├── references/ │ │ └── POLICY_FAQ.md │ └── assets/ │ └── expense-report-template.md └── travel-policy/ └── SKILL.mdskills目录放在项目根下,运行时通过Directory.GetCurrentDirectory()定位。这里有个坑:如果你在 IDE 里直接 F5,当前目录可能是bin/Debug/net8.0,导致找不到 skills 文件夹。稳妥做法是在.csproj里加一段复制规则,把 skills 目录随生成输出一起拷贝:
<ItemGroup> <None Update="skills/**/*"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> </ItemGroup>这样无论从哪里启动,skills 都能被正确发现。
3. 可复制配置:config.toml 骨架与 SKILL.md 写法
3.1 config.toml 骨架
MAF 项目里用config.toml集中管理模型通道参数,避免把 Key 硬编码进代码。下面是一份可直接复制的骨架:
# config.toml [llm] provider = "openai-compatible" base_url = "https://taotoken.net/api" api_key = "sk-你的TaoToken密钥" model = "gpt-4o-mini" temperature = 0.2 max_tokens = 2048 [agent] name = "MultiSkillsAgent" instructions = "你是一个高效的企业助手,使用和用户同样的语言回答问题。" skill_path = "./skills" [skill] auto_load_metadata = true max_skill_tokens = 5000几个参数说明一下。base_url填https://taotoken.net/api,注意不要带多余路径。temperature建议压到 0.2 左右,因为 Skill 场景要求严格按文档回答,太高的随机性会让模型自由发挥。max_skill_tokens是单个技能正文的加载上限,超过这个值会截断,防止某个技能文档写得过长把上下文撑爆。
读取这份配置的代码:
using Tomlyn; using Tomlyn.Model; var tomlText = File.ReadAllText("config.toml"); var model = Toml.ToModel(tomlText); var llm = (TomlTable)model["llm"]; var baseUrl = llm["base_url"].ToString(); var apiKey = llm["api_key"].ToString(); var modelName = llm["model"].ToString();生产环境里不要把 Key 写进 toml 提交到仓库,改用环境变量注入,toml 里只留占位符。
3.2 SKILL.md 的元数据与正文
SKILL.md分两部分:YAML front matter 写元数据,正文写指令。元数据里的name和description是 Agent 启动时唯一会看到的内容,所以description必须写清楚「什么时候该用这个技能」,而不是泛泛描述功能。
以费用报销技能为例:
--- name: expense-report description: 按照 Contoso 公司政策填写和审核员工费用报销。适用于费用报销、报销规则、收据要求、支出限额或费用类别等相关问题。 --- # 费用报销(Expense Report) ## 费用类别与限额 | 类别 | 限额 | 收据要求 | 审批 | |---|---|---|---| | 单人用餐 | $50/天 | >$25 | 无需 | | 团队/客户用餐 | $75/人 | 必须 | 总额>$200需经理 | | 住宿 | $250/晚 | 必须 | 超过3晚需经理 | | 地面交通 | $100/天 | >$15 | 无需 | | 机票 | 经济舱 | 必须 | >$1,500需VP | | 软件/订阅 | $50/月 | 是 | >$200/年需经理 | ## 报销流程 1. 收集收据,需包含商家、日期、金额、支付方式。 2. 按上表分类。 3. 使用模板:assets/expense-report-template.md。 4. 团队/客户用餐需列出参与人及业务目的。 5. 提交:<$500 自动审批;$500–$2,000 需经理;>$2,000 需VP。 6. 报销:10个工作日内通过银行转账。 ## 政策规则 - 需在交易后30天内提交。 - 酒精类费用不予报销。 - 外币按交易日汇率折算为美元,并注明原币种及金额。 - 如有未涵盖的问题,请查阅 FAQ:references/POLICY_FAQ.md。注意正文里引用了references/POLICY_FAQ.md和assets/expense-report-template.md,这两个文件放在技能文件夹下对应位置即可。Agent 只有在判断需要时才会去读它们,这就是第三层按需加载。
差旅技能同理,description写成「公司差旅预订与审批政策。适用于航班预订、酒店预订、差旅审批流程或差旅安全指引等相关问题」,正文里放预订规则表格和流程步骤。
3.3 在 MAF 中注册 SkillsProvider
MAF 1.0.0-rc2 之后提供了FileAgentSkillsProvider,从文件系统发现并加载技能:
var skillsProvider = new FileAgentSkillsProvider( skillPath: Path.Combine(Directory.GetCurrentDirectory(), "skills")); Console.WriteLine("Skills 已从文件系统加载");然后创建 Agent 时把 provider 注入AIContextProviders:
AIAgent agent = chatClient.AsAIAgent(new ChatClientAgentOptions { Name = "MultiSkillsAgent", ChatOptions = new() { Instructions = "你是一个高效的企业助手,使用和用户同样的语言回答用户提出的问题。", }, AIContextProviders = [skillsProvider], }); Console.WriteLine("多技能 Agent 创建成功");chatClient用前面 config.toml 里的 base_url、api_key、model 构造,走 OpenAI 兼容协议即可。
4. 验证请求:跑通第一次技能调用
配置写完了,得验证它真的能按预期加载技能。准备两个测试用例,分别命中两个不同技能。
测试一,差旅政策问题:
var session = await agent.CreateSessionAsync(); var travelQuestion = "我需要预订一张从纽约到伦敦、为期两周项目的航班。我可以乘坐什么舱位?需要审批吗?"; Console.WriteLine($"用户: {travelQuestion}"); var travelResponse = await agent.RunAsync(travelQuestion, session); Console.WriteLine($"Agent: {travelResponse.Text}");预期结果是 Agent 加载travel-policy技能,回答国际航班经济舱、6 小时以上可选高端经济舱、且均需经理审批。如果它答出了这些细节,说明技能被正确激活。
测试二,费用报销问题:
var expenseQuestion = "我上周买了一个 $45/月的项目管理软件订阅,需要什么审批流程?"; var expenseResponse = await agent.RunAsync(expenseQuestion, session); Console.WriteLine($"Agent: {expenseResponse.Text}");预期结果是 Agent 加载expense-report技能,回答软件订阅 $50/月以内、年费超 $200 需经理审批。注意这里模型需要把「$45/月」换算成年费 $540,再对照表格里的「>$200/年需经理」得出结论,能答对说明它确实读了文档而不是瞎猜。
两个用例跑通,就证明从 SKILL.md 定义、config.toml 配置到 TaoToken 通道接入的整条链路是通的。想看更多模型在工具调用上的表现,可以去 https://taotoken.net/chat 手动试几轮,对比不同模型对表格数据的引用准确度。
5. 本篇常见错误排查
技能没被加载,Agent 答非所问。先检查skillPath指向的目录下是否存在以技能名命名的子文件夹,且每个子文件夹里有SKILL.md。MAF 是按文件夹发现的,SKILL.md直接放在 skills 根目录下不会被识别。再确认.csproj里配了复制规则,否则运行时目录里根本没有 skills。
YAML front matter 解析失败。SKILL.md开头必须是三个短横线,name和description之间不能有中文冒号,description里如果含冒号要用引号包起来。一个常见错误是 front matter 前后有多余空行,导致解析器读不到。
401 或 403 报错。检查 config.toml 里的api_key是否完整、有没有多余空格,base_url是否为https://taotoken.net/api。如果 Key 是从环境变量读的,确认变量名拼写一致。Key 泄露或误删的话,去 https://taotoken.net/api-keys 重新生成一个。
模型不调用技能,直接凭记忆回答。多半是description写得太模糊,模型判断不出相关性。把「适用于……」后面的触发场景写具体,列出用户可能问的关键词。另外temperature太高也会让模型倾向于自由发挥,压到 0.2 以下。
技能正文被截断。检查max_skill_tokens设置,如果某个 SKILL.md 特别长,考虑把细节拆到 references 里,正文只留核心指令和索引,让模型按需去读参考文件。
多轮对话里技能状态丢失。确认每次RunAsync用的是同一个session对象。新建 session 会重置上下文,之前激活的技能信息不会保留。
6. 下一步:把技能通道接到长期编码场景
跑通单个技能调用只是起点。真实项目里,你可能会让 Agent 长期驻留在编码流程中,反复读取技能文档、调用工具、生成代码。这种持续性的编码和 Agent 任务,用按次计费的通道成本不好控制,可以考虑 Coding Plan 这类面向长期使用的方案,具体在 https://taotoken.net/coding-plan 看。
接入细节和参数说明都在文档里,遇到协议层面的问题先翻 https://taotoken.net/doc ,大部分兼容性疑问那里都有答案。如果你用的是 Claude Code 这类工具链,Anthropic 兼容通道的配置方式在 https://taotoken.net/claudecode-anthropic 有单独说明。
技能文档建议交给业务方维护,技术团队只管通道和框架。这样业务规则变了,改一个 markdown 文件就行,不用重新发版,职责也清晰。