使用 .NET OpenAI SDK 流式获取 DeepSeek 思考过程
在 DeepSeek-R1 或 Qwen-2.5-Max 等推理模型中,模型会在生成最终答案之前输出一段“思考过程”(Reasoning Content)。目前的 .NET OpenAI SDK (OpenAI包) 尚未完全原生支持将字段映射到强类型对象中。本文将演示如何通过和SseParser手动解析 Server-Sent Events (SSE) 数据流,从而实时获取并展示模型的思考过程。虽

简介
在 DeepSeek-R1 或 Qwen-2.5-Max 等推理模型中,模型会在生成最终答案之前输出一段“思考过程”(Reasoning Content)。
目前的 .NET OpenAI SDK (OpenAI 包) 尚未完全原生支持将 reasoning_content 字段映射到强类型对象中。本文将演示如何通过 GetRawPagesAsync 和 SseParser 手动解析 Server-Sent Events (SSE) 数据流,从而实时获取并展示模型的思考过程。
核心方案
-
JSON Patch 启用功能:使用
ChatCompletionOptions.Patch注入enable_thinking参数。 -
底层 SSE 解析:绕过 SDK 的强类型封装,直接处理原始流数据。
-
双流分离:分别提取
reasoning_content(思考) 和content(回答)。
关键代码实现
1. 初始化与配置
首先配置客户端,并使用 JSON Patch 启用思考模式(以 Qwen 为例):
// 配置 OpenAI 客户端
var clientOptions = new OpenAIClientOptions
{
Endpoint = new Uri(Keys.QwenEndpoint)
};
var openAIClient = new OpenAIClient(new ApiKeyCredential(Keys.QwenApiKey), clientOptions);
var chatClient = openAIClient.GetChatClient("deepseek-v3.2-exp");
// 启用思考模式 (关键步骤)
var options = new ChatCompletionOptions();
#pragma warning disable SCME0001
options.Patch.Set("$.enable_thinking"u8, true);
#pragma warning restore SCME0001
2. SSE 解析逻辑
这是核心部分。我们需要处理原始的 SSE 数据包,手动解析 JSON 并提取字段。
using System.Net.ServerSentEvents;
using System.Text.Json;
// 解析 SSE 页面并分流处理
void ProcessSsePage(ClientResult page, StringBuilder reasoning, StringBuilder answer)
{
var contentStream = page.GetRawResponse().ContentStream;
if (contentStream isnull) return;
// 使用 .NET 内置的 SseParser
var parser = SseParser.Create(contentStream);
foreach (var item in parser.Enumerate())
{
if (string.IsNullOrWhiteSpace(item.Data)) continue;
// 解析 JSON
if (!TryParseJsonMessage(item.Data, outvar message)) continue;
// 提取内容
ProcessDeltaContent(message, reasoning, answer);
}
}
void ProcessDeltaContent(JsonElement message, StringBuilder reasoning, StringBuilder answer)
{
// ... 省略部分校验代码 ...
if (!message.TryGetProperty("choices", outvar choices)) return;
var delta = choices[0].GetProperty("delta");
// 1. 提取思考内容
if (delta.TryGetProperty("reasoning_content", outvar rProp))
{
var text = rProp.GetString();
if (!string.IsNullOrEmpty(text))
{
Console.ForegroundColor = ConsoleColor.Gray;
Console.Write(text); // 实时输出思考
reasoning.Append(text);
}
}
// 2. 提取回答内容
if (delta.TryGetProperty("content", outvar cProp))
{
var text = cProp.GetString();
if (!string.IsNullOrEmpty(text))
{
Console.ResetColor();
Console.Write(text); // 实时输出回答
answer.Append(text);
}
}
}
3. 执行流式请求
使用 GetRawPagesAsync() 获取底层数据流:
var messages = new ChatMessage[] { new UserChatMessage("如何将大象装进冰箱?") };
// 发起流式请求
var chatUpdates = chatClient.CompleteChatStreamingAsync(messages, options);
// 遍历原始 SSE 页面
await foreach (var page in chatUpdates.GetRawPagesAsync())
{
ProcessSsePage(page, reasoningContent, answerContent);
}
总结
虽然 SDK 暂时未跟上模型 API 的更新速度,但通过 .NET 强大的底层网络处理能力(如 SseParser),我们依然可以优雅地解决问题。
最佳实践:
-
关注 SDK 的
GetRawPagesAsync方法,它是处理非标准 API 响应的逃生舱。 -
使用
System.Net.ServerSentEvents处理流式数据,避免手写复杂的流解析逻辑。
如需获取文章配套完整代码,可扫码咨询领取。👇

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)