[LangChain语言模型组件-01]消息:Agent与模型交互的媒介

Agent开发语境下的模型大体分两种,即传统的文本补齐Completions模型和基于多方交谈的Chat模型。前者主要采用纯文本交互,后者使用绑定为某一个角色的消息进行交流。但是LangChain并未对Chat模型输入施加较强的约束,很多与消息类型兼容的类消息类型都可以作为语言模型的输入。

1. 语言模型组件的输入和输出

在LangChain中,对应的语言模型组件类型都继承BaseLanguageModel抽象类,后者是RunnableSerializable的派生类,所以能成为LCEL链上的一环。关于LCEL的详细介绍,可以参考我的系列文章LangChain之链

class BaseLanguageModel(RunnableSerializable[LanguageModelInput, LanguageModelOutputVar], ABC)

LanguageModelInput = PromptValue | str | Sequence[MessageLikeRepresentation]
LanguageModelOutputVar = TypeVar("LanguageModelOutputVar", AIMessage, str)

BaseLanguageModel是一个泛型类型,如下两个泛型参数分别表示语言模型组件的输入和输出类型:

  • LanguageModelInput:表示语言模型组件的输入类型,它是一个联合类型,包含了PromptValue、字符串和MessageLikeRepresentation序列;
  • LanguageModelOutputVar:表示语言模型组件的输出类型,它是一个类型变量,约束为AIMessage或者字符串。

我们时候我们都将提供给LLM的输入称为提示词(Prompt),LangChain将提示词抽象为PromptValue类型。这是是一个抽象类,提供了两个抽象方法to_stringto_messages,分别用于将提示词转换为字符串和消息列表,分别适配上述两种语言模型的输入。

class PromptValue(Serializable, ABC):
    @abstractmethod
    def to_string(self) -> str:

    @abstractmethod
    def to_messages(self) -> list[BaseMessage]

顾名思义,MessageLikeRepresentation代表一个类消息的表示,其目的是为了让语言模型组件的输入可以兼容多种消息类型。这是一个类型类型,包含的联合成员包括:BaseMessage、字符串列表、字符串元组、字符串和字典。BaseMessage是所有消息类型的基类,后面会详细介绍。作为语言模型组件的输出类型,LanguageModelOutputVar是一个类型变量,它的约束为AIMessage或者字符串,这两个类型分别是上述两种语言模型的输出类型。AIMessageBaseMessage的派生类,表示绑定为AI或者Assistant角色的消息类型。

MessageLikeRepresentation = (
    BaseMessage | list[str] | tuple[str, str] | str | dict[str, Any]
)

如下这个UML类图囊括了语言模型的输入和输出涉及到的绝大部分类型,其中包括描述消息的基类BaseMessage以及针对不同角色的消息子类(SystemMessageHumanMessageAIMessageToolMessage等),还包括表示提示词的PromptValue及其子类(StringPromptValueChatPromptValuePromptValueConcrete等)。消息的主体内容通过ContentBlock表示,不同形式的内容对应不同的类型,ContentBlock是这些类型的联合。框起来的部分就是我们接下来着重介绍的部分。

Alternative Text

2. BaseMessage

LangChain的消息类型直接或者间接地继承自如下这个BaseMessage基类,这是一个派生自Serializable的可序列化的类型。

class BaseMessage(Serializable):
    content: str | list[str | dict]
    additional_kwargs: dict = Field(default_factory=dict)
    response_metadata: dict = Field(default_factory=dict)
    type: str
    name: str | None = None
    id: str | None = Field(default=None, coerce_numbers_to_str=True)

    @property
    def content_blocks(self) -> list[types.ContentBlock]

    @property
    def text(self) -> TextAccessor

    def pretty_repr(
        self,
        html: bool = False,
    ) -> str
    def pretty_print(self) -> None

