3. 提示(Prompts)工程:从模板到序列化
提示工程,说白了就是跟大模型打交道的艺术。你给它的提示好不好,直接决定它回你的东西靠不靠谱。我在项目里见过太多人,模型选得挺好,结果提示写得稀烂,最后怪模型不行——其实问题出在自己身上。
LangChain 把提示工程做成了标准化工具。你不用每次都手写提示,也不用担心格式不一致。今天我们就把它拆开揉碎了讲清楚。
3.1 提示模板(PromptTemplate)
先看最基础的。PromptTemplate 就是给提示留几个变量位置,运行时往里填值。举个例子:
from langchain.prompts import PromptTemplate
template = "请用{style}风格,写一段关于{topic}的简介。"
prompt = PromptTemplate(
input_variables=["style", "topic"],
template=template
)
result = prompt.format(style="幽默", topic="机器学习")
print(result)
# 输出:请用幽默风格,写一段关于机器学习的简介。
嗯,这里要注意:input_variables 必须跟模板里的变量名完全一致。我刚开始用的时候拼错过一次,查了半天才发现是变量名写了个空格。
user_name、product_desc。这样不容易跟 Python 关键字冲突,也方便后期维护。
3.2 聊天提示模板(ChatPromptTemplate)
如果你用的是聊天模型(比如 GPT-4、Claude),那 ChatPromptTemplate 更合适。它支持多轮对话结构:
from langchain.prompts import ChatPromptTemplate
from langchain.prompts.chat import SystemMessagePromptTemplate, HumanMessagePromptTemplate
system_template = "你是一个{role}专家,回答要简洁专业。"
human_template = "请解释一下{concept}"
chat_prompt = ChatPromptTemplate.from_messages([
SystemMessagePromptTemplate.from_template(system_template),
HumanMessagePromptTemplate.from_template(human_template)
])
messages = chat_prompt.format_messages(
role="网络安全",
concept="零信任架构"
)
# 返回两个 Message 对象:system 和 human
我在做客服机器人项目时,就靠这个区分了系统指令和用户输入。系统消息里写规则,用户消息里写问题,模型理解得特别清楚。
| 消息类型 | 用途 | 示例 |
|---|---|---|
| SystemMessage | 设定角色、行为规则 | "你是一个耐心的老师" |
| HumanMessage | 用户输入 | "什么是梯度下降?" |
| AIMessage | 模型回复 | "梯度下降是一种优化算法..." |
3.3 示例选择器(ExampleSelector)
少样本提示里,选哪些示例很关键。ExampleSelector 就是帮你做这件事的。它有好几种策略:
- 固定选择:每次都取同样的示例
- 随机选择:从池子里随机抽
- 语义相似度选择:根据输入找最像的示例
我个人最常用的是语义相似度选择器。它会把你的输入跟示例库里的每条记录算相似度,挑最相关的几条。效果比随机选好太多了。
from langchain.prompts.example_selector import SemanticSimilarityExampleSelector
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import FAISS
examples = [
{"input": "苹果", "output": "水果,红色或绿色"},
{"input": "香蕉", "output": "水果,黄色,长条形"},
{"input": "汽车", "output": "交通工具,四个轮子"},
]
selector = SemanticSimilarityExampleSelector.from_examples(
examples,
OpenAIEmbeddings(),
FAISS,
k=2
)
selected = selector.select_examples({"input": "草莓"})
# 会返回"苹果"和"香蕉"这两个示例,因为它们跟"草莓"语义更近
3.4 少样本提示(FewShotPromptTemplate)
FewShotPromptTemplate 把示例选择器和提示模板组合在一起。你给它几个例子,它就能让模型照着例子来回答。
from langchain.prompts import FewShotPromptTemplate, PromptTemplate
example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="输入: {input}\n输出: {output}"
)
few_shot_prompt = FewShotPromptTemplate(
examples=examples, # 就是上面那个示例列表
example_prompt=example_prompt,
prefix="请根据以下示例,为新的输入生成输出:",
suffix="输入: {new_input}\n输出:",
input_variables=["new_input"]
)
result = few_shot_prompt.format(new_input="西瓜")
print(result)
# 输出:
# 请根据以下示例,为新的输入生成输出:
# 输入: 苹果
# 输出: 水果,红色或绿色
# 输入: 香蕉
# 输出: 水果,黄色,长条形
# 输入: 西瓜
# 输出:
你看,它自动把示例拼到了提示里。模型看到前面两个例子,就知道该输出什么格式了。这比直接问"西瓜是什么"要稳定得多。
3.5 提示序列化与加载
项目做大了,提示模板不能都写在代码里。得存成文件,方便管理和复用。LangChain 支持序列化成 JSON 或 YAML:
# 保存为 JSON
prompt.save("my_prompt.json")
# 从 JSON 加载
from langchain.prompts import load_prompt
loaded_prompt = load_prompt("my_prompt.json")
# 保存为 YAML
with open("my_prompt.yaml", "w") as f:
f.write(prompt.to_yaml())
我习惯把提示模板按功能分目录存:
prompts/summary/— 摘要类提示prompts/qa/— 问答类提示prompts/code/— 代码生成类提示
这样团队协作时,谁改了什么一目了然。而且不同环境(开发、测试、生产)可以加载不同版本的提示文件。
summary_v2.json。回滚的时候特别方便。
知识结构总览
下面这张图把本章的知识点串起来了。你可以看到提示工程在 LangChain 里的完整脉络:
从图上你能看到,提示工程不是孤立的知识点。模板负责结构,示例负责引导,序列化负责管理。三块配合好了,你的提示质量才能稳定。