首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
Semantic Kernel 聊天内容模型(Chat Content Models)设计解析:从纯文本到多模态内容
📅 2026/9/11 1:52:10
✍️ 爱科研究院
👁 阅读 3,247
Semantic Kernel 聊天内容模型Chat Content Models设计解析从纯文本到多模态内容【免费下载链接】semantic-kernelIntegrate cutting-edge LLM technology quickly and easily into your apps项目地址: https://gitcode.com/GitHub_Trending/se/semantic-kernel导读本文围绕 Semantic Kernel 的架构决策记录 0025-chat-content-models.mdADR-0025深入解析ChatMessageContent从仅字符串 Content演进为支持文本、图片等多模态内容项集合的完整设计过程。你将了解到OpenAI API 中content字段为何会从string扩展为string | array、三种候选设计方案各自的权衡、最终落地为 Option #3 的原因以及该设计在当前仓库源码中的具体实现形态与多模态调用方式。读完本文你将掌握 Semantic Kernel 中构造多模态用户消息、读取多模态模型响应、以及理解内容类型体系KernelContent/TextContent/ImageContent/FunctionCallContent等的完整实战能力。一、背景与问题OpenAI API 的 content 字段升级在 2023 年底的 OpenAI Chat Completions API 中chat message对象的content属性从原来只接受string类型的单一值扩展为可以接受两种类型string纯文本消息array内容项数组例如同时包含文本描述与图片 URI从而支持视觉问答Vision等多模态请求。这一变化直接冲击了 Semantic Kernel 中ChatMessageContent类仅有string Content属性的实现——它无法表达一条消息由多个不同类型的片段组成这一语义。该 ADR 记录的决策目标非常明确We should update current implementation ofChatMessageContentclass withstring Contentproperty to support this API.值得注意的是设计者从一开始就划定了两条硬性约束Decision Drivers新设计不能与 OpenAI API 耦合必须对其它 AI 提供方如 Google、Mistral、Ollama 等同样成立类与属性的命名必须一致、直观consistent and intuitive。这两条约束决定了后续方案评审的方向不能简单地为 OpenAI 单独造一个字符串数组模型而必须在 Semantic Kernel 抽象的层面建立通用的内容模型。二、候选方案对比三种设计路线的权衡ADR-0025 提出了三个候选方案其中前两个方案的变体可以互相组合。逐一分析其思路与代价才能理解最终选择。Option #1重命名 引入新的内容类型体系该方案对命名体系做了一次较大的语义澄清ChatMessageContent更名为ChatMessageStreamingChatMessageContent同理更名为StreamingChatMessageGetChatMessageContent系列方法更名为GetChatMessage新增抽象类ChatMessageContent持有ChatMessageContentType Type属性取值先支持text、image未来扩展到audio、videoChatMessage内部持有内容项集合IListChatMessageContent Contents提供两个具体实现ChatMessageTextContent与ChatMessageImageContent。文档给出了类型定义草案public readonly struct ChatMessageContentType : IEquatableChatMessageContentType { public static ChatMessageContentType Text { get; } new(text); public static ChatMessageContentType Image { get; } new(image); public string Label { get; } // Implementation of IEquatable... }public abstract class ChatMessageContent { public ChatMessageContentType Type { get; set; } public ChatMessageContent(ChatMessageContentType type) { this.Type type; } }public class ChatMessage : ContentBase { public AuthorRole Role { get; set; } public IListChatMessageContent Contents { get; set; } }public class ChatMessageTextContent : ChatMessageContent { public string Text { get; set; } public ChatMessageTextContent(string text) : base(ChatMessageContentType.Text) { this.Text text; } }public class ChatMessageImageContent : ChatMessageContent { public Uri Uri { get; set; } public ChatMessageImageContent(Uri uri) : base(ChatMessageContentType.Image) { this.Uri uri; } }对应的多模态调用方式var chatHistory new ChatHistory(You are friendly assistant.); // Construct request var userContents new ListChatMessageContent { new ChatMessageTextContent(Whats in this image?), new ChatMessageImageContent(new Uri(https://link-to-image.com)) }; chatHistory.AddUserMessage(userContents); // Get response var message await chatCompletionService.GetChatMessageAsync(chatHistory); foreach (var content in message.Contents) { // Possibility to get content type (text or image). var contentType content.Type; // Cast for specific content type // Extension methods can be provided for better usability // (e.g. message GetContentChatMessageTextContent()). if (content is ChatMessageTextContent textContent) { Console.WriteLine(textContent); } if (content is ChatMessageImageContent imageContent) { Console.WriteLine(imageContent.Uri); } }评价语义上最清晰——消息Message与消息内容Content彻底分离Type字段提供了运行时判别能力也为未来的audio、video预留了扩展点。但代价是对现有代码层级ChatMessageContent类、GetChatMessageContent方法进行大规模重命名属于破坏性变更迁移成本最高。Option #2不重命名用ChatMessageContentItem区分层次与 Option #1 思路一致但避免任何命名变更。为了区分消息与消息内容两个概念Chat Message仍然叫ChatMessageContent保持现状Chat Message Content命名为ChatMessageContentItem。要点新增抽象类ChatMessageContentItem持有ChatMessageContentItemType Type属性取值text、image同样预留audio、videoChatMessageContent持有内容项集合IListChatMessageContentItem Items具体实现为ChatMessageTextContentItem与ChatMessageImageContentItem。public readonly struct ChatMessageContentItemType : IEquatableChatMessageContentItemType { public static ChatMessageContentItemType Text { get; } new(text); public static ChatMessageContentItemType Image { get; } new(image); public string Label { get; } // Implementation of IEquatable... }public abstract class ChatMessageContentItem { public ChatMessageContentItemType Type { get; set; } public ChatMessageContentItem(ChatMessageContentItemType type) { this.Type type; } }public class ChatMessageContent : ContentBase { public AuthorRole Role { get; set; } public IListChatMessageContentItem Items { get; set; } }public class ChatMessageTextContentItem : ChatMessageContentItem { public string Text { get; set; } public ChatMessageTextContentItem(string text) : base(ChatMessageContentType.Text) { this.Text text; } }public class ChatMessageImageContentItem : ChatMessageContentItem { public Uri Uri { get; set; } public ChatMessageImageContentItem(Uri uri) : base(ChatMessageContentType.Image) { this.Uri uri; } }var chatHistory new ChatHistory(You are friendly assistant.); // Construct request var userContentItems new ListChatMessageContentItem { new ChatMessageTextContentItem(Whats in this image?), new ChatMessageImageContentItem(new Uri(https://link-to-image.com)) }; chatHistory.AddUserMessage(userContentItems); // Get response var message await chatCompletionService.GetChatMessageContentAsync(chatHistory); foreach (var contentItem in message.Items) { // Possibility to get content type (text or image). var contentItemType contentItem.Type; // Cast for specific content type // Extension methods can be provided for better usability // (e.g. message GetContentChatMessageTextContentItem()). if (contentItem is ChatMessageTextContentItem textContentItem) { Console.WriteLine(textContentItem); } if (contentItem is ChatMessageImageContentItem imageContentItem) { Console.WriteLine(imageContentItem.Uri); } }评价通过Item后缀保留现有命名避免了大规模重命名但仍需要新增一整套ChatMessageContentItem/ChatMessageContentItemType类型体系抽象层次有所增加。Option #3保留string Content追加内容项集合属性最终方案该方案保留ChatMessageContent类名与string Content属性不动仅新增一个类型化集合属性Items其中的元素类型直接复用当时已有的ContentBase基类即文本/图片等具体内容类型共同的祖先public class ChatMessageContent : ContentBase { public AuthorRole Role { get; set; } public string? Content { get; set; } public ChatMessageContentItemCollection? Items { get; set; } }同时新增集合类型ChatMessageContentItemCollectionpublic class ChatMessageContentItemCollection : IListContentBase, IReadOnlyListContentBase { // Implementation of IListContentBase, IReadOnlyListContentBase to catch null values. }注意文档注释中特别强调该集合实现IListContentBase/IReadOnlyListContentBase的目的之一是在 Add/Set 时拦截 null 值保证集合不会出现空元素。使用示例var chatCompletionService kernel.GetRequiredServiceIChatCompletionService(); var chatHistory new ChatHistory(You are a friendly assistant.); chatHistory.AddUserMessage(new ChatMessageContentItemCollection { new TextContent(Whats in this image?), new ImageContent(new Uri(ImageUri)) }); var reply await chatCompletionService.GetChatMessageContentAsync(chatHistory); Console.WriteLine(reply.Content);评价对既有调用方几乎零破坏——Content依然可用同时通过Items表达多模态内容。改动量小、可用性好是三者中迁移成本最低的方案。三、决策结果为什么最终选择了 Option #3ADR-0025 的 Decision Outcome 明确写道Option #3 was preferred as it requires small amount of changes to existing hierarchy and provides clean usability for end-user.即选择 Option #3 的核心理由是对现有类层级改动最小——ChatMessageContent类名、Content属性、GetChatMessageContent方法名全部保留已有代码无需大规模重写终端用户使用体验干净——多模态消息通过ChatMessageContentItemCollection构造集合初始化器语法直观内容类型体系天然可扩展——ContentBase后演进为KernelContent作为共同基类TextContent、ImageContent乃至后续的AudioContent、FunctionCallContent等都能统一放入集合。该 ADR 还附带一张类关系示意图完整呈现了 Chat 模型与 Text 模型的抽象结构四、源码落地当前仓库中的内容模型实现ADR 通过之后设计在 dotnet/src/SemanticKernel.Abstractions/Contents 目录下落地为一系列内容类型。从源码结构看Option #3 的核心设计被完整保留并在后续演进中进一步泛化。4.1 ChatMessageContentContent 便捷属性 Items 集合当前实现位于 ChatMessageContent.cs。其核心特征与 ADR 的 Option #3 完全一致但有两处值得注意的演进演进一Content从存储字段变成便捷属性。在 ChatMessageContent.cs 中Content被标记为[JsonIgnore]其 getter 返回Items中第一个TextContent的Textsetter 则要么更新已有的首个TextContent要么自动把字符串包装为新的TextContent加入Items。也就是说文本仍然是存储在Items集合里的Content只是对首个文本项的一层语法糖[JsonIgnore] public string? Content { get { var textContent this.Items.OfTypeTextContent().FirstOrDefault(); return textContent?.Text; } set { var textContent this.Items.OfTypeTextContent().FirstOrDefault(); if (textContent is not null) { textContent.Text value; } else if (value is not null) { this.Items.Add(new TextContent(text: value, ...)); } } }单元测试 ChatMessageContentTests.cs 专门验证了这一行为new ChatMessageContent(AuthorRole.User, fake-content)构造后Items恰好包含一个TextContent直接给Content赋字符串也会自动向Items追加TextContent。演进二内容项集合元素类型从ContentBase泛化为KernelContent。Items属性的声明为ChatMessageContentItemCollection Items而 ChatMessageContentItemCollection.cs 中集合实现为IListKernelContent, IReadOnlyListKernelContent。KernelContent是当前内容模型的统一基类KernelContent.cs相比 ADR 撰写时的ContentBase这个体系容纳了更多内容形态。4.2 内容项家族TextContent / ImageContent 与更多Contents 目录下可以找到完整的 Kernel 内容类型家族TextContent.cs纯文本内容项持有string? Text与Encoding它定义了public static implicit operator TextContent(string text)因此可以隐式地把字符串当作文本内容项使用ImageContent.cs图片内容项继承自BinaryContent支持三种构造方式——Uri、dataUri字符串、ReadOnlyMemorybyte字节数组加 MIME 类型AudioContent.cs音频内容项对应 ADR 中未来扩展到 audio的预留方向BinaryContent.cs通用二进制内容基类FunctionCallContent.cs 与 FunctionResultContent.cs函数调用与函数执行结果承载 Function Calling 场景ReasoningContent.cs模型推理过程内容FileReferenceContent.cs 与 AnnotationContent.cs文件引用与注解。由此可以看出ADR-0025 设计的一条消息 一组异构内容项模型最终成为 Semantic Kernel 统一表达文本、图片、音频、函数调用、推理等多模态能力的基石。4.3 流式场景的对称设计非流式消息有ChatMessageContent流式场景则有对称的 StreamingChatMessageContent.cs。它同样提供Content便捷属性读取Items中第一个StreamingTextContent的文本并持有StreamingKernelContentItemCollection Items用于承载流式返回的内容片段。这样无论调用GetChatMessageContentAsync还是GetStreamingChatMessageContentsAsync使用者面对的都是同一套消息 内容项集合的心智模型。4.4 ChatHistory多模态消息的便捷入口ChatHistory.cs 为内容项集合提供了专用重载public void AddMessage(AuthorRole authorRole, ChatMessageContentItemCollection contentItems, ...) public void AddUserMessage(ChatMessageContentItemCollection contentItems) this.AddMessage(AuthorRole.User, contentItems);也就是说你可以直接通过chatHistory.AddUserMessage(new ChatMessageContentItemCollection { ... })把多模态内容项组装成一条用户消息加入对话历史与 ADR 示例中的用法完全对应。4.5 与 Microsoft.Extensions.AI 的互操作在 ChatMessageContentExtensions.cs 中ToChatMessage()扩展方法演示了Items集合是如何被逐项映射到Microsoft.Extensions.AI.ChatMessage的TextContent→TextContentImageContent→DataContent/UriContentAudioContent、BinaryContent、FunctionCallContent、FunctionResultContent均有对应的映射分支。这印证了 ADR 决策驱动第一条——不耦合于 OpenAI API对其它 AI 提供方同样适用——内容模型是提供方无关的通用抽象。五、实战在 Semantic Kernel 中构造与读取多模态消息结合上述源码一个完整的看图问答多模态调用可以这样组织using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.ChatCompletion; var kernel Kernel.CreateBuilder() .AddOpenAIChatCompletion(modelId: gpt-4o, apiKey: ...) .Build(); var chatCompletionService kernel.GetRequiredServiceIChatCompletionService(); var chatHistory new ChatHistory(You are a helpful assistant.); // 构造多模态用户消息文本 图片 URI 混合 chatHistory.AddUserMessage(new ChatMessageContentItemCollection { new TextContent(Whats in this image?), new ImageContent(new Uri(https://example.com/cat.png)) }); // 获取模型回复 var reply await chatCompletionService.GetChatMessageContentAsync(chatHistory); // 读取回复既可以走便捷属性取文本 Console.WriteLine(reply.Content); // 也可以遍历 Items按类型分别处理如文本、图片、函数调用等 foreach (var item in reply.Items) { switch (item) { case TextContent text: Console.WriteLine($[text] {text.Text}); break; case ImageContent image: Console.WriteLine($[image] {image.Uri ?? image.DataUri}); break; case FunctionCallContent call: Console.WriteLine($[function] {call.FunctionName}); break; } }关键要点构造侧ImageContent支持Uri、dataUri或原始字节三种来源因此既能引用远程图片也能传入 base64 data URI 或本地读取的字节流读取侧reply.Content是首个TextContent的文本的便捷访问当需要完整信息如同时返回文本与函数调用时应遍历reply.Items并按类型判断历史侧ChatHistory的AddUserMessage/AddMessage均提供接收ChatMessageContentItemCollection的重载多模态消息可以像普通文本一样进出对话历史。仓库示例中还有大量现成参考例如 OpenAI_ChatCompletionWithVision.cs、Google_GeminiVision.cs、Ollama_ChatCompletionWithVision.cs 分别演示了 OpenAI、Gemini、Ollama 三种提供方下的视觉多模态调用而 MistralAI_ChatCompletion.cs 展示了文本消息的常规用法——这恰好从侧面证明了 ADR 所要求的提供方无关设计目标。六、设计影响与经验小结回看 ADR-0025它是一次典型的以最小破坏换取最大通用性的抽象演进兼容性是第一优先级Option #3 保留ChatMessageContent、Content、GetChatMessageContent*等既有公共 API存量应用几乎无需改动通用内容模型取代提供方特化Items集合承载任意KernelContent派生类型天然适配 OpenAI 的content: string | array同时适用于 Google、Mistral、Ollama 等其它提供方便捷性与完整性兼顾Content作为语法糖覆盖 90% 的纯文本场景Items提供 100% 的多模态表达能力为未来扩展预留空间ADR 中将扩展到 audio、video的设想已在当前源码中通过AudioContent、BinaryContent等类型成为现实。对于需要在 Semantic Kernel 中构建多模态 AI 应用视觉问答、图文对话、混合函数调用的开发者而言理解消息ChatMessageContent 内容项集合Items 内容类型KernelContent派生体系三层结构是掌握整个聊天与 Agent 内容模型的关键起点。相关资源索引架构决策原文docs/decisions/0025-chat-content-models.md类关系图docs/decisions/diagrams/chat-text-models.png核心实现ChatMessageContent.cs、ChatMessageContentItemCollection.cs、ChatHistory.cs内容类型家族Contents 目录单元测试ChatMessageContentTests.cs多模态示例OpenAI_ChatCompletionWithVision.cs、Google_GeminiVision.cs、Ollama_ChatCompletionWithVision.cs【免费下载链接】semantic-kernelIntegrate cutting-edge LLM technology quickly and easily into your apps项目地址: https://gitcode.com/GitHub_Trending/se/semantic-kernel创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/9/11 1:52:10
Diffusers 中的 AsymmetricAutoencoderKL:面向修复任务的不对称 VAE 详解与实战
2026/9/11 1:47:09
Filament Tables ColorColumn 完整指南:颜色列渲染、剪贴板复制与换行配置
2026/9/11 1:47:09
ML-For-Beginners 第 6 模块 NLP 课程:常见自然语言处理任务与 TextBlob 实战指南
2026/9/11 2:32:13
软件企业站HTML模板拆解:从Flexbox布局到JS交互实战
2026/9/11 2:32:13
ChatGPT Work写作风格学习:让AI学会你的语气,成为你的代笔
2026/9/11 2:32:13
飞牛NAS远程访问方案:神卓N600让SMB共享走出局域网
2026/9/11 2:32:13
合并两个有序链表:从哑节点到递归的完整拆解
2026/9/11 2:32:13
五款抓包工具深度对比:Charles、Wireshark、Fiddler、Proxyman、TraceEagle选型指南
2026/9/11 2:27:13
六款AI编程工具全栈Web项目横评:Claude Code与Cursor实战对比
2026/9/11 0:02:03
数据容灾核心指标与实战方案解析
2026/9/11 0:02:03
Huly 平台 ClickUp 任务导入实战指南:从 CSV 导出到一键迁移全流程解析
2026/9/11 0:02:03
PyTorch 构建与代码生成工具链深度解析:从 tools 目录看懂构建流程、autograd/JIT 代码生成与 HIPify 移植
2026/9/10 2:30:52
超人会飞不算本事:系统稳定依赖清晰规则与边界设计
2026/9/10 5:51:31
超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论
2026/9/10 8:32:02
基于CNN的调制信号识别:MATLAB实现时频图分类实战