LangChain Messages 篇章学习笔记 — 模型交互的输入输出单元

本文基于 LangChain 官方文档 Messages 章节学习整理,梳理消息对象的结构、四种消息类型、多种传参格式与多轮对话机制,适合 AI Agent 开发入门者理解模型交互的数据载体。


一、Message 是什么

Message 是 LangChain 中从模型获取上下文的基础工具(fundamental utility)。Model 是基础单元,而 Message 反映了模型的输入和输出。它携带 content(内容)和 metadata(元数据),用于与 AI 交流。

Message 是一个对象(object),包含以下核心字段:

字段 说明 示例
role 消息类型(角色) system / user / assistant / tool
content 消息内容 文本、图片、音频、文档等
metadata 可选元数据 message id、token usage 等

LangChain 提供了标准的 Message 模型类别,可以在所有模型提供商中工作,确保一致性行为,不管你用的是哪个模型。


二、基础使用

2.1 创建 Message 对象

最简单的方式是直接创建 Message 实体,然后把它们放到 invoke / stream / batch 里调用。

from langchain.messages import SystemMessage, HumanMessage

# SystemMessage:给模型定义角色、背景、风格
SystemMessage(content="你是一个专业的 Python 翻译助手")

# HumanMessage:用户的问题
HumanMessage(content="Translate: I love programming.")

2.2 三种传参方式

方式一:Text Prompts(字符串简写)

调用时直接传入字符串,LangChain 自动包装为 HumanMessage。

response = llm.invoke("什么是递归?")
# 等价于 llm.invoke(HumanMessage(content="什么是递归?"))
方式二:Message Prompts(消息对象列表)

创建一个 list,放入各种 Message 对象。适用于多轮对话和 system instruction。

from langchain.messages import SystemMessage, HumanMessage, AIMessage

messages = [
    SystemMessage(content="你是英文到法文翻译助手"),
    HumanMessage(content="Translate: I love programming."),
    AIMessage(content="J'adore la programmation."),       # 上一轮 AI 的回答
    HumanMessage(content="Translate: I love building applications."),
]
response = llm.invoke(messages)

多轮对话的关键:把 AI 之前说的内容(AIMessage)放回消息列表,让 AI 有了短暂的记忆能力。你说一句、AI 说一句,再把 AI 说的内容传回去,形成多轮对话。

方式三:Dictionary Format(字典格式)

用字典构建消息,OpenAI 兼容最简洁的写法。

messages = [
    {"role": "system", "content": "你是翻译助手"},
    {"role": "user", "content": "Translate: I love programming."},
    {"role": "assistant", "content": "J'adore la programmation."},
    {"role": "user", "content": "Translate: I love building applications."},
]
response = llm.invoke(messages)

Q:字典格式里的 role 和 content 是规定好的吗?能随便传吗?

A:不能随便传。 role 只能是 system / user / assistant / tool 这 4 个固定值,这是 OpenAI API 规范,所有兼容端点(包括百炼)都遵循。content 是你自己传的内容字符串。字典格式只是对象格式的简写,底层 LangChain 自动转成对应对象。

字典 role LangChain 对象 作用
"system" SystemMessage 定义模型角色
"user" HumanMessage 用户输入
"assistant" AIMessage 模型输出
"tool" ToolMessage 工具返回结果

三、四种 Message 类型详解

3.1 SystemMessage — 角色定义

告诉模型行为并提供交互上下文,给模型定角色、提供背景。

SystemMessage(content="你是一个专业的 Python 翻译助手,只回答翻译结果")

用途

  • 设置语调(正式/幽默/简洁)
  • 定义模型角色(翻译助手/代码专家/数据分析师)
  • 为 response 建立指导规则
  • 给模型一个"性格"和"回答风格"

3.2 HumanMessage — 用户输入

代表用户 input 和 interaction,是用户与模型交互的开始。

HumanMessage(content="什么是装饰器?")

多模态内容:HumanMessage 的 content 不仅可以是文本,还可以包含 audio、图片、files 等多模态内容。

HumanMessage 的字段

字段 是否必填 类型 用途
content 必填 str 或 list 消息内容(文本/图片/音频)
name 选填 str 多用户场景区分发言者
id 选填 str 消息唯一标识,用于追踪/删除

Q:content、name、id 是规定好的还是自己创建的?

A:字段名是 LangChain 预定义的(不能改),值是你自己传的。 类比 Java:就像一个 HumanMessage 类有三个属性,字段名是类定义好的,但属性值是你 new 的时候传的。

3.3 AIMessage — 模型输出

