当前位置: 首页 > news >正文

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 = truepublic 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 文件夹,即可获取到源代码

更多技术博客,请参阅 博客导航

http://www.jsqmd.com/news/409957/

相关文章:

  • 从基础到实战:Java全栈开发工程师的面试实录
  • 服务效率提升实战:排队理论与多场景仿真案例
  • 安装开发环境
  • 深入解析Stable Diffusion核心组件:超越基础文本到图像的内部机制
  • 避免在 onbind 方法调用 getCallingUid 与 getCallingPid 方法
  • 好用的skills清单
  • 在 Android Studio 中,新建 AIDL 文件按钮是灰色
  • Android 开发问题:The direction of ‘data‘ is not specified. array can be an in, out, or inout parameter.
  • Android 多进程开发 - AIDL 回调、RemoteCallbackList、AIDL 安全校验
  • 为什么 Controller 层坚决不能直接调 DAO 层?
  • Redis 的 ZipList 是什么?它是怎么解决内存碎片问题的?
  • 小遥搜索v1.2.0版本更新【已支持-语雀数据源集成】
  • 梦笔记20260225
  • 2026年智能系统门窗公司综合评估:五大品牌实力对比 - 2026年企业推荐榜
  • 2026年河南氧系漂白剂直销公司综合评估与精选推荐 - 2026年企业推荐榜
  • 2026年静音系统门窗诚信服务商综合评测与选购指南 - 2026年企业推荐榜
  • 2026年河南道闸广告平台深度盘点与选择指南 - 2026年企业推荐榜
  • 2026年实力MBBR填料厂商盘点与选型指南 - 2026年企业推荐榜
  • 移动硬盘被system占用无法弹出
  • 2026年河南过氧碳酸钠采购指南:五大优质供应商深度解析 - 2026年企业推荐榜
  • 2026年河南灯光秀广告公司选购指南:实力品牌深度解析 - 2026年企业推荐榜
  • 2026年江西全屋高端木作品牌深度评测 - 2026年企业推荐榜
  • 基于SpringBoot+Vue的和智慧生活商城系统管理系统设计与实现【Java+MySQL+MyBatis完整源码】
  • 成都地区官网搭建公司选择要点 - 2026年企业推荐榜
  • 2026北京厨房家装门窗直销厂商综合评测与选型指南 - 2026年企业推荐榜
  • ARM逆向工程权威公开资料
  • 2026年濮阳过碳酸钠服务商选择指南与优质厂商推荐 - 2026年企业推荐榜
  • 2026年初成都视频号推广服务商选购权威指南 - 2026年企业推荐榜
  • 东南亚海外仓人员管理痛点:本土员工难管?这套海外仓WMS帮你规范化
  • 2026年武汉咸宁名牌箱包回收机构深度测评与选购指南 - 2026年企业推荐榜