1. 为什么不只传一个字符串?
聊天模型除了文本,还要知道谁说了什么。系统指令、用户输入、模型回复和工具结果有不同语义。统一的消息对象可以保留角色、内容、元数据和工具调用信息;供应商适配器再把这些消息转换为各家 API 需要的格式。
python
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
messages = [
SystemMessage(content="你是一位善于用例子解释概念的老师。"),
HumanMessage(content="用一句话解释 LangChain。"),
AIMessage(content="LangChain 是用于组合模型与应用逻辑的框架。"),
]
for message in messages:
print(message.type, "=>", message.content)
2. 用提示词模板产生消息
将变化的用户数据与固定指令分开,能降低字符串拼接错误,也让提示词更方便复用。
python
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages(
[
("system", "你是一个简洁的 Python 教师。"),
("human", "请解释 {concept},并给一个简短例子。"),
]
)
prompt_value = prompt.invoke({"concept": "函数"})
print(prompt_value.to_messages())
prompt.invoke 只是渲染模板,仍然不需要模型凭证。打印结果可见两条消息,其中 {concept} 已替换。
3. 何时才真正请求模型
本例需要基础包 langchain 和 OpenAI 集成包 langchain-openai。在独立的项目目录中选择一种包管理方式(不要混用以下两种);使用 uv 时可先用 uv init langchain-message-demo 创建项目,再进入目录执行:
bash
uv add langchain langchain-openai
bash
# macOS / Linux
python -m venv .venv
source .venv/bin/activate
python -m pip install langchain langchain-openai
powershell
# Windows PowerShell
python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install langchain langchain-openai
设置 OPENAI_API_KEY 环境变量(替换为你自己的密钥,不要把真实密钥写入文章或源码);然后按当前 shell 选择命令:
bash
# macOS / Linux
export OPENAI_API_KEY="你的密钥"
powershell
# Windows PowerShell
$env:OPENAI_API_KEY="你的密钥"
下面会访问在线 API 并可能产生费用;请先确认模型标识在你的服务商账号和区域可用。
python
from langchain.chat_models import init_chat_model
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
model = init_chat_model("openai:替换为当前服务商支持的模型标识")
prompt = ChatPromptTemplate.from_messages(
[("system", "你是 Python 助教。"), ("human", "用一句话解释 {topic}。")]
)
chain = prompt | model | StrOutputParser()
print(chain.invoke({"topic": "列表"}))
Prompt → Model → Parser 是常见模式:模板输出 PromptValue,模型输出 AI 消息,字符串解析器抽取文本内容。下文会深入理解 | 组合。
4. 源码设计解析:消息模板如何变成模型输入?
LangChain 的消息类型不只是给字符串附上一个 role 标签。BaseMessage 统一保存 content、additional_kwargs、response_metadata、消息 type 等字段;HumanMessage、AIMessage 和 SystemMessage 分别表达用户输入、模型输出和系统指令。比如模型输出中的工具调用信息可以保存在 AI 消息的 tool_calls 中,token 用量和模型名等信息可保存在 response_metadata 或标准化的 usage_metadata 中。这个统一对象让应用能处理比纯文本更丰富的对话数据。
ChatPromptTemplate.from_messages(...) 把一组消息模板转成模板对象。调用 prompt.invoke({...}) 时,模板会为每条消息填入对应变量,形成 PromptValue,而不是请求模型。下方是这一流程的简化形状:
python
prompt = ChatPromptTemplate.from_messages([
("system", "你是 Python 助教。"),
("human", "请解释 {topic}。"),
])
prompt_value = prompt.invoke({"topic": "列表"})
messages = prompt_value.to_messages()
只有执行到 model.invoke(...) 时,chat model 才把输入转换为内部 PromptValue,进入实际生成流程。它最终返回 AIMessage,而非一个必然是纯字符串的值。provider 集成负责将通用消息翻译为供应商 API 所需的消息格式,再将 API 响应转换回 LangChain 消息。因此业务可以围绕统一消息对象组织逻辑,供应商差异集中在适配器边界。
prompt | model | StrOutputParser() 的数据流是:模板接收输入字典并生成消息;模型接收消息并生成 AIMessage;解析器再从 AI 消息提取文本。如果后续还需要分析工具调用、token 用量或响应元数据,就应保留 AIMessage,不要过早转换成字符串。
源码位置(以下路径相对 LangChain 仓库根目录):
libs/core/langchain_core/messages/base.py:BaseMessage定义消息通用字段与序列化基础;HumanMessage、AIMessage等具体消息类型在相关模块中扩展角色语义。libs/core/langchain_core/prompts/chat.py:ChatPromptTemplate组织多条消息模板,并在format_messages中填入变量、生成消息列表。libs/core/langchain_core/language_models/chat_models.py:BaseChatModel.invoke将输入转成消息并返回AIMessage,基类还定义模型生成契约。libs/core/langchain_core/messages/ai.py:AIMessage承载模型回复、工具调用信息和 token 用量等元数据。
设计观察:模板、消息对象、模型调用和输出解析各司其职。若都挤在一个字符串拼接函数里,角色语义、工具调用、元数据就很难可靠传递;而单独保留消息边界,开发者可以在需要时选择何时转换成普通文本。
5. 常见误区
AIMessage并不意味着文本回复一定只有字符串,内容可能包含多模态块、工具调用等信息。- Prompt template 负责生成输入,不保证模型一定照做。
init_chat_model(...)创建模型对象通常不等于已经发起请求;真正请求发生在invoke等执行方法调用时。StrOutputParser是一种输出处理选择,不要在仍需读取 tool_calls 或元数据时过早丢弃消息结构。
小结
消息对象是聊天模型协作的共同数据格式,提示词模板把应用输入映射为消息,模型负责生成响应。这样的分层让应用逻辑不必直接操纵每个供应商的请求结构。