从零开始-用CSharp调用你的第一个大模型
作为开发者我们不能只停留在「用」的层面——我们需要理解它。这个系列的目标是带你从.NET开发者的视角一层一层拆开AI应用开发的全貌。从最基础的模型调用到记忆系统再到工具调用和Agent架构——每一篇都在前一篇的基础上叠加一层能力。而这一切的起点就是今天这篇文章。大模型到底是什么抛开复杂的数学和神经网络架构从软件开发者的视角来看大语言模型可以简化为一个非常朴素的概念本质上它是一个函数输入Prompt → 大语言模型 → 输出Completion你可以把它理解成一个超级强大的字符串到字符串的映射函数string Ask(string prompt) ... // 模型内部有数千亿参数在做这件事给它一段文本Prompt它返回一段文本Completion。就这么简单。但简单背后有几个关键特征特征说明概率性同样的输入每次输出可能不同——它是在预测下一个最可能的词无状态每次调用都是独立的模型不会记住你上次问了什么上下文依赖输出质量完全取决于你给的输入Prompt有多好Token驱动模型不直接处理文字而是把文字切分成Token词元来处理什么是TokenToken是大模型处理文本的基本单位。你可以粗略地理解为一个词或一个词片段你好世界 → [你, 好, 世, 界] 中文大约1个字 ≈ 1-2个Token Hello World → [Hello, World] 英文大约1个词 ≈ 1-1.5个TokenToken很重要因为API按Token计费输入Token 输出Token 你的花费模型有上下文窗口限制比如8K、32K、128K Token超过就处理不了速度取决于Token生成Token是逐个进行的Token越多越慢从「裸函数」到「对话API」上面说的Ask(prompt)只是一个概念模型。实际上大模型服务商提供的是更结构化的对话API。以OpenAI兼容协议目前行业标准为例一次API调用由以下几个核心部分组成┌─────────────────────────────────────────────┐ │ 一次API请求的组成 │ ├─────────────┬───────────────────────────────┤ │ model │ 用哪个模型如glm-5.1 │ │ │ │ │ messages │ 对话消息列表 │ │ │ - system: 系统提示词人设 │ │ │ - user: 用户说的话 │ │ │ - assistant: AI之前的回复 │ │ │ │ │ parameters │ 控制参数 │ │ │ - temperature: 创造性0~1 │ │ │ - max_tokens: 最大输出长度 │ │ │ - top_p: 采样范围 │ └─────────────┴───────────────────────────────┘Messages对话的核心messages是整个请求中最重要的部分。它不是简单的一段文字而是一个结构化的消息列表每条消息都有一个角色rolemessages [ { role: system, content: 你是一个专业的编程助手 }, ← 告诉AI它是谁 { role: user, content: 什么是递归 }, ← 用户的问题 { role: assistant, content: 递归是... }, ← AI之前的回答 { role: user, content: 能给个例子吗 } ← 用户的追问 ]三种角色的分工角色职责比喻System定义AI的身份、行为规则、输出格式相当于岗位说明书User用户的输入相当于客户的问题AssistantAI的历史回复相当于之前的回答记录关键认知因为模型是无状态的所以每次调用都要把完整的对话历史重新发一遍。这就是AI记忆的真相——不是模型记住了而是你每次都帮它复习了一遍。一次完整的请求-响应理解了上面的概念一次API调用的全貌就清晰了请求你发给模型的{ model: glm-5.1, messages: [ { role: system, content: 你是一个有帮助的AI助手。 }, { role: user, content: 用一句话解释什么是API。 } ], temperature: 0.7, max_tokens: 1024 }响应模型返回的{ id: chatcmpl-xxx, model: glm-5.1, choices: [{ message: { role: assistant, content: API应用程序编程接口是一组定义软件组件之间如何交互的规则和协议。 }, finish_reason: stop }], usage: { prompt_tokens: 28, completion_tokens: 35, total_tokens: 63 } }响应的关键字段choices[0].message.content模型的回复内容finish_reason为什么停止stop正常结束length达到长度限制usage本次调用消耗的Token数大模型能做什么理解了大模型的接口形式我们再来看看它能做什么。本质上大模型的能力可以归结为文本的理解与生成能力示例问答C#中async和await的作用是什么创作写一篇关于微服务的博客大纲翻译把这段话翻译成英文...摘要用100字总结这篇文章...代码生成用C#写一个快速排序算法数据分析分析这段JSON数据的结构推理如果ABBC那么A和C的关系是但大模型也有明确的局限局限说明知识截止训练数据有截止日期不知道最新的事无法联网不能搜索网页、查天气、查数据库无法执行不能运行代码、操作文件、发邮件幻觉可能一本正经地胡说八道后面几篇文章我们将逐一解决这些局限——通过记忆系统解决知识遗忘通过Function Calling让AI学会调用工具通过MCP协议让AI接入工具生态。环境准备理论讲完了接下来进入实战。在开始之前你需要去 智谱AI开放平台 注册一个智谱AI账号注册后会免费送一些Token拿到你的API Key和接口地址克隆本项目代码https://github.com/MapleWithoutWords/AIStudyDemos智谱提供OpenAI兼容协议接口你也可以换成其他兼容的服务商如DeepSeek、月之暗面等只需修改baseUrl和apiKey即可。动手实践用C#调用大模型环境准备就绪打开项目中的AICallConsole1项目配置好你的API Key后直接运行即可体验效果。下面我们来看看代码里做了什么。C#中有两种方式调用大模型方式一HttpClient直接调用——看透本质大模型服务商几乎都遵循OpenAI兼容协议本质上就是一个RESTful API。最直接的调用方式就是用HttpClient发送HTTP请求using var httpClient new HttpClient(); httpClient.BaseAddress new Uri(baseUrl); httpClient.DefaultRequestHeaders.Authorization new AuthenticationHeaderValue(Bearer, apiKey); var jsonContent JsonSerializer.Serialize(new { model glm-5.1, messages new[] { new { role system, content 你是一个有帮助的AI助手。 }, new { role user, content 你好请介绍一下你自己。 } }, temperature 0.7, max_tokens 1024 }); var content new StringContent(jsonContent, Encoding.UTF8, application/json); var response await httpClient.PostAsync(chat/completions, content); var responseBody await response.Content.ReadAsStringAsync();关键点解析字段说明model要调用的模型名称如glm-5.1、gpt-4o等messages对话消息数组包含system系统提示词和user用户消息temperature控制输出随机性0-1之间越高越发散max_tokens限制模型最大输出token数chat/completionsOpenAI兼容协议的标准端点这种方式虽然原始但它能让你完全理解请求和响应的原始结构。在生产环境中我们更推荐使用SDK。方式二SDK调用——Microsoft.Extensions.AI.OpenAI理解了HTTP裸调的原理后我们来看看更优雅的SDK方式。微软推出的Microsoft.Extensions.AI系列库采用了两层架构包名职责Microsoft.Extensions.AI抽象层定义IChatClient、ChatMessage等统一接口Microsoft.Extensions.AI.OpenAIOpenAI适配层提供OpenAI.Chat.ChatClient等具体实现我们只需安装适配层包即可它会自动引入抽象层PackageReference IncludeMicrosoft.Extensions.AI.OpenAI Version10.7.0 /核心接口是IChatClient它屏蔽了不同模型供应商的差异using Microsoft.Extensions.AI; IChatClient client new OpenAI.Chat.ChatClient( model, new ApiKeyCredential(apiKey), new OpenAI.OpenAIClientOptions { Endpoint new Uri(baseUrl) }) .AsIChatClient();这里我们用Microsoft.Extensions.AI.OpenAI提供的OpenAI.Chat.ChatClient创建客户端再通过.AsIChatClient()适配到Microsoft.Extensions.AI定义的抽象接口。这意味着——无论你用的是OpenAI、智谱GLM、DeepSeek还是其他兼容协议的服务商代码几乎不用改。构建交互式对话有了客户端我们来构建一个可以持续对话的控制台应用while (true) { Console.Write(\n你: ); var userInput Console.ReadLine(); if (string.IsNullOrWhiteSpace(userInput)) continue; if (userInput.Equals(exit, StringComparison.OrdinalIgnoreCase)) { Console.WriteLine(再见); break; } ListChatMessage chatMessages [ new ChatMessage(ChatRole.System, 你是一个有用的AI助手请用中文回答用户的问题。), new ChatMessage(ChatRole.User, userInput) ]; Console.Write(\nAI: ); await foreach (var update in client.GetStreamingResponseAsync(chatMessages)) { foreach (var item in update.Contents) { if (item is TextContent text) { Console.Write(text.Text); } } } }理解消息角色ChatMessage有三种核心角色System系统提示词定义AI的行为准则和人设User用户输入的消息AssistantAI的历史回复后续讲记忆时会用到流式输出 vs 一次性响应一次性响应var response await client.GetResponseAsync(chatMessages); Console.WriteLine(response.Text);简单直接但需要等待模型生成完毕后才返回。对于长文本生成用户会感到明显的等待。流式响应推荐await foreach (var update in client.GetStreamingResponseAsync(chatMessages)) { foreach (var item in update.Contents) { if (item is TextReasoningContent textReasoning isNeedReasoningContent) { Console.Write(${textReasoning.Text}); } else if (item is TextContent text) { Console.Write(text.Text); } else if (item is UsageContent usageContent) { Console.WriteLine(JsonSerializer.Serialize(usageContent.Details)); } } }流式输出有三个内容类型需要注意类型说明TextReasoningContent模型的思考过程推理类模型如DeepSeek-R1会输出TextContent最终的正文输出UsageContentToken用量统计信息流式输出的体验远优于等待完整响应——用户几乎可以实时看到AI的回答。核心架构一览用户输入 ↓ 构建 ChatMessage 列表 (System User) ↓ IChatClient.GetStreamingResponseAsync() ↓ 逐块接收 StreamingResponseUpdate ↓ 解析 TextReasoningContent / TextContent / UsageContent ↓ 实时输出到控制台小结这篇文章我们学习了大模型的本质一个从字符串到字符串的映射函数具有概率性、无状态、上下文依赖、Token驱动四大特征对话API的组成model messagessystem/user/assistant 控制参数Token的概念大模型处理文本的基本单位直接影响成本和速度大模型的能力与局限能做问答、创作、翻译、推理但无法联网、无法执行、有知识截止环境准备使用智谱AI服务商通过UserSecrets安全管理API Key两种调用方式HttpClient裸调 vs SDK封装前者帮助理解原理后者用于生产Microsoft.Extensions.AI系列库Microsoft.Extensions.AI抽象层Microsoft.Extensions.AI.OpenAI适配层的两层架构一套代码兼容多个模型服务商