MEAI 最近更新了 10.4.0 版本,引入了TextReasoningContent 类型用于表述推理内容,基于该类型,可以更方便的获取推理字段。

以下内容选自我精心打造的《.NET+AI | 智能体开发进阶》课程,如需系统学习,不妨阅读原文了解详情。

启用推理模型与获取思考过程

在 07-meai-chatoptions-extension.ipynb[1] 中,我们介绍了 ChatOptions 的扩展点 RawRepresentationFactory,可以用来配置推理模型的特有参数。

本节课将深入展示如何:

  • • 🧠 启用推理模型的思考模式

  • • 📤 非流式获取推理内容(Reasoning Content)

  • • 🔄 流式获取推理内容(Streaming Reasoning Content)

什么是推理内容(Reasoning Content)?

国内的 DeepSeek千问 AI 等推理模型支持混合思考模式,能够输出:

  1. 1. 推理过程(Reasoning Content):模型的内部思考过程

  2. 2. 最终答案(Message Content):面向用户的最终回答

这两部分内容分开返回,帮助开发者:

  • • ✅ 理解模型的推理逻辑

  • • ✅ 调试和优化提示词

  • • ✅ 在 UI 中展示"思考中..."效果

课程目标

  • • 掌握如何通过 RawRepresentationFactory 启用推理模式

  • • 学习使用 JsonPatch 动态设置提供商参数

  • • 实现流式和非流式两种方式获取推理内容

推理内容流转示意图

enable_thinking=true

用户请求

IChatClient

启用推理?

推理模型

生成推理内容

生成最终答案

流式响应?

StreamingChatResponseUpdate

OfType

OfType

response.Messages.Single

OfType

OfType

关键概念:

  • • 📤 推理内容(Reasoning Content):模型内部思考,不直接展示给最终用户

  • • 💬 消息内容(Message Content):最终回答,面向用户

步骤 1:环境准备

首先导入必要的帮助类和依赖包:

#!import ../helper/AIClientHelper.cs

using Microsoft.Extensions.AI;
using OpenAI.Chat;

Console.WriteLine("✅ 依赖包加载完成");

步骤 2:启用推理模式

要启用推理模式,需要通过 RawRepresentationFactory 设置底层提供商的参数。

工作原理

AI 模型 API OpenAI SDK Microsoft.Extensions.AI 应用代码 AI 模型 API OpenAI SDK Microsoft.Extensions.AI 应用代码 ChatOptions + RawRepresentationFactory调用 Factory 创建 ChatCompletionOptionsPatch.Set("$.enable_thinking", true)发送请求(包含 enable_thinking)返回响应(含推理内容)原始响应ChatCompletion / StreamingChatCompletionUpdate

代码实现

阿里百炼平台提供了enable_thinking参数,用于控制在使用混合思考(回复前既可思考也可不思考)模型时,是否开启思考模式。

  • • 千问系列模型:适用于 Qwen3 、Qwen3-Omni-Flash、Qwen3-VL模型,默认不开启思考模式。阿里百炼千问模型API文档[2]

  • • DeepSeek模型:适用于 deepseek-v3.2、deepseek-v3.2-exp与deepseek-v3.1。默认不开启思考模式。deepseek-v3.2是DeepSeek推出的首个将思考融入工具使用的模型,同时支持思考模式与非思考模式的工具调用。阿里百炼DeepSeek模型API文档[3]

关于阿里百炼平台更多推理模型的默认行为和参数设置,可参考阿里百炼-深度思考[4]

对于DeepSeek 平台,deepseek-chat 和 deepseek-reasoner 都已经升级为 DeepSeek-V3.2。deepseek-chat 对应 DeepSeek-V3.2 的非思考模式,deepseek-reasoner 对应 DeepSeek-V3.2 的思考模式,意味着无需手动设置 enable_thinking 参数即可使用对应模式。更多信息请参考DeepSeek-API文档[5]

下面使用阿里百炼平台创建一个带有推理模式的 ChatOptions

#pragma warning disable SCME0001 // RawRepresentationFactory 使用警告


// 创建启用推理模式的 ChatOptions
var reasoningOptions = new ChatOptions()
{
    RawRepresentationFactory = (client) =>
    {
        // 创建底层 OpenAI SDK 的 ChatCompletionOptions
        var options = new ChatCompletionOptions();
        
        // 使用 JsonPatch 设置 enable_thinking 参数
        options.Patch.Set("$.enable_thinking"u8, true);
        
        return options;
    }
};

