第 4 章 · 消息:对话的基本单位
4.1 消息是什么?(定义)
消息:模型上下文的基本单位,代表对话中的一句话/一条信息。
每条消息包含三样东西:
- 角色:谁说的(system / user / assistant / tool)
- 内容:说了什么(文本、图片、音频等)
- 元数据:附加信息(消息 ID、token 用量等,可选)
为什么要用"消息列表"和模型对话?因为模型本身没有记忆------每次调用都是独立的。你发给它的消息列表,就是它能"看到"的全部世界。想让模型记住上文,就得把历史消息一起发过去。
4.2 四种消息类型(必背!)
| 消息类型 | 类名 | 角色 | 谁产生的 | 干什么用 |
|---|---|---|---|---|
| 系统消息 | SystemMessage |
system | 开发者 | 设定 AI 的人设和行为规则 |
| 人类消息 | HumanMessage |
user | 用户 | 用户的输入 |
| AI 消息 | AIMessage |
assistant | 模型 | 模型的回复(可含工具调用) |
| 工具消息 | ToolMessage |
tool | 你的代码 | 工具执行的结果(回传给模型) |
4.3 三种写消息的方式
python
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage, AIMessage, SystemMessage
model = init_chat_model("openai:gpt-4o-mini")
# ========== 方式一:直接传字符串(最简单) ==========
# 适合单次提问,字符串会自动被包装成一个 HumanMessage
response = model.invoke("什么是机器学习?")
# ========== 方式二:消息对象列表(推荐,最清晰) ==========
messages = [
SystemMessage("你是一位诗歌专家"), # 设定角色
HumanMessage("写一首关于春天的俳句"), # 用户提问
AIMessage("樱花瓣飘落..."), # 模型上次的回答(手动塞进历史)
HumanMessage("再写一首关于秋天的"), # 新问题
]
response = model.invoke(messages)
# ========== 方式三:字典格式(和 OpenAI 原生 API 一样的写法) ==========
messages = [
{"role": "system", "content": "你是一位诗歌专家"},
{"role": "user", "content": "写一首关于春天的俳句"},
{"role": "assistant", "content": "樱花瓣飘落..."},
{"role": "user", "content": "再写一首关于秋天的"},
]
response = model.invoke(messages)
三种方式效果相同,方式二类型安全、编辑器有提示,推荐日常使用。
4.4 系统消息:给 AI 立"人设"
SystemMessage 是你私下 给模型的指令,用户看不到。放位置于消息列表最前面:
python
system_msg = SystemMessage("""
你是一位资深 Python 开发工程师,精通 Web 框架。
回答时始终提供代码示例,并解释你的思路。
简洁但完整。
""")
messages = [system_msg, HumanMessage("怎么创建一个 REST API?")]
response = model.invoke(messages)
print(response.text)
写好系统提示的技巧:具体、可执行。"你是一个有帮助的助手"太笼统;"你是英语翻译专家,只把用户输入翻译成法语,不回答任何问题"就好得多。
4.5 AIMessage 的常用属性
invoke() 的返回值是一个 AIMessage 对象,不只是文本:
python
response = model.invoke("你好!")
print(type(response)) # <class 'AIMessage'>
print(response.text) # 回复的文本内容
print(response.id) # 这条消息的唯一 ID
print(response.usage_metadata) # token 用量统计(记账必备!)
# {'input_tokens': 8, 'output_tokens': 304, 'total_tokens': 312, ...}
usage_metadata 很重要------你调 API 是按 token 计费的,用它来监控成本。
4.6 用消息列表实现"手动记忆"
模型没记忆,但你可以自己维护对话历史:
python
from langchain.chat_models import init_chat_model
from langchain.messages import SystemMessage, HumanMessage
model = init_chat_model("openai:gpt-4o-mini")
# 对话历史:一开始只有系统设定
history = [SystemMessage("你是一个简洁的中文助手")]
# 第一轮
history.append(HumanMessage("我叫小明,我喜欢打篮球"))
response = model.invoke(history)
history.append(response) # 把模型的回复也存进历史
print(response.text)
# 第二轮:因为历史里有第一轮的记录,模型"记得"小明是谁
history.append(HumanMessage("我叫什么名字?我喜欢什么运动?"))
response = model.invoke(history)
print(response.text) # "你叫小明,你喜欢打篮球"
这就是"记忆"的本质:把历史消息塞进请求里。第 8 章会讲 LangChain 如何自动化这个过程(而且更聪明)。
📝 第 4 章练习
- 写一个 10 轮的
while True命令行聊天程序:用input()接收用户输入,维护消息历史,输入 "quit" 退出 - 打印每轮的
usage_metadata,算算 10 轮总共花了多少 token