琴英 發表於 2026-2-25 07:25:00

Microsoft Agent Framework 取出 DeepSeek 思考内容

<p>本文提供的方法适用于 DeepSeek 和豆包等模型</p>
<p>前置博客:</p>
<ul>
<li>Microsoft Agent Framework 与 DeepSeek 对接</li>
</ul>

<ul>
<li>C# Microsoft Agent Framework 与 豆包 对接</li>
</ul>

<hr>
<p>更新:</p>
<p>在正式的 1.0.0 版本,已经内置支持了获取思考内容。只需要判断输出的 AIContent 为 TextReasoningContent 类型即可,核心代码如下</p>
<pre><code class="language-csharp">ChatClient chatClient = ...;

ChatClientAgent aiAgent = chatClient.AsAIAgent();

ChatMessage message = new ChatMessage(ChatRole.User, "请讲一个笑话");

bool isThinking = false;

await foreach (var agentResponseUpdate in aiAgent.RunStreamingAsync(message))
{
    foreach (AIContent aiContent in agentResponseUpdate.Contents)
    {
      if (aiContent is TextReasoningContent textReasoningContent)
      {
            Console.Write(textReasoningContent.Text);
            isThinking = true;
      }
      else if(aiContent is TextContent textContent)
      {
            if (string.IsNullOrEmpty(textContent.Text))
            {
                continue;
            }

            if (isThinking)
            {
                Console.WriteLine();
                Console.WriteLine("--------");
            }

            Console.Write(textContent.Text);
            isThinking = false;
      }
    }
}
</code></pre>
<p>以上代码放在 github 和 gitee 上,可以使用如下命令行拉取代码。我整个代码仓库比较庞大,使用以下命令行可以进行部分拉取,拉取速度比较快</p>
<p>先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码</p>
<pre><code>git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin b5d26e974a4b9a5e331a4e1c4e95e739e77e057c
</code></pre>
<p>以上使用的是国内的 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码,可以发邮件向我要代码</p>
<pre><code>git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin b5d26e974a4b9a5e331a4e1c4e95e739e77e057c
</code></pre>
<p>获取代码之后,进入 SemanticKernelSamples/HaneakilabeaGocurwarjur 文件夹,即可获取到源代码</p>
<hr>
<p>以下方法已不推荐,建议直接更新 Microsoft.Agents.AI 版本,然后如上文提供的方法,直接用 TextReasoningContent 就可以了</p>
<p>核心原理是从 AgentResponseUpdate 里面的 RawRepresentation 获取 reasoning_content 字段</p>
<p>核心代码如下</p>
<pre><code class="language-csharp">AIAgent agent = ...;
IEnumerable&lt;ChatMessage&gt; messages = ...;
AgentSession? session = ...;
AgentRunOptions? options = ...;

      await foreach (AgentResponseUpdate agentRunResponseUpdate in agent.RunStreamingAsync(messages, session, options, cancellationToken))
      {
            var contentIsEmpty = string.IsNullOrEmpty(agentRunResponseUpdate.Text);

            if (contentIsEmpty &amp;&amp; agentRunResponseUpdate.RawRepresentation is Microsoft.Extensions.AI.ChatResponseUpdate streamingChatCompletionUpdate)
            {
                if (streamingChatCompletionUpdate.RawRepresentation is StreamingChatCompletionUpdate chatCompletionUpdate)
                {
#pragma warning disable SCME0001 // Patch 属性是实验性内容
                  ref JsonPatch patch = ref chatCompletionUpdate.Patch;
                  if (patch.TryGetJson("$.choices.delta"u8, out var data))
                  {
                        var jsonElement = JsonElement.Parse(data.Span);
                        if (jsonElement.TryGetProperty("reasoning_content", out var reasoningContent))
                        {
                            // 拿到的 reasoningContent 就是思考内容
                        }
                  }

#pragma warning restore SCME0001
                }
            }
</code></pre>
<p>我将这段代码封装为扩展方法,方便上层业务使用,代码如下</p>
<pre><code class="language-csharp">using System.ClientModel.Primitives;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text.Json;
using OpenAI.Chat;
using ChatMessage = Microsoft.Extensions.AI.ChatMessage;

namespace Microsoft.Agents.AI.Reasoning;

public static class ReasoningAIAgentExtension
{
    public static IAsyncEnumerable&lt;ReasoningAgentRunResponseUpdate&gt; RunReasoningStreamingAsync(this AIAgent agent, ChatMessage message,
      AgentSession? session = null,
      AgentRunOptions? options = null,
      CancellationToken cancellationToken = default)
    {
      return RunReasoningStreamingAsync(agent, , session, options, cancellationToken);
    }

    public static async IAsyncEnumerable&lt;ReasoningAgentRunResponseUpdate&gt; RunReasoningStreamingAsync(this AIAgent agent, IEnumerable&lt;ChatMessage&gt; messages,
      AgentSession? session = null,
      AgentRunOptions? options = null,
      
      CancellationToken cancellationToken = default)
    {
      bool? isThinking = null;
      bool isFirstOutputContent = true;

      await foreach (AgentResponseUpdate agentRunResponseUpdate in agent.RunStreamingAsync(messages, session, options, cancellationToken))
      {
            var contentIsEmpty = string.IsNullOrEmpty(agentRunResponseUpdate.Text);

            if (contentIsEmpty &amp;&amp; agentRunResponseUpdate.RawRepresentation is Microsoft.Extensions.AI.ChatResponseUpdate streamingChatCompletionUpdate)
            {
                if (streamingChatCompletionUpdate.RawRepresentation is StreamingChatCompletionUpdate chatCompletionUpdate)
                {
#pragma warning disable SCME0001 // Patch 属性是实验性内容
                  ref JsonPatch patch = ref chatCompletionUpdate.Patch;
                  if (patch.TryGetJson("$.choices.delta"u8, out var data))
                  {
                        var jsonElement = JsonElement.Parse(data.Span);
                        if (jsonElement.TryGetProperty("reasoning_content", out var reasoningContent))
                        {
                            // 拿到的 reasoningContent 就是思考内容
                        }
                  }

#pragma warning restore SCME0001
                }
            }

            if (!contentIsEmpty)
            {
                var responseUpdate = new ReasoningAgentRunResponseUpdate(agentRunResponseUpdate);

                if (isFirstOutputContent)
                {
                  responseUpdate.IsFirstOutputContent = true;
                }

                if (isThinking is true &amp;&amp; isFirstOutputContent)
                {
                  responseUpdate.IsThinkingEnd = true;
                }

                isFirstOutputContent = false;
                isThinking = false;

                yield return responseUpdate;
            }
      }
    }
}
</code></pre>
<p>用到的辅助类 ReasoningAgentRunResponseUpdate 代码如下</p>
<pre><code class="language-csharp">namespace Microsoft.Agents.AI.Reasoning;

public class ReasoningAgentRunResponseUpdate : AgentResponseUpdate
{
    public ReasoningAgentRunResponseUpdate(AgentResponseUpdate origin) : base(origin.Role, origin.Contents)
    {
      Origin = origin;
      AdditionalProperties = origin.AdditionalProperties;
      AuthorName = origin.AuthorName;
      CreatedAt = origin.CreatedAt;
      MessageId = origin.MessageId;
      RawRepresentation = origin.RawRepresentation;
      ResponseId = origin.ResponseId;
      ContinuationToken = origin.ContinuationToken;
      AgentId = origin.AgentId;
    }

    public AgentResponseUpdate Origin { get; }

    public string? Reasoning { get; set; }

    /// &lt;summary&gt;
    /// 是否首次输出内容,前面输出的都是内容
    /// &lt;/summary&gt;
    /// 仅内容输出,无思考的首次内容输出:
    /// - IsFirstOutputContent = true
    /// - IsFirstThinking = false
    /// - IsThinkingEnd = false
    /// 有思考,完成思考后的首次内容输出:
    /// - IsFirstOutputContent = true
    /// - IsFirstThinking = false
    /// - IsThinkingEnd = true
    public bool IsFirstOutputContent { get; set; }

    /// &lt;summary&gt;
    /// 思考的首次输出
    /// &lt;/summary&gt;
    public bool IsFirstThinking { get; set; }

    /// &lt;summary&gt;
    /// 是否思考结束
    /// &lt;/summary&gt;
    public bool IsThinkingEnd { get; set; }
}
</code></pre>
<p>业务层使用示例:</p>
<pre><code class="language-csharp">ChatClientAgent aiAgent = ...;

ChatMessage message = new ChatMessage(ChatRole.User, "请讲一个笑话");

await foreach (var agentRunResponseUpdate in aiAgent.RunReasoningStreamingAsync(message))
{
    if (agentRunResponseUpdate.IsFirstThinking)
    {
      Console.WriteLine("思考:");
    }

    if (agentRunResponseUpdate.Reasoning is not null)
    {
      Console.Write(agentRunResponseUpdate.Reasoning);
    }

    if (agentRunResponseUpdate.IsThinkingEnd)
    {
      Console.WriteLine();
      Console.WriteLine("--------");
    }

    var text = agentRunResponseUpdate.Text;
    if (!string.IsNullOrEmpty(text))
    {
      Console.Write(text);
    }
}
</code></pre>
<p>输出效果如下:</p>
<pre><code>思考:
用户让我讲一个笑话,那得选个轻松有趣、贴近生活的,别太复杂。首先,得考虑不同年龄段都能听懂,最好有点小反转,这样笑点更足。

比如可以从日常场景入手,像去超市、和朋友互动,或者宠物相关的?宠物的笑话通常很受欢迎,因为可爱又有反差感。

哦,想到一个关于猫和主人的:

主人发现猫最近总偷喝鱼缸里的水,于是教训它:“你再偷喝,我就把你和鱼一起炖了!”
第二天,主人发现鱼缸里的鱼全没了,猫蹲在旁边舔爪子,抬头一脸无辜:“主人,我帮你提前处理了食材~”

这个有小反转,猫的逻辑很可爱,应该挺逗的。或者换个更接地气的,比如关于吃饭的:

昨天去吃火锅,服务员问我要辣的还是不辣的,我说:“微辣就行,别太辣。”
服务员:“好的,我们这边微辣是‘外地人眼中的特辣’,特辣是‘本地人眼中的微辣’,您要哪个?”
我:“……给我来个‘外地人眼中的微辣’的微辣吧。”
服务员愣了一下:“好的,清水锅加一片辣椒!”

这个也很真实,很多地方的微辣都很辣,有共鸣感。不过还是宠物的那个更可爱,可能用户会更喜欢。再调整一下,让语言更自然:

有只猫特别爱偷喝鱼缸里的水,主人每次看见都批评它:“再偷喝,我就把你和鱼一起炖了!”
结果第二天起床,主人发现鱼缸里的鱼全不见了,猫正蹲在旁边舔嘴巴,看见主人过来,还一脸邀功地说:“主人你看!我提前把配菜都处理好了??”

对,这样更生动,有表情符号也更活泼。或者再短一点的?比如冷笑话?不过可能用户想要正常的笑话,不是冷的。那就选这个猫的吧,应该不错。
--------
有只猫特别爱偷喝鱼缸里的水,主人每次看见都批评它:“再偷喝,我就把你和鱼一起炖了!”

结果第二天起床,主人发现鱼缸里的鱼全不见了,猫正蹲在旁边舔嘴巴,看见主人过来,还一脸邀功地说:“主人你看!我提前把配菜都处理好了”
</code></pre>
<p>本文代码放在 github 和 gitee 上,可以使用如下命令行拉取代码。我整个代码仓库比较庞大,使用以下命令行可以进行部分拉取,拉取速度比较快</p>
<p>先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码</p>
<pre><code>git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin fc4cf4f485ea3e2268a67c0c6900827d9803d9b3
</code></pre>
<p>以上使用的是国内的 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源进行拉取代码。如果依然拉取不到代码,可以发邮件向我要代码</p>
<pre><code>git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin fc4cf4f485ea3e2268a67c0c6900827d9803d9b3
</code></pre>
<p>获取代码之后,进入 SemanticKernelSamples/LadelallkeacheWhikurwearqobakaju 文件夹,即可获取到源代码</p>
<p>更多技术博客,请参阅 博客导航</p>


</div>
<div id="MySignature" role="contentinfo">
    <p>博客园博客只做备份,博客发布就不再更新,如果想看最新博客,请访问 https://blog.lindexi.com/</p>

<p>如图片看不见,请在浏览器开启不安全http内容兼容</p>

<img alt="知识共享许可协议" style="border-width: 0" src="https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png"><br>本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名[林德熙](https://www.cnblogs.com/lindexi)(包含链接:https://www.cnblogs.com/lindexi ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我[联系](mailto:lindexi_gd@163.com)。<br><br>
来源:https://www.cnblogs.com/lindexi/p/19635618
頁: [1]
查看完整版本: Microsoft Agent Framework 取出 DeepSeek 思考内容