Console.WriteLine("✅ 推理模式配置完成");

new 
{ 
    配置说明 = "enable_thinking=true",
    用途 = "启用模型推理过程输出",
    适用模型 = "DeepSeek, 千问等推理模型"
}.Display();

代码说明:

  1. 1. #pragma warning disable SCME0001:禁用编译器警告,因为 RawRepresentationFactory 是高级功能

  2. 2. RawRepresentationFactory:工厂方法,用于创建底层 SDK 的选项对象

  3. 3. ChatCompletionOptions:OpenAI SDK 的原生配置类

  4. 4. Patch.Set():使用 JsonPatch 设置参数,u8 后缀表示 UTF-8 字节数组

  5. 5. enable_thinking:推理模型的特有参数,用于启用思考输出

步骤 3:创建推理模型客户端

使用 AIClientHelper 创建支持推理的 AI 客户端。本示例使用 DeepSeek 的 deepseek-chat 模型:

// 使用阿里百炼平台的deepseek-v3.2-exp模型(支持推理模式)
var qwenClient = AIClientHelper.GetQwenClient(true);

var chatClient = qwenClient.GetChatClient("deepseek-v3.2-exp").AsIChatClient();

Console.WriteLine("✅ DeepSeek 推理模型客户端创建完成");

new 
{
    提供商 = "DeepSeek",
    模型 = "deepseek-v3.2-exp",
    功能 = "支持推理内容输出"
}.Display();

步骤 4:非流式获取推理内容

使用 GetResponseAsync 获取完整的响应,包括推理内容和最终答案。

Console.WriteLine("⏱️ 开始发送请求...\n");

// 发送请求,传入启用推理的 ChatOptions
var response = await chatClient.GetResponseAsync("将大象装进冰箱需要几步?", reasoningOptions);

Console.WriteLine("✅ 响应接收完成\n");


// 以下两种方式均可提取大模型的回复内容:
var message = response.Messages.Single();
message.Display();

var text = message.Contents.OfType<TextContent>().Single();
text.Display();


// 显示基本响应信息
new 
{
    消息内容 = response.Text,
    Token使用 = response.Usage?.TotalTokenCount,
    完成原因 = response.FinishReason
}.Display();

提取推理内容

要获取推理内容,可以直接在响应消息中提取 TextReasoningContent 类型的内容:

⚠️ 重要:: 推理内容仅在启用 enable_thinking 时才会返回

// 提取并显示推理内容
var reasoning = message.Contents.OfType<TextReasoningContent>().Single();
reasoning.Display();

步骤 5:流式获取推理内容

在实际应用中,我们通常希望实时展示推理过程,提升用户体验。
推荐做法是直接从 StreamingChatResponseUpdate.Contents 中提取 TextReasoningContent

流式响应的工作流程

AI 模型 IChatClient 应用 AI 模型 IChatClient 应用 loop[流式输出] GetStreamingResponseAsyncChatResponseUpdateupdate.ContentsOfType<TextReasoningContent>()OfType<TextContent>()

实现流式推理内容获取

推荐方式是直接读取 TextReasoningContent,更加直观且类型安全:

推荐方式:提取 TextReasoningContent 类型的内容
// 1. 获取流式响应
var streamingUpdates = chatClient.GetStreamingResponseAsync(
    "将大象装进冰箱需要几步?", 
    options: reasoningOptions
);


