Skip to content

消息 Messages

概述

消息(Messages)是 LangChain 中一切对话交互的基础数据单元。无论是用户输入、模型回复、系统指令还是工具调用结果,都被表示为特定类型的消息对象。LangChain 1.0 进一步引入了 Content Blocks 机制,为不同 Provider(Anthropic、OpenAI、Google 等)的异构内容格式提供了统一的访问接口。

本页将覆盖:消息类型、消息结构字段、Content Blocks 七种块类型、对话历史管理和消息序列化。

前端类比

消息类似 HTTP 请求/响应对象——结构化、有类型、包含头部(元数据)和正文(内容)。HumanMessage 相当于 Request,AIMessage 相当于 Response,SystemMessage 相当于请求头中的全局配置。

Content Blocks 则类似 GraphQL 的统一响应格式——不管后端数据源如何各异,前端拿到的都是一致的 schema,无需为每个 Provider 写特殊处理逻辑。

理解类比之后,也请记住消息的原生语义:在 LLM 语境中,消息列表构成了模型的"上下文窗口",模型根据完整的消息历史来生成下一个回复。消息类型(role)决定了模型如何理解每条信息的来源和权重。

核心概念

四种消息类型

LangChain 提供四种核心消息类型,全部从 langchain_core.messages(或 langchain.messages)导入:

消息类型对应 role用途前端类比
HumanMessageuser用户输入前端发送的请求 body
AIMessageassistant模型响应后端返回的响应 body
SystemMessagesystem系统指令,设定模型行为请求头中的全局配置
ToolMessagetool工具执行结果,回传给模型Webhook 回调数据

消息结构字段

每个消息对象都包含以下关键字段:

python
from langchain.messages import HumanMessage

msg = HumanMessage(content="你好,请帮我分析数据")

# 核心字段
msg.content          # str 或 list — 消息内容(原生格式)
msg.type             # str — 消息类型标识,如 "human"、"ai"
msg.content_blocks   # list[dict] — 标准化内容块(推荐访问方式)
msg.additional_kwargs  # dict — 扩展元数据(Provider 特有信息)
msg.response_metadata  # dict — 响应元数据(仅 AIMessage,含 token 用量等)

Content Blocks:统一内容访问

不同 Provider 返回的内容格式各不相同——Anthropic 用 thinking 块表示推理过程,OpenAI 用 tool_calls 数组表示工具调用。LangChain 1.0 通过 content_blocks 属性将它们统一为 7 种标准块类型

块类型说明典型来源
text文本内容所有 Provider
reasoning模型推理/思考步骤Anthropic extended thinking、DeepSeek
tool_call工具调用请求所有支持 function calling 的 Provider
image图片内容多模态模型
audio音频内容支持语音的模型
video视频内容支持视频的模型
file通用文件MCP 工具等

核心价值:无论底层 Provider 如何组织数据,你只需要写一套 content_blocks 处理代码。

代码示例 1: 创建基础消息

最基础的用法——用消息对象构建对话并发送给模型:

python
from langchain.messages import HumanMessage, AIMessage, SystemMessage
from langchain_anthropic import ChatAnthropic

model = ChatAnthropic(model="claude-sonnet-4-5-20250929")

# 方式 1:使用消息对象(推荐,类型安全)
messages = [
    SystemMessage("你是一位资深 Python 开发工程师,回答简洁准确。"),
    HumanMessage("什么是装饰器?"),
]

response = model.invoke(messages)
print(response.content)
# => 装饰器是一种特殊的函数,用于在不修改原函数代码的情况下...
python
# 方式 2:使用字典格式(简洁,适合快速原型)
messages = [
    {"role": "system", "content": "你是一位资深 Python 开发工程师。"},
    {"role": "user", "content": "什么是装饰器?"},
]

response = model.invoke(messages)

两种方式等价,但消息对象在代码补全、类型检查和 Content Blocks 访问上更有优势。

代码示例 2: 构建对话历史

消息列表天然构成了对话历史。通过维护一个有序的消息列表,可以实现多轮对话:

python
from langchain.messages import HumanMessage, AIMessage, SystemMessage

# 构建多轮对话历史
conversation = [
    SystemMessage("你是一位 Python 导师,用简单易懂的方式教学。"),
    HumanMessage("Python 的列表和元组有什么区别?"),
    AIMessage("列表(list)是可变的,元组(tuple)是不可变的。"
              "列表用方括号 [],元组用圆括号 ()。"),
    HumanMessage("那什么时候该用元组?"),
]

