博客 / 詳情

返回

Microsoft Agent Framework 取出 DeepSeek 思考內容

本文提供的方法適用於 DeepSeek 和豆包等模型

前置博客:

  • Microsoft Agent Framework 與 DeepSeek 對接
  • C# Microsoft Agent Framework 與 豆包 對接

核心原理是從 AgentResponseUpdate 裏面的 RawRepresentation 獲取 reasoning_content 字段

核心代碼如下

AIAgent agent = ...;
IEnumerable<ChatMessage> messages = ...;
AgentSession? session = ...;
AgentRunOptions? options = ...;

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

            if (contentIsEmpty && 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[0].delta"u8, out var data))
                    {
                        var jsonElement = JsonElement.Parse(data.Span);
                        if (jsonElement.TryGetProperty("reasoning_content", out var reasoningContent))
                        {
                            // 拿到的 reasoningContent 就是思考內容
                        }
                    }

#pragma warning restore SCME0001
                }
            }

我將這段代碼封裝為擴展方法,方便上層業務使用,代碼如下

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<ReasoningAgentRunResponseUpdate> RunReasoningStreamingAsync(this AIAgent agent, ChatMessage message,
        AgentSession? session = null,
        AgentRunOptions? options = null,
        CancellationToken cancellationToken = default)
    {
        return RunReasoningStreamingAsync(agent, [message], session, options, cancellationToken);
    }

    public static async IAsyncEnumerable<ReasoningAgentRunResponseUpdate> RunReasoningStreamingAsync(this AIAgent agent, IEnumerable<ChatMessage> messages,
        AgentSession? session = null,
        AgentRunOptions? options = null,
        [EnumeratorCancellation]
        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 && 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[0].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 && isFirstOutputContent)
                {
                    responseUpdate.IsThinkingEnd = true;
                }

                isFirstOutputContent = false;
                isThinking = false;

                yield return responseUpdate;
            }
        }
    }
}

用到的輔助類 ReasoningAgentRunResponseUpdate 代碼如下

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; }

    /// <summary>
    /// 是否首次輸出內容,前面輸出的都是內容
    /// </summary>
    /// 僅內容輸出,無思考的首次內容輸出:
    /// - IsFirstOutputContent = true
    /// - IsFirstThinking = false
    /// - IsThinkingEnd = false
    /// 有思考,完成思考後的首次內容輸出:
    /// - IsFirstOutputContent = true
    /// - IsFirstThinking = false
    /// - IsThinkingEnd = true
    public bool IsFirstOutputContent { get; set; }

    /// <summary>
    /// 思考的首次輸出
    /// </summary>
    public bool IsFirstThinking { get; set; }

    /// <summary>
    /// 是否思考結束
    /// </summary>
    public bool IsThinkingEnd { get; set; }
}

業務層使用示例:

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);
    }
}

輸出效果如下:

思考:
用户讓我講一個笑話,那得選個輕鬆有趣、貼近生活的,別太複雜。首先,得考慮不同年齡段都能聽懂,最好有點小反轉,這樣笑點更足。

比如可以從日常場景入手,像去超市、和朋友互動,或者寵物相關的?寵物的笑話通常很受歡迎,因為可愛又有反差感。

哦,想到一個關於貓和主人的:

主人發現貓最近總偷喝魚缸裏的水,於是教訓它:“你再偷喝,我就把你和魚一起燉了!”
第二天,主人發現魚缸裏的魚全沒了,貓蹲在旁邊舔爪子,抬頭一臉無辜:“主人,我幫你提前處理了食材~”

這個有小反轉,貓的邏輯很可愛,應該挺逗的。或者換個更接地氣的,比如關於吃飯的:

昨天去吃火鍋,服務員問我要辣的還是不辣的,我説:“微辣就行,別太辣。”
服務員:“好的,我們這邊微辣是‘外地人眼中的特辣’,特辣是‘本地人眼中的微辣’,您要哪個?”
我:“……給我來個‘外地人眼中的微辣’的微辣吧。”
服務員愣了一下:“好的,清水鍋加一片辣椒!”

這個也很真實,很多地方的微辣都很辣,有共鳴感。不過還是寵物的那個更可愛,可能用户會更喜歡。再調整一下,讓語言更自然:

有隻貓特別愛偷喝魚缸裏的水,主人每次看見都批評它:“再偷喝,我就把你和魚一起燉了!”
結果第二天起牀,主人發現魚缸裏的魚全不見了,貓正蹲在旁邊舔嘴巴,看見主人過來,還一臉邀功地説:“主人你看!我提前把配菜都處理好了??”

對,這樣更生動,有表情符號也更活潑。或者再短一點的?比如冷笑話?不過可能用户想要正常的笑話,不是冷的。那就選這個貓的吧,應該不錯。
--------
有隻貓特別愛偷喝魚缸裏的水,主人每次看見都批評它:“再偷喝,我就把你和魚一起燉了!”

結果第二天起牀,主人發現魚缸裏的魚全不見了,貓正蹲在旁邊舔嘴巴,看見主人過來,還一臉邀功地説:“主人你看!我提前把配菜都處理好了”

本文代碼放在 github 和 gitee 上,可以使用如下命令行拉取代碼。我整個代碼倉庫比較龐大,使用以下命令行可以進行部分拉取,拉取速度比較快

先創建一個空文件夾,接着使用命令行 cd 命令進入此空文件夾,在命令行裏面輸入以下代碼,即可獲取到本文的代碼

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin fc4cf4f485ea3e2268a67c0c6900827d9803d9b3

以上使用的是國內的 gitee 的源,如果 gitee 不能訪問,請替換為 github 的源。請在命令行繼續輸入以下代碼,將 gitee 源換成 github 源進行拉取代碼。如果依然拉取不到代碼,可以發郵件向我要代碼

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin fc4cf4f485ea3e2268a67c0c6900827d9803d9b3

獲取代碼之後,進入 SemanticKernelSamples/LadelallkeacheWhikurwearqobakaju 文件夾,即可獲取到源代碼

更多技術博客,請參閲 博客導航

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.