04 | Prompt Engineering:提示词工程
提示词工程是 LLM 应用开发的核心技能。本模块详细讲解 LangChain 中 Prompt 的创建、管理、模板化以及高级用法。
一、Prompt 的本质
Prompt 就是你发给 LLM 的指令。一个好的 Prompt = 角色设定 + 任务描述 + 输出格式要求。
┌─────────────────────────────────────────────┐
│ 你是一个专业的{role}工程师 │ ← 角色设定
│ 请回答我的问题,我的问题是:{question} │ ← 任务描述
│ 结果返回json格式 │ ← 格式要求
└─────────────────────────────────────────────┘
LangChain 提供了 PromptTemplate 和 ChatPromptTemplate 两个核心类来 规范化创建和管理提示词。
二、PromptTemplate 基础用法
2.1 构造方法实例化
python
# 04_prompt/prompt_templates/PromptTemplate_Constructor.py
from langchain_core.prompts import PromptTemplate
# 方式1:构造方法
template = PromptTemplate(
template="你是一个专业的{role}工程师,请回答我的问题:{question}",
input_variables=['role', 'question'],
)
prompt = template.format(role="python开发", question="冒泡排序怎么写?")
# 输出:你是一个专业的python开发工程师,请回答我的问题:冒泡排序怎么写?
2.2 from_template 快捷创建
python
# 04_prompt/prompt_templates/PromptTemplate_FromTemplate.py
# 方式2:from_template 工厂方法
template = PromptTemplate.from_template(
"你是一个专业的{role}工程师,请回答我的问题:{question}"
)
prompt = template.format(role="Agent开发工程师", question="如何开发Agent")
2.3 三种调用方法对比
| 方法 | 返回值类型 | 使用场景 |
|---|---|---|
format() |
str |
获取纯文本字符串,用于打印或传给其他系统 |
invoke() |
PromptValue |
LCEL 链式调用的标准入口 |
partial() |
PromptTemplate |
预填充部分变量,返回新模板 |
python
# format() - 返回字符串
prompt_str = template.format(role="Python工程师", question="冒泡排序怎么写?")
print(type(prompt_str)) # <class 'str'>
# invoke() - 返回 PromptValue 对象(LCEL 标准入口)
prompt_value = template.invoke({"role": "Python工程师", "question": "冒泡排序怎么写?"})
print(prompt_value.to_string()) # 转为字符串
print(prompt_value.to_messages()) # 转为消息列表
# partial() - 预填充部分变量,返回新模板
partial_template = template.partial(role="Python开发")
final_prompt = partial_template.format(question="冒泡排序怎么写?")
三、部分变量(Partial Variables)
有些变量值需要在运行时动态生成(如当前时间),可以用 partial_variables 预设:
python
# 04_prompt/prompt_templates/PromptTemplate_PartialVariables.py
from datetime import datetime
# 方式1:构造时预设
template = PromptTemplate.from_template(
"现在时间是:{time},我的问题是:{question}",
partial_variables={"time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
)
# 方式2:调用 partial() 方法
template = PromptTemplate.from_template(
"现在时间是:{time},我的问题是:{question}"
)
prompt = template.partial(time=datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
四、模板组合(拼接)
多个模板可以像字符串一样拼接:
python
# 04_prompt/prompt_templates/PromptTemplate_Combined.py
prompt_a = PromptTemplate.from_template("请用一句话介绍{topic},要求通俗易懂,")
prompt_b = PromptTemplate.from_template("内容不超过{length}个字")
# 使用 + 拼接
prompt_all = prompt_a + prompt_b
result = prompt_all.format(topic="LangChain", length=100)
# 请用一句话介绍LangChain,要求通俗易懂,内容不超过100个字
五、ChatPromptTemplate:多角色对话模板
普通的 PromptTemplate 只生成一段文本,而 ChatPromptTemplate 可以构建 多角色消息列表。
5.1 三种消息格式
python
# 格式1:Tuple 列表 [(role, content), ...]
chatPromptTemplate = ChatPromptTemplate([
("system", "你是一个AI开发工程师,你的名字是{name}。"),
("human", "你能帮我做什么?"),
("ai", "我能开发很多{thing}。"),
("human", "{user_input}"),
])
# 格式2:Dict 列表 [{"role":..., "content":...}, ...]
chat_prompt = ChatPromptTemplate.from_messages([
{"role": "system", "content": "你是AI助手,你的名字叫{name}。"},
{"role": "user", "content": "请问:{question}"}
])
# 格式3:Message 对象列表
from langchain_core.messages import SystemMessage, HumanMessage
chat_prompt = ChatPromptTemplate([
SystemMessage(content="你是AI助手,你的名字叫{name}。"),
HumanMessage(content="请问:{question}")
])
5.2 ChatPromptTemplate 的三种执行方法
python
# format_messages() - 返回 List[BaseMessage]
messages = chat_prompt.format_messages(role="python开发", question="什么是LangChain")
# invoke() - 返回 PromptValue
prompt_value = chat_prompt.invoke({"role": "python开发", "question": "堆排序怎么写"})
# format() - 返回字符串
prompt_str = chat_prompt.format(role="python开发", question="快速排序怎么写")
六、MessagesPlaceholder:动态插入历史消息
在多轮对话中,历史消息的数量和内容是不确定的。MessagesPlaceholder 解决了这个问题:
python
# 04_prompt/chat_prompt_template/placeholder/ChatPromptTemplate_ExplicitPlaceholder.py
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
prompt = ChatPromptTemplate([
("system", "你是一个资深的Python应用开发工程师"),
MessagesPlaceholder("memory"), # 历史消息注入点
("human", "{question}")
])
prompt_value = prompt.invoke({
"memory": [
HumanMessage("我的名字叫亮仔,是一名程序员"),
AIMessage("好的,亮仔你好")
],
"question": "请问你是谁"
})
显式写法:MessagesPlaceholder("memory")
隐式写法(简写):("placeholder", "{memory}")
两者等价。
ChatPromptTemplate 结构
SystemMessage
角色设定
MessagesPlaceholder
历史对话注入点
HumanMessage
当前问题
七、LLM 的 6 种调用方式
LangChain 提供了 3 种同步 + 3 种异步调用方式:
| 方法 | 类型 | 说明 |
|---|---|---|
invoke() |
同步 | 单次调用,等待完整结果 |
stream() |
同步 | 单次流式调用,逐字返回 |
batch() |
同步 | 批量调用,一次处理多个输入 |
ainvoke() |
异步 | invoke 的 async 版本 |
astream() |
异步 | stream 的 async 版本 |
abatch() |
异步 | batch 的 async 版本 |
7.1 同步调用示例
python
# invoke:单次调用
response = llm.invoke(message)
print(response.content)
# stream:流式输出
for chunk in llm.stream(message):
print(chunk.content, end="", flush=True)
# batch:批量处理
questions = ["什么是redis?", "Python生成器?", "Docker和K8s?"]
responses = llm.batch(questions)
for q, r in zip(questions, responses):
print(f"问题:{q}\n回答:{r.content}")
7.2 异步调用示例
python
import asyncio
# ainvoke:异步单次调用
async def main():
response = await llm.ainvoke("什么是LangChain,简洁回答")
print(response.content)
asyncio.run(main())
# astream:异步流式输出
async def async_stream():
async for chunk in llm.astream(message):
print(chunk.content, end="", flush=True)
asyncio.run(async_stream())
# abatch:异步批量处理
async def async_batch():
responses = await llm.abatch(questions)
for q, r in zip(questions, responses):
print(f"问题:{q}\n回答:{r.content}")
asyncio.run(async_batch())
八、从外部文件加载 Prompt
大型项目通常将 Prompt 存储在外部文件中,便于维护和版本管理。
8.1 JSON 格式
json
// 04_prompt/load_external/prompt.json
{
"_type": "prompt",
"input_variables": ["name", "what"],
"template": "请{name}讲一个{what}的故事"
}
8.2 YAML 格式
yaml
# 04_prompt/load_external/prompt.yaml
_type: "prompt"
input_variables: ["name", "what"]
template: "请{name}讲一个{what}的故事"
8.3 加载外部 Prompt
python
from langchain_core.prompts import load_prompt
# 从 JSON 加载
template = load_prompt("prompt.json", "utf-8")
prompt = template.format(name="张三", what="搞笑的")
# 从 YAML 加载
template = load_prompt("prompt.yaml", "utf-8")
prompt = template.format(name="张三", what="搞笑的")
九、知识地图
文本模板
多角色消息
显式
隐式
PromptTemplate
format / invoke / partial
ChatPromptTemplate
from_messages
MessagesPlaceholder
MessagesPlaceholder name
('placeholder', '{name}')
invoke: 同步单次
stream: 流式输出
batch: 批量处理
ainvoke / astream / abatch
外部文件
prompt.json
prompt.yaml
十、小结
| 要点 | 说明 |
|---|---|
PromptTemplate |
文本提示词模板,变量用 {var} 标记 |
ChatPromptTemplate |
多角色消息模板,支持 system/human/ai 角色 |
MessagesPlaceholder |
动态插入历史消息的占位符 |
invoke / stream / batch |
3 种同步调用方式 |
ainvoke / astream / abatch |
3 种异步调用方式 |
load_prompt |
从 JSON/YAML 文件加载提示词 |