# 模型会参考整个对话上下文生成回复
response = model.invoke(conversation)
print(response.content)
# => 当你确定数据不会被修改时,使用元组更合适...

要点

  • 消息列表按时间顺序排列,模型会参考完整上下文
  • SystemMessage 通常放在列表开头,设定全局行为
  • 过长的对话历史需要截断或摘要,否则会超出上下文窗口限制
  • 在 Agent 场景中,LangChain 会自动管理对话历史(参见短期记忆

代码示例 3: Content Blocks 跨 Provider 处理

Content Blocks 的核心价值是跨 Provider 兼容。以下示例展示如何用统一代码处理不同 Provider 的响应:

python
from langchain.messages import HumanMessage
from langchain_anthropic import ChatAnthropic

model = ChatAnthropic(model="claude-sonnet-4-5-20250929")

response = model.invoke([HumanMessage("解释什么是闭包")])

# 通过 content_blocks 统一访问(推荐)
for block in response.content_blocks:
    if block["type"] == "text":
        print(f"文本: {block['text']}")
    elif block["type"] == "reasoning":
        # Anthropic extended thinking 产生的推理块
        print(f"推理过程: {block['reasoning'][:100]}...")
        # extras 可能包含签名等 Provider 特有信息
        print(f"附加信息: {block.get('extras', {})}")

下面是一个通用的 Content Blocks 处理函数,适用于任何 Provider

python
def process_ai_response(ai_message):
    """统一处理 AIMessage 的 content_blocks,兼容所有 Provider"""
    result = {
        "texts": [],
        "reasoning": [],
        "tool_calls": [],
        "media": [],
    }

    for block in ai_message.content_blocks:
        match block["type"]:
            case "text":
                result["texts"].append(block["text"])
            case "reasoning":
                result["reasoning"].append(block["reasoning"])
            case "tool_call":
                result["tool_calls"].append({
                    "name": block["name"],
                    "args": block["args"],
                    "id": block.get("id"),
                })
            case "image" | "audio" | "video" | "file":
                result["media"].append({
                    "type": block["type"],
                    "url": block.get("url"),
                    "mime_type": block.get("mime_type"),
                })

    return result


# 使用示例
response = model.invoke([HumanMessage("你好")])
parsed = process_ai_response(response)
print(parsed["texts"])  # => ["你好!有什么我可以帮助你的吗?"]

代码示例 4: 工具调用与 ToolMessage

当模型决定调用工具时,AIMessagecontent_blocks 中会包含 tool_call 类型的块。工具执行结果通过 ToolMessage 回传给模型:

python
from langchain.messages import HumanMessage, ToolMessage
from langchain_anthropic import ChatAnthropic

def get_weather(city: str) -> str:
    """获取指定城市的天气"""
    return f"{city}今天晴,气温 22°C"

model = ChatAnthropic(model="claude-sonnet-4-5-20250929").bind_tools([get_weather])

# 第 1 步:模型决定调用工具
response = model.invoke([HumanMessage("北京天气怎么样?")])

# 检查 content_blocks 中的工具调用
tool_calls = [
    block for block in response.content_blocks
    if block["type"] == "tool_call"
]

if tool_calls:
    call = tool_calls[0]
    print(f"模型请求调用: {call['name']}({call['args']})")

    # 第 2 步:执行工具,获取结果
    tool_result = get_weather(**call["args"])

    # 第 3 步:构建 ToolMessage 回传结果
    tool_msg = ToolMessage(
        content=tool_result,
        tool_call_id=call["id"],  # 必须匹配 tool_call 的 id
    )

    # 第 4 步:将完整对话(含工具结果)发回模型
    final_response = model.invoke([
        HumanMessage("北京天气怎么样?"),
        response,     # AIMessage(含 tool_call)
        tool_msg,     # ToolMessage(工具结果)
    ])
    print(final_response.content)
    # => 北京今天天气晴朗,气温 22°C,适合外出活动。

注意

ToolMessagetool_call_id 必须与对应 tool_call 块的 id 匹配,否则模型无法正确关联工具结果。

在实际项目中,推荐使用 create_agent 自动管理工具调用循环,而不是手动处理。参见 智能体 Agent

代码示例 5: 多模态消息

LangChain 1.0 通过 content_blocks 原生支持多模态输入:

python
from langchain.messages import HumanMessage

# 发送图文混合消息
message = HumanMessage(content_blocks=[
    {"type": "text", "text": "请描述这张图片中的内容"},
    {"type": "image", "url": "https://example.com/photo.jpg"},
])

response = model.invoke([message])
print(response.content)
python
# 使用 base64 编码的本地图片
import base64
from pathlib import Path

image_data = base64.b64encode(Path("chart.png").read_bytes()).decode()

message = HumanMessage(content_blocks=[
    {"type": "text", "text": "这个图表展示了什么趋势?"},
    {
        "type": "image",
        "base64": image_data,
        "mime_type": "image/png",
    },
])

response = model.invoke([message])
for block in response.content_blocks:
    if block["type"] == "text":
        print(block["text"])

代码示例 6: 消息序列化

消息可以序列化为字典格式(JSON 兼容),方便持久化存储或网络传输:

python
from langchain.messages import (
    HumanMessage, AIMessage, SystemMessage,
    messages_to_dict, messages_from_dict,
)

# 构建消息列表
messages = [
    SystemMessage("你是一位助手。"),
    HumanMessage("你好"),
    AIMessage("你好!有什么可以帮你的?"),
]

# 序列化为字典列表
serialized = messages_to_dict(messages)
print(serialized)
# [
#   {"type": "system", "content": "你是一位助手。", ...},
#   {"type": "human", "content": "你好", ...},
#   {"type": "ai", "content": "你好!有什么可以帮你的?", ...},
# ]

# 从字典列表反序列化
restored = messages_from_dict(serialized)
assert restored[0].content == "你是一位助手。"
assert isinstance(restored[1], HumanMessage)

应用场景

  • 将对话历史保存到数据库(Redis / PostgreSQL)
  • 通过 API 在前后端之间传递对话状态
  • 对话日志记录与调试分析

消息流转全景图

下图展示了消息在 Agent 执行过程中的完整流转:

最佳实践

1. 优先使用 content_blocks

python
# 推荐:统一、跨 Provider 兼容
for block in response.content_blocks:
    if block["type"] == "text":
        print(block["text"])

# 不推荐:content 格式因 Provider 而异
print(response.content)  # 可能是 str,也可能是 list[dict]

2. SystemMessage 放在列表开头

python
# 正确:SystemMessage 在最前面
messages = [
    SystemMessage("你是一位专业翻译。"),
    HumanMessage("翻译:Hello World"),
]

# 不推荐:SystemMessage 放在中间或末尾
messages = [
    HumanMessage("翻译:Hello World"),
    SystemMessage("你是一位专业翻译。"),  # 部分模型可能忽略
]

3. 控制对话历史长度

python
# 保留最近 N 轮对话,避免超出上下文窗口
MAX_HISTORY = 20

def trim_messages(messages: list, max_count: int = MAX_HISTORY) -> list:
    """保留 SystemMessage + 最近 N 条消息"""
    system_msgs = [m for m in messages if m.type == "system"]
    other_msgs = [m for m in messages if m.type != "system"]
    return system_msgs + other_msgs[-max_count:]

4. ToolMessage 必须匹配 tool_call_id

确保每个 ToolMessagetool_call_id 与对应的 tool_callid 一一匹配,否则模型会混淆工具调用结果的对应关系。

常见问题

Q: content 和 content_blocks 有什么区别?

content 是消息的原生内容字段,格式因 Provider 而异(可能是 str,也可能是 list[dict])。content_blocks 是 LangChain 1.0 新增的标准化访问属性,将所有 Provider 的内容统一转换为 7 种块类型。新代码建议始终使用 content_blocks

Q: 使用字典还是消息对象?

两者都可以传给 model.invoke()。字典格式适合快速原型开发,消息对象适合生产代码——它提供更好的类型检查和 IDE 补全,还能直接访问 content_blocksadditional_kwargs 等属性。

Q: ToolMessage 和 AIMessage 中的 tool_call 有什么关系?

AIMessagecontent_blocks 包含 tool_call 块,表示模型请求调用某个工具。ToolMessage 则是工具执行后的结果回传。两者通过 tool_call_id 关联。

Q: 如何处理 Anthropic 的 thinking 块?

Anthropic 的 extended thinking 会在 content 中返回 thinking 类型的原生块。通过 content_blocks 访问时,它们会被标准化为 reasoning 类型:

python
for block in response.content_blocks:
    if block["type"] == "reasoning":
        print(f"模型思考过程: {block['reasoning']}")

下一步

  • 模型 — 了解如何配置和使用不同的 LLM Provider
  • 工具 — 学习如何定义工具,让模型通过 tool_call 调用
  • 流式响应 — 使用 content_blocks 实现 Token 级别的实时输出
  • 短期记忆 — Agent 如何自动管理对话历史中的消息

参考资源

学习文档整合站点