WPF + Semantic Kernel 实现流式输出

发布时间:2026/10/17 17:28:26
WPF + Semantic Kernel 实现流式输出 打字机效果完整代码一、什么是流式输出普通AI调用等待 → 等待 → 等待 → 一次性返回全部结果流式输出开始返回 → 逐字/逐词输出 → 像打字机一样用户体验差距非常明显。这也是为什么 ChatGPT 用打字机效果而不是等全部生成完再显示。二、Semantic Kernel 流式输出 API核心方法是GetStreamingChatMessageContentsAsync和普通调用的对比// 普通调用一次性返回var result await chatService.GetChatMessageContentAsync(history);string content result.Content;// 流式调用逐块返回await foreach (var chunk in chatService.GetStreamingChatMessageContentsAsync(history)){string piece chunk.Content; // 每次一小块}三、在服务层封装流式方法使用 IAsyncEnumerable 作为返回类型配合 yield return 逐块返回内容using System.Runtime.CompilerServices;public async IAsyncEnumerable SummarizeStreamAsync(string content,[EnumeratorCancellation] CancellationToken ct default){var history new ChatHistory();history.AddUserMessage($“请总结以下内容\n{content}”);await foreach (var chunk in _chatService! .GetStreamingChatMessageContentsAsync( history, cancellationToken: ct)) { if (!string.IsNullOrEmpty(chunk.Content)) yield return chunk.Content; }}注意事项必须加 [EnumeratorCancellation] 特性才能正确处理取消操作过滤空 chunk避免UI无意义刷新CancellationToken 要透传支持用户取消四、在 WPF 里实时更新 UI关键点流式输出在后台线程产生数据需要用 Progress 或者直接更新绑定属性来刷新UI。因为 ObservableCollection 和INotifyPropertyChanged 的绑定属性在 WPF 里可以跨线程更新只要是通过属性setter更新的// 在处理循环里var sb new StringBuilder();await foreach (var chunk in_aiService.SummarizeStreamAsync(content, ct)){sb.Append(chunk);// 直接更新绑定属性WPF自动刷新UI item.Result sb.ToString();}item.Status ProcessStatus.Completed;五、完整测试代码控制台版先用控制台验证流式输出效果using Microsoft.SemanticKernel;using Microsoft.SemanticKernel.ChatCompletion;var builder Kernel.CreateBuilder();builder.AddOpenAIChatCompletion(modelId: “deepseek-ai/DeepSeek-V3”,apiKey: “你的硅基流动Key”,endpoint: new Uri(“https://api.siliconflow.cn/v1”));var kernel builder.Build();var chatService kernel.GetRequiredService();var history new ChatHistory();history.AddUserMessage(“用200字介绍一下人工智能的发展历史”);Console.WriteLine(“流式输出开始”);await foreach (var chunk in chatService.GetStreamingChatMessageContentsAsync(history)){if (!string.IsNullOrEmpty(chunk.Content))Console.Write(chunk.Content); // 不换行逐字追加}Console.WriteLine(“\n完成”);六、踩过的坑坑1忘记加 [EnumeratorCancellation]IAsyncEnumerable 方法里如果有CancellationToken 参数必须加 [EnumeratorCancellation] 特性否则取消操作不会正确传递。// ❌ 错误public async IAsyncEnumerable StreamAsync(CancellationToken ct default)// ✅ 正确public async IAsyncEnumerable StreamAsync([EnumeratorCancellation] CancellationToken ct default)坑2chunk.Content 可能为 null 或空流式返回的每个 chunkContent 属性可能是 null 或空字符串必须过滤// ❌ 不过滤可能空字符串刷新UIyield return chunk.Content;// ✅ 过滤空值if (!string.IsNullOrEmpty(chunk.Content))yield return chunk.Content;坑3StringBuilder 要在循环外声明// ❌ 每次chunk都新建结果只有最后一块await foreach (var chunk in …){var sb new StringBuilder(); // 错误位置sb.Append(chunk.Content);item.Result sb.ToString();}// ✅ 循环外声明累积所有内容var sb new StringBuilder(); // 正确位置await foreach (var chunk in …){sb.Append(chunk.Content);item.Result sb.ToString();}七、效果对比普通调用处理10秒钟 → 结果突然全部出现用户不知道有没有在运行很焦虑流式输出开始后立刻看到文字蹦出来用户直观感受到AI在工作体验好很多八、完整项目这是我做的「文省事」AI文档批量处理工具里用到的核心技术。工具功能批量生成文档摘要批量提取关键信息支持PDF/Word/TXT结果导出Excel感兴趣的可以咸鱼搜索「文省事」。如果本文对你有帮助点个赞后续持续更新C# AI实战内容。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询