相关的属性和方法说明如下:

  • content:消息的文本内容或多模态内容。用法:最常用的是纯字符串(str)。如果是多模态模型,它可以是一个列表,包含文本块和图片信息,如{"type": "image_url", "image_url": ...}
  • additional_kwargs:额外附加的字段。用于存放特定模型供应商(如OpenAI、Anthropic等)特有的、但LangChain标准字段未涵盖的参数;
  • response_metadata:响应元数据。主要由大模型在生成响应时填充。通常包含Token消耗统计(token_usage)、模型名称、停止原因(finish_reason)以及系统日志等非内容信息;
  • type:消息的类型标识。用法:硬编码的字符串,用于在序列化或条件判断时区分消息角色。例如HumanMessagetypehumanAIMessagetypeai
  • name:消息发送者的名称。多角色对话时非常有用。例如在Tool调用场景中,ToolMessagename可以设置为被调用工具的名称,帮助大模型区分这是哪个工具返回的结果;
  • id:消息的唯一标识符。用于在数据库或追踪系统(如LangSmith)中唯一标记这条消息。coerce_numbers_to_str=True表示如果传入数字ID,会自动转换为字符串;
  • content_blocks:内容块列表访问器。将统一的content转换为结构化的ContentBlock对象列表。这在处理复杂的多模态内容(文本 + 图片 + 音频)时,方便程序进行迭代和类型安全的解析;
  • text:纯文本访问器。一个快捷工具。无论content内部是字符串还是复杂的多模态列表,通过该字段都可以方便地直接提取出其中的纯文本字符串部分;
  • pretty_repr:美化版的字符串表达。返回一个格式化良好、易于阅读的消息字符串。如果设置html=True,则会返回带有HTML标签和样式的字符串,适合在Jupyter Notebook等支持富文本的终端中展示;
  • pretty_print:直接打印。用法:内部调用了pretty_repr并直接将其输出到终端。调试时非常常用,可以直观地看到消息的类型、角色和内容,比直接 print(message) 堆积的JSON字典更具可读性。

语言模型可能需要经历耗时的处理流程后才能生成完整的内容,但它可以利用流式传输实时返回当前生成的消息碎片。此消息碎片通过如下这个BaseMessageChunk类表示,虽然我们以碎片称呼它,其实一个BaseMessageChunk也可以视为一个消息,因为它继承了BaseMessage基类。

class BaseMessageChunk(BaseMessage):
    def __add__(self, other: Any) -> BaseMessageChunk

由于分块传输的碎片是完整消息的一部分,所以它们应该可以拼接成完整的消息,这个拼接的能力以重写的__add__方法被赋予,所以我们可以采用如下的形式使用+操作符对其实施拼接。

result = AIMessageChunk(content="Hello", ...) + AIMessageChunk(content=" World", ...)
# AIMessageChunk(content="Hello World", ...)

3. ChatMessage/ChatMessageChunk

传统的基于文本补齐的消息交互方式已经全面转向了多角色参与的聊天模式,后者涉及的消息类型以ChatMessage/ChatMessageChunk为基类。ChatMessage利用role字段命名发出该消息的角色,type字段的默认值为chat,对于派生于它的ChatMessageChunk,其type字段为ChatMessageChunk

class ChatMessage(BaseMessage):
    role: str
    type: Literal["chat"] = "chat"

class ChatMessageChunk(ChatMessage, BaseMessageChunk):
    type: Literal["ChatMessageChunk"] = "ChatMessageChunk" 

4. SystemMessage/SystemMessageChunk

系统消息是用于定义模型人格运行规则的核心组件。它告诉AI你是谁(例如资深Python开发者、苏格拉底式的导师、或是一只可爱的猫)。规定模型不能做什么(例如严禁提及竞争对手、不准输出代码、只能用JSON格式回答)。它通常位于消息列表的最顶端,作为整个对话的宪法,其权重通常高于普通的消息。它们具有专属的类型systemSystemMessageChunk

class SystemMessage(BaseMessage):
    type: Literal["system"] = "system"

class SystemMessageChunk(SystemMessage, BaseMessageChunk):
    type: Literal["SystemMessageChunk"] = "SystemMessageChunk"  

5. HumanMessage/HumenMessageChunk

HumanMessage/HumanMessageChunk代表了对话的需求侧,即真实用户发送给模型的消息。它是用户意图的直接表达,包含了模型需要完成的具体任务或提出的疑问,它们对应的专属类型分别为humanHumanMessageChunk

class HumanMessage(BaseMessage):
    type: Literal["human"] = "human"
