微软 AI Agent 入门课的 .NET 示例跑不起来:SDK 版本与包引用对一遍
Python 那一侧的 notebook 顺顺当当跑完了,切到同一课的 .cs,结果连模型都没碰到就停了——这是 ai-agents-for-beginners 里 .NET 侧很容易撞上的开局。问题一般不在代码,而在这些文件的组织方式和 Python 那侧根本不是一回事。下面按仓库里能查到的东西,把该对的三处对一遍。
先看清这些 .cs 是什么形态
打开 01-intro-to-ai-agents/code_samples/01-dotnet-agent-framework.cs,文件头是这样的(原样取自仓库文件):
#!/usr/bin/dotnet run
#:package Microsoft.Extensions.AI@10.4.1
#:package Microsoft.Agents.AI.OpenAI@1.1.0
#:package Azure.AI.OpenAI@2.1.0
#:package Azure.Identity@1.13.1
课程目录里没有 .csproj。依赖靠 #:package 这几行写在源文件顶部,同目录的 01-dotnet-agent-framework.md 在「Additional Resources」里给出的参考链接标题是 .NET Single File Apps。所以你不能用「打开解决方案、还原、F5」的老习惯去找入口——按仓库文档给的方式,直接跑这个 .cs 文件:
dotnet run ./01-dotnet-agent-framework.cs
同一份 md 里还给了另一条路子,注意它写在 zsh/bash 段落下:
# zsh/bash
chmod +x ./01-dotnet-agent-framework.cs
./01-dotnet-agent-framework.cs
这条依赖首行那个 shebang,Windows 上用不上,Windows 侧就用上面的 dotnet run。顺带一提,05-agentic-rag/code_samples/05-dotnet-agent-framework.cs 同样是这个形态,而该课目录下另有一个 05-dotnet-agent-framework.ipynb,它的 kernelspec 写的是 .NET (C#) / .net-csharp,属于 polyglot-notebook,跑它需要的是另一套内核,不是 dotnet run。
顺便说一个会让你白找半天的地方:AGENTS.md 在描述目录结构时写的是 <number>-dotnet-agent-framework.ipynb,但第 01、02、03、04、07、08 课实际落盘的是 .cs。文档和文件形态对不上,这不是你环境的问题,按 .cs 找就行。
判定动作一:SDK 版本
仓库根有 global.json,里面写着 "version": "10.0.100"、"rollForward": "latestFeature"、"allowPrerelease": false。这三个键各自的精确匹配规则属于 .NET SDK 自身的行为,仓库里没有解释,需要以 .NET 官方文档为准;但它摆在根目录这件事本身说明——仓库对 SDK 版本有硬约束,不是装了哪个都行。
判定动作用 00-course-setup/README.md 里给出的那条命令:
dotnet --list-sdks
同一份 README 在 Requirements 一节写明「.NET 10+:For the sample codes using .NET」,01-dotnet-agent-framework.md 与 02-dotnet-agent-framework.md 的 Prerequisites 也都写 .NET 10 SDK 或更高。
这里有一处仓库内部不一致值得你知道:05-dotnet-agent-framework.md 的 Prerequisites 写的是「.NET 9.0 SDK or higher」,和上面几处以及根 global.json 对不上。仓库里没有解释这个差异。遇到这种冲突,按更严的那一条准备环境要省事得多。
判定动作二:包引用是怎么钉的
第 01 课钉的是确定版本(就是上面那四行)。而 02、03、04、07 各课的 .cs 文件头写的是 Microsoft.Extensions.AI@10.* 与 Microsoft.Agents.AI.OpenAI@1.*-* 这种带 * 的形态。两种写法并列在同一个仓库里,直接的后果是:你在第 01 课拿到的包版本,和在第 04 课拿到的未必是同一个;* 具体怎么被解析,属于 NuGet 的行为,仓库里没有说明。
第 05 课更要单独看一眼。它引的不是 Microsoft.Agents.AI.OpenAI,而是 Microsoft.Agents.AI.AzureAI 与 Azure.AI.Agents.Persistent,并且这几个包的版本后缀带 -preview 和 -beta——这是预览包,仓库里就是这么写的,预览包的接口在后续版本变动是常态。同一个文件里 Microsoft.Extensions.AI 钉的还是 9 开头的版本,比第 01 课低一个主版本号。以上都是仓库当前代码里的钉法,随版本可能变动。
还有一类容易漏的是 #:property。07-planning-design/code_samples/07-dotnet-agent-framework.cs 顶部除了包引用还有一行 #:property JsonSerializerIsReflectionEnabledByDefault=true,这个文件里同时 using System.Text.Json 并做了序列化;00-course-setup/AzureSearch.cs 顶部则写着 #:property PublishAot=false。也就是说,单文件形态下需要调整的 MSBuild 属性是逐文件写在文件头的——你把代码复制进自己新建的项目时,最容易漏掉的就是这两行。
再补一处细节:01-dotnet-agent-framework.cs 的 using 段里有 using OpenAI.Chat;,而同目录 md 内嵌的那份代码块里没有这一行。md 自己写了「See 01-dotnet-agent-framework.cs for the complete code」,所以以 .cs 为准。
判定动作三:环境变量到底从哪读
这一条最容易被想当然,因为它和 Python 那侧的直觉正好相反。
第 01 课的读取写法是这样的:
var azureEndpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")
?? throw new InvalidOperationException("AZURE_OPENAI_ENDPOINT is not set.");
var deployment = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") ?? "gpt-5-mini";
注意它直接读进程环境变量,没有加载 .env。第 01 到 04 课的 .cs 都是这样。而 Python 那侧的做法是根目录放 .env(.env.example 是模板),requirements.txt 里有 python-dotenv。所以「我明明在 .env 里配好了」在 .NET 侧不成立——如果你看到的报错文本正是 AZURE_OPENAI_ENDPOINT is not set.,那就是这一条命中了,它就是上面那行 throw 抛出来的。
.md 里给了两个平台的设置方式,Windows 侧原样如下:
# PowerShell
$env:AZURE_OPENAI_ENDPOINT = "https://<your-resource>.openai.azure.com"
$env:AZURE_OPENAI_DEPLOYMENT = "gpt-5-mini"
# Then sign in so AzureCliCredential can get a token
az login
gpt-5-mini 是仓库示例里用的部署名,换成你自己 Foundry 项目里的部署名。zsh/bash 那侧对应的是 export 写法。
从第 05 课开始才引入 DotNetEnv@3.1.1,代码里出现 using DotNetEnv; 与 Env.Load(...)。这里同样有一处不一致:07 与 08 两课的 .cs 写的是 Env.Load("../../.env"),而 05 那份写的是 Env.Load("../../../.env")——两个文件在仓库里的目录深度是一样的(都在 <课程目录>/code_samples/ 下)。这个相对路径以什么为基准、解析不到时是什么行为,仓库里没有说明。实用的做法是:跑之前先确认你在哪个目录下执行,别在仓库根随手 dotnet run 一个深层文件。
变量名也是两套。.env.example 里 Python 主线用的是 AZURE_AI_PROJECT_ENDPOINT 与 AZURE_AI_MODEL_DEPLOYMENT_NAME(指向 Microsoft Foundry 项目),而第 01 课的 .NET 示例读的是 AZURE_OPENAI_ENDPOINT 与 AZURE_OPENAI_DEPLOYMENT;第 05 课的 .NET 又读回 AZURE_AI_PROJECT_ENDPOINT 与 AZURE_AI_MODEL_DEPLOYMENT_NAME。写哪课就照那课的 .cs 里出现的变量名配,别按记忆配。
认证这一环单独确认
第 01 课构造客户端用的是 new AzureOpenAIClient(new Uri(azureEndpoint), new AzureCliCredential())。AzureCliCredential 取的是 Azure CLI 的登录态,所以 az login 是硬前置——00-course-setup/README.md 的 Step 3 写明大多数示例通过 AzureCliCredential 或 DefaultAzureCredential 认证、不需要 API key。
.env.example 里确实有 AZURE_OPENAI_API_KEY 这一项,注释标着 Optional。但第 01 课的代码里没有任何地方读它——你把 key 填得再对,这个示例也不会用。
处置后怎么验证
按顺序过:dotnet --list-sdks 的输出里有满足 global.json 的 SDK;在示例文件所在目录执行 dotnet run ./01-dotnet-agent-framework.cs;先看它有没有再抛 AZURE_OPENAI_ENDPOINT is not set.,没有就说明环境变量这一环通了。
有一点要提醒:仓库里的 scripts/validate-notebooks.ps1 默认把 .NET 相关的文件排除在外(脚本里的筛选条件是 $IncludeDotnet -or ($_.FullName -notmatch 'dotnet|dotNET')),.agents/skills/testing-course-samples/SKILL.md 也写明 *-dotnet-* 的 notebook 需要 .NET Interactive 内核、默认排除。所以那份 PASS/FAIL 矩阵全绿,不能当作 .NET 侧能跑的证据。
什么情况说明不是这个原因
- 报错文本里没有
is not set,SDK 也确认过是 10 开头,包也还原成功了,卡点却出现在调用模型时——那是账号侧:部署名对不上、az login登的租户不对、或该部署不支持所用接口,与本文这三处无关。 - 你跑的是
05-dotnet-agent-framework.ipynb而不是.cs,报的是找不到内核——那是 .NET Interactive 环境的事,不是包引用的事。 - 报错来自
Azure.AI.Agents.Persistent这类预览包的接口签名对不上:预览包本身在变,先确认你手上的文件头钉的版本还是不是仓库里当前那一行。 08-multi-agent/code_samples/workflows-agent-framework/dotNET/下几个文件名里带ghmodel,但它们代码里读的是AZURE_OPENAI_ENDPOINT;.env.example的注释也写明 GitHub Models 已弃用且不支持 Responses API、示例已全部改用 Azure OpenAI。所以文件名和实际接入目标已经脱节,按代码里读的变量名配就对了,不必去找 GitHub Models 那侧的配置。
最后一句实话:这三处(SDK 约束、包引用钉法、环境变量来源)都写在文件里,翻一遍比试错快得多。该项目持续更新,以仓库最新内容为准。
本文依据 github.com/microsoft/ai-agents-for-beginners 仓库于 2026-08-18 的公开内容整理,
事实来自仓库内的课程正文与代码示例。我们没有跑过文中涉及的代码,
因此不涉及运行结果、耗时与 Agent 实际表现的任何描述。
该课程持续更新,文中涉及的文件路径、依赖与接口写法随版本变动,请以仓库最新内容为准。
文中涉及的云端服务调用会产生费用并可能上传数据,请自行评估密钥与数据边界。
安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。