提示词模板

提示词模板

2026年9月21日·#编程学习/langchain学习笔记LangChain/AI·6858 字 34 分钟
浏览量加载中...
AI 摘要- DeepSeek

PromptTemplate 与 ChatPromptTemplate 的用法与取舍,以及从"字符串提示词"到"消息列表"的机制演进

封面图
提示词模板 - 封面图

为什么要用提示词模板#

构造提示词有两种方式:Python 字符串拼接(f-string、format()、+)和 LangChain 的 PromptTemplate / ChatPromptTemplate。

# 方式一:字符串拼接
topic = "Python"
difficulty = "初学者"
prompt_str = f"你是一个{difficulty}级别的编程导师。请用简单易懂的语言解释{topic}。"
response = model.invoke(prompt_str)
字符串拼接提示词模板
上手成本简单直接,零学习成本有一点学习成本,初期写法略复杂
可读性变量一多就混乱结构清晰(变量占位)
可维护性修改容易出错易维护、可复用
变量校验❌ 无(容易漏/拼错)✅ 自动校验(更安全)
复杂场景❌ 多轮对话 / RAG / Few-shot 难做✅ 支持对话 / RAG / Agent
生态集成—与 LangChain 生态无缝集成,便于调试与日志追踪
Tip

开发建议:小项目 / 临时 demo 用字符串拼接;正式开发做 AI 应用,提示词模板是必选。

PromptTemplate:生成字符串#

from langchain_core.prompts import PromptTemplate # ← 1.x 里必须从 langchain_core 导入
topic = "Python"
difficulty = "初学者"
template = PromptTemplate.from_template(
"你是一个{difficulty}级别的编程导师。请用简单易懂的语言解释{topic}。"
)
# 用 format() 填变量,得到最终提示词字符串
prompt = template.format(difficulty=difficulty, topic=topic)
response = model.invoke(prompt)
Warning

导入路径踩坑:课程讲义里写的是 from langchain.prompts import ...,但在 LangChain 1.x 上会直接报 ModuleNotFoundError: No module named 'langchain.prompts'(本机 langchain 1.2.12 实测)。 正确写法一律从 langchain_core 导入:from langchain_core.prompts import PromptTemplate, ChatPromptTemplate。

ChatPromptTemplate:生成消息列表(1.x 首选)#

from langchain_core.prompts import ChatPromptTemplate
prompt_template = ChatPromptTemplate([
("system", "你是一个AI开发工程师. 你的名字是 {name}."),
("human", "{user_input}"),
])
# invoke() 返回的是【消息列表】(可直接传给模型)
prompt = prompt_template.invoke({"name": "小谷AI", "user_input": "你能帮我做什么?"})
print(prompt)

实例化有两种方式:from_messages()(推荐) 和 初始化方法传 messages 参数;传入的都是”元组构成的列表”,每个元组是 (role, content)。

角色字符串对照#

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

PromptTemplate 用 format() 返回字符串;ChatPromptTemplate 用 invoke() 返回消息列表——这个差别决定了它能直接喂给聊天模型,也因此成为 1.x 的主力工具。

模板调用的 3 种方式#

同一个模板,三个方法三副面孔,先记住返回类型,就知道该在什么场合用哪个:

方式返回类型用途
invoke({...})ChatPromptValue最常用,里面装着 messages,可直接传给模型
format(...)str想看”拼出来长什么样”,或要落库/打日志
format_messages(...)list要一个真正的 List[BaseMessage](自己再拼装)
from langchain_core.prompts import ChatPromptTemplate
prompt_template = ChatPromptTemplate([
("system", "你是一个AI开发工程师. 你的名字是 {name}."),
("human", "你能开发哪些AI应用?"),
("ai", "我能开发很多AI应用, 比如聊天机器人, 图像识别, 自然语言处理等."),
("human", "{user_input}"),
])
# 方式1:invoke() → ChatPromptValue
prompt = prompt_template.invoke({"name": "小谷AI", "user_input": "你能帮我做什么?"})
print(type(prompt)) # <class 'langchain_core.prompt_values.ChatPromptValue'>
print(len(prompt.messages)) # 4
# 方式2:format() → 字符串
prompt_str = prompt_template.format(name="小谷AI", user_input="你能帮我做什么?")
print(type(prompt_str)) # <class 'str'>

format() 打印出来长这样——角色名 + 内容一行行拼好,就是个”给人看”的预览:

System: 你是一个AI开发工程师. 你的名字是 小谷AI.
Human: 你能开发哪些AI应用?
AI: 我能开发很多AI应用, 比如聊天机器人, 图像识别, 自然语言处理等.
Human: 你能帮我做什么?
# 方式3:format_messages() → 消息列表
messages = prompt_template.format_messages(name="小谷AI", user_input="你能帮我做什么?")
print(type(messages)) # <class 'list'>
Note

三种方式参数写法不一样:invoke() 收一个字典(invoke({...})),format() / format_messages() 收关键字参数(format(name=..., user_input=...))。另外还有个 format_prompt(),返回的也是 ChatPromptValue——和 invoke() 等价,只是名字更”老派”。 实测(本机 langchain-core 1.2.18):invoke → ChatPromptValue、format → str、format_messages → list、format_prompt → ChatPromptValue,与课程完全一致。

结合 LLM 调用#

模板 + 模型连起来的完整流程——“提供大模型 → 提供提示词 → 结合调用”三步:

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 岁**。
Important

chat_prompt.invoke({...}) 的结果可以直接塞给 model.invoke(...)——因为前者给的就是一个消息列表。这就是”模板 ↔ 模型”之间最顺的接口:模板负责填变量,模型负责生成,中间不需要你做任何转换。

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

前面一直用”元组构成的列表”,其实那只是六种合法元素里的第一种。看源码签名:

def __init__(
self,
messages: Sequence[
BaseMessagePromptTemplate | BaseMessage | BaseChatPromptTemplate
| tuple[str | type, str | list[dict] | list[object]] | str | dict[str, Any]
],
*,
template_format: Literal["f-string", "mustache", "jinja2"] = "f-string",
**kwargs: Any,
) -> None: ...

from_messages() 的签名一模一样(from_messages() 的底层也是调用类的 __init__() 方法)。结论:参数是列表类型,列表的元素可以是字符串、字典、元组、Message 对象、MessagePromptTemplate、ChatPromptTemplate。

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

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

类型2:tuple 列表——最常用((role, content)):

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

类型3:dict 列表——和 JSON 消息格式一致,适合从配置/文件里读模板:

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

类型4:Message 列表——已经实例化好的 SystemMessage / HumanMessage 直接放进去(等于”写死的消息”):

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

在 XxxMessage 里不能有占位符!写 HumanMessage(content="我的问题是:{word}英文怎么说?") 再 invoke({"word": "人工智能"}),结果是原样透传:

messages=[..., HumanMessage(content='我的问题是:{word}英文怎么说?', ...)]

{word} 不会被替换、也不会报错——就这么静静地错下去了。要填变量就用元组 / PromptTemplate / 下面的 MessagePromptTemplate。

类型5:MessagePromptTemplate 列表——SystemMessagePromptTemplate、HumanMessagePromptTemplate、AIMessagePromptTemplate 分别用来”生成”对应的消息:

  • 模板化:支持变量占位符,运行时填充
  • 格式化:能把模板 + 输入变量合成最终消息
  • 输出类型:生成对应角色对象(HumanMessagePromptTemplate → HumanMessage,content + role="human")
  • 设计目的:简化模板化构造,避免重复定义角色
from langchain_core.prompts import (
ChatPromptTemplate,
HumanMessagePromptTemplate,
SystemMessagePromptTemplate,
)
system_message_prompt = SystemMessagePromptTemplate.from_template("你是一个{role}")
human_message_prompt = HumanMessagePromptTemplate.from_template("给我解释{concept},用浅显易懂的语言")
chat_prompt = ChatPromptTemplate.from_messages([
system_message_prompt,
human_message_prompt,
])
formatted_messages = chat_prompt.invoke({"role": "物理学家", "concept": "相对论"})
print(formatted_messages)
# messages=[SystemMessage(content='你是一个物理学家', ...), HumanMessage(content='给我解释相对论,用浅显易懂的语言', ...)]

类型6:嵌套的 ChatPromptTemplate——可以理解为”ChatPromptTemplate 里嵌套了 ChatPromptTemplate”:

nested_prompt_template1 = ChatPromptTemplate.from_messages([
("system", "我是一个人工智能助手,我的名字叫{name}")
])
nested_prompt_template2 = ChatPromptTemplate.from_messages([
("human", "很高兴认识你,我的问题是{question}")
])
prompt_template = ChatPromptTemplate.from_messages([
nested_prompt_template1, nested_prompt_template2,
])
prompt_template.invoke({"name": "小智", "question": "你为什么这么帅?"})

两种实例模板的变量会被合并到同一次 invoke 里(上面 name 和 question 就是分别填进两个嵌套模板的)。

综合使用——六种元素可以混着放:

