ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

.NET开发者如何系统学习生成式AI?微软开源课程实战解读

.NET开发者如何系统学习生成式AI?微软开源课程实战解读 最近我在重新梳理 .NET 生态里的生成式人工智能学习路线时发现微软官方开源的《生成式人工智能初学者 .NET 第二版》课程已经在 GitHub 上迭代到了很完整的状态。它的定位很明确给 .NET 开发者一条从零开始接触 LLM、RAG、Function Calling 甚至 Agent 的实战路径。如果你是一个 C# 后端工程师想搞清楚“我这个项目里要不要上语义搜索”“章智能体怎么落地”“Semantic Kernel 到底怎么用”这套课程是目前我见过最适合直接照着撸一遍的免费资料。我花了一周时间把八个章节全部过完又用课程里的模板改造了一个内部知识库问答工具。这篇文章我会把课程的设计思路、核心章节结构、常见坑位和我的实操记录一起整理出来。不管你是刚接触 .NET 的新手还是已经在生产环境摸爬滚打几年的老手这篇文章都能帮你少走不少弯路。1. 课程整体设计与思路拆解1.1 这门课到底解决什么问题很多 .NET 开发者这两年都有一种焦虑生成式 AI 的示例代码几乎全是 PythonOpenAI、LangChain、LlamaIndex 的生态里 C# 的出场率很低。但实际上微软在 .NET 上的 AI 投入一直在加码Semantic Kernel 就是微软官方主推的 AI 编排框架还有像 ML.NET、Azure AI Search、Qdrant 的 .NET SDK 也都很成熟。这套课程解决的问题就是两件事第一让你知道 .NET 做生成式 AI 完全可行不需要转语言第二用官方维护的文档和代码带你把“调用模型”这件小事升级成“构建 AI 应用”这套完整能力。课程不是教你背 API而是通过八个章节层层递进让你理解从 Prompt 到向量检索再到 Agent 决策的完整链路。课程完全免费GitHub 上有中文版 README所有示例代码都基于 .NET 8 和 Semantic Kernel 1.x基本上是你拿到手就能跑的状态。对比市面上那些“付费专栏”里的零散 demo这门课的工程完整度高太多了。1.2 八个章节的内容地图与学习路径第二版课程的整体结构是这样的章节核心主题学完你能做什么01生成式 AI 与 LLM 基础说清楚 Token、Temperature、Prompt 这些概念02创建第一个 AI 应用跑通一个最小可用的控制台聊天程序03核心概念掌握 System Prompt、Few-shot、多模态输入04向量数据库与 Embedding理解语义搜索的原理并动手实现05构建 RAG 应用让模型基于自己的文档回答问题06Function Calling让模型调用 C# 方法完成真实操作07构建 AI Agent组合模型、工具和流程做一个会“自己干活”的智能体08用户体验设计给 AI 应用加上流式输出、聊天历史等细节这个顺序是精心排过的。第 1 到第 3 章解决“模型是什么、怎么对话”的问题第 4 到第 5 章解决“模型没有你私域数据”的问题第 6 到第 7 章解决“模型只能说话不能动手”的问题最后第 8 章解决“怎么让用户用得舒服”的问题。每章之间都有依赖关系不建议跳跃式学习。1.3 第二版对比第一版有哪些关键升级第一版的课程里 Semantic Kernel 还在用旧版的IKernel接口很多 API 到后面都废弃了。第二版全面切换到 .NET 8 和 Semantic Kernel 1.x引入了一些非常重要的新能力统一的Kernel对象把模型、插件、记忆、过滤器都挂载在同一个容器里基于KernelFunction的特性标注写插件变得非常简洁支持 OpenAI 和 Azure OpenAI 双通道配置也兼容 Ollama 这类本地模型服务新增了对 OpenTelemetry 的支持可以观测 Token 消耗和链路耗时强化了 Function Calling 的自动调用机制模型可以自动选择要执行的函数如果你是看过第一版再来看第二版最需要适应的变化就是IKernel变成了Kernel而且原来的UseAzureOpenAIChatCompletion这类扩展方法也改了签名。课程里每个示例都对应最新的推荐写法直接跟着它走就可以了。2. 核心细节解析与实操要点2.1 环境准备.NET 8、IDE 和模型服务缺一不可实操之前先把环境补齐。最简单的组合是.NET 8 SDK我建议直接装 SDK 而不是 runtime开发调试都方便Visual Studio 2022 或者 VS Code C# Dev Kit 扩展一个可以访问的模型服务OpenAI、Azure OpenAI、Ollama 都行如果你是个人学习又不想在 API 上花钱我强烈推荐先用 Ollama 跑本地模型比如qwen2.5:7b或llama3.1:8b。课程官方示例默认用的是 OpenAI 格式的配置但通过在代码里替换服务注册那段完全可以让 Semantic Kernel 连到本地的 Ollama 端点。课程第一个示例的配置大概长这样{ OpenAI: { ModelId: gpt-4o-mini, ApiKey: sk-... } }如果你用 Azure OpenAI就换成{ AzureOpenAI: { DeploymentName: gpt-4o-mini, Endpoint: https://your-resource.openai.azure.com/, ApiKey: ... } }有一个细节很多初学者会栽跟头Azure OpenAI 的ModelId要填部署名不是模型名。你在 Azure 门户里起的部署名叫什么代码里就要写什么否则请求会报 404。OpenAI 官方接口则填模型名比如gpt-4o-mini。2.2 用 Semantic Kernel 快速搭建第一个聊天应用课程第 2 章的核心代码其实非常短。我把官方的示例简化一下一个能跑的 C# 控制台应用只需要这几步dotnet new console -n FirstAIApp cd FirstAIApp dotnet add package Microsoft.SemanticKernel然后修改Program.csusing Microsoft.SemanticKernel; using Microsoft.Extensions.Configuration; var builder new ConfigurationBuilder() .AddUserSecretsProgram() .AddJsonFile(appsettings.json, optional: true); var config builder.Build(); var kernelBuilder Kernel.CreateBuilder(); // 使用 OpenAI kernelBuilder.AddOpenAIChatCompletion( modelId: config[OpenAI:ModelId], apiKey: config[OpenAI:ApiKey] ); // 或者使用本地 Ollama // kernelBuilder.AddOpenAIChatCompletion( // modelId: qwen2.5:7b, // apiKey: ollama, // endpoint: new Uri(http://localhost:11434/v1) // ); var kernel kernelBuilder.Build(); var prompt 用一句话解释什么是 RAG; var response await kernel.InvokePromptAsync(prompt); Console.WriteLine(response);这里有几个我很在意的点不要硬编码 API Key。课程虽然为了演示方便会在配置文件里写 Key但实际项目里请一律用环境变量或者dotnet user-secrets。命令行执行dotnet user-secrets init然后把 Key 放进去避免泄露到 Git 仓库。AddOpenAIChatCompletion的第四个参数可以传endpoint这是接入本地模型和国内兼容服务的关键。很多国内服务商都提供了 OpenAI 风格接口只要 endpoint 指对代码几乎不用改。InvokePromptAsync只是最基础的用法后面章节会看到KernelFunction才是真正强大的抽象。如果你第一次跑出来的中文是乱码先检查控制台编码在csproj里加上InvariantGlobalizationfalse/InvariantGlobalization或者启动前执行Console.OutputEncoding System.Text.Encoding.UTF8;这个问题在 Windows 上非常常见。2.3 从零理解 Embedding、向量数据库与 RAG整个课程里最值钱的部分我认为是第 4 章和第 5 章也就是嵌入模型、向量数据库和 RAG。很多人在聊 RAG 的时候只知道“把文档塞给模型”这其实是一个巨大的误解。RAG 的完整流程是把文档拆成小块比如按段落或按固定 token 数切分用嵌入模型把每个文本块转换成向量把向量存进向量数据库用户提问时先把问题转成向量在向量库里找与问题最相似的几个文本块把这些文本块作为上下文拼到 Prompt 里让模型基于上下文生成回答嵌入的本质是把文字映射到高维空间里的坐标语义相近的文本在空间里距离更近。课程里用的向量数据库之一是 Qdrant它在 .NET 下的 SDK 写起来很直观。一个典型的入库流程这样写using Qdrant.Client; using Qdrant.Client.Grpc; var client new QdrantClient(localhost, 6333); var vector await embeddingService.GenerateEmbeddingAsync(text); await client.UpsertAsync( collectionName: knowledge_base, points: new ListPointStruct { new PointStruct { Id Guid.NewGuid(), Vectors vector, Payload new Dictionarystring, object { [text] text, [source] manual.pdf } } } );查询的时候var queryVector await embeddingService.GenerateEmbeddingAsync(question); var results await client.SearchAsync( collectionName: knowledge_base, vector: queryVector, limit: 5 );这里必须注意查询用的嵌入模型和入库用的嵌入模型必须是同一个否则向量空间不一致检索结果基本是随机的。我身边真有同事在这上面踩坑换了个 embedding 模型没重新入库结果语义搜索效果差得离谱。课程里的示例更多是用内存中的简单向量存储来演示原理但真实业务里你肯定需要一个独立的向量数据库。除了 Qdrant我也推荐关注 Azure AI Search 和 Redis Stack特别是如果你的 .NET 应用本来就在用 Redis直接用它扩展出向量检索能力运维成本会低很多。2.4 Function Calling 与插件化开发RAG 解决了模型“不知道”的问题Function Calling 解决的则是模型“做不到”的问题。第 6 章会让模型学会调用你写好的 C# 方法这个思路在现实场景里极其有用。比如你想让 AI 助手去查数据库里某个订单的状态模型本身不会连数据库但通过 Function Calling模型会识别出“用户想知道订单状态”然后生成一个调用GetOrderStatus(orderId)函数的请求你的程序执行这个函数拿到真实数据再交给模型生成最终回答。Semantic Kernel 里写一个函数非常简单public class OrderPlugin { [KernelFunction(get_order_status)] [Description(根据订单号查询订单状态)] public string GetOrderStatus(string orderId) { // 这里写真实的数据库查询逻辑 return $订单 {orderId} 的状态是已发货; } } var kernelBuilder Kernel.CreateBuilder(); kernelBuilder.AddOpenAIChatCompletion(modelId, apiKey); var kernel kernelBuilder.Build(); kernel.Plugins.AddFromTypeOrderPlugin();之后当用户问“帮我看看订单 12345 发没发货”模型就会自动选择get_order_status来获取答案。这个能力是构建 Agent 的基础没有它AI 永远只是一个聊天的玩具。不过你要小心函数描述的重要性。模型是根据Description来决定调不调用这个函数的太模糊的描述会导致模型在错误的时候调用它。建议描述里写清楚函数的作用、参数的格式和适用场景。2.5 生产环境下的用户体验设计课程最后一章讲的是用户体验可能有些技术人觉得这部分“很软”但实际做 AI 应用时体验设计比后端逻辑更容易劝退用户。核心就两点流式输出和聊天历史。流式输出也就是打字机效果直接决定用户等待时的焦虑感。Semantic Kernel 里支持StreamToMessagesAsync示例代码不复杂但体验差距巨大。聊天历史的管理也很有讲究。你不能把从古到今的所有对话都塞给模型否则 Token 很快就爆了。比较通用的做法是维护一个滑动窗口只把最近 N 轮对话送进去系统 Prompt 和最新的工具结果则一直保留。课程里有现成的对话框窗体示例照着抄就行。3. 实操过程与核心环节实现3.1 快速跑通官方示例的完整步骤我建议你直接把官方仓库克隆到本地先跑通再改造。以下是完整步骤git clone https://github.com/microsoft/generative-ai-for-beginners-dotnet cd generative-ai-for-beginners-dotnet cd 02-SetupAndFirstPrompt各个章节的示例都是独立的 .NET 项目所以需要单独还原和构建dotnet restore dotnet build运行前检查一下appsettings.json里的模型配置然后用环境变量或 user-secrets 注入真实密钥。我用的是 Azure OpenAI所以在项目根目录执行了dotnet user-secrets init dotnet user-secrets set AzureOpenAI:ApiKey your-key dotnet user-secrets set AzureOpenAI:DeploymentName gpt-4o-mini dotnet user-secrets set AzureOpenAI:Endpoint https://your-resource.openai.azure.com/然后运行dotnet run看到控制台里打印出 AI 回复就算打通了。整个过程如果顺利半小时内可以跑完前 3 章。有个坑我想提醒一下仓库里有些示例项目引用的 NuGet 包版本比较新如果你本地的 .NET SDK 版本不够dotnet restore会提示找不到某个 target framework。解决办法是把csproj里的TargetFrameworknet8.0/TargetFramework改成你本地已安装的版本比如net9.0但要注意 SDK 和运行时版本匹配。3.2 用课程模板做一个人事制度问答系统我这次的实战目标很明确做一个公司内部知识库问答系统能让员工用自然语言问“年假怎么申请”“加班调休怎么算”。数据源是一堆 Word 文档和 PDF。第一步是解析文档。课程示例里用的是 Markdown 和纯文本但真实业务里 Word、PDF 才是大头。处理 Word 我用了 Aspose.Words for .NET虽然它是商业库但确实能把 .docx 解析成流式文本对做知识库预处理非常方便。如果你不想引入商业库也可以先手工把文档转成 Markdown再用课程里的方案。第二步是分块。我按标题和段落结构切块每块控制在 300 到 500 字块之间重叠 50 字左右。这一步的细节很影响检索质量。切太大了向量里混入太多无关内容检索精度下降切太小了上下文信息不完整模型回答缺少背景。我后来在课程第 5 章的练习基础上自己写了一个简单的分块器按\n\n和标题层级切开效果比固定长度切好很多。第三步是生成向量入库。我用的 embedding 模型是text-embedding-3-small向量维度 1536存到 Qdrant。中文场景下我没有加额外的分词器因为现代嵌入模型对中文的支持已经不错了。如果你用比较老的模型可以把中文文本先做分词的“空 Token”拼接但现阶段真没必要。第四步是查询链路。用户在 Blazor 页面里输入问题后端调用嵌入模型生成向量到 Qdrant 召回 Top 5 文本块把文本块拼到 Prompt 里最后交给 GPT 生成回答。整体代码如下所示var question 我想申请年假流程是什么; var questionEmbedding await embeddingService.GenerateEmbeddingAsync(question); var searchResults await vectorStore.SearchAsync(questionEmbedding, topN: 5); var context string.Join(\n\n, searchResults.Select(r r.Text)); var prompt $ 你是企业内部知识库助手。 请根据以下参考内容回答用户问题 --- {context} --- 问题{question} ; var response await chatService.CompleteAsync(prompt);这个系统上线后员工不再需要翻几十页制度文件直接问就能得到带出处的答案。相比传统的搜索框这种方式的理解能力和回答体验都好很多。3.3 用 Semantic Kernel 实现一个带工具调用的日报助手除了知识库问答我还照着课程第 6 章和 7 章做了一个“日报助手”的 demo用户告诉助手“我今天处理了 5 个工单修复了登录超时问题”助手会自动调用两个工具——SaveDailyReport把内容写入数据库GetTodayReportStats返回今天已提交报表数量。实现起来关键就是注册插件和让模型自动选择函数。核心的插件逻辑我都放在一个类里public class ReportPlugin { [KernelFunction(save_daily_report)] [Description(保存日报参数为日报正文内容)] public string SaveDailyReport(string content) { // 写入数据库 return $日报已保存{DateTime.Now:yyyy-MM-dd}; } [KernelFunction(get_today_report_stats)] [Description(获取今天已提交的日报数量)] public string GetTodayReportStats() { var count 0; // 从数据库查询 return $今天已提交 {count} 份日报; } }然后通过kernel.Plugins.AddFromTypeReportPlugin()注册。当模型发现用户语句里有“保存日报”的意图时就会自动带上参数调用这个方法。这个 demo 让我真正理解了“Agent 模型 工具 状态”这句话。模型负责理解和决策工具负责执行状态则保存在外部系统里。你不需要让模型记住一切只需要让它知道去哪里查、怎么做。课程第 7 章后面的内容就是在这个基础上引入循环、多步骤任务和规划器但核心思想没有变。3.4 可观测性给 AI 应用装个监控课程第二版新增了 OpenTelemetry 相关的内容这是非常贴近生产的章节。AI 应用藏着两个很难排查的问题Token 消耗过高和响应延迟波动。你不可能等用户投诉了再去猜必须有链路追踪。Semantic Kernel 1.x 提供了一组WithLoggerFactory和 OpenTelemetry 集成只需要在构建 Kernel 时配置好ActivitySource就能看到每次调用模型的 Prompt、Token 数、耗时等关键信息。var kernelBuilder Kernel.CreateBuilder(); kernelBuilder.Services.AddLogging(builder { builder.AddConsole(); builder.AddOpenTelemetry(options { options.IncludeFormattedMessage true; }); });虽然课程里的例子更偏演示但我建议你在自己的项目里把日志落到一个可查询的服务上比如 Application Insights 或 Seq。一旦线上出问题你能立刻知道是向量库慢了还是模型返回超时不用靠猜。4. 常见问题与排查技巧实录4.1 API Key 与模型配置导致的身份验证错误跑课程代码时最常见的错误就是 401 和 404。401 说明 Key 不对或没有传404 分两种情况一种是终结点不对另一种是模型名/部署名写错。我整理的排查顺序如下症状可能原因解决建议401 UnauthorizedAPI Key 为空或错误检查 user-secrets 是否正确注入404 Not Found模型名或终结点错误确认是 OpenAI 模型名还是 Azure 部署名429 Rate Limit请求频率过高或配额不足降低并发检查模型配额Request timed out网络不通或模型响应过慢设置更长的超时时间检查服务健康很多 404 其实是把 Azure OpenAI 的DeploymentName填成了gpt-4o而不是你在 Azure 上创建的部署名。这个只能靠自查报错信息里会带上具体请求的 URL仔细看一眼就能发现问题。4.2 本地模型接入时的兼容性问题如果你按照我的建议用 Ollama 跑本地模型有一个地方需要注意不是所有模型都完全支持 Function Calling。像llama3.1和qwen2.5的表现还行但参数较小的旧模型经常出现“模型知道了有工具但不会正确生成工具调用参数”的情况。解决办法有两个方向一是换更大更新的模型二是放弃 Function Calling改用 Prompt 强行让模型输出固定格式的函数调用 JSON然后自己在代码里解析。后者虽然麻烦但可控性强适合对格式一致性要求极高的场景。另外使用 Ollama 时Semantic Kernel 里的AddOpenAIChatCompletion要显式传入endpoint为http://localhost:11434/v1否则默认会请求 OpenAI 的官网地址必现网络错误或连接超时。4.3 RAG 检索效果差的几个隐藏原因我见过太多人一上来就怪模型不行结果最后发现是检索环节出了问题。RAG 问答系统最影响效果的几个因素我按优先级排序分块策略块太大导致语义被稀释块太小导致上下文不足。最稳妥的办法是按文档结构分块比如按 Markdown 标题或 Word 段落分。嵌入模型一致性入库和查询必须用同一个模型。向量数据库阈值相似度分数太低的结果应该直接丢弃不能硬塞给模型。召回数量不是越多越好超过 5 个文本块时模型注意力会被稀释反而容易胡说。课程里有个练习是让你比较不同分块大小对回答质量的影响我强烈建议你亲手跑一遍。只有亲眼看懂同一个问题在不同分块策略下的表现差异以后做生产方案时才会对参数敏感。4.4 一个工业场景小例子把 Modbus 报文解析结果交给 LLM课程主要是 To C 和 To B 办公场景但我在做工业数据项目时也尝试过类似的思路。比如工厂里有 Modbus 设备需要实时解析设备状态报文并生成告警说明。传统写法是自己写规则但规则多了就维护不动。我参考了课程里 Function Calling 的思路先用SequenceReaderbyte按 Modbus RTU 协议解析原始字节流提取出寄存器地址、数值和 CRC 校验结果然后把这部分结构化数据作为上下文交给模型让模型生成自然语言的设备诊断说明。var reader new SequenceReaderbyte(bytes); // 按 Modbus RTU 帧格式读取地址、功能码、数据长度和数据这个结合点给了我一个启发生成式 AI 并不总是需要直接面对原始非结构化数据很多场景下它需要的是被传统代码“清洗过”的结构化输入。课程里的函数调用正好完成了这个衔接。所以不管你是做 ERP、工厂软件还是 IoT 平台都能找到可落地的切入点。5. 学习这件课程后的几点个人体会最后分享一些我实际操作中的体会。第一个体会是这门课程不能只“看”不“写”。每一个示例我都建议在本地新建项目重新敲一遍而不是复制粘贴。敲的过程你会注意到很多细节比如 NuGet 包的版本、Kernel.CreateBuilder()和new KernelBuilder()的区别这些都是面试和实际开发中容易暴露问题的地方。第二个体会是把第八章节提到前面来看不会吃亏。很多初学者前几章写出来的东西只是一个能对话的命令行程序没有流式输出、没有对话历史用户用起来就是“一问一卡”。先知道产品体验的目标再回去学原理会觉得每条知识都有归处。第三个体会是课程之外一定要有自己的实践项目。课程示例是“正确”的但不属于你的业务。我是用内部人事制度文档做了一个问答机器人过程中遇到了 Word 解析乱码、分块分得稀碎、Qdrant 连接被防火墙拦截等一系列问题。这些坑才是真正帮你成长的地方。你现在学完这门课下一步不如想想自己手头哪份文档是最常被同事问到的先拿它开刀做一个小助手。做完之后你再回头看这套课程会发现它其实不是终点而是一张进入 AI 应用世界的入场券。
返回列表