前两篇已经完成了本地 Qwen3 部署,并分别使用 OpenAI Python SDK 和 LangChain 调用了这个模型。
调用 ChatOpenAI 时,既可以直接传入一个字符串,也可以传入一个消息列表。字符串适合一次独立的问题;当程序需要设置模型身份、保存历史对话或者区分用户与 AI 的发言时,就需要使用消息。
本文从最基础的 SystemMessage、HumanMessage 和 AIMessage 开始,依次介绍单轮对话、多轮对话、消息字段、其他消息类型和历史裁剪。代码只围绕消息本身展开,不提前实现 Tool、Agent 或其他复杂功能。
一次模型调用中,消息对象、消息列表和模型返回值的关系如下:

图中的 messages 列表由应用程序按对话顺序组装,再整体传给聊天模型。模型返回新的 AIMessage 后,是否把它保存到历史并用于下一轮,仍然由应用程序决定。
1. 为什么聊天模型需要消息
1.1 字符串只能表达当前问题
最简单的模型调用可以直接传入一个字符串:
response = llm.invoke("请用两句话介绍春天。")
这段代码能表达用户当前的问题,但没有明确告诉模型:
- 模型应该扮演什么角色。
- 回答时要遵守什么规则。
- 用户之前说过什么。
- 模型上一轮回答了什么。
如果只是一次独立问答,这些信息可能并不需要。对话开始出现规则和历史以后,一段普通字符串就不够用了。
1.2 消息列表保存一段对话
例如,用户先说:
我最喜欢的水果是苹果。
下一轮再问:
我最喜欢的水果是什么?
如果模型只看到第二句话,它无法知道答案。应用程序需要把前面的对话一起传给模型:
系统:你是一个友好的聊天助手。
用户:我最喜欢的水果是苹果。
AI:好的,我记住了,你最喜欢的水果是苹果。
用户:我最喜欢的水果是什么?
这就是消息列表的作用:按时间顺序保存模型当前需要看到的上下文。
1.3 一条消息包含什么
一条消息通常包含三类信息:
| 信息 | 作用 |
|---|---|
| 消息类型或角色 | 说明这句话来自系统、用户、AI 还是工具 |
| content | 保存消息正文,例如文字或其他内容块 |
| 元数据 | 保存消息 ID、模型信息、Token 用量等附加数据 |
初学阶段最常见的 content 是字符串。LangChain 的消息也可以承载图片、音频、文件等内容块,但是否能真正处理这些内容,还取决于所连接的模型和服务是否支持多模态输入。本文只使用文字消息。
2. 运行环境与代码目录
代码位于:
~/Documents/git/blog/source/_posts/llm_learning/langchain/p04_messages
当前目录包含八个示例:
p04_messages/
├── 01_message_objects_basic.py
├── 02_single_turn_messages.py
├── 03_multi_turn_messages.py
├── 04_json_role_messages.py
├── 05_message_name_metadata.py
├── 06_other_message_types.py
├── 07_keep_recent_messages.py
└── 08_invoke_model_with_messages.py
进入项目并激活 Python 3.12 虚拟环境:
cd ~/Documents/git/blog/source/_posts/llm_learning
source .venv/bin/activate
检查本篇直接使用的 langchain-core:
python -c 'import importlib.metadata as m; print(m.version("langchain-core"))'
本文固定使用的 langchain-core 版本为:
1.0.2
前七个脚本只创建和打印消息,不需要启动本地模型服务。第八个脚本会真正调用 Qwen3,运行前需要先启动第二篇文章中的 mlx_lm.server。
3. 三种基础消息类型
3.1 SystemMessage:系统消息
SystemMessage 用来给模型提供初始规则和上下文,通常放在消息列表最前面。
适合放在系统消息中的内容包括:
| 内容 | 例子 |
|---|---|
| 身份 | 你是一个耐心的生活小助手 |
| 回答语言 | 请使用中文回答 |
| 回答风格 | 回答要简洁,适合初学者阅读 |
| 行为边界 | 不确定时说明原因,不要编造 |
| 输出要求 | 用三句话回答,先给结论再解释 |
例如:
SystemMessage(content="你是一个友好的中文助手。")
系统消息不是用户问题,也不是程序中的强制校验。它会引导模型的行为,但不能像 if 判断或数据库约束一样保证模型绝对服从。
3.2 HumanMessage:用户消息
HumanMessage 表示用户输入的问题、指令或数据。
HumanMessage(content="请用两句话介绍春天。")
它可以表示不同形式的请求:
请介绍一种适合早餐的食物。
把下面这段话总结成三点。
帮我写一句生日祝福。
在 OpenAI 兼容接口中,用户角色通常写作 user;在 LangChain 消息对象中,对应的类名是 HumanMessage,message.type 为 human。
3.3 AIMessage:AI 消息
AIMessage 表示模型返回的消息。
AIMessage(content="春天是万物复苏的季节。")
调用 LangChain 聊天模型后,llm.invoke() 通常就会返回一个 AIMessage。它不只可能包含文本,还可能带有:
| 字段 | 作用 |
|---|---|
| content | 模型生成的正文 |
| tool_calls | 模型提出的工具调用请求 |
| usage_metadata | 输入、输出和总 Token 数量 |
| response_metadata | 模型名、结束原因等响应信息 |
| id | 消息或模型响应 ID |
本文只使用 content。工具调用会在后续专门学习 Tool 时再展开。
3.4 创建三种消息对象
代码文件为 01_message_objects_basic.py:
# 这个文件演示 LangChain 中最基础的三种消息对象:系统消息、用户消息、AI 消息。
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
# SystemMessage 通常用来告诉模型:你是谁、要遵守什么规则、回答风格是什么。
system_message = SystemMessage(content="你是一个耐心的生活小助手。")
# HumanMessage 表示用户输入的问题、需求或指令。
human_message = HumanMessage(content="请用一句话介绍一种适合早餐的食物。")
# AIMessage 表示模型已经回答过的内容,常用于保存历史对话。
ai_message = AIMessage(content="燕麦粥营养丰富、做法简单,是一种适合早餐的食物。")
messages = [system_message, human_message, ai_message]
print("三种基础消息对象:")
for message in messages:
print("-" * 40)
print("消息类型:", type(message).__name__)
print("消息内容:", message.content)
print("消息 type:", message.type)
运行:
python langchain/p04_messages/01_message_objects_basic.py
真实输出:
三种基础消息对象:
----------------------------------------
消息类型: SystemMessage
消息内容: 你是一个耐心的生活小助手。
消息 type: system
----------------------------------------
消息类型: HumanMessage
消息内容: 请用一句话介绍一种适合早餐的食物。
消息 type: human
----------------------------------------
消息类型: AIMessage
消息内容: 燕麦粥营养丰富、做法简单,是一种适合早餐的食物。
消息 type: ai
这里同时打印了 Python 类名和 LangChain 的 message.type。它们与 OpenAI 兼容接口中常见的 role 对应关系如下:
| OpenAI 兼容 role | LangChain 类 | LangChain message.type |
|---|---|---|
| system | SystemMessage | system |
| user | HumanMessage | human |
| assistant | AIMessage | ai |
不要把 role 和 message.type 当成同一个字段。它们属于不同表示方式,只是表达的角色含义相互对应。
4. 单轮对话
单轮对话表示用户提出一次问题,模型返回一次回答。最常见的输入结构是:
SystemMessage + HumanMessage
代码文件为 02_single_turn_messages.py:
# 这个文件演示单轮对话:一次用户提问,加上必要的系统规则。
from langchain_core.messages import HumanMessage, SystemMessage
# 单轮对话通常包含一条 system 消息和一条 human 消息。
messages = [
SystemMessage(content="你是一个友好的中文助手。"),
HumanMessage(content="请用两句话介绍春天。"),
]
print("单轮对话消息列表:")
for index, message in enumerate(messages, start=1):
print(f"{index}. {type(message).__name__}: {message.content}")
print("\n说明:")
print("真正调用聊天模型时,会把这个 messages 列表整体传给模型。")
运行:
python langchain/p04_messages/02_single_turn_messages.py
真实输出:
单轮对话消息列表:
1. SystemMessage: 你是一个友好的中文助手。
2. HumanMessage: 请用两句话介绍春天。
说明:
真正调用聊天模型时,会把这个 messages 列表整体传给模型。
如果把这个列表交给第二篇创建的 ChatOpenAI,写法就是:
response = llm.invoke(messages)
模型返回的 response 是一个 AIMessage。
5. 多轮对话
5.1 模型不会自动记住上一轮
大模型每次调用都只处理当前收到的输入。网页聊天工具看起来能够“记住”前面的内容,是因为应用程序保存了历史消息,并在下一次调用时重新发送给模型。
一个基本的多轮消息列表通常是:
SystemMessage
HumanMessage
AIMessage
HumanMessage
AIMessage
...
消息顺序不能打乱,因为模型会按照这个顺序理解对话过程。
下面这张图展示了两次请求之间真正发生的事情:第一轮消息由应用保存,第二轮再与当前问题一起发送给模型。

