LangChain 消息与提示词模板全解析:从 Message 标准到 ChatPromptTemplate 实战

文章目录

  • 一、认识消息(Message)
    • [1.1 消息的内部结构](#1.1 消息的内部结构)
    • [1.2 消息的类型](#1.2 消息的类型)
    • [1.3 消息格式](#1.3 消息格式)
    • [1.4 举例](#1.4 举例)
    • [1.5 消息对象字段说明](#1.5 消息对象字段说明)
      • [1.5.1 SystemMessage 参数列表](#1.5.1 SystemMessage 参数列表)
      • [1.5.2 HumanMessage 参数列表](#1.5.2 HumanMessage 参数列表)
      • [1.5.3 AIMessage 参数列表](#1.5.3 AIMessage 参数列表)
      • [1.5.4 ToolMessage 参数列表(拓展)](#1.5.4 ToolMessage 参数列表(拓展))
    • [1.6 实战](#1.6 实战)
      • [1.6.1 对话历史管理](#1.6.1 对话历史管理)
      • [1.6.2 对话历史优化](#1.6.2 对话历史优化)
      • [1.6.3 多轮对话聊天机器人](#1.6.3 多轮对话聊天机器人)
    • [1.7 拓展 - 消息属性:content、content_blocks](#1.7 拓展 - 消息属性:content、content_blocks)
      • [1.7.1 content](#1.7.1 content)
      • [1.7.2 content_blocks](#1.7.2 content_blocks)
  • [二、提示词模板(Prompt Templates)](#二、提示词模板(Prompt Templates))
    • [2.1 为什么推荐提示词模板?](#2.1 为什么推荐提示词模板?)
    • [2.2 提示词机制演进](#2.2 提示词机制演进)
    • [2.3 ChatPromptTemplate 的使用](#2.3 ChatPromptTemplate 的使用)
      • [2.3.1 两种实例化方式](#2.3.1 两种实例化方式)
      • [2.3.2 模板调用的 3 种方式](#2.3.2 模板调用的 3 种方式)
      • [2.3.3 结合 LLM 调用](#2.3.3 结合 LLM 调用)
      • [2.3.4 更丰富的初始化参数类型](#2.3.4 更丰富的初始化参数类型)

消息(Message)是模型交互的最基本单元,提示词模板(Prompt Template)是构建可靠 AI 应用的基石。掌握这两者,才能写出结构清晰、易于维护的 LangChain 应用。


一、认识消息(Message)

大模型没有记忆,它的输出只和输入模型的内容有关(上下文)。很多大模型 API 服务也没有在服务端维护会话历史,是"无状态"的。因此,如果应用需要"记住"对话历史,需要在程序中维护消息列表。

在 LangChain 中,Message(消息)是模型交互的最基本单元 。它既代表模型接收到的输入(Input) ,也代表模型生成的输出(Output)。

每一轮与大模型的对话,都由一条或多条 Message 构成。每个 Message 不仅包含文字内容 ,还携带描述上下文状态的元信息(metadata),用于保持对话的一致性和可追踪性。比如,模型在多轮交互中理解"谁在说话"、"说了什么"、"这条信息属于哪一轮对话"。

LangChain 在 1.0 中提供了跨模型统一的 Message 标准。无论你使用的是 OpenAI、Anthropic、Gemini 还是本地模型,这一标准都能保持一致的行为。好处:

  • 兼容性强:不同模型的消息格式自动对齐
  • 可扩展性高:方便添加多模态内容或自定义字段
  • 可追踪性好:为 LangSmith 等调试工具提供一致的上下文数据结构

1.1 消息的内部结构

LangChain 的消息(Message)对象包含三种字段:

字段 说明
Role 消息所属的角色或类型,如 system、user、assistant
Content 消息内容
Metadata (可选)元数据,存储额外信息。如:消息 ID、响应时间、token 消耗量、消息标签等

1.2 消息的类型

LangChain 定义了很多消息类型,通过 role 区分。常用的有四种:

1、系统消息(SystemMessage)

也称为系统提示词,用于在对话开始时为模型设定角色、行为准则和上下文背景。它像是给 AI 助手的一份工作说明书,决定了其回答问题的风格、领域和专业范围。

json 复制代码
{"role": "system", "content": "你是个精通编程的软件架构师"}

2、用户消息(HumanMessage)

也称为用户提示词,在多轮对话中,它表示用户的一次输入。可以包含简单的文本问题,也可以是复杂的多模态内容(如图片、音频、文档等)。

json 复制代码
{"role": "user", "content": "你好啊~"}

3、助手(AI)消息(AIMessage)

代表模型的回复,包括生成的文本、工具调用、元数据等。

json 复制代码
{"role": "assistant", "content": "我也很高兴认识你"}

{
  "role": "assistant",
  "content": "",
  "tool_calls": [{
    "name": "get_weather",
    "args": {"location": "北京"},
    "id": "call_00_nUD2NC9QRN5Cg1GaoIkBJQ4s"
  }]
}

4、工具调用消息(ToolMessage)

工具调用结果匹配的消息类型。将此消息返回给模型,让模型基于这个结果继续生成回复。

json 复制代码
{"role": "tool", "content": "今天天气很好", "tool_call_id": "call_00_nUD2NC9QRN5Cg1GaoIkBJQ4s"}

为什么使用不同的消息类型?

  • 明确角色:清晰区分系统提示、用户输入和 AI 回复
  • 控制行为:通过 SystemMessage 精确控制 AI 的行为
  • 对话历史:构建完整的多轮对话上下文
  • 调试友好:更容易追踪和调试对话流程

1.3 消息格式

LangChain 支持两种消息格式:JSON 格式和对象格式。

角色 字典格式 对象格式 用途 示例
System {"role": "system", ...} SystemMessage(...) 设定 AI 的行为、角色、规则 "你是一个专业的数学老师"
User {"role": "user", ...} HumanMessage(...) 用户输入 "什么是微积分?"
Assistant {"role": "assistant", ...} AIMessage(...) AI 的回复 "微积分是研究变化率的数学分支..."
Tool {"role": "tool", ...} ToolMessage(...) 工具执行的结果 "今天北京天气晴朗,万里无云"

对象格式的完整导入:

python 复制代码
from langchain_core.messages import (
    HumanMessage,   # 用户消息
    AIMessage,      # AI 消息
    SystemMessage,  # 系统消息
    ToolMessage     # 工具返回消息
)

# 消息列表示例
messages = [
    SystemMessage(content="你是一个助手"),
    HumanMessage(content="你好"),
    AIMessage(content="你好!有什么可以帮你?"),
    HumanMessage(content="天气怎么样?"),
    AIMessage(content="让我查询一下..."),
    ToolMessage(content="北京:晴天", tool_call_id="call_123"),
    AIMessage(content="北京今天是晴天")
]

1.4 举例

举例1:JSON 格式

python 复制代码
messages = [
    {"role": "system", "content": "你是一个善于给出通俗易懂解释的AI助手"},
    {"role": "user", "content": "你好"},
    {"role": "assistant", "content": "你好!我能帮你什么?"},
    {"role": "user", "content": "什么是机器学习"}
]
response = model.invoke(messages)
print(response.content)

举例2:对象格式

python 复制代码
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage

messages = [
    SystemMessage("你是一个善于给出通俗易懂解释的AI助手"),
    HumanMessage("你好"),
    AIMessage("你好!我能帮你什么?"),
    HumanMessage("什么是机器学习"),
]
response = model.invoke(messages)
print(response.content)

两种格式输出效果一致,但对象格式更推荐,因为类型安全、编辑器自动补全。

1.5 消息对象字段说明

1.5.1 SystemMessage 参数列表

python 复制代码
# content:消息内容,字段名可以省略
SystemMessage("你是个善解人意的助手")
# 相当于
SystemMessage(content="你是个善解人意的助手")

1.5.2 HumanMessage 参数列表

python 复制代码
# content:消息内容,字段名可以省略
HumanMessage("你好啊~")
# 相当于
HumanMessage(content="你好啊~")

# metadata:元数据字段,可以有很多,自定义
HumanMessage(
    content="Hello!",
    name="alice",     # 可选,用户名
    id="msg_123",     # 可选,message 的 ID
)

name 和 id 都属于元数据字段 ,当消息类型相同,对消息进行区分。但不是所有模型都支持这一功能,是否支持取决于模型供应商,需要查看官方手册。

举例:多人对话场景中使用 name

python 复制代码
messages = [
    SystemMessage("你是一个信息抽取器。你会收到多条来自不同发言者的 user 消息。每条消息可能带有 name 字段。你的任务是:严格根据每条消息的 name 提取发言者及其观点,并输出 JSON。禁止使用"第一个人/第二个人"这种相对称呼。若某条消息没有 name,则输出 unknown。输出格式:{\"speakers\":[{\"name\":\"...\",\"claim\":\"...\"}]}"),
    HumanMessage(content="我认为 1+1=2", name="Bob"),
    HumanMessage(content="我认为 1+1>2", name="Tom"),
    HumanMessage(content="请列出谁说了什么,不要判断对错。", name="audience")
]
response = model.invoke(messages)
print(response.content)

CloseAI 平台输出:

json 复制代码
{"speakers":[{"name":"Bob","claim":"我认为 1+1=2"},{"name":"Tom","claim":"我认为 1+1>2"},{"name":"audience","claim":"请列出谁说了什么,不要判断对错。"}]}

OpenRouter 平台输出(未正确传递 name):

json 复制代码
{"speakers":[{"name":"unknown","claim":"我认为 1+1=2"},{"name":"unknown","claim":"我认为 1+1>2"}]}

说明 :模型加载了 name 传递的信息,这在多人对话场景很有用。但不同平台支持度不同。

1.5.3 AIMessage 参数列表

python 复制代码
# content:模型输出的原始内容,字段名可以省略
AIMessage("你好~")
# 相当于
AIMessage(content="你好~")

AIMessage 特有属性:

① response_metadata

LLM 的响应中附加元数据,根据不同模型会有不同,如可能会包含本次 token 使用量等信息。

② tool_calls

表示工具调用信息。当 LLM 决定调用工具时,在 AIMessage 中就会包含这个属性,没有工具调用则为空。

结构如下:

python 复制代码
tool_calls=[
    {
        'name': 'get_weather',              # 应调用的工具名
        'args': {'city': '杭州'},           # 调用工具的参数
        'id': 'call_00_gIXYOD1Q1OkEXmdDBqXR1578',  # 工具调用的唯一标识 ID
        'type': 'tool_call'
    },
    {'name': 'get_news', 'args': {}, 'id': 'call_01_jD3phD5PEaIZf0mVLhKt0861', 'type': 'tool_call'}
]

③ usage_metadata

用量信息。

举例:AIMessage 完整返回

python 复制代码
messages = [
    SystemMessage("你叫小智,是一名助人为乐的助手。"),
    HumanMessage("你好,好久不见,请介绍下你自己。")
]
response = model.invoke(messages)
rprint(response)

输出(节选):

python 复制代码
AIMessage(
    content='你好,好久不见!我叫小智,是一名助人为乐的助手...',
    response_metadata={
        'token_usage': {
            'completion_tokens': 118,
            'prompt_tokens': 34,
            'total_tokens': 152,
            ...
        },
        'model_provider': 'openai',
        'model_name': 'gpt-5.4-mini-2026-03-17',
        ...
    },
    tool_calls=[],
    usage_metadata={
        'input_tokens': 34,
        'output_tokens': 118,
        'total_tokens': 152,
        ...
    }
)

1.5.4 ToolMessage 参数列表(拓展)

python 复制代码
ToolMessage(
    content="<工具输出>",                         # 文件内容
    name="get_weather",                          # 工具名称
    tool_call_id="call_00_nUD2NC9QRN5Cg1GaoIkBJQ4s"  # 工具调用唯一 ID
)

重要 :ToolMessage 必须紧邻匹配的 AIMessage,且 tool_call_id 和前者 tool_calls 中的 id 一致。

举例:完整的工具调用流程(JSON 格式)

python 复制代码
def get_weather(city: str) -> str:
    return "不错哦~"

model_with_tools = model.bind_tools([get_weather])

ai_message = {
    "role": "assistant",
    "content": "",
    "tool_calls": [{
        "name": "get_weather",
        "args": {"location": "北京"},
        "id": "call_00_nUD2NC9QRN5Cg1GaoIkBJQ4s"
    }]
}

tool_message = {
    "role": "tool",
    "content": "今天北京天气晴朗,万里无云~",
    "tool_call_id": "call_00_nUD2NC9QRN5Cg1GaoIkBJQ4s"
}

messages = [
    {"role": "user", "content": "北京天气如何"},
    ai_message,
    tool_message
]

response = model.invoke(messages)
print(response.content)  # 今天北京天气晴朗,万里无云。

对象格式等价写法:

python 复制代码
ai_message = AIMessage(
    content=[],
    tool_calls=[{
        "name": "get_weather",
        "args": {"location": "北京"},
        "id": "call_00_nUD2NC9QRN5Cg1GaoIkBJQ4s"
    }]
)

tool_message = ToolMessage(
    content="今天北京天气晴朗,万里无云~",
    tool_call_id="call_00_nUD2NC9QRN5Cg1GaoIkBJQ4s"
)

messages = [
    HumanMessage(content="北京天气如何"),
    ai_message,
    tool_message
]

1.6 实战

1.6.1 对话历史管理

关键规则:每次调用必须传递完整的对话历史!

也就是说:

复制代码
第 1 轮:[system, user] → AI 回复 → 保存回复
第 2 轮:[system, user, assistant, user] → AI 回复 → 保存回复
第 3 轮:[system, user, assistant, user, assistant, user] → AI 回复

注意 :每次对话都要在原有的消息列表中添加新消息,不可重新创建新的列表。

错误举例 1❌:没传历史

python 复制代码
# 第一次
response1 = model.invoke("我叫张三")
# 第二次(没传历史)
response2 = model.invoke("我叫什么?")  # AI 不记得!

错误举例 2❌:重新创建列表

python 复制代码
conversation = [{"role": "user", "content": "问题1"}]
response1 = model.invoke(conversation)
conversation = [{"role": "user", "content": "问题2"}]  # 重新创建!
response2 = model.invoke(conversation)  # 丢失了历史

错误举例 3❌:忘记保存 AI 回复

python 复制代码
conversation = []
conversation.append({"role": "user", "content": "问题1"})
response1 = model.invoke(conversation)
# 忘记保存 response1.content!
conversation.append({"role": "user", "content": "问题2"})
response2 = model.invoke(conversation)  # AI 不知道之前的回答

正确做法✅:

python 复制代码
conversation = []

# 第一次
conversation.append({"role": "user", "content": "我叫张三"})
response1 = model.invoke(conversation)
# 关键:保存 AI 回复
conversation.append({"role": "assistant", "content": response1.content})

# 第二次(传递完整历史)
conversation.append({"role": "user", "content": "我叫什么?"})
response2 = model.invoke(conversation)  # AI 记得!

1.6.2 对话历史优化

问题:对话历史会越来越长,消耗大量 tokens 和成本。

解决方案:只保留最近 N 轮对话。具体的:

  • 总是保留 system 消息(定义角色)
  • 只保留最近 N 轮对话,丢弃更早的历史
python 复制代码
def keep_recent_messages(messages, max_pairs=3):
    """
    保留最近的 N 轮对话
    max_pairs: 保留的对话轮数(每轮 = user + assistant)
    """
    # 分离 system 和对话
    system_msgs = [m for m in messages if m.get("role") == "system"]
    conversation_msgs = [m for m in messages if m.get("role") != "system"]
    
    # 只保留最近的
    recent_msgs = conversation_msgs[-(max_pairs * 2):]
    
    # 返回:system + 最近对话
    return system_msgs + recent_msgs

测试:

python 复制代码
long_conversation = [
    {"role": "system", "content": "你是 Python 导师"}
]

# 第 1 轮
long_conversation.append({"role": "user", "content": "什么是列表?用一句解释"})
r1 = model.invoke(long_conversation)
long_conversation.append({"role": "assistant", "content": r1.content})

# 第 2 轮
long_conversation.append({"role": "user", "content": "列表和元组有什么区别?用一句解释"})
r2 = model.invoke(long_conversation)
long_conversation.append({"role": "assistant", "content": r2.content})

# 第 3 轮
long_conversation.append({"role": "user", "content": "什么是字典呢?用一句解释"})
r3 = model.invoke(long_conversation)
long_conversation.append({"role": "assistant", "content": r3.content})

print(f"原始消息数: {len(long_conversation)}")  # 7

# 优化:只保留最近 2 轮
optimized = keep_recent_messages(long_conversation, max_pairs=2)
print(f"优化后消息数: {len(optimized)}")  # 5

# 添加新的用户问题
optimized.append({"role": "user", "content": "我第一个问题问的是什么?"})

# 使用优化后的历史
response = model.invoke(optimized)
print(f"\nAI 回复: {response.content}")

输出:

复制代码
原始消息数: 7
优化后消息数: 5
保留的内容: system + 最近2轮对话
AI 回复: 你第一个问题问的是:**"列表和元组有什么区别?用一句解释"**

1.6.3 多轮对话聊天机器人

python 复制代码
from langchain.chat_models import init_chat_model
import os
from dotenv import load_dotenv

load_dotenv(override=True)

# 1. 基础配置
MODEL_NAME = "gpt-5.4-mini"
MAX_PAIRS_HISTORY = 10
EXIT_WORD = "quit"

# 2. 初始化模型
model = init_chat_model(
    model=MODEL_NAME,
    model_provider="openai",
    api_key=os.getenv("CLOSEAI_API_KEY"),
    base_url=os.getenv("CLOSEAI_BASE_URL")
)

# 3. 初始化消息列表
messages = [
    {
        "role": "system",
        "content": "你是一个耐心、友好的智能助手。我会用自然、清晰的方式回答用户问题。"
    }
]

# 4. 启动提示
print(f"✨ 请输入问题,输入 {EXIT_WORD} 结束对话\n")

# 5. 多轮对话主循环
i = 1
while True:
    print("\n", "=" * 10, f'-> 第 {i} 轮对话开始 <-', "=" * 10, "\n")
    user_input = input("🙋 请输入:")
    
    if user_input.lower() == EXIT_WORD:
        print("🌙 对话已结束,欢迎下次再来!")
        break
    
    # 追加用户消息
    messages.append({"role": "user", "content": user_input})
    
    # 流式输出模型回复
    print("🧚 助手:", end="", flush=True)
    reply_content = ""
    
    # 优化历史记忆
    memory_messages = keep_recent_messages(messages, max_pairs=MAX_PAIRS_HISTORY)
    
    # 流式调用
    for chunk in model.stream(memory_messages):
        if chunk.content:
            print(chunk.content, end="", flush=True)
            reply_content += chunk.content
    
    print("\n", "=" * 10, f'-> 第 {i} 轮对话结束 <-', "=" * 10, "\n")
    i += 1
    
    # 追加 AI 回复
    messages.append({"role": "assistant", "content": reply_content})

1.7 拓展 - 消息属性:content、content_blocks

1.7.1 content

消息的 content 可以理解为数据内容,它是弱类型的,支持字符串和列表(列表元素通常为字典)。

举例1:存储字符串

python 复制代码
from langchain.messages import HumanMessage

msg1 = HumanMessage(content="你好啊")
msg2 = HumanMessage("你好啊")
print(msg1)
print(msg2)

说明:当 content 内容只有字符串时,可以省略参数名称。

举例2:存储字典列表

如果需要发送的不只是文本,如多模态内容,则需要 content 的字典列表形式。

字典内容遵循模型供应商的 API 规范,以 OpenAI gpt-4.1 为例:

python 复制代码
import base64
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage

def encode_image(img_path, img_type='jpeg'):
    """将本地图片转换成 Base64 编码的 Data URI 字符串"""
    with open(img_path, "rb") as img_file:
        return f"data:image/{img_type};base64,{base64.b64encode(img_file.read()).decode('utf-8')}"

img_path = "image_test.png"
base64_image = encode_image(img_path)

response = model.invoke([
    HumanMessage(
        content=[
            {'type': 'text', 'text': '这张图里有什么?'},
            {
                'type': 'image_url',
                "image_url": base64_image,
            }
        ]
    )
])
print(response.content)

1.7.2 content_blocks

在 LangChain 1.x 中,content_blocks 是消息对象(BaseMessage)的一项重大升级 。它的核心目标是提供一种跨模型供应商、标准化的多模态数据结构。

过去,处理图片、音频、甚至是模型生成的"思维链(Reasoning)"内容时,不同供应商(OpenAI、Anthropic、Google 等)的 API 格式各异,导致开发者需要写大量的适配代码。content_blocks 的出现终结了这种混乱。

数据结构 :它是一个 list[TypedDict]。

统一格式 :每个 block 都有一个 type 字段,用于区分内容类型。

支持类型 :包括 text(文本)、image(图片)、audio(音频)、video(视频)、tool_call(工具调用)以及 reasoning(推理/思维链)。

① 输入格式化

对于复杂的对话(带图片或工具结果),建议使用 content_blocks 列表形式构建 HumanMessage 或 AIMessage。借助 content_blocks,我们可以用一套标准代码,无缝地在不同厂商的模型之间切换。

举例1:OpenAI 模型

python 复制代码
response = model.invoke([
    HumanMessage(
        content_blocks=[
            {'type': 'text', 'text': '这张图里有什么?'},
            {
                'type': 'image',
                'base64': base64_image,
                'mime_type': 'image/png',
            }
        ]
    )
])
print(response.content)

举例2:Anthropic 模型

python 复制代码
model = init_chat_model(
    model="claude-haiku-4-5",
    model_provider="openai",
    api_key=os.getenv("CLOSEAI_API_KEY"),
    base_url=os.getenv("CLOSEAI_BASE_URL")
)

response = model.invoke([
    HumanMessage(
        content_blocks=[
            {'type': 'text', 'text': '这张图里有什么?'},
            {
                'type': 'image',
                'base64': base64_image,
                'mime_type': 'image/png',
            }
        ]
    )
])
print(response.content)

关键价值:同一套代码,OpenAI 和 Anthropic 都能工作。

② 输出格式化

content_blocks 还可用于输出格式化。以 DeepSeek 的 deepseek-v4-flash 为例,其输出包含思考内容,后者位于 additional_kwargs 的 reasoning_content 字段下。

不同模型其输出格式可能不同,仅为提取思考内容,切换模型都可能需要更改代码,非常不方便。content_blocks 提供了统一的输出格式,可以将不同格式的响应统一为标准格式。

python 复制代码
model = init_chat_model(
    model="deepseek:deepseek-v4-flash",
    extra_body={"thinking": {"type": "enabled"}},
)

response = model.invoke("你好,一句话回答")

print('=' * 20, '-> response.content <-', '=' * 20)
print(response.content)

print('=' * 20, '-> response.content_blocks <-', '=' * 20)
print(response.content_blocks)

输出:

python 复制代码
==================== -> response.content <- ====================
你好,请说出您的问题,我会用一句话回答。

==================== -> response.content_blocks <- ====================
[
    {'type': 'reasoning', 'reasoning': '好的,用户说"一句话回答",那说明他希望我回答得简洁直接...'},
    {'type': 'text', 'text': '你好,请说出您的问题,我会用一句话回答。'}
]

说明 :content_blocks 是懒加载 的,即调用时才会解析。优先检查 response.content_blocks 而不是 response.content,特别是当你需要获取"思维链"或者"引用(Citations)"信息时。


二、提示词模板(Prompt Templates)

2.1 为什么推荐提示词模板?

在 LangChain 开发中,构造提示词既可以直接使用 Python 字符串拼接(如 f-string、format() 或 +),也可以使用 LangChain 提供的 PromptTemplate 或 ChatPromptTemplate。

举例1:字符串拼接方式

python 复制代码
# 字符串拼接
topic = "Python"
difficulty = "初学者"

# 难以维护,容易出错
prompt_str = f"你是一个{difficulty}级别的编程导师。请用简单易懂的语言解释{topic}。"
response = model.invoke(prompt_str)

优点✅:

  • 简单直接,上手快
  • 适合临时 demo
  • 无额外学习成本

缺点❌:

  • 可读性差(变量多时混乱)
  • 不易维护(修改容易出错)
  • 无变量校验(容易漏/拼错)
  • 难以支持复杂场景(多轮对话 / RAG / Few-shot)

举例2:提示词模板

python 复制代码
from langchain.prompts import PromptTemplate

topic = "Python"
difficulty = "初学者"

template = PromptTemplate.from_template(
    "你是一个{difficulty}级别的编程导师。请用简单易懂的语言解释{topic}。"
)

# 使用模板生成提示词
prompt = template.format(difficulty=difficulty, topic=topic)
response = model.invoke(prompt)

优点✅:

  • 结构清晰(变量占位)
  • 易维护、可复用
  • 自动变量校验(更安全)
  • 支持复杂场景(对话 / RAG / Agent)
  • 可与 LangChain 生态无缝集成
  • 便于调试与日志追踪

开发建议:

  • 小项目 / 临时用 → 字符串拼接
  • 正式开发 / AI 应用 → 提示词模板(必选)

2.2 提示词机制演进

LangChain 1.0 的架构变革中,核心的演进之一体现在 Prompt 机制上:一个结构化的、富含元数据的消息列表已经取代单一字符串,成为与模型交互的标准数据格式。

1、旧时代:LLM + PromptTemplate(输入与输出均为字符串)

  • 模型接口 :对应于 LangChain 中的 LLM 类,主要面向早期的文本补全模型
  • 工作方式 :模型接受一个单一的字符串作为输入,基于此预测并生成后续的文本内容(文本补全)
  • Prompt 工具:核心工具是 PromptTemplate。它的职责是接收一组变量,并通过模板渲染,最终输出一个完整的字符串
python 复制代码
from langchain.prompts import PromptTemplate

prompt_template = PromptTemplate.from_template(
    "请给我一个关于{topic}的{type}解释。"
)

prompt = prompt_template.format(type="详细", topic="量子力学")
print(prompt)

局限性:当我们需要用这种方式模拟多轮聊天时,开发者必须在字符串中手动拼接和伪造对话角色,例如:

复制代码
"Human:你好\nAI:你好!有什么我能帮忙的吗?\nHuman:..."

这种方式不仅导致 Prompt 的结构混乱、难以维护,也极易让模型混淆对话的边界与上下文,影响生成质量。

2、新时代:ChatModel + ChatPromptTemplate(输入与输出均为消息列表)

  • 模型接口:对应 LangChain 1.0 的主流接口 ChatModel
  • 工作方式 :现代聊天模型 API 已原生支持角色概念 。它们不再接受单一字符串,而是要求输入一个结构化的消息列表。为构建复杂、可靠的多轮对话智能体系统奠定了坚实的基础
  • Prompt 工具 :ChatPromptTemplate 因此成为 LangChain 1.0 中最核心的 Prompt 工具。它的职责是接收变量,并输出一个 List[BaseMessage](消息列表),该列表可直接传递给聊天模型

二者对比:

特性 PromptTemplate ChatPromptTemplate
输出格式 纯文本字符串 消息列表
角色支持 ❌ 无 ✅ system/user/assistant
对话历史 ❌ 不支持 ✅ 支持
适用场景 简单提示 聊天、对话、多轮交互

角色字符串对照:

角色字符串 含义 用途
"system" 系统消息 设定 AI 的行为、角色、规则
"user" / "human" 用户消息 用户的输入/问题
"assistant" / "ai" AI 消息 AI 的回复(用于对话历史)

ChatPromptTemplate 取代了生成字符串的 PromptTemplate,成为构建现代 LangChain 应用的首选工具。

2.3 ChatPromptTemplate 的使用

ChatPromptTemplate 是创建聊天消息列表的提示模板。它比普通 PromptTemplate 更适合处理多角色、多轮次的对话场景。支持 System / Human / AI 等不同角色的消息模板。

2.3.1 两种实例化方式

方式1(推荐):调用 from_messages()

该方法允许传入一个由元组(Tuple)构成的列表,列表中的每一个元组都代表一条具有特定角色的消息。

python 复制代码
from langchain_core.prompts import ChatPromptTemplate

chat_template = ChatPromptTemplate.from_messages([
    ("system", "你是一个有帮助的AI机器人,你的名字是{name}。"),
    ("human", "你好,最近怎么样?"),
    ("ai", "我很好,谢谢!"),
    ("human", "{user_input}"),
])

prompt = chat_template.invoke({"name": "小明", "user_input": "你叫什么名字?"})
print(prompt)

输出:

python 复制代码
messages=[
    SystemMessage(content='你是一个有帮助的AI机器人,你的名字是小明。', ...),
    HumanMessage(content='你好,最近怎么样?', ...),
    AIMessage(content='我很好,谢谢!', ...),
    HumanMessage(content='你叫什么名字?', ...)
]

方式2:使用实例初始化方法

python 复制代码
from langchain_core.prompts import ChatPromptTemplate

prompt_template = ChatPromptTemplate([
    ("system", "你是一个AI开发工程师. 你的名字是 {name}."),
    ("human", "你能开发哪些AI应用?"),
    ("ai", "我能开发很多AI应用, 比如聊天机器人, 图像识别, 自然语言处理等."),
    ("human", "{user_input}")
])

prompt = prompt_template.invoke({"name": "小谷AI", "user_input": "你能帮我做什么?"})

说明 :from_messages() 的底层,也是调用的类的 __init()__ 方法。

2.3.2 模板调用的 3 种方式

方式1:使用 invoke() --- 返回 ChatPromptValue

python 复制代码
prompt = prompt_template.invoke({"name": "小谷AI", "user_input": "你能帮我做什么?"})
print(type(prompt))
# <class 'langchain_core.prompt_values.ChatPromptValue'>
print(prompt)
print(len(prompt.messages))  # 4

方式2:使用 format() --- 返回字符串

python 复制代码
prompt = prompt_template.format(name="小谷AI", user_input="你能帮我做什么?")
print(type(prompt))
# <class 'str'>
print(prompt)
# System: 你是一个AI开发工程师. 你的名字是 小谷AI.
# Human: 你能开发哪些AI应用?
# AI: 我能开发很多AI应用...
# Human: 你能帮我做什么?

方式3:使用 format_messages() --- 返回消息列表

python 复制代码
prompt = prompt_template.format_messages(name="小谷AI", user_input="你能帮我做什么?")
print(type(prompt))
# <class 'list'>
print(prompt)
# [SystemMessage(...), HumanMessage(...), AIMessage(...), HumanMessage(...)]

2.3.3 结合 LLM 调用

python 复制代码
from dotenv import load_dotenv
from langchain_core.prompts import ChatPromptTemplate
import os
from langchain.chat_models import init_chat_model

# 1、提供大模型
load_dotenv(override=True)
model = init_chat_model(
    model="gpt-5.4-mini",
    model_provider="openai",
    api_key=os.getenv("CLOSEAI_API_KEY"),
    base_url=os.getenv("CLOSEAI_BASE_URL")
)

# 2、提供提示词
chat_prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个数学家,你可以计算任何算式"),
    ("human", "{text}"),
])

# 输入提示
prompt_value = chat_prompt.invoke({
    "text": "我今年18岁,我的舅舅今年38岁,我的爷爷今年72岁,我和舅舅一共多少岁了?"
})

# 3、结合提示词,调用大模型
output = model.invoke(prompt_value)
print(output.content)

输出:

复制代码
你今年 18 岁,舅舅今年 38 岁。
一共是:
18 + 38 = **56 岁**
所以,你和舅舅一共 **56 岁**。

2.3.4 更丰富的初始化参数类型

ChatPromptTemplate 的参数是列表类型,列表的元素可以是字符串、字典、字符串构成的元组、消息类型、提示词模板类型、消息提示词模板类型等。

类型1:str 列表类型(不推荐,因为默认角色都是 human)

python 复制代码
chat_template = ChatPromptTemplate.from_messages([
    "Hello, {name}!"  # 等价于 ("human", "Hello, {name}!")
])
messages = chat_template.invoke({"name": "小谷AI"})
# messages=[HumanMessage(content='Hello, 小谷AI!', ...)]

类型2:tuple 列表类型

python 复制代码
prompt = ChatPromptTemplate.from_messages([
    ("system", "你的名字是{role}."),
    ("human", "很高兴认识你"),
])
print(prompt.invoke({"role": "小智"}))
# messages=[SystemMessage(content='你的名字是小智.', ...), HumanMessage(content='很高兴认识你', ...)]

类型3:dict 列表类型

python 复制代码
prompt = ChatPromptTemplate.from_messages([
    {"role": "system", "content": "你的名字是{role}."},
    {"role": "human", "content": "很高兴认识你"},
])
print(prompt.invoke({"role": "小智"}))

类型4:Message 列表类型

python 复制代码
from langchain_core.messages import SystemMessage, HumanMessage

chat_prompt_template = ChatPromptTemplate.from_messages([
    SystemMessage(content="我是一个贴心的智能助手"),
    HumanMessage(content="我的问题是:人工智能英文怎么说?")
])
messages = chat_prompt_template.invoke({})

注意:在 XxxMessage 中

相关推荐
deepseek231 小时前
DeepSeek-V4.1-Flash 拆解:8B/16B 非对称激活,KV 缓存压到 890 字节/Token 才是 1M Agent 经济账
agent·kv缓存·deepseek·稀疏注意力·moe架构·v4.1-flash
BD_Marathon10 小时前
调用DeesSeek官网的DeepSeek模型
langchain
也非非也10 小时前
DeepSeek没发公告,但它把Agent装进了你的电脑
人工智能·开源·agent·deepseek·dsh
weixin_6665939910 小时前
从一句话建表看时空智能体的元数据治理架构——Harness、MCP、Skill如何协同
人工智能·agent·结构化数据·建库
DeepAgent11 小时前
AI Agent 工程实践(49):一次真实优化——从 Agent v1 到 v2
开发语言·人工智能·agent
素男12 小时前
对上的,和没对上的——两篇之间那条链
人工智能·agent·self-becoming·ai长期记忆·ai自我介绍
七夜zippoe13 小时前
多模态 Agent 入门:让 Agent 看懂图片、听懂语音、生成内容
ai·agent·多模态·看懂·听懂·生成内容
L@ncor13 小时前
第五章 基于低代码平台的智能体搭建 · 学习笔记(Coze / Dify / FastGPT / n8n)
笔记·学习·低代码·agent·prompt工程
SFLYQ14 小时前
dsh 插件 dsh-waker:唤醒专属你的 AI 员工
agent·deepseek