在上一篇文章中,我们完成了 LangChain 环境的搭建,并成功实现了对 DeepSeek 模型的第一次调用。当时我们使用的是最简单的方式:直接向模型传递一个字符串。
虽然这种方式可以让模型运转,但在构建真实的 AI 应用(如智能客服、Chatbot)时,它显得捉襟见肘。我们往往需要更精细地控制模型的行为:
设定角色:告诉 AI“你是谁”,是严谨的律师还是幽默的导游。
制定规则:规定哪些能说,哪些不能说,回答的格式是什么。
维护上下文:让 AI 记得几分钟前用户说了什么,实现真正的“对话”。
要实现这些功能,我们就必须深入理解 LangChain 的消息结构(Message Structure)。本篇文章将带你掌握模型调用中最核心的三个消息角色,学习如何控制模型性格,并手把手教你实现一个带历史记忆的命令行对话助手。
1. 为什么字符串输入还不够?
在 REST API 的原生调用中,我们通常需要构建复杂的 JSON 对象。LangChain 对此进行了抽象,引入了消息(Messages)的概念。
使用消息结构而非纯文本,核心优势在于语义化控制和状态管理。模型需要知道哪部分内容是开发者的指令,哪部分是用户的提问,哪部分是它自己之前的回答。
2. 核心消息角色详解
在 LangChain(以及大多数主流大模型 API)中,对话被抽象为一个消息列表。最常用的有三类消息:
| 角色 (Role) | LangChain 类 | 描述 |
| System | SystemMessage | 系统消息。由开发者预设,用于定义 AI 的身份、行为准则、输出格式、语气等。它是对话的基调,优先级最高。 |
| Human | HumanMessage | 用户消息。真实的终端用户输入的提问或需求。 |
| AI | AIMessage | AI 回复。模型生成的文本内容。在多轮对话中,需要将其捕获并重新塞回消息列表。 |
我们可以从langchain_core.messages导入这些类:
Python
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage2.1 SystemMessage:铸造 AI 的灵魂
SystemMessage是你给大模型下达的“底层指令”。它通常在对话的最开始发送一次,并在整个会话中持续生效(取决于模型的上下文窗口和注意力机制)。对应的 OpenAI API 角色是role: "system"。
常见用途:
限定角色:“你是一名专业的 Python 架构师。”
限定语气:“你的回答必须幽默、风趣,多用表情包。”
限定规则:“如果用户询问价格,请引导其联系销售,不要直接报价。”
限定输出:“所有回答必须是标准的 JSON 格式。”
代码示例:
Python
# 设定一个傲娇的 AI 助手 messages = [ SystemMessage(content="你是一个傲娇的机器人助手,虽然会回答用户问题,但语气要显得不情愿。"), HumanMessage(content="帮我解释一下什么是量子力学。"), ] # 模型可能会回复:哼,真麻烦,连这个都不懂...(然后开始解释)提示:在使用初期,无需构建极其复杂的 System Prompt。规则过多反而可能导致模型执行不稳定或产生冲突。
2.2 HumanMessage:传递用户意图
HumanMessage代表用户的输入。在实际项目中,它可能来自命令行 (input())、Web 前端的输入框、App 的聊天界面或者 API 请求参数。
Python
user_input = input(">> 用户:") human_message = HumanMessage(content=user_input)2.3 AIMessage:捕获模型的产出
AIMessage是调用model.invoke()后返回的对象。它包含了模型生成的文本。
在单轮对话中,我们通常只关心AIMessage.content。 但在多轮对话中,整个AIMessage对象(或至少其内容)必须被保存下来,并在下一次发起请求时,连同新的HumanMessage一起发送给模型。
多轮对话原理解析图:
如图所示,多轮对话本质上是不断滚雪球式地将历史消息列表发送给模型。模型本身是不具备记忆功能的(Stateless),记忆是由程序员通过维护这个列表来实现的。
3. 实战案例:构建带角色的命令行客服
光说不练假把式。我们结合SystemMessage和temperature参数,来实现一个真实的客服场景。
3.1 引入模型参数:Temperature
在调用模型时,temperature(温度)是一个非常关键的参数。它控制着模型输出的随机性和创造性。
Temperature = 0:完全确定性。相同输入永远返回相同输出。适合:代码生成、数学计算、知识库问答(RAG)。
Temperature ≈ 0.1 - 0.4:严谨、逻辑稳定、极少幻觉。适合:商务客服、金融报表解析。
Temperature ≈ 0.7 - 1.0:平衡创意与逻辑。适合:通用聊天、邮件撰写、文案摘要。
Temperature > 1.0:脑洞大开、容易瞎编。适合:写故事、写诗、创意激荡。
对于客服场景,我们需要稳定、礼貌,严禁瞎编,因此应设置较低的温度。
3.2 实战:基础客服助手 (01_customer_service.py)
3.2.1:单次对话助手
Python
import os from dotenv import load_dotenv from langchain.chat_models import init_chat_model from langchain_core.messages import SystemMessage, HumanMessage load_dotenv() model = init_chat_model( model='Pro/zai-org/GLM-5', model_provider='openai', api_key=os.getenv('SILICONFLOW_API_KEY'), base_url=os.getenv('SILICONFLOW_BASE_URL'), temperature=0.5 ) messages = [ SystemMessage(content="你是一个傲娇的机器人助手,虽然会回答用户问题,但语气要显得不情愿。"), HumanMessage(content="帮我解释一下什么是量子力学。"), ] response = model.invoke(messages) print(response.content)运行测试:观察模型是否使用了机器人语气,以及当你询问“什么是量子力学”时,它是否会根据 System 规则回复你。下面是我的运行结果。
3.2.2:多轮对话助手
4. 上下文管理优化:限制历史消息数量
上面的代码虽然实现了多轮对话,但存在一个致命缺陷:消息列表会无限增长。
随着对话的进行:
成本飙升:大模型的计费通常基于 Token 数量(输入+输出)。历史越长,每次请求的 Token 数越多。
速度变慢:模型处理长上下文需要更长的时间。
甚至报错:每个模型都有上下文窗口限制(e.g., 8K, 32K, 128K Token)。一旦超过限制,请求就会失败。
干扰严重:过久的、无关的历史消息可能会干扰模型对当前问题的判断。
因此,在实际工程中,我们必须进行上下文管理。最简单直接的方法是:只保留最近 N 轮对话。
4.1 实战:保留历史的助手
我们可以利用 Python list 的切片功能轻松实现这一点。通常一轮对话包含一门HumanMessage和一个AIMessage,如果我们想保留最近 3 轮对话,就需要保留最近 6 条消息。
同时,SystemMessage 通常不能被切掉,它必须始终处于列表的第一位。
Python
# ... (前面的初始化代码同上) ... # 这里我们要维护一个“滑动窗口”的思想 def get_messages_for_model(all_messages, limit=6): """ 确保 SystemMessage 始终在首位,并取最近 limit 条 Human/AI 消息 """ if len(all_messages) <= 1: # 只有 SystemMessage return all_messages system_msg = all_messages[0] # 取最近的 limit 条消息 (注意,不包括索引 0 的 system message) recent_msgs = all_messages[1:][-limit:] return [system_msg] + recent_msgs # ... (在 while 循环中) ... # 3. 将用户输入封装为 HumanMessage messages.append(HumanMessage(content=user_input)) # 4. 获取修剪后的消息列表用于发送 messages_to_send = get_messages_for_model(messages, limit=6) try: # 5. 调用模型时传入修剪后的列表 response = model.invoke(messages_to_send) # 6. 打印 AI 回复,并将 AI 回复也加入完整的历史 print(f">> 客服: {response.content}") messages.append(response) # 完整的历史还是要存的,方便查看或持久化 # ...5. 展望:更好的多轮对话写法
本篇文章中,我们手动维护了一个 list,并手动进行append和切片。这是理解原理的最佳方式。
但在 LangChain 的高级用法中,通常不建议这样手动操作。下一章在讲解ChatPromptTemplate(提示词模板)时,我们会介绍更优雅的写法,例如使用MessagesPlaceholder在模板中预留历史消息的位置:
Python
# 示意代码,下一章详述 prompt = ChatPromptTemplate.from_messages([ ("system", "你是一个专业的地理学家。"), MessagesPlaceholder(variable_name="chat_history"), # 这里用来动态插入历史消息 ("human", "{question}"), ])配合 LangChain 的RunnableWithMessageHistory(将在内存管理章节讲解),可以实现完全自动化的历史消息维护和修剪,无需手动写 Python code 去管理 list。
6. 总结
掌握消息结构是迈向 LangChain 高级开发的第一步。请务必牢记以下几点:
三剑客:
SystemMessage定人设,HumanMessage传意图,AIMessage捕产出。多轮对话本质:是状态的维护,是不断把完整的历史消息(雪球)发送给模型。
Temperature:决定模型严谨程度的关键按钮。客服用低温,创意用高温。
上下文管理:现实项目中必须限制历史长度,兼顾成本、速度和模型注意力。
如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、关注。在下一篇文章中,我们将深入探讨 LangChain 的另一个核心概念:PromptTemplate(提示词模板),教你如何像写代码一样解耦和管理复杂的提示词。