内容纲要
- 提示词模板核心概念
PromptTemplate:面向纯文本 LLM 的字符串模板ChatPromptTemplate:面向对话模型的结构化模板MessagesPlaceholder:在消息列表中动态插入内容的占位符- 使用
SystemMessage、HumanMessage、AIMessage灵活组合模板 - 自定义模板:继承
StringPromptTemplate实现特殊逻辑
- 关键流程:定义模板 → 声明变量 → 格式化(
format/format_messages)→ 提交给 LLM - 涉及组件:
langchain_core.prompts、langchain_core.messages - 代码演示:完整可运行的 Python 脚本,涵盖所有模板类型
引言
在构建 AI Agent 或大语言模型应用时,提示词(Prompt)是连接业务逻辑与模型能力的桥梁。LangChain 提供了一套丰富的提示词模板系统,使开发者可以像写配置一样管理复杂的提示词结构,而无需手动拼接字符串。本文将系统讲解五种最常用的提示词模板:字符串模板、对话模板、消息占位符、Message 组合模板以及自定义模板,并提供可直接运行的代码示例。
一、字符串模板:PromptTemplate
字符串模板是最基础的提示词模板,适用于传统的文本补全模型(LLM),它接收一个包含占位符的字符串,通过 format 方法将参数注入,最终生成一段完整的文本提示。
核心特点:输入输出均为纯文本,不支持结构化消息。
python
from langchain_core.prompts import PromptTemplate
# 定义模板,花括号内为变量
template = "你是一个{role},帮我起一个具有{country}特色的{gender}名字。"
prompt = PromptTemplate.from_template(template)
# 传入参数格式化
formatted = prompt.format(role="算命大师", country="法国", gender="女孩")
print(formatted)
输出:你是一个算命大师,帮我起一个具有法国特色的女孩名字。
使用时需注意:模板中声明的变量数量必须与 format 传入的参数完全一致,否则会抛出异常。
二、对话模板:ChatPromptTemplate
对话模板是当前应用最广泛的形式,专为对话类模型(Chat Model)设计。它返回的是一个结构化的消息列表,每条消息都带有明确的角色(system、human、ai),便于下游处理。
python
from langchain_core.prompts import ChatPromptTemplate
# 使用 from_messages 构建消息列表
template = ChatPromptTemplate.from_messages([
("system", "你是一个起名大师,你的名字叫{name}。"),
("human", "你好,感觉如何?"),
("ai", "你好,我状态非常好。"),
("human", "你叫什么名字?"),
("ai", "你好,我叫{name}。"),
("human", "{user_input}"),
])
# 注意:对话模板使用 format_messages 而非 format
messages = template.format_messages(
name="陈大师",
user_input="你的爸爸是谁呢?"
)
print(messages)
输出将是一个包含 SystemMessage、HumanMessage、AIMessage 的列表,每条消息都带有 content 字段,并保留了元数据,非常适合在应用中进行结构化解析和后续调用。
三、消息占位符:MessagesPlaceholder
当需要在消息列表的特定位置动态插入一组消息(例如历史聊天记录),但又不确定其具体数量或类型时,可以使用 MessagesPlaceholder。它相当于一个"插槽",在最终调用模型前可以被替换为实际的消息列表。
python
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage
# 构造带占位符的模板
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个厉害的人工智能助手。"),
MessagesPlaceholder(variable_name="history"),
("human", "{question}")
])
# 在调用阶段动态传入消息列表
result = prompt.invoke({
"history": [HumanMessage(content="你好,请多关照!")],
"question": "今天天气如何?"
})
print(result.messages)
此外,也可以直接用 ("placeholder", "{history}") 这种简写方式,效果等价。占位符的灵活性在于,它允许在 invoke 阶段(而不一定在 format 阶段)再确定插入的内容,这对构建复杂 Agent 的多轮对话记忆非常实用。
四、使用 Message 直接组合模板
除了通过 ChatPromptTemplate.from_messages 声明式构建,还可以直接用 SystemMessage、HumanMessage、AIMessage 等消息对象手动拼装,形成消息列表。这种方式更灵活,适合将公共的系统消息或用户消息进行复用组合。
python
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
from langchain_core.prompts import ChatPromptTemplate
# 手动构建消息
system_msg = SystemMessage(content="你是一个起名大师", additional_kwargs={"大师名字": "陈瞎子"})
human_msg = HumanMessage(content="请问大师叫什么?")
ai_msg = AIMessage(content="我叫陈瞎子。")
# 直接拼成列表,即是一个合法的消息模板
chat_list = [system_msg, human_msg, ai_msg]
prompt = ChatPromptTemplate.from_messages(chat_list)
print(prompt.format_messages())
这种方式下,你可以将某个 SystemMessage 作为公共提示词,在多个场景中复用,并自由组合后续对话流程,大幅提升代码的模块化程度。
五、自定义提示词模板
当内置的 PromptTemplate 和 ChatPromptTemplate 无法满足需求时,可以通过继承 StringPromptTemplate 来实现自己的模板逻辑。例如,创建一个"函数大师"模板:输入函数名,自动获取函数源代码,并生成一段要求解释代码的提示词。
步骤:
- 继承
StringPromptTemplate - 重写
format方法,在其中实现自定义逻辑(如获取源代码、拼接提示) - 调用时和普通模板一致
python
import inspect
from langchain_core.prompts import StringPromptTemplate
# 定义一个示例函数
def hello_world(a, b, c):
"""测试函数"""
print("hello world")
return a, b, c
# 自定义模板类
class CustomCodeExplainerTemplate(StringPromptTemplate):
def format(self, **kwargs) -> str:
func_name = kwargs["function_name"]
# 获取函数源代码
source_code = inspect.getsource(globals()[func_name])
# 内嵌提示词
prompt_template = (
"你是一个非常有经验和天赋的程序员。现在给你如下函数名称,"
"你会按照如下格式输出这段代码的名称、源代码和中文解释。\n"
"函数名称: {function_name}\n"
"源代码: {source_code}\n"
"代码解释: "
)
return prompt_template.format(function_name=func_name, source_code=source_code)
# 使用自定义模板
custom_prompt = CustomCodeExplainerTemplate(input_variables=["function_name"])
final_prompt = custom_prompt.format(function_name="hello_world")
print(final_prompt)
输出已包含函数名称和源代码。后续你可以将该提示词直接交给 LLM,得到代码解释。这种模式允许你在模板内部封装任何复杂的预处理逻辑,从而将提示词构建过程完全自动化。
五种模板对比
| 模板类型 | 输入变量占位 | 输出格式 | 适用模型 | 灵活性 |
|---|---|---|---|---|
PromptTemplate |
字符串花括号 | 纯文本 | LLM(补全) | 低 |
ChatPromptTemplate |
消息列表变量 | 结构化消息列表 | Chat Model | 中 |
MessagesPlaceholder |
动态消息插槽 | 含占位符的消息列表 | Chat Model | 高 |
| Message 对象组合 | 无 | 消息列表 | Chat Model | 高 |
自定义 StringPromptTemplate |
自定义 | 任意 | 均可 | 极高 |
模板工作流程
以下 Mermaid 时序图展示了从定义模板到 LLM 调用的一般流程。
大语言模型 提示词模板 开发者 大语言模型 提示词模板 开发者 #mermaid-svg-tJO4NZYYDTNM5X66{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tJO4NZYYDTNM5X66 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tJO4NZYYDTNM5X66 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tJO4NZYYDTNM5X66 .error-icon{fill:#552222;}#mermaid-svg-tJO4NZYYDTNM5X66 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tJO4NZYYDTNM5X66 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tJO4NZYYDTNM5X66 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tJO4NZYYDTNM5X66 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tJO4NZYYDTNM5X66 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tJO4NZYYDTNM5X66 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tJO4NZYYDTNM5X66 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tJO4NZYYDTNM5X66 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tJO4NZYYDTNM5X66 .marker.cross{stroke:#333333;}#mermaid-svg-tJO4NZYYDTNM5X66 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tJO4NZYYDTNM5X66 p{margin:0;}#mermaid-svg-tJO4NZYYDTNM5X66 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tJO4NZYYDTNM5X66 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-tJO4NZYYDTNM5X66 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-tJO4NZYYDTNM5X66 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-tJO4NZYYDTNM5X66 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-tJO4NZYYDTNM5X66 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-tJO4NZYYDTNM5X66 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-tJO4NZYYDTNM5X66 .sequenceNumber{fill:white;}#mermaid-svg-tJO4NZYYDTNM5X66 #sequencenumber{fill:#333;}#mermaid-svg-tJO4NZYYDTNM5X66 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-tJO4NZYYDTNM5X66 .messageText{fill:#333;stroke:none;}#mermaid-svg-tJO4NZYYDTNM5X66 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tJO4NZYYDTNM5X66 .labelText,#mermaid-svg-tJO4NZYYDTNM5X66 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-tJO4NZYYDTNM5X66 .loopText,#mermaid-svg-tJO4NZYYDTNM5X66 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-tJO4NZYYDTNM5X66 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-tJO4NZYYDTNM5X66 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-tJO4NZYYDTNM5X66 .noteText,#mermaid-svg-tJO4NZYYDTNM5X66 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-tJO4NZYYDTNM5X66 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tJO4NZYYDTNM5X66 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tJO4NZYYDTNM5X66 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-tJO4NZYYDTNM5X66 .actorPopupMenu{position:absolute;}#mermaid-svg-tJO4NZYYDTNM5X66 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-tJO4NZYYDTNM5X66 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-tJO4NZYYDTNM5X66 .actor-man circle,#mermaid-svg-tJO4NZYYDTNM5X66 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-tJO4NZYYDTNM5X66 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 定义模板及变量传入参数 (format / format_messages / invoke)返回格式化后的提示词或消息列表将格式化结果作为输入发送返回模型响应
完整可运行代码
以下代码整合了上述五种模板的示例,可直接复制执行。执行前请安装依赖:
bash
pip install langchain langchain-core
python
import inspect
from langchain_core.prompts import (
PromptTemplate,
ChatPromptTemplate,
MessagesPlaceholder,
StringPromptTemplate
)
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
# ========== 1. 字符串模板 ==========
str_template = PromptTemplate.from_template(
"你是一个{role},帮我起一个具有{country}特色的{gender}名字。"
)
str_result = str_template.format(role="算命大师", country="法国", gender="女孩")
print("字符串模板结果:\n", str_result, "\n")
# ========== 2. 对话模板 ==========
chat_template = ChatPromptTemplate.from_messages([
("system", "你是一个起名大师,你的名字叫{name}。"),
("human", "你好,感觉如何?"),
("ai", "你好,我状态非常好。"),
("human", "你叫什么名字?"),
("ai", "你好,我叫{name}。"),
("human", "{user_input}"),
])
chat_result = chat_template.format_messages(
name="陈大师",
user_input="你的爸爸是谁呢?"
)
print("对话模板结果:\n", chat_result, "\n")
# ========== 3. 消息占位符 ==========
placeholder_template = ChatPromptTemplate.from_messages([
("system", "你是一个厉害的人工智能助手。"),
MessagesPlaceholder(variable_name="history"),
("human", "{question}")
])
ph_result = placeholder_template.invoke({
"history": [HumanMessage(content="你好,请多关照!")],
"question": "今天天气如何?"
})
print("消息占位符结果:\n", ph_result.messages, "\n")
# ========== 4. Message 组合模板 ==========
sys_msg = SystemMessage(content="你是一个起名大师", additional_kwargs={"大师名字": "陈瞎子"})
human_msg = HumanMessage(content="请问大师叫什么?")
ai_msg = AIMessage(content="我叫陈瞎子。")
composed_list = [sys_msg, human_msg, ai_msg]
composed_template = ChatPromptTemplate.from_messages(composed_list)
comp_result = composed_template.format_messages()
print("Message 组合模板结果:\n", comp_result, "\n")
# ========== 5. 自定义模板 ==========
def hello_world(a, b, c):
"""测试函数"""
print("hello world")
return a, b, c
class CustomCodeExplainerTemplate(StringPromptTemplate):
def format(self, **kwargs) -> str:
func_name = kwargs["function_name"]
source_code = inspect.getsource(globals()[func_name])
prompt_template = (
"你是一个非常有经验和天赋的程序员。现在给你如下函数名称,"
"你会按照如下格式输出这段代码的名称、源代码和中文解释。\n"
"函数名称: {function_name}\n"
"源代码: {source_code}\n"
"代码解释: "
)
return prompt_template.format(function_name=func_name, source_code=source_code)
custom_prompt = CustomCodeExplainerTemplate(input_variables=["function_name"])
custom_result = custom_prompt.format(function_name="hello_world")
print("自定义模板结果:\n", custom_result, "\n")
总结
本文详细介绍了 LangChain 中五种提示词模板的用法及适用场景,从最简单的字符串模板到高度灵活的自定义模板,覆盖了绝大多数 LLM 应用的提示词构建需求。
通过对比和完整代码示例,开发者可以快速选择适合自己项目的模板类型,并基于此构建稳健、可维护的 AI Agent 系统。