from langchain_core.prompts import (
ChatPromptTemplate,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
)
from langchain_core.messages import SystemMessage, HumanMessage
# 示例 1: 使用 BaseMessage(已实例化的消息)
system_msg = SystemMessage(content="你是一个AI工程师。")
human_msg = HumanMessage(content="你好!")
# 示例 2: 使用 BaseMessagePromptTemplate
system_prompt = SystemMessagePromptTemplate.from_template("你是一个{role}.")
human_prompt = HumanMessagePromptTemplate.from_template("{user_input}")
# 示例 3: 使用 BaseChatPromptTemplate(嵌套的 ChatPromptTemplate)
nested_prompt = ChatPromptTemplate.from_messages([("system", "嵌套提示词")])
prompt = ChatPromptTemplate.from_messages([
system_msg, # MessageLike (BaseMessage)
human_msg, # MessageLike (BaseMessage)
system_prompt, # MessageLike (BaseMessagePromptTemplate)
human_prompt, # MessageLike (BaseMessagePromptTemplate)
nested_prompt, # MessageLike (BaseChatPromptTemplate)
])
prompt.invoke({"role": "人工智能专家", "user_input": "介绍一下大模型的应用场景"})
Tip

template_format 还有三种取值:"f-string"(默认)、"mustache"、"jinja2"。默认的 f-string 用 {变量};换 mustache 或 jinja2 时同样写 {{ name }} 也能正确渲染——处理”用户提供的模板”时,jinja2/mustache 语法更不容易和别的占位符冲突:

ChatPromptTemplate.from_messages([("human", "Hello {{ name }}!")], template_format="mustache")
# messages=[HumanMessage(content='Hello 小谷!', ...)]

提示词机制演进:字符串 → 消息列表#

LangChain 1.0 的核心演进之一:一个结构化、富含元数据的消息列表,取代了单一字符串,成为与模型交互的标准数据格式。

旧时代:LLM + PromptTemplate新时代:ChatModel + ChatPromptTemplate
模型接口LLM(文本进文本出)ChatModel(主流接口)
输入/输出都是纯文本字符串都是消息列表
角色支持❌ 无✅ system / user / assistant
对话历史❌ 不支持✅ 支持
适用场景简单提示聊天、对话、多轮交互

旧时代的痛苦:要用字符串模拟多轮聊天,开发者必须手动拼接、伪造对话角色:

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

这样不仅结构混乱、难以维护,还极易让模型混淆对话的边界与上下文,影响生成质量。现代聊天模型 API 已原生支持角色概念,不再接受单一字符串,而是要求结构化的消息列表——这也为构建复杂、可靠的多轮对话智能体系统打下了基础。

高级特性#

模板会用了,接下来四个特性解决的是”真实项目里反复出现的四种麻烦”:变量老是重复填、历史消息要动态插、模板散落各处、提示词要拼装。

预填充变量:partial()#

痛点:某些变量在所有调用里都相同(比如”你是客服专员”),每次 invoke 都要重复写一遍。

做法:用 partial() 预填充固定变量,创建模板的变体。适合”部分变量固定 + 需要为不同用户/场景定制模板”。

from langchain_core.prompts import ChatPromptTemplate
# 原始模板
template = ChatPromptTemplate.from_messages([
("system", "你是{role},目标用户是{audience}"),
("user", "{task}"),
])
# 部分填充:把 role 和 audience 固定下来
customer_support_template = template.partial(
role="客服专员",
audience="普通用户",
)
# 现在只需要提供 task
messages = customer_support_template.invoke({"task": "解释退款政策"})
print(messages)
# messages=[SystemMessage(content='你是客服专员,目标用户是普通用户', ...),
# HumanMessage(content='解释退款政策', ...)]

典型场景:一套基础模板,派生出多个部门的变体——这就是”提示词的多态”:

base_template = ChatPromptTemplate.from_messages([
("system", "你是{department}的{role}"),
("user", "{task}"),
])
# IT 部门
it_template = base_template.partial(department="IT 部门", role="技术支持")
# 销售部门
sales_template = base_template.partial(department="销售部门", role="销售顾问")
sales_template.invoke({"task": "为什么每年年底汽车会促销"})
# ChatPromptValue(messages=[SystemMessage(content='你是销售部门的销售顾问', ...),
# HumanMessage(content='为什么每年年底汽车会促销', ...)])
Tip

partial() 返回的是新模板,原模板不受影响——所以 base_template 还能继续派生出第三、第四个变体。被预填充的变量从”必填”变成”已填”,invoke 时再传也不会报错(以预填值为准)。

