[对比学习LangChain和MAF-04]针对消息的设计

📅 2026/6/28 5:26:42
[对比学习LangChain和MAF-04]针对消息的设计
基于对话的Chat Agent是目前最主流的Agent类型它采用的基于角色的消息是一种结构化对话机制它通过将对话内容划分为不同的预设身份Roles来引导模型理解其职责和当前上下文。这种机制主要由三类核心角色组成System用于设定模型的人格、行为准则和专业领域定义AI是谁、该怎么说话、不能做什么。示例“你是一位资深的Python程序员请用幽默简洁的风格回答问题不要使用过多的技术术语。”它具有最高优先级能从宏观上约束模型的输出风格确保模型不会在长对话中出戏User代表真实用户提出的问题、指令或输入的信息。提供具体的任务请求比如“帮我写一段冒泡排序的代码”。这种类型的效用用于触发模型的响应机制是互动的核心驱动力Assistant代表AI模型之前生成的响应内容。它会记录对话历史维持上下文连贯性。在多轮对话中开发者会将模型之前的回答标记为assistant重新传给模型。这样模型才知道自己刚刚说过什么从而实现记忆功能Tool代表模型调用工具的输入和输出。它用于实现模型与外部工具如API、数据库、文件系统等的交互。通过将工具调用的输入输出封装成Tool消息模型能够理解何时需要调用工具以及如何处理工具返回的结果这种基于多角色对话机制用以解决如下的问题上下文管理通过区分谁说了什么模型能准确区分用户的要求和自己的回答避免产生逻辑混乱人设稳定性在角色扮演场景中系统角色可以持续强化角色的背景故事和性格提供沉浸感安全性与约束开发者可以在系统消息中设置安全边界防止模型产生违规内容少样本学习 (Few-shot)开发者常利用user和assistant通过示例对话来引导模型学习特定的任务格式或风格LangChain和MAF针对基于角色的消息设计了一个完整的消息体系但是它们的设计思路和实现方式却有所不同。1. LangChainLangChain的消息类型直接或者间接地继承自如下这个BaseMessage基类这是一个派生自Serializable的可序列化的类型。对于基类的BaseMessage来说表示消息内容的content字段可以是单纯的字符串或字典Key为字符串的列表其他于消息相关信息存储在additional_kwargs字段对应的字典中比如LLM返回的AIMessage可以利用它来保存涉及的工具调用。response_metadata字段用于存储响应的元数据比如响应的Header、LLM的名称涉及的Token消费数据等。class BaseMessage(Serializable): content: str | list[str | dict] additional_kwargs: dict Field(default_factorydict) response_metadata: dict Field(default_factorydict) type: str name: str | None None id: str | None Field(defaultNone, coerce_numbers_to_strTrue) property def content_blocks(self) - list[types.ContentBlock] class ChatMessage(BaseMessage): role: str type: Literal[chat] chat消息承载的内容多种多样可以是简单的文本还可以是多媒体图片、音频和视频还可以是一个任意的二进制文件不同类型的内容具有不同的处理方式所以LangChain利用ConentBlock这个类型实现了内容的标准化。类似于HTTP的MIME类型每个ConentBlock对象都关联一个标准的类型名称很多采用的就是MIME类型。BaseMessage的content_blocks属性实现了原始形态的内容到ConentBlock列表的转换。我们可以指定绑定的角色创建一个ChatMessage作为Chat模型的消息也可以使用如下这些已经绑定好角色的消息类型。1.1 四种预设消息类型1.1.1 SystemMessage在LangChain以及底层的 Chat API 架构中系统消息是用于定义模型人格与运行规则的核心组件。它告诉 AI “你是谁”例如资深Python开发者、苏格拉底式的导师、或是一只可爱的猫娘。规定模型不能做什么例如严禁提及竞争对手、不准输出代码、只能用JSON格式回答。它通常位于消息列表的最顶端作为整个对话的宪法其权重通常高于普通的消息。class SystemMessage(BaseMessage): type: Literal[system] system1.1.2 HumanMessageHumanMessage代表了对话的需求侧即真实用户发送给模型的消息。它是用户意图的直接表达包含了模型需要完成的具体任务或提出的疑问。class HumanMessage(BaseMessage): type: Literal[human] human1.1.3 AIMessageAIMessage代表模型生成的响应。它是对话闭环的关键承载了AI的回答、推理逻辑及工具调用指令。它们是模型在接收到SystemMessage和HumanMessage后产生的输出。如果涉及针对工具的调用描述每个工具调用的ToolCall会出现在tool_calls字段返回的列表中另一个invalid_tool_calls字段返回于工具调用相关的错误。ToolCall是一个类型化字典定义了调用的工具名称、传入的参数和当前工具调用的唯一标识。class AIMessage(BaseMessage): tool_calls: list[ToolCall] Field(default_factorylist) invalid_tool_calls: list[InvalidToolCall] Field(default_factorylist) usage_metadata: UsageMetadata | None None type: Literal[ai] ai property def content_blocks(self) - list[types.ContentBlock] class ToolCall(TypedDict): name: str args: dict[str, Any] id: str | None type: NotRequired[Literal[tool_call]]1.1.4 ToolMessageToolMessage属于对话的执行层用于向模型反馈外部工具执行的结果。当Agent接收到LLM发出的带有tool_calls的AIMessage后它会执行对应的工具并将结果包装在ToolMessage中反馈给LLM。它的tool_call_id和status字段分别标识工具调用的标识和状态前者用于关联AIMessage中的某个具体的ToolCall。class ToolMessage(BaseMessage, ToolOutputMixin): tool_call_id: str type: Literal[tool] tool artifact: Any None status: Literal[success, error] success1.2 消息内容块BaseMessage的content_blocks字段承载消息主体内容它返回一个ContentBlock对象的列表。表示内容块的ContentBlock被定义成如下所示的Union类型,接下来我们看看具体的类型承载了什么样的内容ContentBlock ( TextContentBlock | InvalidToolCall | ReasoningContentBlock | NonStandardContentBlock | DataContentBlock | ToolContentBlock )1.2.1 TextContentBlockTextContentBlock块是最常见的内容块类型它承载了文本内容以及与文本相关的元信息。它的text字段存储了文本内容annotations字段存储了文本的注释信息如加粗、斜体、链接等index字段用于表示文本在原始消息中的位置extras字段可以存储一些额外的信息比如文本的语言、情感倾向等。class TextContentBlock(TypedDict): type: Literal[text] id: NotRequired[str] text: str annotations: NotRequired[list[Annotation]] index: NotRequired[int | str] extras: NotRequired[dict[str, Any]]1.2.2 InvalidToolCall当LLM返回的AIMessage中包含工具调用时如果工具调用的格式不正确或者参数有误就会被记录在invalid_tool_calls字段中。InvalidToolCall类型化字典定义了无效工具调用的相关信息包括工具名称、传入的参数、错误信息以及当前工具调用的唯一标识等。class InvalidToolCall(TypedDict): type: Literal[invalid_tool_call] id: str | None name: str | None args: str | None error: str | None index: NotRequired[int | str] extras: NotRequired[dict[str, Any]]1.2.3 ReasoningContentBlockReasoningContentBlock块用于承载LLM的推理过程中的一些中间结果或者推理步骤的描述信息。它的reasoning字段可以存储LLM在推理过程中生成的一些解释性文本或者推理步骤的描述index字段用于表示这个推理内容块在原始消息中的位置extras字段可以存储一些额外的信息比如推理的类型、相关的输入输出等。class ReasoningContentBlock(TypedDict): type: Literal[reasoning] id: NotRequired[str] reasoning: NotRequired[str] index: NotRequired[int | str] extras: NotRequired[dict[str, Any]]1.2.4 DataContentBlockDataContentBlock块用于承载一些非文本类型的内容比如图片、视频、音频或者文件等。它是一个Union类型可以表示不同类型的非文本内容块。可以是ImageContentBlock、VideoContentBlock、AudioContentBlock、PlainTextContentBlock或者FileContentBlock中的任意一种每种类型都对应着不同的内容格式和相关的元信息。DataContentBlock ( ImageContentBlock | VideoContentBlock | AudioContentBlock | PlainTextContentBlock | FileContentBlock )1.2.5 ToolContentBlockToolContentBlock块用于承载与工具调用相关的内容。它是一个Union类型可以表示不同类型的工具内容块。可以是ToolCall、ToolCallChunk、ServerToolCall、ServerToolCallChunk或者ServerToolResult中的任意一种每种类型都对应着不同的工具调用格式和相关的元信息。ToolContentBlock ( ToolCall | ToolCallChunk | ServerToolCall | ServerToolCallChunk | ServerToolResult )对于LangChain中的各种消息和内容块类型在我们的如下两篇文章中具有详细的介绍消息——Agent与模型交互的媒介多形态的消息内容——多模态AI解决方案的基础2. MAF消息在MAF通过如下这个ChatMessage类来表示。public class ChatMessage { public string? AuthorName{ get; set; } public DateTimeOffset? CreatedAt { get; set; } public ChatRole Role { get; set; } public string Text Contents.ConcatText(); public IListAIContent Contents{ get; set; } public string? MessageId { get; set; } public object? RawRepresentation { get; set; } public AdditionalPropertiesDictionary? AdditionalProperties { get; set; } }ChatMessage的属性成员说明如下AuthorName消息发送者的名称可以是用户、模型或者工具等CreatedAt消息创建的时间戳Role消息发送者的角色通常是一个枚举类型如用户、模型、工具等Text消息的文本内容实际上是对Contents中所有内容的文本进行拼接后的结果Contents消息的内容列表每个内容都是一个AIContent对象AIContent是一个抽象类代表了消息内容的基类可以有不同类型的内容如文本、图片、文件等MessageId消息的唯一标识符可以用于消息的追踪和管理RawRepresentation消息的原始表示可以用于存储一些与消息相关的原始数据如模型返回的原始响应等AdditionalProperties一个字典用于存储一些额外的属性信息可以在Agent的运行过程中使用这些属性来进行一些自定义的逻辑处理出于可扩展的考虑Role并不是一个简单的枚举类型而是一个具有更丰富功能的结构体。它预定义了System、Assistant、User和Tool四个角色并且允许用户自定义角色。ChatMessage中的Role属性就是利用这个ChatRole结构体来表示消息发送者的角色。public readonly struct ChatRole : IEquatableChatRole { public static ChatRole System { get; } new ChatRole(system); public static ChatRole Assistant { get; } new ChatRole(assistant); public static ChatRole User { get; } new ChatRole(user); public static ChatRole Tool { get; } new ChatRole(tool); public string Value { get; }