Skip to content
Go back

.NET AI 核心构建块:Microsoft.Extensions.AI 详解

微软 .NET 团队正式推出了面向 .NET 开发者的 AI 构建块系列。这是一个四篇文章的系列,分别介绍四个核心组件:

本文是系列第一篇,专注于 Microsoft.Extensions.AI(以下简称 MEAI)。

多路 AI 提供商数据流汇聚成统一管道的示意图

什么是 Microsoft.Extensions.AI

MEAI 是 .NET 中与生成式 AI 交互的基础库,由原 Semantic Kernel 团队贡献了核心抽象与 API 设计,再结合 ASP.NET、Minimal APIs 和 Blazor 中已有的依赖注入、中间件、构建者模式等成熟实践。

如果你用过 Semantic Kernel,可以把 MEAI 理解为对 SK 基础层的替换和升级——更轻量,更专注于底层接口。

统一 API,切换提供商零成本

MEAI 最直接的价值是:不用再维护多套 SDK,用一套 API 就能对接 OpenAI、OllamaSharp、Azure OpenAI 等主流提供商。

以 OllamaSharp 为例,原始用法是这样的:

var uri = new Uri("http://localhost:11434");
var ollama = new OllamaApiClient(uri)
{
    SelectedModel = "mistral:latest"
};
await foreach (var stream in ollama.GenerateAsync("How are you today?"))
{
    Console.Write(stream.Response);
}

原始 OpenAI SDK 用法:

OpenAIResponseClient client = new("o3-mini", Environment.GetEnvironmentVariable("OPENAI_API_KEY"));

OpenAIResponse response = await client.CreateResponseAsync(
    [ResponseItem.CreateUserMessageItem("How are you today?")]
);
foreach (ResponseItem outputItem in response.OutputItems)
{
    if (outputItem is MessageResponseItem message)
    {
        Console.WriteLine($"{message.Content.FirstOrDefault()?.Text}");
    }
}

两者的 API 形状完全不同。MEAI 引入了 IChatClient 接口来统一它们。OllamaSharp 已原生实现该接口;OpenAI 客户端则需要通过适配器包(Microsoft.Extensions.AI.OpenAI)转换:

IChatClient client =
    new OpenAIClient(key).GetChatClient("o3-mini").AsIChatClient();

转换之后,发送请求的代码对所有提供商完全一致:

await foreach (ChatResponseUpdate update in client.GetStreamingResponseAsync("How are you today?"))
{
    Console.Write(update);
}

同一段业务代码,背后接的是哪家服务,换个 IChatClient 实例就行。

结构化输出:省掉解析代码

结构化输出让你能直接拿到强类型对象,而不是从字符串里手工解析 JSON。MEAI 把这件事简化到了极致。

先看使用原始 OpenAI SDK 的写法:

class Family
{
    public List<Person> Parents { get; set; }
    public List<Person>? Children { get; set; }

    class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

ChatCompletionOptions options = new()
{
    ResponseFormat = StructuredOutputsExtensions.CreateJsonSchemaFormat<Family>("family", jsonSchemaIsStrict: true),
    MaxOutputTokenCount = 4096,
    Temperature = 0.1f,
    TopP = 0.1f
};

List<ChatMessage> messages =
[
    new SystemChatMessage("You are an AI assistant that creates families."),
    new UserChatMessage("Create a family with 2 parents and 2 children.")
];

ParsedChatCompletion<Family?> completion = chatClient.CompleteChat(messages, options);
Family? family = completion.Parsed;

使用 MEAI 的泛型扩展方法,同样的目标只需这几行:

class Family
{
    public List<Person> Parents { get; set; }
    public List<Person>? Children { get; set; }

    class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

var family = await client.GetResponseAsync<Family>(
[
    new ChatMessage(ChatRole.System, "You are an AI assistant that creates families."),
    new ChatMessage(ChatRole.User, "Create a family with 2 parents and 2 children.")
]);

GetResponseAsync<T> 内部会自动构造 Schema、发送请求、反序列化结果。不用手写 ResponseFormat,不用手写 JsonSerializer.Deserialize

请求与响应的标准化

ChatOptions 类统一了所有提供商的调参接口,包括:

在返回结果中,UsageDetails 实例包含本次请求消耗的 Token 数,方便你在成本敏感场景下做监控和控制。

中间件:在请求管道中插入逻辑

.NET Web 开发者对中间件不陌生,ASP.NET 的请求管道就是这个模型。MEAI 把同样的机制引入到了 LLM 调用流程里,让你可以在请求到达模型之前或响应返回之后插入逻辑:

MEAI 内置了日志和 OpenTelemetry(OTEL)中间件。以下示例展示如何通过构建者模式给任意 IChatClient 加上这两种能力:

public IChatClient BuildEnhancedChatClient(
    IChatClient innerClient,
    ILoggerFactory? loggerFactory = null)
{
    var builder = new ChatClientBuilder(innerClient);

    if (loggerFactory is not null)
    {
        builder.UseLogging(loggerFactory);
    }

    var sensitiveData = false; // 调试时改为 true

    builder.UseOpenTelemetry(
        configure: options =>
            options.EnableSensitiveData = sensitiveData);

    return builder.Build();
}

OTEL 事件可以推送到 Application Insights 等云服务,或者在使用 .NET Aspire 时直接显示在 Aspire 仪表板上——Aspire 已经更新以支持智能应用的额外上下文,LLM 相关的追踪会用”✨“图标标记。

DataContent:多模态对话

现代 AI 模型不只处理文本,越来越多的多模态模型可以接收图像、音频,并返回同类内容。MEAI 中的内容类型都派生自 AIContent

其中 DataContent 最为通用。想让模型分析一张照片?传入字节数组加 MIME 类型就行:

var instructions = "You are a photo analyst able to extract the utmost detail from a photograph and provide a description so thorough and accurate that another LLM could generate almost the same image just from your description.";

var prompt = new TextContent("What's this photo all about? Please provide a detailed description along with tags.");

var image = new DataContent(File.ReadAllBytes(@"c:\photo.jpg"), "image/jpeg");

var messages = new List<ChatMessage>
{
    new(ChatRole.System, instructions),
    new(ChatRole.User, [prompt, image])
};

record ImageAnalysis(string Description, string[] tags);

var analysis = await chatClient.GetResponseAsync<ImageAnalysis>(messages);

用户消息里同时包含文字和图片,MEAI 负责把它组装成提供商接受的格式。

其他能力

除了本文介绍的内容,MEAI 还提供:

系列延伸

这是四篇系列文章的第一篇,后续三篇将分别介绍:

  1. Microsoft.Extensions.VectorData:向量扩展与语义搜索(Part 2)
  2. Microsoft Agent Framework:智能体框架(Part 3)
  3. Model Context Protocol(MCP):互操作性(Part 4)

如果你想马上动手,可以参考以下资源:

参考


Tags


Previous

.NET 向量数据实战:Microsoft.Extensions.VectorData 构建 RAG 应用

Next

EF Core 10 乐观并发控制:ASP.NET Core Web API 实战指南