消息占位符:往指定位置塞一串消息#

痛点:多轮对话的历史、Agent 的中间步骤,都是”一段条数不定的消息”——它们的角色也不知道(human 还是 ai 混着来),没法用 ("human", "{x}") 这种写法。

做法:用消息占位符,在特定位置插入整个消息列表。多轮对话系统存历史、Agent 处理中间步骤时非常有用。

方式1:JSON 形式(("placeholder", "{变量}"))

from langchain_core.prompts import ChatPromptTemplate
template = ChatPromptTemplate.from_messages([
("system", "你是一个有用的AI助手"),
("placeholder", "{conversation}"),
])
prompt_value = template.invoke({
"conversation": [
("human", "你好!"),
("ai", "今天我能帮你做什么?"),
("human", "你能给我做一个冰激凌吗?"),
("ai", "抱歉,我没有这样的能力"),
]
})
print(prompt_value)

填充进去的元组被逐个翻译成真正的消息对象(("human", ...) → HumanMessage,("ai", ...) → AIMessage):

messages=[SystemMessage(content='你是一个有用的AI助手', ...),
HumanMessage(content='你好!', ...),
AIMessage(content='今天我能帮你做什么?', ...),
HumanMessage(content='你能给我做一个冰激凌吗?', ...),
AIMessage(content='抱歉,我没有这样的能力', ...)]

方式2:MessagesPlaceholder 实例

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage
prompt_template = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant"),
MessagesPlaceholder("msgs"),
])
prompt_template.invoke({"msgs": [HumanMessage(content="hi!")]})
# 也可以用 format_messages:
# prompt_template.format_messages(msgs=[HumanMessage(content="hi!")])

生成的是”系统消息 + 你传进来的那些消息”:传 1 条就是 2 条,传 5 条就是 6 条——这正是”把一串消息插到指定位置”的价值。

Note

MessagesPlaceholder("msgs") 的位置参数就是变量名(等价于 MessagesPlaceholder(variable_name="msgs")),两种写法都可以。

实战:存对话历史——system 固定、history 动态、question 每次不同:

prompt_template = ChatPromptTemplate.from_messages([
("system", "你是一个非常友好的AI助手"),
MessagesPlaceholder(variable_name="history"),
("human", "{question}"),
])
prompt_template.invoke({
"history": [
("human", "5 + 2 = ?"),
("ai", "5 + 2 = 7"),
],
"question": "结果再乘以4呢?",
})
# ChatPromptValue(messages=[SystemMessage('你是一个非常友好的AI助手'),
# HumanMessage('5 + 2 = ?'), AIMessage('5 + 2 = 7'),
# HumanMessage('结果再乘以4呢?')])
Tip

这就是”手动维护对话历史”的正规写法:以前要在列表里手工拼接 {"role": ..., "content": ...},现在模板本身就留好了历史的坑位——每次只需把 history 换成最新的消息列表。第 7 篇最后那台聊天机器人,改用这个模板会清爽很多。

可复用模板库#

痛点:模板一多就散落在各个文件里。实际项目中建议创建模板库,统一收集、集中管理。

举例1:templates.py 用一个类当”模板仓库”

from langchain_core.prompts import ChatPromptTemplate
class PromptLibrary:
"""可复用的提示词模板库"""
TRANSLATOR = ChatPromptTemplate.from_messages([
("system", "你是专业翻译,精通{source_lang}和{target_lang}"),
("user", "翻译以下文本:\n{text}"),
])
CODE_REVIEWER = ChatPromptTemplate.from_messages([
("system", "你是{language}代码审查专家,重点关注{focus}"),
("user", "审查代码:\n```{language}\n{code}\n```"),
])
SUMMARIZER = ChatPromptTemplate.from_messages([
("system", "你是内容摘要专家"),
("user", "将以下内容总结为{num}个要点:\n{content}"),
])
TUTOR = ChatPromptTemplate.from_messages([
("system", "你是{subject}导师,学生水平:{level}"),
("user", "{question}"),
])

其它文件里直接取用,一行调用、变量照填:

from templates import PromptLibrary
messages = PromptLibrary.TRANSLATOR.format_messages(
source_lang="英语",
target_lang="中文",
text="Hello World",
)

举例2:按功能拆成包——模板多了以后,比”一个大类”更好维护:

templates/
# ├── __init__.py
# ├── common.py # 通用模板
# ├── translation.py # 翻译相关
# └── coding.py # 编程相关
common.py
from langchain_core.prompts import ChatPromptTemplate
FRIENDLY_ASSISTANT = ChatPromptTemplate.from_messages([
("system", "你是一个友好的助手"),
("user", "{input}"),
])