class HumanMessageChunk(HumanMessage, BaseMessageChunk):
    type: Literal["HumanMessageChunk"] = "HumanMessageChunk" 

6. AIMessage/AIMessageChunk

AIMessage/AIMessageChunk代表模型生成的响应。它是对话闭环的关键,承载了AI的回答、推理逻辑及工具调用指令。它们是模型在接收到SystemMessageHumanMessage后产生的输出。它们对应的专属类型为aiAIMessageChunk。如果涉及针对工具的调用,描述每个工具调用的ToolCall会出现在tool_calls字段返回的列表中,另一个invalid_tool_calls字段返回于工具调用相关的错误。出现在AIMessageChunk中针对工具调用的描述类型为AIMessageChunk

class AIMessage(BaseMessage):
    tool_calls: list[ToolCall] = Field(default_factory=list)
    invalid_tool_calls: list[InvalidToolCall] = Field(default_factory=list)
    usage_metadata: UsageMetadata | None = None
    type: Literal["ai"] = "ai"

    @property
    def content_blocks(self) -> list[types.ContentBlock]
    @override
    def pretty_repr(self, html: bool = False) -> str:

class AIMessageChunk(AIMessage, BaseMessageChunk):
    type: Literal["AIMessageChunk"] = "AIMessageChunk" 
    tool_call_chunks: list[ToolCallChunk] = Field(default_factory=list)
    chunk_position: Literal["last"] | None = None

在流式传输中,模型返回的是tool_call_chunks(这是碎片的、不完整的、无法直接调用的JSON片段)。为了性能和鲁棒性,LangChain不会在每一个碎片到达时都尝试进行昂贵的完全解析,而是选择将这些碎片拼接起来。当一个带有chunk_position="last"AIMessageChunk被合并进当前的流时,它像一个发令枪告诉框架:“流已经结束,现在可以安全地将累积的tool_call_chunks解析成正式的、结构化的tool_calls列表了”。

ToolCallToolCallChunk定义如下,它们都是一个类型化字典。共同字段nameargsid分别表示调用的工具名称、传入的参数和当前工具调用的唯一标识。它们同样具有专属的类型,分别为tool_calltool_call_chunkToolCallChunk具有一个表示偏移量的index字段。

class ToolCall(TypedDict):
    name: str
    args: dict[str, Any]
    id: str | None
    type: NotRequired[Literal["tool_call"]]

class ToolCallChunk(TypedDict):
    name: str | None
    args: str | None
    id: str | None
    index: int | None
    type: NotRequired[Literal["tool_call_chunk"]]

7. ToolMessage/ToolMessageChunk

ToolMessage/ToolMessageChunk属于对话的执行层,用于向模型反馈外部工具执行的结果。当Agent接收到语言模型发出的带有tool_callsAIMessage后,它会执行对应的工具,并将结果包装在ToolMessage中反馈给语言模型。它们专属的类型分别是toolToolMessageChunktool_call_idstatus字段分别标识工具调用的标识和状态。

class ToolMessage(BaseMessage, ToolOutputMixin):
    tool_call_id: str
    type: Literal["tool"] = "tool"
    artifact: Any = None
    status: Literal["success", "error"] = "success"

class ToolMessageChunk(ToolMessage, BaseMessageChunk):
    type: Literal["ToolMessageChunk"] = "ToolMessageChunk"  

有时候工具返回的数据非常庞大(如几千行的DataFrame、原始图像字节流、复杂的API响应对象),如果全部放入content字段传给LLM,会导致Token爆炸或者超出模型上下文窗口的限制,我们在这种情况下可以将它们存储在artifact字段上,该字段允许我们将原始执行结果保留在消息对象中,但不发送给模型。

8. FunctionMessage/FunctionMessageChunk

工具调用的前身是函数调用,函数调用的结果被封装成FunctionMessage/FunctionMessageChunk对象后被发送给语言模型,它们已经逐渐被ToolMessage/ToolMessageChunk代替。这两个类型对应的专属类型为functionFunctionMessageChunk

class FunctionMessage(BaseMessage):
    name: str
    type: Literal["function"] = "function"

class FunctionMessageChunk(FunctionMessage, BaseMessageChunk):
    type: Literal["FunctionMessageChunk"] = "FunctionMessageChunk"  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值