第二次请求不是只发送“我最喜欢的水果是什么?”,而是重新发送系统消息、第一轮用户消息、第一轮 AI 回答和当前问题。模型能够回答“苹果”,依赖的是这次请求中包含的完整上下文。
5.2 构造多轮消息列表
代码文件为 03_multi_turn_messages.py:
# 这个文件演示多轮对话:把前面用户问过的话和 AI 回答过的话继续放回 messages。
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
# 多轮对话不是模型自动记住历史,而是我们把历史消息再次传给模型。
messages = [
SystemMessage(content="你是一个友好的聊天助手。"),
HumanMessage(content="我最喜欢的水果是苹果。"),
AIMessage(content="好的,我记住了,你最喜欢的水果是苹果。"),
HumanMessage(content="我最喜欢的水果是什么?"),
]
print("多轮对话消息列表:")
for index, message in enumerate(messages, start=1):
print(f"{index}. {message.type}: {message.content}")
print("\n说明:")
print("模型需要看到前面的消息,才知道用户最喜欢的水果是苹果。")
运行:
python langchain/p04_messages/03_multi_turn_messages.py
真实输出:
多轮对话消息列表:
1. system: 你是一个友好的聊天助手。
2. human: 我最喜欢的水果是苹果。
3. ai: 好的,我记住了,你最喜欢的水果是苹果。
4. human: 我最喜欢的水果是什么?
说明:
模型需要看到前面的消息,才知道用户最喜欢的水果是苹果。
前三条消息表示历史,最后一条 HumanMessage 表示当前问题。
5.3 把多轮消息真正传给本地模型
前面的示例只打印消息。下面使用 08_invoke_model_with_messages.py 把同样的多轮消息真正传给本地 Qwen3:
# 这个文件演示把多轮 LangChain 消息列表真正传给本地 Qwen3 模型。
# 运行前需要先按照第二篇文章启动 mlx_lm.server。
import httpx
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
from langchain_openai import ChatOpenAI
# 创建 LangChain 聊天模型,并连接本地 OpenAI 兼容接口。
llm = ChatOpenAI(
base_url="http://127.0.0.1:18080/v1",
api_key="not-needed",
model="default_model",
temperature=0,
max_tokens=32,
http_client=httpx.Client(trust_env=False),
)
# 前三条消息表示上一轮对话,最后一条消息是当前问题。
messages = [
SystemMessage(content="你是一个友好的中文助手。"),
HumanMessage(content="我最喜欢的水果是苹果。"),
AIMessage(content="好的,我记住了,你最喜欢的水果是苹果。"),
HumanMessage(content="我最喜欢的水果是什么?只回答水果名称。"),
]
# 把完整消息列表交给模型,返回值是 AIMessage。
response = llm.invoke(messages)
print("返回消息类型:", type(response).__name__)
print("模型回答:", response.content)
运行前先在另一个终端启动模型服务:
"$VIRTUAL_ENV/bin/python" -m mlx_lm server \
--model model \
--host 127.0.0.1 \
--port 18080 \
--chat-template-args '{"enable_thinking": false}'
然后运行:
python langchain/p04_messages/08_invoke_model_with_messages.py
本次真实输出:
PyTorch was not found. Models won't be available and only tokenizers, configuration and file/data utilities can be used.
返回消息类型: AIMessage
模型回答: 苹果
模型回答“苹果”,说明它确实读取了前面的历史消息。返回类型是 AIMessage,说明 LangChain 又把本次模型回答包装成了一条新的 AI 消息。
真实程序通常会把这条 response 追加到消息列表:
messages.append(response)
下一轮再加入新的 HumanMessage,就形成了持续增长的多轮对话。
6. 字典消息与 LangChain 消息对象
OpenAI 兼容接口通常使用字典表示消息:
{"role": "user", "content": "你好,很高兴认识你。"}
LangChain 可以使用对应的消息对象:
HumanMessage(content="你好,很高兴认识你。")
代码文件为 04_json_role_messages.py:
# 这个文件演示字典消息和 LangChain 消息对象之间的对应关系。
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
# OpenAI 兼容接口常见的是这种 JSON / dict 风格。
json_messages = [
{"role": "system", "content": "你是一个友好的 AI 助手。"},
{"role": "user", "content": "你好,很高兴认识你。"},
{"role": "assistant", "content": "你好,我也很高兴认识你。"},
]
# LangChain 也可以使用更明确的消息对象。
object_messages = [
SystemMessage(content="你是一个友好的 AI 助手。"),
HumanMessage(content="你好,很高兴认识你。"),
AIMessage(content="你好,我也很高兴认识你。"),
]
print("字典消息:")
for message in json_messages:
print(message)
print("\nLangChain 消息对象:")
for message in object_messages:
print(type(message).__name__, message.content)
print("\n对应关系:")
print("system -> SystemMessage")
print("user / human -> HumanMessage")
print("assistant / ai -> AIMessage")
运行:
python langchain/p04_messages/04_json_role_messages.py
真实输出:
字典消息:
{'role': 'system', 'content': '你是一个友好的 AI 助手。'}
{'role': 'user', 'content': '你好,很高兴认识你。'}
{'role': 'assistant', 'content': '你好,我也很高兴认识你。'}
LangChain 消息对象:
SystemMessage 你是一个友好的 AI 助手。
HumanMessage 你好,很高兴认识你。
AIMessage 你好,我也很高兴认识你。
对应关系:
system -> SystemMessage
user / human -> HumanMessage
assistant / ai -> AIMessage
两种写法表达的是同一段对话。字典格式接近 HTTP 接口原始数据,消息对象则更适合继续使用 LangChain 的模板、模型和消息处理能力。
7. 消息中的可选字段
除了 content,消息还可以包含可选字段。
| 字段 | 作用 |
|---|---|
| name | 标识同一种消息类型下的不同发言人 |
| id | 保存消息唯一标识,便于日志和追踪 |
| additional_kwargs | 保存模型服务商返回的额外字段 |
| response_metadata | 保存模型名、结束原因等响应信息 |
| usage_metadata | 保存输入、输出和总 Token 数量 |
代码文件为 05_message_name_metadata.py:
# 这个文件演示消息里的可选字段:name、id、additional_kwargs、response_metadata。
from langchain_core.messages import AIMessage, HumanMessage
# name 可以标识同一种角色下的不同说话人。
human_message = HumanMessage(
content="我叫小明,很高兴认识你。",
name="小明",
id="user-message-001",
)
# additional_kwargs 常用来保存模型服务商返回的额外字段。
# response_metadata 常用来保存 token、模型名、结束原因等响应元数据。
ai_message = AIMessage(
content="你好,小明,我也很高兴认识你。",
id="ai-message-001",
additional_kwargs={"example_extra_field": "这里演示额外字段"},
response_metadata={"model_name": "local-qwen3-demo", "finish_reason": "stop"},
)
print("带 name 的用户消息:")
print(human_message)
print("\n带元数据的 AI 消息:")
print(ai_message)
print("\n单独读取字段:")
print("human_message.name =", human_message.name)
print("ai_message.response_metadata =", ai_message.response_metadata)
运行:
python langchain/p04_messages/05_message_name_metadata.py
真实输出:
带 name 的用户消息:
content='我叫小明,很高兴认识你。' additional_kwargs={} response_metadata={} name='小明' id='user-message-001'
带元数据的 AI 消息:
content='你好,小明,我也很高兴认识你。' additional_kwargs={'example_extra_field': '这里演示额外字段'} response_metadata={'model_name': 'local-qwen3-demo', 'finish_reason': 'stop'} id='ai-message-001'
单独读取字段:
human_message.name = 小明
ai_message.response_metadata = {'model_name': 'local-qwen3-demo', 'finish_reason': 'stop'}
这些字段不是每条消息都必须提供。name 是否会被模型服务使用也取决于服务商;response_metadata 和 usage_metadata 通常由模型响应填充。初学阶段先掌握消息类型和 content,再根据实际需要读取元数据。
8. 其他消息类型
SystemMessage、HumanMessage 和 AIMessage 足以完成普通文本对话。LangChain 还提供了其他消息类型,用于工具结果、自定义角色、流式输出和消息管理。
| 类型 | 作用 | 本文范围 |
|---|---|---|
| ToolMessage | 把一次工具执行结果返回给模型 | 只理解结构 |
| FunctionMessage | 表示函数执行结果的消息格式 | 了解兼容用途 |
| ChatMessage | 使用任意自定义 role | 简单展示 |
| AIMessageChunk | 表示流式输出中的一段 AI 内容 | 演示合并 |
| HumanMessageChunk | 用户消息分块类型 | 了解名称 |
| SystemMessageChunk | 系统消息分块类型 | 了解名称 |
| ToolMessageChunk | 工具消息分块类型 | 了解名称 |
| RemoveMessage | 表示从消息状态中删除指定消息 | 只理解概念 |
代码文件为 06_other_message_types.py:
# 这个文件概念性展示其他消息类型:ToolMessage、FunctionMessage、ChatMessage、消息 chunk。
from langchain_core.messages import (
AIMessageChunk,
ChatMessage,
FunctionMessage,
ToolMessage,
)
# ToolMessage 表示工具执行后的结果,通常要和 AIMessage 中的 tool_call_id 对上。
tool_message = ToolMessage(
content="北京今天晴,气温 25 摄氏度。",
tool_call_id="call_weather_001",
)
# FunctionMessage 是早期函数调用格式中常见的消息类型。
function_message = FunctionMessage(
name="get_weather",
content="北京今天晴,气温 25 摄氏度。",
)
# ChatMessage 可以自定义 role,普通初学阶段更常用 System/Human/AIMessage。
chat_message = ChatMessage(
role="teacher",
content="今天我们来聊聊周末计划。",
)
# Chunk 表示流式输出中的一小段内容,多个 chunk 可以拼成完整 AIMessage。
chunk1 = AIMessageChunk(content="你好,")
chunk2 = AIMessageChunk(content="很高兴认识你。")
merged_chunk = chunk1 + chunk2
print("ToolMessage:")
print(tool_message)
print("\nFunctionMessage:")
print(function_message)
print("\nChatMessage:")
print(chat_message)
print("\nAIMessageChunk 合并结果:")
print(merged_chunk)
运行:
python langchain/p04_messages/06_other_message_types.py
真实输出:
ToolMessage:
content='北京今天晴,气温 25 摄氏度。' tool_call_id='call_weather_001'
FunctionMessage:
content='北京今天晴,气温 25 摄氏度。' additional_kwargs={} response_metadata={} name='get_weather'
ChatMessage:
content='今天我们来聊聊周末计划。' additional_kwargs={} response_metadata={} role='teacher'
AIMessageChunk 合并结果:
content='你好,很高兴认识你。' additional_kwargs={} response_metadata={}
ToolMessage.tool_call_id 必须对应模型提出的那次工具调用。本文只是手动创建对象,不实现工具执行流程。AIMessageChunk 则常见于 llm.stream(),多个 chunk 可以使用 + 合并;合并结果仍是 AIMessageChunk,只是其中的 content 已经拼接起来。
9. 历史消息太长怎么办
多轮对话会不断追加 HumanMessage 和 AIMessage。历史越长,需要发送给模型的 Token 越多,并且可能超过模型上下文窗口。
最简单的教学方案是:保留系统消息,再保留最近 N 轮用户与 AI 对话。
代码文件为 07_keep_recent_messages.py:
# 这个文件演示如何手动保留最近 N 轮对话,避免历史消息无限增长。
def keep_recent_messages(messages, max_pairs=2):
"""保留 system 消息和最近 N 轮 user/assistant 对话。"""
# system 消息通常是全局规则,应该保留下来。
system_messages = [m for m in messages if m["role"] == "system"]
# 对话消息指用户和助手之间来回产生的消息。
conversation_messages = [m for m in messages if m["role"] != "system"]
# 一轮对话通常包含 user + assistant 两条消息,所以 N 轮就是 N * 2 条。
recent_messages = conversation_messages[-(max_pairs * 2) :]
return system_messages + recent_messages
messages = [
{"role": "system", "content": "你是一个友好的聊天助手。"},
{"role": "user", "content": "我叫小明。"},
{"role": "assistant", "content": "你好,小明。"},
{"role": "user", "content": "我喜欢吃苹果。"},
{"role": "assistant", "content": "好的,我记住了你喜欢吃苹果。"},
{"role": "user", "content": "我周末想去公园。"},
{"role": "assistant", "content": "祝你周末在公园玩得开心。"},
]
recent_messages = keep_recent_messages(messages, max_pairs=2)
print("原始消息数量:", len(messages))
print("裁剪后消息数量:", len(recent_messages))
print("\n裁剪后的消息:")
for message in recent_messages:
print(message)
运行:
python langchain/p04_messages/07_keep_recent_messages.py
真实输出:
原始消息数量: 7
裁剪后消息数量: 5
裁剪后的消息:
{'role': 'system', 'content': '你是一个友好的聊天助手。'}
{'role': 'user', 'content': '我喜欢吃苹果。'}
{'role': 'assistant', 'content': '好的,我记住了你喜欢吃苹果。'}
{'role': 'user', 'content': '我周末想去公园。'}
{'role': 'assistant', 'content': '祝你周末在公园玩得开心。'}
最早一轮“我叫小明”被删除,因此模型之后也不会再知道这个名字。裁剪能够控制长度,但会丢失信息。
这个函数只是帮助初学者理解原理,它假设每一轮严格由一条 user 和一条 assistant 组成。真实对话如果包含工具消息、连续用户消息或尚未回答的问题,就不能简单地每两条切一次。
10. 初学者常见误区
10.1 把多轮对话理解成模型自己的记忆
模型不是自动记住历史,而是应用程序把历史消息再次发送给模型。没有把旧消息放入当前请求,模型就看不到旧内容。
10.2 把 SystemMessage 当成绝对命令
系统消息用于引导模型,不是安全边界。真正不能违反的权限、输入校验和业务规则仍然要由普通程序代码处理。
10.3 混淆 role、type 和 Python 类名
user、human 和 HumanMessage 都表示用户一方,但分别属于接口 role、LangChain type 和 Python 类名。编写代码时要看当前 API 需要哪种格式。
10.4 只保存用户消息,不保存 AIMessage
多轮历史必须包含双方已经说过的内容。如果只保存用户问题,模型无法知道自己上一轮回答了什么,也无法正确理解“刚才那个结果”之类的指代。
10.5 无限保存全部历史
历史消息会占用上下文窗口。消息越多,不代表回答一定越好;过多无关内容还可能干扰当前任务。需要根据应用需求保留、删除或整理历史。
10.6 认为 content 永远是字符串
本文为了教学只使用字符串。LangChain 消息还可以使用内容块表示图片、音频和文件,但客户端、模型服务和模型本身必须同时支持相应格式,不能只修改 content 就认为多模态调用一定可用。
11. 小结
消息是 LangChain 向聊天模型提供上下文的基本单位。系统消息负责提供初始规则,用户消息表示当前输入,AI 消息保存模型返回内容;把这些消息按时间顺序组成列表,就能表达单轮或多轮对话。
本篇还验证了一个关键事实:将历史 HumanMessage 和 AIMessage 一起传给本地 Qwen3 后,模型能够根据历史回答“苹果”,并返回新的 AIMessage。
下一篇将继续学习提示词模板,解决消息中的固定内容、变量和历史占位符应该怎样组织和复用。
12. 最终知识整理
| 知识点 | 需要掌握的结论 |
|---|---|
| SystemMessage | 给模型提供身份、规则和回答要求,但不是强制安全边界 |
| HumanMessage | 表示用户输入,对应 OpenAI 兼容接口中的 user |
| AIMessage | 表示模型输出,可包含文本、工具调用和响应元数据 |
| 单轮对话 | 常见结构是 SystemMessage + HumanMessage |
| 多轮对话 | 需要按顺序重新发送必要的历史 Human/AI 消息 |
| role 与 type | user/assistant 与 human/ai 含义对应,但字段体系不同 |
| 字典与消息对象 | 都能表达对话;消息对象更方便继续使用 LangChain 组件 |
| ToolMessage | 保存工具执行结果,并通过 tool_call_id 对应工具调用 |
| Message Chunk | 表示流式输出片段,可以合并成完整消息 |
| 消息元数据 | 可保存 ID、Token 用量、模型信息和结束原因 |
| 历史裁剪 | 可以控制上下文长度,但被删除的信息模型将无法再看到 |
| 本地模型实测 | 多轮消息列表成功传给 Qwen3,返回 AIMessage(content=”苹果”) |