模板组合:拼出复杂提示词#

痛点:一个 system 提示词往往由”角色 + 风格 + 限制”几段拼成,想复用其中某一段。

方法1:字符串组合——在 Python 层面把片段先拼好,再交给模板:

# 定义可复用的部分
role_part = "你是一个{domain}专家。"
style_part = "回答风格:{style}。"
constraint_part = "限制:{constraint}。"
# 组合
full_system = role_part + style_part + constraint_part
template = ChatPromptTemplate.from_messages([
("system", full_system),
("user", "{question}"),
])

方法2:用 + 运算符——模板对象直接相加(LangChain 1.0 支持),合并后的模板包含两边所有消息:

template1 = ChatPromptTemplate.from_messages([("system", "你是助手")])
template2 = ChatPromptTemplate.from_messages([("user", "{input}")])
# 组合(LangChain 1.0 支持)
combined = template1 + template2
Note

两种方法的差别:字符串组合拼的是同一条消息的内容;+ 拼的是消息与消息(template1 的 system + template2 的 user)。 实测(本机 langchain-core 1.2.18):template1 + template2 返回的还是 ChatPromptTemplate,combined.invoke({"input": "hi"}) 得到 [SystemMessage('你是助手'), HumanMessage('hi')]——两边的变量会自动合并,一次 invoke 全填上。

相关#

练习题#

一、知识回顾(读完直接做下面的实践题)#

  1. 构造提示词的两条路:Python 字符串拼接、LangChain 的提示词(PromptTemplate、ChatPromptTemplate)模板;模板相对拼接的优势是变量自动校验(漏填/拼错会立刻报错)、支持对话 / RAG(Few-shot) / Agent 这类复杂场景、与生态集成便于调试与日志追踪;开发建议是”小项目 / 临时 demo 用字符串拼接,正式开发提示词模板是必选”
  2. 字符串模板的用法:先 from_template 建模板,再用 format() 填变量,返回的是字符串;聊天模板用 invoke() 填变量,返回的是消息列表,可以直接传给聊天模型
  3. 聊天模板有两种实例化方式:from_messages()(推荐)与初始化方法传 messages 参数;两种方式传入的都是”元组构成的列表”,每个元组是 (角色, 内容)
  4. 角色字符串对照:“system” 系统消息、user 或 “human” 用户消息、assistant 或 “ai” AI 消息
  5. 同一个模板的三种取结果方式与返回类型:invoke → ChatPromptValue、format → str、format_messages → list;还有一个更”老派”的 format_prompt(),返回的也是 ChatPromptValue(与 invoke 等价);三者的参数写法也不同——invoke 收一个字典,另外两个收关键字参数
  6. 模板列表里能放六类元素:str、元组(tuple)、dict、已经实例化的 Message 对象、MessagePromptTemplate、嵌套的 ChatPromptTemplate;其中纯字符串列表默认角色是 human;而在写死的消息对象里放占位符会原样透传(不报错也不替换)
  7. template_format 三种取值:默认的 “f-string”、“mustache”、“jinja2”;处理”用户提供的模板”时后两种更不容易与别的占位符冲突
  8. 机制演进:旧时代是 LLM + PromptTemplate(字符串进、字符串出,要在字符串里手动拼接、伪造对话角色);新时代是 ChatModel + ChatPromptTemplate(消息列表进、消息列表出,原生支持角色与对话历史)
  9. 预填充用 partial():它返回的是新模板(原模板不受影响),被预填的变量从”必填”变成”已填”;消息占位符也有两种写法——JSON 形式的 ("placeholder", "{变量}") 与 MessagesPlaceholder 实例,后者的位置参数就是变量名
  10. 模板库的两种组织方式:用一个类当”模板仓库”、按功能拆成包(多个模块);模板组合的两种方法:字符串组合拼的是同一条消息的内容,+ 运算符拼的是消息与消息(合并后两边的变量会自动合并,一次 invoke 全填上)