Console.WriteLine("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
Console.WriteLine("🧠 模型推理过程(实时流式输出):\n");
List<ChatResponseUpdate> updates = [];
await foreach (var update in streamingUpdates)
{
    // 实时显示推理内容 
    if (update.Contents.OfType<TextReasoningContent>().Any()){
        var reasoningContent = update.Contents.OfType<TextReasoningContent>().Single();
        Console.Write(reasoningContent.Text);
    }
    updates.Add(update);
}

// 汇总显示完整推理内容
var reasoningContents = updates.SelectMany(u => u.Contents).OfType<TextReasoningContent>().Select(r => r.Text);
string reasoningText = string.Concat(reasoningContents);


// 汇总显示完整回复内容
var msgContents = updates.SelectMany(u => u.Contents).OfType<TextContent>().Select(c => c.Text);
string msgText = string.Concat(msgContents);

new
{
    完整推理 = reasoningText,
    完整回复 = msgText
}.Display();

备注TextReasoningContent 本质上也是通过从底层响应中提取 JsonPatch 实现的,只是封装成了更友好的 API,避免了直接操作 JsonPatch 的复杂性和潜在错误。具体实现思路,见下列章节的兼容方式示例。

兼容方式(不推荐):先转换再从 JsonPatch 增量更新中提取

以下方式可用于兼容旧代码或排查底层响应结构,不建议作为新项目主方案:

JsonPatch 快速入门

在使用 RawRepresentationFactory 之前,我们需要了解 JsonPatch 的基本概念。

什么是 JsonPatch?

JsonPatch 是一种用于描述 JSON 文档修改操作的标准格式(RFC 6902),在 OpenAI SDK 中用于动态设置请求参数。

在 OpenAI 和 Azure.AI.OpenAI SDK 中,ChatCompletionOptions.Patch 是一个 JsonPatch 对象,用于设置 AI 提供商的特有参数。

JsonPatch 基本用法

// 1. 创建 ChatCompletionOptions
var options = new ChatCompletionOptions();

// 2. 使用 Patch.Set() 设置参数
// 语法:Patch.Set(路径, 值)
options.Patch.Set("$.enable_thinking"u8, true);
options.Patch.Set("$.temperature"u8, 0.7);
options.Patch.Set("$.max_tokens"u8, 1000);

JsonPath 路径语法

路径

说明

示例

$

根对象

$.enable_thinking
.

属性访问

$.choices[0].delta
[]

数组索引

$.choices[0]

常用推理模型参数

参数

类型

说明

示例值

enable_thinking bool

启用推理模式

true
thinking.type string

思考类型(DeepSeek)

"enabled"
enable_search bool

启用联网搜索(千问)

true

⚠️ 注意:

  • • u8 后缀表示 UTF-8 字节数组字面量,用于高性能字符串处理

  • • 不同 AI 提供商的参数名称可能不同,需参考官方文档

Console.WriteLine("⏱️ 开始流式请求...\n");
Console.WriteLine("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
Console.WriteLine("🧠 模型推理过程(实时流式输出):\n");

#pragma warning disable SCME0001,OPENAI001 // JsonPatch 使用警告

// 1. 获取流式响应
var streamingUpdates = chatClient.GetStreamingResponseAsync(
    "将大象装进冰箱需要几步?", 
    options: reasoningOptions
);
// 2. 转换为 OpenAI SDK 的流式更新对象
var openAIUpdates = streamingUpdates.AsOpenAIStreamingChatCompletionUpdatesAsync();

// 3. 遍历流式更新,提取推理内容
await foreach (var update in openAIUpdates)
{
    // 使用 JsonPatch 从更新中提取 reasoning_content
    if (update.Patch.TryGetValue("$.choices[0].delta.reasoning_content"u8, out string reasoningContentChunk))
    {
        Console.Write(reasoningContentChunk);
    }
}

Console.WriteLine("\n\n✅ 流式推理内容接收完成");

代码说明:

  • • ⚠️ AsOpenAIStreamingChatCompletionUpdatesAsync() + Patch.TryGetValue() 属于兼容写法,不推荐作为首选方案。

  • • ✅ 推荐优先使用 update.Contents.OfType<TextReasoningContent>() 直接获取推理内容。

API

说明

用途

GetStreamingResponseAsync()

MEAI 流式响应接口

返回 IAsyncEnumerable<ChatResponseUpdate>

update.Contents.OfType<TextReasoningContent>()

强类型提取方式(推荐)

直接获取推理内容片段

update.Contents.OfType<TextContent>()

强类型提取方式(推荐)

获取最终回答内容片段

AsOpenAIStreamingChatCompletionUpdatesAsync()

OpenAI SDK 转换扩展(兼容)

仅用于兼容旧代码或排查底层字段

Patch.TryGetValue()

JsonPatch 读取方法(兼容)

从底层增量中读取指定路径值

兼容方式的 JsonPath 路径示例

{
  "choices": [
    {
      "delta": {
        "reasoning_content": "首先,需要打开冰箱门...",  // ← 目标字段
        "content": "将大象装进冰箱需要三步..."
      }
    }
  ]
}
  • • $: 根对象

  • • choices[0]: 第一个选择(数组索引)

  • • delta: 增量更新对象

  • • reasoning_content: 推理内容字段

同时获取推理内容和最终答案

在实际应用中,通常需要同时展示推理过程和最终回答:

#pragma warning disable SCME0001 // JsonPatch 使用警告
Console.WriteLine("⏱️ 开始完整流式请求...\n");

var streamingUpdates2 = chatClient.GetStreamingResponseAsync(
    "用简洁的方式说明如何将大象装进冰箱", 
    options: reasoningOptions
);

var openAIUpdates2 = streamingUpdates2.AsOpenAIStreamingChatCompletionUpdatesAsync();

Console.WriteLine("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
Console.WriteLine("🧠 推理过程:\n");

bool hasReasoningContent = false;
bool hasMessageContent = false;

await foreach (var update in openAIUpdates2)
{
    // 提取推理内容
    if (update.Patch.TryGetValue("$.choices[0].delta.reasoning_content"u8, out string reasoningChunk))
    {
        if (!hasReasoningContent)
        {
            hasReasoningContent = true;
        }
        Console.Write(reasoningChunk);
    }
    
    // 提取消息内容
    if (update.Patch.TryGetValue("$.choices[0].delta.content"u8, out string messageChunk))
    {
        if (!hasMessageContent)
        {
            Console.WriteLine("\n\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
            Console.WriteLine("💬 最终回答:\n");
            hasMessageContent = true;
        }
        Console.Write(messageChunk);
    }
}

Console.WriteLine("\n\n✅ 完整流式响应接收完成");

输出示例:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🧠 推理过程:

首先,这是一个经典的脑筋急转弯问题。实际上,将大象装进冰箱是不可能的物理操作...
我应该按照问题的字面意思,给出简洁的步骤说明。

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
💬 最终回答:

将大象装进冰箱需要三步:
1. 打开冰箱门
2. 将大象放进去
3. 关上冰箱门

完整工作流程对比

非流式 vs 流式

特性

非流式(GetResponseAsync)

流式(GetStreamingResponseAsync)

用户体验

等待完整响应后一次性显示

实时显示推理过程和回答

延迟感知

延迟明显(需等待全部完成)

延迟低(逐步输出)

适用场景

后台处理、批量任务

对话界面、实时展示

推理内容提取 message.Contents.OfType<TextReasoningContent>() update.Contents.OfType<TextReasoningContent>()
实现复杂度

简单

简单(强类型提取)

完整流程图

流式

发送请求

接收流式更新

ChatResponseUpdate

OfType

实时显示推理

OfType

实时显示回答

非流式

发送请求

等待完整响应

response.Messages.Single

OfType

显示推理内容

OfType

显示最终答案

最佳实践与注意事项

✅ 最佳实践

  1. 1. 选择合适的模式

  • • 对话应用 → 使用流式,实时展示思考过程

  • • 后台任务 → 使用非流式,简化处理逻辑

  • 2. 推理内容的用途

    • • 📊 调试提示词:查看模型理解是否正确

    • • 🎓 教学场景:展示 AI 推理逻辑

    • • 🔍 审计追踪:记录模型决策过程

  • 3. 成本优化

    • • 推理内容会消耗额外的 Token(输入 Token)

    • • 生产环境中,可根据需要选择性启用

    • • 使用缓存策略减少重复推理

    ⚠️ 注意事项

    问题

    说明

    解决方案

    模型支持

    并非所有模型都支持推理模式

    使用 DeepSeek、千问等明确支持的模型

    参数差异

    不同提供商参数名可能不同

    参考官方文档,使用正确的参数名

    类型提取 TextReasoningContent

     可能不存在

    使用 OfType<TextReasoningContent>().SingleOrDefault() 并判空

    Token 计费

    推理内容计入 Token 使用量

    关注 API 计费,合理使用推理模式

    🔍 常见问题

    Q: 为什么取不到 TextReasoningContent

    • • 检查是否设置了 enable_thinking=true

    • • 确认使用的模型支持推理模式

    • • 使用 SingleOrDefault() 并添加空值判断,避免 Single() 异常

    Q: 流式响应中只有推理内容,没有最终答案?

    • • 检查 JsonPath 路径是否正确

    • • 确认同时提取了 reasoning_content 和 content

    Q: 如何在 UI 中展示"思考中..."效果?

    • • 使用流式响应,当接收到 reasoning_content 时显示"正在思考..."

    • • 当接收到 content 时切换为显示最终答案

    扩展示例:不同推理模型的参数配置

    DeepSeek Reasoner(专用推理模型)

    DeepSeek 提供了专门的推理模型 deepseek-reasoner,使用不同的参数配置:

    var deepseekReasonerOptions = new ChatOptions()
    {
        RawRepresentationFactory = (client) =>
        {
            var options = new ChatCompletionOptions();
            // DeepSeek Reasoner 使用 thinking.type 参数
            options.Patch.Set("$.thinking.type"u8, "enabled");
            return options;
        }
    };
    
    var reasonerClient = AIClientHelper.GetDefaultChatClient("DeepSeek", "deepseek-reasoner");
    var response = await reasonerClient.GetResponseAsync("分析量子计算的未来发展", deepseekReasonerOptions);

    千问推理模型(Qwen)

    千问模型支持推理 + 联网搜索:

    var qwenOptions = new ChatOptions()
    {
        RawRepresentationFactory = (client) =>
        {
            var options = new ChatCompletionOptions();
            options.Patch.Set("$.enable_thinking"u8, true);
            options.Patch.Set("$.enable_search"u8, true); // 启用联网搜索
            return options;
        }
    };
    
    var qwenClient = AIClientHelper.GetDefaultChatClient("Qwen", "qwen-max");
    var response = await qwenClient.GetResponseAsync("2025年最新的AI技术趋势", qwenOptions);

    参数对比:

    提供商

    推理参数

    额外功能

    DeepSeek (reasoner)

    thinking.type

    专用推理模型

    千问

    enable_thinking

     + enable_search

    推理 + 联网搜索

    本节小结

    本节课我们学习了如何在 Microsoft.Extensions.AI 中启用推理模型并获取推理内容:

    核心知识点

    1. 1. JsonPatch 基础

    • • ✅ 使用 Patch.Set() 设置底层提供商参数

    • • ✅ JsonPath 语法:$.property$.array[0].field

    • • ✅ u8 后缀表示 UTF-8 字节数组字面量

  • 2. 启用推理模式

    • • ✅ 通过 RawRepresentationFactory 创建 ChatCompletionOptions

    • • ✅ 设置 enable_thinking=true 启用推理输出

    • • ✅ 不同模型参数可能不同(参考官方文档)

  • 3. 非流式获取推理内容(推荐)

    • • ✅ 使用 GetResponseAsync 获取完整响应

    • • ✅ 从 response.Messages 中读取 message.Contents

    • • ✅ OfType<TextReasoningContent>() 提取推理内容

  • 4. 流式获取推理内容(推荐)

    • • ✅ 使用 GetStreamingResponseAsync 获取流式响应

    • • ✅ 从 update.Contents 中直接提取强类型内容

    • • ✅ OfType<TextReasoningContent>() 获取推理片段,OfType<TextContent>() 获取答案片段

  • 5. 兼容方式(不推荐)

    • • ℹ️ 可通过 AsOpenAIStreamingChatCompletionUpdatesAsync() + Patch.TryGetValue() 读取底层字段

    • • ℹ️ 建议仅用于兼容旧代码或调试底层响应

    实际应用场景

    • • 💬 对话应用:实时展示 AI 思考过程,提升用户体验

    • • 🔍 调试工具:查看模型推理逻辑,优化提示词

    • • 📊 审计日志:记录 AI 决策过程,满足合规要求

    引用链接

    [1] 07-meai-chatoptions-extension.ipynb: ./07-meai-chatoptions-extension.ipynb
    [2] 阿里百炼千问模型API文档: https://bailian.console.aliyun.com/?tab=api#/api/?type=model&url=2712576&userCode=okjhlpr5
    [3] 阿里百炼DeepSeek模型API文档: https://bailian.console.aliyun.com/?tab=api#/api/?type=model&url=2868565&userCode=okjhlpr5
    [4] 阿里百炼-深度思考: https://bailian.console.aliyun.com/?tab=doc#/doc/?type=model&url=2870973
    [5] DeepSeek-API文档: https://api-docs.deepseek.com/zh-cn/

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