代表大语言模型返回的生成内容,包括信息、工具调用(tool_calls)和元数据等。

# 普通回答
AIMessage(content="递归是一种函数调用自身的编程思想...")

# 带工具调用的回答
AIMessage(
    content="",
    tool_calls=[{
        "id": "call_001",
        "name": "get_weather",
        "args": {"city": "杭州"},
    }]
)

Q:AIMessage 是动态的,下轮问答时需要先提取 content 再传进去吗?

A:不需要手动提取,直接把整个 AIMessage 对象追加到消息列表就行。

# ✅ 正确:直接追加 AIMessage 对象
messages = [SystemMessage("你是助手"), HumanMessage("你好")]
resp = llm.invoke(messages)
messages.append(resp)  # AIMessage 直接追加
messages.append(HumanMessage("那明天呢?"))
resp2 = llm.invoke(messages)  # 模型能看到完整多轮对话

底层原理:模型 API 本身就接受 assistant 角色的消息。在 Agent 中,这个"追加消息→调用模型→再追加"的循环是 Agent 框架自动做的,你不需要手写循环。

3.4 ToolMessage — 工具返回结果

Agent 可以使用工具调用,ToolMessage 用于把工具执行结果回传给模型。

ToolMessage(
    content="晴,25度",
    tool_call_id="call_001",  # 必须与 AIMessage.tool_calls[i]["id"] 严格对应
    name="get_weather",
)

Q:ToolMessage 里的工具返回信息是放在 AIMessage 里面吗?

A:不是!ToolMessage 是独立的消息类型,和 AIMessage 是并列关系。

完整流程:

1. 模型返回 AIMessage(带 tool_calls)  → "我想调用 get_weather(杭州)"
2. 你执行工具,得到结果                 → "晴,25度"
3. 你把结果包成 ToolMessage 放回消息列表 → ToolMessage("晴,25度")
4. 再次调用模型,模型看到 ToolMessage    → 生成最终回答

为什么 tool_call_id 必须对应? 因为一次对话中模型可能同时调多个工具,模型靠 id 知道哪个 ToolMessage 对应哪个 tool_call:

AIMessage 的 tool_calls:
  ├── {id: "call_001", name: "get_weather", args: {city: "杭州"}}
  └── {id: "call_002", name: "get_time", args: {}}

ToolMessage(tool_call_id="call_001", content="晴,25度")  ← 对应 weather
ToolMessage(tool_call_id="call_002", content="14:30")    ← 对应 time

四、多轮对话的消息流转

用户提问                    模型回答
   │                          │
   ▼                          ▼
HumanMessage("你好")  →  AIMessage("你好!有什么帮你的?")
        │                          │
        └──── 都追加到 messages 列表 ────┘
                     │
                     ▼
HumanMessage("那明天呢?")  →  AIMessage("明天有什么需要帮忙的?")
        │                          │
        └──── 继续追加 ─────────────┘

手动多轮对话(理解原理用,实际用 Agent 自动处理)

messages = [SystemMessage(content="你是助手")]
for round in range(3):
    user_input = input("你:")
    messages.append(HumanMessage(user_input))
    resp = llm.invoke(messages)
    messages.append(resp)  # AIMessage 直接追加
    print(f"AI:{resp.content}")

五、三种传参格式对比

格式 代码 适合场景
字符串简写 llm.invoke("你好") 单次快速问答
消息对象列表 [SystemMessage(...), HumanMessage(...)] 多轮对话、需要附加 id/name、类型安全
字典格式 [{"role": "user", "content": "..."}] OpenAI 兼容最简洁、从 API 文档快速移植

选择建议

  • 学习/调试:消息对象列表(类型明确,IDE 补全好)
  • 快速验证:字符串简写
  • 生产环境:字典格式(简洁,易序列化)

六、总结

概念 一句话理解
Message 模型交互的输入输出单元,携带 role + content + metadata
SystemMessage 角色定义,给模型设定背景和风格
HumanMessage 用户输入,可含多模态内容
AIMessage 模型输出,可带 tool_calls(工具调用意图)
ToolMessage 工具返回结果,tool_call_id 必须与 AIMessage 对应
字符串简写 最简调用,自动包装为 HumanMessage
消息对象列表 多轮对话标配,类型安全
字典格式 OpenAI 兼容最简写,role 固定 4 值
多轮对话 把 AIMessage 直接追加回列表,模型即有"记忆"

核心认知:Message 是模型交互的"信封",role 标识谁说的,content 是说的内容。四种 Message 类型对应对话中的四个角色,ToolMessage 是工具调用闭环的关键一环。

更多推荐