二、裸写题#

  • 2-1 一个提示词,三种”取结果”方式 ① 用字符串拼接写一句”你是一个 {难度} 级别的编程导师,请解释 {主题}“,直接调用模型; ② 用字符串模板重写同一句,用它的填充方法拿到最终提示词再调用模型; ③ 用聊天模板把同一句拆成”系统消息 + 用户消息”两条,然后分别用三种调用方式把变量填进去,打印每次返回值的类型,并把其中”能直接交给模型”的那一份传给模型、打印回复。

    提示(先自己想,实在想不出再点开)

    一级 · 思路:字符串模板只能出字符串,聊天模板出的是消息列表——这正是它能直接喂给模型的原因 二级 · 方法:PromptTemplate.from_template(...).format(...);ChatPromptTemplate.from_messages([...]) 的 .invoke({...}) / .format(...) / .format_messages(...) 三级 · 骨架:打印类型会分别得到 ChatPromptValue / str / list;注意前两个接收参数的方式不一样(一个收字典、两个收关键字参数)

  • 2-2 六类元素混着放(并验证一个坑) 构造一个模板,列表里同时放:① 一条写死的系统消息对象、② 一条写死的用户消息对象、③ 一个能生成系统消息的模板(带占位符)、④ 一个能生成用户消息的模板(带占位符)、⑤ 嵌套进来的另一个模板。一次性填变量,打印生成的消息列表(按顺序打印每条的角色和内容),确认嵌套模板的变量也被填上了。 再做一个实验:往写死的消息对象里塞一个 {变量},看填变量时它到底会不会被替换。

    提示(先自己想,实在想不出再点开)

    一级 · 思路:模板列表的元素可以是”对象""元组""模板”等多种形态,变量会被合并到同一次调用里一起填 二级 · 方法:SystemMessagePromptTemplate.from_template(...) / HumanMessagePromptTemplate.from_template(...) / 嵌套的 ChatPromptTemplate.from_messages([...]) 三级 · 骨架:prompt.invoke({"role": ..., "user_input": ...}) 后遍历 filled.messages;实验项的结果是”原样透传”,既不替换也不报错——要变量就得用元组、字符串模板或消息模板

  • 2-3 预填充:一套模板派生多个变体 写一个基础模板:系统消息里有两个变量(部门 + 角色),用户消息是任务。用预填充把”部门 + 角色”固定下来,派生两个不同部门(如 IT 部门 / 销售部门)的变体;每个变体只填”任务”就能拿到消息列表,打印出来对比;最后再验证原模板没被改动——用它派生第三个部门。 被预填的变量在调用时再传一次会怎样?也试一试。

    提示(先自己想,实在想不出再点开)

    一级 · 思路:把”所有调用里都相同”的变量提前填死,剩下的每次现填;要理解它返回的是新模板而不是改原模板 二级 · 方法:base_template.partial(department="IT 部门", role="技术支持") 三级 · 骨架:变体只要 invoke({"task": "..."});被预填的变量再传也以预填值为准

  • 2-4 用消息占位符管理对话历史 写一个模板:“系统消息(固定)+ 一串对话历史(条数与角色都不定)+ 本次问题(每次不同)“,用两种写法把历史插进去(JSON 形式 / 占位符实例)。 然后准备一段 4 条的假历史,分别传 0 条、2 条、4 条,打印每次生成的消息总条数,验证”传 N 条历史就多 N 条消息”。

    提示(先自己想,实在想不出再点开)

    一级 · 思路:历史消息的角色是混着的,没法用 ("human", "{x}") 这种写法表达,所以要一个”占位符” 二级 · 方法:("placeholder", "{history}") 或 MessagesPlaceholder(variable_name="history")(也支持位置参数) 三级 · 骨架:template.invoke({"history": [("human", "..."), ("ai", "...")], "question": "..."});元组会被逐个翻译成真正的消息对象

