5:微软AI库Microsoft.Extensions.AI的使用与流式响应
摘要: 本文展示了一个使用Microsoft.Extensions.AI抽象接口调用阿里云百炼大模型的C#代码示例。该代码通过OpenAI SDK兼容模式访问Qwen3模型,实现了英语学习辅助功能:提取英文句子中超出初中教材范围的单词并转换为原型形式,对超出范围的问题会拒绝回答
1. Microsoft.Extensions.AI代码内容
using Microsoft.Extensions.AI;
using System.ClientModel;
using OpenAI;
// 配置
const string platformName = "阿里云百炼";
var apiKey = "**********";
var baseUrl = "https://dashscope.aliyuncs.com/compatible-mode/v1/";
var model = "qwen3-max";
// 构建消息
ChatMessage[] messages =
[
new ChatMessage(ChatRole.System,
@"你是一个有经验的针对中文为母语的英语学习者提供英语学习服务的老师,你会把用户提供的英文句子中的中国初中英语教材之外的单词提取出来。把复数、过去式等转换为原型形式。超出英语学习范围的东西,请回复‘这个问题我回答不了,换个问题吧。’"
),
new ChatMessage(ChatRole.User,
//@"Far out in the uncharted backwaters of the unfashionable end of the Western Spiral arm of the Galaxy lies a small unregarded yellow sun.")
@"马云是谁")
];
Console.WriteLine($"正在调用{platformName}平台...\n");
// 发送请求并获取响应
IChatClient client =
new OpenAI.Chat.ChatClient(model, new ApiKeyCredential(apiKey),
new OpenAIClientOptions() { Endpoint = new Uri(baseUrl) }).AsIChatClient();
var chatResponse = await client.GetResponseAsync(messages);
// 输出结果
Console.WriteLine($"\nAI 回复: {chatResponse.Text}");
// 输出 token 使用情况
if (chatResponse.Usage != null)
{
Console.WriteLine($"\n使用 tokens: {chatResponse.Usage.TotalTokenCount}");
Console.WriteLine($" - Prompt tokens: {chatResponse.Usage.InputTokenCount}");
Console.WriteLine($" - Completion tokens: {chatResponse.Usage.OutputTokenCount}");
}
2. Microsoft.Extensions.AI代码效果
2.1. 效果一
正在调用阿里云百炼平台...
AI 回复: 以下是从句子中提取出的、超出中国初中英语教材范围的单词(已转换为原型形式):
- uncharted
- backwater
- unfashionable
- spiral
- galaxy
注:
- “lies” 是动词 “lie”(位于)的第三人称单数形式,但 “lie” 属于初中词汇,故不列入。
- “yellow” 和 “sun” 均为初中词汇。
- “arm” 在此处指“旋臂”,虽“arm”本身是初中词,但该用法属天文术语,不过按规则只考虑单词本身是否超纲,因此不单独列出。
以上列表仅包含原型且超出初中范围的词汇。
使用 tokens: 266
- Prompt tokens: 112
- Completion tokens: 154
D:\Code\CSharp\AI\LearnAIWithZack\Basic\CallAiWithMicrosoft_Extensions_AI1\bin\Debug\net9.0\CallAiWithMicrosoft_Extensions_AI1.exe (进程 16196)已退出,代码为 0 (0x0)。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
2.2. 效果二
正在调用阿里云百炼平台...
AI 回复: 这个问题我回答不了,换个问题吧。
使用 tokens: 92
- Prompt tokens: 83
- Completion tokens: 9
D:\Code\CSharp\AI\LearnAIWithZack\Basic\CallAiWithMicrosoft_Extensions_AI1\bin\Debug\net9.0\CallAiWithMicrosoft_Extensions_AI1.exe (进程 61004)已退出,代码为 0 (0x0)。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
3. Microsoft.Extensions.AI代码整理说明
3.1. 这段代码“本质上在做什么”
一句话总结:
这是一个用
Microsoft.Extensions.AI作为“统一抽象接口”,
实际通过 OpenAI SDK,调用“阿里云百炼(DashScope)OpenAI-Compatible 接口”,
使用 Qwen3 模型完成一次 Chat Completion 的示例。
换成结构图就是:
你的业务代码
↓(IChatClient 抽象)
Microsoft.Extensions.AI
↓(适配器)
OpenAI SDK
↓(OpenAI-compatible 协议)
阿里云百炼 / DashScope
↓
qwen3-max
3.2. 逐块拆解代码在干什么
1️⃣ using 引入的含义(非常关键)
using Microsoft.Extensions.AI;
using System.ClientModel;
using OpenAI;
| 命名空间 | 作用 |
|---|---|
| Microsoft.Extensions.AI | AI 抽象接口(IChatClient / ChatMessage) |
| System.ClientModel | Azure / OpenAI SDK 用的 Credential / Pipeline 基础设施 |
| OpenAI | OpenAI 官方 .NET SDK |
👉 这里体现了一个重要事实:
Microsoft.Extensions.AI 本身不发请求,它只是“接口 + 统一模型”
2️⃣ 配置部分:OpenAI-Compatible 的典型特征
var baseUrl = "https://dashscope.aliyuncs.com/compatible-mode/v1/";
var model = "qwen3-max";
这两点非常关键:
baseUrl明确是 OpenAI Compatible Modemodel是 Qwen,而不是 GPT
👉 说明:
你用的是 OpenAI SDK 的“协议”,但不是 OpenAI 的“模型”
这正是 OpenAI-compatible 生态 的核心价值。
3️⃣ Prompt / 消息构建(抽象层已生效)
ChatMessage[] messages =
[
new ChatMessage(ChatRole.System, "..."),
new ChatMessage(ChatRole.User, "马云是谁")
];
这里已经完全脱离 OpenAI SDK 的 Message 类型:
- ❌ 没有
ChatCompletionMessage - ❌ 没有
role = "user"
而是:
- ✅
Microsoft.Extensions.AI.ChatMessage - ✅
ChatRole.System / User
👉 这一步说明:
你的业务逻辑已经只依赖 Microsoft.Extensions.AI 的模型
4️⃣ 最关键的一行:AsIChatClient()
IChatClient client =
new OpenAI.Chat.ChatClient(...)
.AsIChatClient();
这一行做了三件事:
- 使用 OpenAI SDK 的 ChatClient
- 指定:
- API Key
- Endpoint(DashScope)
- Model(qwen3-max)
- 通过 Adapter 转成
IChatClient
这是整个示例的 核心价值点:
OpenAI SDK → 适配 → Microsoft.Extensions.AI 抽象
5️⃣ 发送请求(完全抽象化)
var chatResponse = await client.GetResponseAsync(messages);
此时你的代码已经:
- ❌ 不知道底层是 OpenAI
- ❌ 不知道是 Qwen
- ❌ 不知道是 DashScope
它只知道:
“我在调用一个 Chat AI”
这正是 Microsoft.Extensions.AI 想要的效果。
6️⃣ 响应解析(统一结构)
Console.WriteLine(chatResponse.Text);
以及:
chatResponse.Usage.TotalTokenCount
说明 Microsoft.Extensions.AI 做了什么?
- 统一了:
- 返回文本
- Token Usage
- 抹平了不同厂商的字段差异
4. Microsoft.Extensions.AI流式输出
4.1. 一句话定义
流式输出(Streaming Output)
是指 模型在“生成过程中”就把结果分段返回,而不是等全部生成完一次性返回。
对比一下最直观:
| 模式 | 返回方式 |
|---|---|
| 非流式 | 等模型生成完 → 一次性返回 |
| 流式 | 生成一个 token / 片段 → 立刻返回 |
4.2. 为什么流式输出在大模型里“特别重要”
1️⃣ 大模型生成是“逐 token”的
从模型内部看:
输入 Prompt
↓
预测 token1
↓
预测 token2
↓
预测 token3
↓
……
👉 本来就是一个“流”
非流式只是:
把这个流 缓存在服务器,最后一次性吐给你
2️⃣ 非流式的核心问题
❌ 延迟高(Latency)
- 模型生成 500 token
- 你要 等 5–10 秒
- 用户看到的是“卡住”
❌ 无法实时反馈
- UI 不能打字机效果
- CLI / WebSocket / SSE 体验差
❌ 浪费用户时间
很多时候:
- 用户看到前两句就知道“这回答不对”
- 但你已经为后面的 token 付费了
3️⃣ 流式输出解决了什么
✅ 感知延迟极低
- 200ms 内就能看到第一个字
- 用户心理上觉得“很快”
✅ 可随时中断
- 用户点“停止”
- 客户端断流
- 后续 token 不再生成(节省钱)
✅ 更适合交互式场景
- Chat
- IDE Copilot
- 教学逐字讲解
- 命令行 AI
4.3. 流式输出 ≠ 一次返回很多次
一个常见误解:
“流式是不是服务器把结果切几段返回?”
❌ 不完全是
真实情况是:
- 模型本身 边算边吐
- 服务端只是 转发 token 流
4.4. 什么时候“必须”用流式输出?
必须用的场景
| 场景 | 原因 |
|---|---|
| 聊天 UI | 用户体验 |
| IDE Copilot | 实时提示 |
| 教学讲解 | 边讲边看 |
| CLI AI | 反馈感 |
可以不用的场景
| 场景 | 原因 |
|---|---|
| 后台批处理 | 不看过程 |
| 报告生成 | 一次性 |
| 评分 / 分类 | 只要结果 |
4.5. 一句“工程总结”
流式输出不是锦上添花,而是“大模型交互系统的标配能力”。
它让 AI 从“远程函数调用”,变成了“实时协作对象”。
5. 流式输出代码实现
using Microsoft.Extensions.AI;
using System.ClientModel;
using HttpMataki.NET.Auto;
using OpenAI;
// 配置
const string platformName = "阿里云百炼";
var apiKey = "*********************";
var baseUrl = "https://dashscope.aliyuncs.com/compatible-mode/v1/";
var model = "qwen3-max";
//HttpClientAutoInterceptor.StartInterception();
// 构建消息
ChatMessage[] messages =
[
new ChatMessage(ChatRole.User,
@"请讲一个关于程序员的笑话,大约200字。")
];
Console.WriteLine($"正在调用{platformName}平台...\n");
// 创建聊天客户端
IChatClient client =
new OpenAI.Chat.ChatClient(model, new ApiKeyCredential(apiKey),
new OpenAIClientOptions() { Endpoint = new Uri(baseUrl) }).AsIChatClient();
// 使用流式输出
await foreach (var update in client.GetStreamingResponseAsync(messages))
{
Console.Write(update.Text);
}
Console.WriteLine("\n输出完成!");
6. 流式输出代码实现效果

更多推荐



所有评论(0)