04 | Prompt Engineering:提示词工程

04 | Prompt Engineering:提示词工程

提示词工程是 LLM 应用开发的核心技能。本模块详细讲解 LangChain 中 Prompt 的创建、管理、模板化以及高级用法。


一、Prompt 的本质

Prompt 就是你发给 LLM 的指令。一个好的 Prompt = 角色设定 + 任务描述 + 输出格式要求

复制代码
┌─────────────────────────────────────────────┐
│  你是一个专业的{role}工程师                  │  ← 角色设定
│  请回答我的问题,我的问题是:{question}       │  ← 任务描述
│  结果返回json格式                            │  ← 格式要求
└─────────────────────────────────────────────┘

LangChain 提供了 PromptTemplateChatPromptTemplate 两个核心类来 规范化创建和管理提示词


二、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 文件加载提示词
相关推荐
郝学胜-神的一滴1 小时前
干货版《算法导论》04:渐近复杂度与序列接口实战
java·开发语言·数据结构·c++·python·算法
神明9311 小时前
CSS 背景图滑动切换:纯 CSS 实现右进左出轮播效果
jvm·数据库·python
wang3zc1 小时前
CSS如何让最后一行项目左对齐_利用flex布局配合伪元素空项填充
jvm·数据库·python
2303_821287381 小时前
如何用 Chrome 的 Rendering 面板监控页面的重排频率
jvm·数据库·python
_Rookie._1 小时前
部署python后端,以及Dockerfile 的 RUN CMD ENTRYPOINT字段
开发语言·python
m0_631529821 小时前
C#怎么解析XML文件 C#如何用XmlDocument和LINQ to XML读写XML数据【基础】
jvm·数据库·python
dr_yingli1 小时前
Pictologics提取图像特征(类似pyradiomics影像组学包)的详细使用指南
python
Jetev1 小时前
如何配置MongoDB驱动以支持快速的主备切换感知_SRV记录与拓扑监控
jvm·数据库·python
m0_631529821 小时前
golang如何实现目录大小统计_golang目录大小统计实现方案
jvm·数据库·python