参考答案(做完再点开)
import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from langchain_core.prompts import (
PromptTemplate,
ChatPromptTemplate,
MessagesPlaceholder,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
)
from langchain_core.messages import SystemMessage, HumanMessage
load_dotenv(override=True)
model = init_chat_model(
model="deepseek-v4-flash",
model_provider="openai",
base_url=os.getenv("DEEPSEEK_BASE_URL"),
api_key=os.getenv("DEEPSEEK_API_KEY"),
)
# ========== 2-1 一个提示词,三种"取结果"方式 ==========
topic, difficulty = "装饰器", "初学者"
r0 = model.invoke(f"你是一个{difficulty}级别的编程导师。请用简单易懂的语言解释{topic}。")
print("[字符串拼接]", r0.content)
template = PromptTemplate.from_template(
"你是一个{difficulty}级别的编程导师。请用简单易懂的语言解释{topic}。"
)
r1 = model.invoke(template.format(difficulty=difficulty, topic=topic))
print("[字符串模板]", r1.content)
chat_template = ChatPromptTemplate.from_messages([
("system", "你是一个{difficulty}级别的编程导师。"),
("human", "请用简单易懂的语言解释{topic}。"),
])
value_invoke = chat_template.invoke({"difficulty": difficulty, "topic": topic})
value_format = chat_template.format(difficulty=difficulty, topic=topic)
value_msgs = chat_template.format_messages(difficulty=difficulty, topic=topic)
print("invoke ->", type(value_invoke)) # ChatPromptValue
print("format ->", type(value_format)) # str
print("format_messages ->", type(value_msgs)) # list
print("format() 打印出来长这样:")
print(value_format) # System: ... / Human: ...
print("[模型回复]", model.invoke(value_invoke).content)
# ========== 2-2 六类元素混着放 ==========
system_msg = SystemMessage("你是一个AI工程师。")
human_msg = HumanMessage("你好!")
system_prompt = SystemMessagePromptTemplate.from_template("你是一个{role}。")
human_prompt = HumanMessagePromptTemplate.from_template("{user_input}")
nested_prompt = ChatPromptTemplate.from_messages([("system", "嵌套提示词")])
prompt = ChatPromptTemplate.from_messages([
system_msg, # 写死的系统消息
human_msg, # 写死的用户消息
system_prompt, # 能生成系统消息的模板
human_prompt, # 能生成用户消息的模板
nested_prompt, # 嵌套的另一个模板
])
filled = prompt.invoke({"role": "人工智能专家", "user_input": "介绍一下大模型的应用场景"})
for m in filled.messages:
print(type(m).__name__, "->", m.content)
# 坑:写死的消息对象里放占位符,不会被替换
bad = ChatPromptTemplate.from_messages([
HumanMessage(content="我的问题是:{word}英文怎么说?")
])
print("占位符实验结果:", bad.invoke({"word": "人工智能"}).messages[0].content)
# 输出:我的问题是:{word}英文怎么说? —— 原样透传,不替换也不报错
# ========== 2-3 预填充:一套模板派生多个变体 ==========
base_template = ChatPromptTemplate.from_messages([
("system", "你是{department}的{role}"),
("user", "{task}"),
])
it_template = base_template.partial(department="IT 部门", role="技术支持")
sales_template = base_template.partial(department="销售部门", role="销售顾问")
print("IT 变体:", it_template.invoke({"task": "解释退款政策"}).messages[0].content)
print("销售变体:", sales_template.invoke({"task": "为什么每年年底汽车会促销"}).messages[0].content)
# 原模板不受影响,还能继续派生
hr_template = base_template.partial(department="人力资源部", role="招聘专员")
print("第三个变体:", hr_template.invoke({"task": "怎么写好一份 JD"}).messages[0].content)
# ========== 2-4 用消息占位符管理对话历史 ==========
# 写法1:JSON 形式
template_json = ChatPromptTemplate.from_messages([
("system", "你是一个有用的AI助手"),
("placeholder", "{conversation}"),
])
pv = template_json.invoke({
"conversation": [("human", "你好!"), ("ai", "今天我能帮你做什么?")],
})
print("写法1:", [type(m).__name__ for m in pv.messages])
# 写法2:占位符实例
template = ChatPromptTemplate.from_messages([
("system", "你是一个非常友好的AI助手"),
MessagesPlaceholder(variable_name="history"),
("human", "{question}"),
])
history_pool = [
("human", "5 + 2 = ?"), ("ai", "5 + 2 = 7"),
("human", "结果再乘以 3 呢?"), ("ai", "等于 21"),
]
for n in (0, 2, 4):
filled = template.invoke({"history": history_pool[:n], "question": "结果再乘以4呢?"})
print(f"历史 {n} 条 -> 总共 {len(filled.messages)} 条消息") # 2 / 4 / 6

三、综合题#

  • 3-1 小型提示词工厂 把模板库、模板组合、预填充串起来:

    1. 在文件顶部写一个”模板仓库”类,里面至少放两个模板:翻译模板(system 里带”源语言 / 目标语言”两个变量)、代码审查模板(system 里带”语言 / 关注点”两个变量);
    2. 再写一个只含用户消息的模板(比如”另外,请用 {num} 条要点总结你的建议”);
    3. 把”代码审查模板”和它拼成一个组合模板;
    4. 用预填充把”语言 / 关注点”固定下来;
    5. 填入剩下的变量,打印合并后每条消息的类名(确认一条是系统消息、一条是用户消息),再把整份消息交给模型、打印回复。
    提示(先自己想,实在想不出再点开)

    一级 · 思路:模板库管收集、+ 管拼装、预填充管固定值,最后一次性 invoke 交给模型 二级 · 方法:PromptLibrary.CODE_REVIEWER + ask_more,再 .partial(language="Python", focus="边界条件") 三级 · 骨架:msgs = fixed.invoke({"code": "...", "num": "3"}),然后 model.invoke(msgs);两边的变量会合并,一次全填

参考答案(做完再点开)
# ========== 3-1 小型提示词工厂 ==========
class PromptLibrary:
"""可复用的提示词模板库"""
TRANSLATOR = ChatPromptTemplate.from_messages([
("system", "你是专业翻译,精通{source_lang}和{target_lang}"),
("user", "翻译以下文本:\n{text}"),
])
CODE_REVIEWER = ChatPromptTemplate.from_messages([
("system", "你是{language}代码审查专家,重点关注{focus}"),
("user", "审查代码:\n{code}"),
])
ask_more = ChatPromptTemplate.from_messages([
("user", "另外,请用{num}条要点总结你的建议"),
])
combined = PromptLibrary.CODE_REVIEWER + ask_more # 模板组合
fixed = combined.partial(language="Python", focus="边界条件") # 预填充固定变量
msgs = fixed.invoke({"code": "def div(a, b): return a / b", "num": "3"})
print("组合后的消息:", [type(m).__name__ for m in msgs.messages])
print("第一条:", msgs.messages[0].content) # 你是Python代码审查专家,重点关注边界条件
print("模型回复:", model.invoke(msgs).content)

评论区

[ 标签 ]
# AI37# AI 编程2# AI工具1# AI绘图1# AI辅助开发2# Ajax7# Apifox1# AstrBot3# Astro2# CC Switch1# CDN2# Claude Code1# claudecode2# ClaudeCode1# Cloudflare2# CloudFlare2# CloudFlare-ImgBed3# coc4# CSS10# DeepSeek6# deepseek2# DELETE1# Docker5# EdgeOne3# Gist1# git1# GitHub1# hexo-circle-of-friends1# HTML9# HTTP10# ImageManager1# Java23# java13# JavaScript11# JavaWeb113# JDBC7# JSON2# JUnit1# LangChain25# Linux4# Logback1# Maven17# Muse Spark1# Mybatis1# MyBatis18# MySQL45# MySql1# NapCat1# Node.js1# obsidian2# Obsidian5# OpenCode4# ORM1# PathVariable1# PicGo1# PyCharm1# Python70# RequestBody1# RequestMapping1# RESTful风格1# skills1# Slf4j1# SpringBoot37# SpringMVC5# SQL2# Streamlit5# Svelte2# TailwindCSS1# Telegram3# Tlias2# Vercel1# vscode2# Vue20# Waline3# Web3# WebDAV1# Web基础6# Web开发6# WinSCP1# YAML1# 三层架构1# 中二宣言1# 书籍1# 使用文档10# 写作1# 函数2# 刷步数1# 前端52# 动态1# 动漫1# 包1# 单词2# 博客8# 博客工作流1# 博客开发2# 参数接收1# 友链1# 反思2# 后端48# 图床6# 地图1# 备份2# 大模型1# 奇思妙想1# 存储1# 学习方法6# 学校1# 宝塔面板3# 宝宝10# 实战4# 对象1# 导航栏1# 工具2# 布局2# 开发1# 开发工具1# 开发规范1# 开心1# 异常处理1# 影视2# 微信1# 性能优化2# 总结1# 想法15# 感受1# 感悟11# 指南1# 提示词工程2# 插件5# 故障排除1# 效率工具2# 教程10# 数据分析9# 数据库27# 数据结构1# 文件操作2# 斩神1# 日常95# 日志框架1# 朋友圈1# 朱元璋1# 模块1# 模板1# 正则表达式2# 测试3# 游戏2# 爬虫7# 生活迁移1# 电影2# 电脑1# 碎碎念1# 视觉识别1# 类1# 类型注解1# 网络基础2# 网络教室1# 羊毛2# 脚本2# 脚本工具1# 自动化2# 蓝奏云1# 表单1# 订阅推荐2# 记录2# 评论系统1# 词根1# 词缀1# 说说1# 足迹1# 跑步2# 路径参数1# 转载2# 运动1# 部署11# 部落冲突1# 配置1# 随机图1# 面向对象5# 音乐3# 音标1# 项目实战11# 饮食1# 驼峰命名1# 高德地图1
[ 公告 ]

如果你喜欢,那么欢迎来到我的世界!

了解更多
[ 音乐 ]
封面

音乐

暂未播放

0:000:00
暂无歌词
找不到相关结果。
[ contents ]
[ 全部文章 ]