1. 为什么需要提示词模板?
在构建LLM应用时,我们经常遇到一个核心矛盾:既希望提示词保持结构化以控制输出质量,又需要足够的灵活性来适应不同场景。直接拼接字符串的方式会导致几个典型问题:
- 代码中散落着大量重复的提示词片段,修改时需要全局搜索替换
- 不同环境的提示词版本管理混乱(开发/测试/生产环境)
- 难以系统性地评估提示词变更对最终效果的影响
LangChain的PromptTemplate正是为解决这些问题而生。它本质上是一个参数化的提示词生成器,通过将变量部分与固定结构分离,实现了三个关键特性:
- 可管理性:集中存储所有模板,修改时只需调整模板定义
- 可组合性:支持模板嵌套和链式调用,构建复杂提示结构
- 可测试性:每个模板可以独立验证,变更影响范围清晰可控
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础模板使用指南
2.1 快速创建第一个模板
最基本的PromptTemplate使用方式如下:
python复制from langchain.prompts import PromptTemplate
# 定义包含变量的模板
template = """你是一个专业的{role},请用{style}风格回答以下问题:
问题:{question}
回答:"""
prompt = PromptTemplate(
input_variables=["role", "style", "question"],
template=template,
)
# 填充变量生成最终提示词
filled_prompt = prompt.format(
role="营养师",
style="简明扼要",
question="糖尿病患者适合吃什么水果?"
)
关键点说明:
input_variables声明模板中需要外部传入的变量名- 变量使用花括号
{}包裹,命名建议采用snake_case风格 - 模板字符串推荐使用Python的三引号格式,便于维护多行内容
2.2 模板的进阶配置
实际项目中,我们通常需要更多控制选项:
python复制from langchain.prompts import PromptTemplate
prompt = PromptTemplate(
input_variables=["product", "features"],
template_format="f-string", # 默认为f-string,也支持jinja2
validate_template=True, # 启用模板语法校验
template="""
为{product}撰写商品描述,突出以下核心卖点:
{features}
要求:
- 使用第二人称"您"
- 长度不超过100字
- 包含情感号召力
"""
)
特别有用的参数:
template_format:支持f-string(默认)或jinja2语法validate_template:启动时会检查变量声明与模板是否匹配partial_variables:允许预设部分变量(后文详述)
注意:当使用jinja2格式时,需要额外安装jinja2包。这种格式更适合需要复杂逻辑控制的场景。
3. 模板组合技术
3.1 模板的链式调用
通过PipelinePromptTemplate可以实现模板的级联:
python复制from langchain.prompts import PipelinePromptTemplate, PromptTemplate
# 定义基础模板
base_template = """根据以下上下文回答问题:
{context}
问题:{question}
答案:"""
# 定义上下文生成模板
context_template = """相关背景知识:
{knowledge}"""
base_prompt = PromptTemplate.from_template(base_template)
context_prompt = PromptTemplate.from_template(context_template)
# 组合模板
full_prompt = PipelinePromptTemplate(
final_prompt=base_prompt,
pipeline_prompts=[
("context", context_prompt),
],
)
# 使用示例
result = full_prompt.format(
knowledge="LangChain是一个用于构建LLM应用的框架",
question="LangChain的主要用途是什么?"
)
这种结构特别适合:
- 需要动态插入不同模块内容的场景
- 保持核心问题模板稳定,只调整上下文部分
- 实现关注点分离,提高代码可维护性
3.2 模板的嵌套使用
更复杂的组合方式是通过FewShotPromptTemplate实现示例学习:
python复制from langchain.prompts import FewShotPromptTemplate, PromptTemplate
# 示例数据
examples = [
{
"input": "气候变化",
"output": "讨论全球变暖对极地生态系统的影响"
},
{
"input": "人工智能",
"output": "分析深度学习在医疗影像诊断中的应用前景"
}
]
# 单个示例的展示模板
example_template = """
输入:{input}
输出:{output}"""
example_prompt = PromptTemplate(
input_variables=["input", "output"],
template=example_template
)
# 最终提示词模板
few_shot_prompt = FewShotPromptTemplate(
examples=examples,
example_prompt=example_prompt,
prefix="你是一个话题扩展专家,根据简单输入生成深入讨论方向",
suffix="输入:{user_input}\n输出:",
input_variables=["user_input"],
example_separator="\n---\n" # 示例之间的分隔符
)
# 使用示例
prompt_text = few_shot_prompt.format(user_input="区块链")
这种模式的价值在于:
- 通过示例指导模型输出格式
- 动态调整示例数量而不改变核心逻辑
- 示例数据可以从外部数据源动态加载
4. 高级技巧与最佳实践
4.1 部分变量预设
当某些变量需要预先固定时,可以使用partial方法:
python复制from langchain.prompts import PromptTemplate
prompt = PromptTemplate(
template="作为{role},请用{tone}语气回答:{question}",
input_variables=["role", "tone", "question"]
)
# 预设部分变量
formal_doctor_prompt = prompt.partial(
role="资深医师",
tone="专业严谨"
)
# 使用时只需提供剩余变量
final_prompt = formal_doctor_prompt.format(
question="高血压患者应该如何安排饮食?"
)
典型应用场景:
- 角色设定固定但内容变化的对话系统
- 多阶段流程中保持部分参数一致
- 创建具有统一风格的衍生模板
4.2 模板的版本管理
建议采用如下目录结构管理模板:
code复制prompts/
├── v1/
│ ├── customer_service/
│ │ ├── general_query.jinja2
│ │ └── complaint_handling.jinja2
│ └── data_analysis/
│ ├── summary.jinja2
│ └── visualization.jinja2
└── v2/
└── customer_service/
└── general_query.jinja2
配合配置系统实现模板的热加载:
python复制from langchain.prompts import load_prompt
def get_prompt(version, domain, name):
path = f"./prompts/{version}/{domain}/{name}.jinja2"
return load_prompt(path)
# 使用示例
prompt = get_prompt("v2", "customer_service", "general_query")
4.3 模板的单元测试
为关键模板编写验证测试:
python复制import unittest
from langchain.prompts import PromptTemplate
class TestPrompts(unittest.TestCase):
def test_summary_prompt(self):
template = """总结以下文本:
{text}
要求:
- 不超过{max_words}字
- 包含所有关键事实"""
prompt = PromptTemplate(
input_variables=["text", "max_words"],
template=template,
validate_template=True
)
# 测试变量填充
test_prompt = prompt.format(
text="测试内容",
max_words=100
)
self.assertIn("测试内容", test_prompt)
self.assertIn("100", test_prompt)
if __name__ == "__main__":
unittest.main()
测试要点应包括:
- 变量填充是否正确
- 必填参数是否完备
- 输出长度是否符合预期
- 敏感词过滤是否生效
5. 常见问题排查
5.1 变量缺失错误
典型错误信息:
code复制ValueError: Missing required input variables: ['var_name']
解决方案:
- 检查模板中声明的
input_variables是否包含所有需要的变量 - 确保调用
format()时传入了所有必需参数 - 考虑使用
partial预设部分变量
5.2 模板语法错误
当使用jinja2格式时可能遇到模板语法错误:
code复制jinja2.exceptions.TemplateSyntaxError: expected token 'end of print statement', got '}'
调试建议:
- 使用
validate_template=False暂时跳过验证,定位问题区域 - 确保jinja2的特殊标签
{% %}和{{ }}正确配对 - 复杂逻辑建议先在单独的jinja2验证工具中测试
5.3 性能优化技巧
当处理大量模板时:
- 复用PromptTemplate实例(避免重复创建)
- 对不变的部分使用
partial - 考虑使用
RedisPromptCache等缓存机制 - 异步处理模板生成(如使用
asyncio)
6. 实战案例:构建客服问答系统
6.1 系统架构设计
code复制用户请求 → 路由层 → 选择模板 → 填充变量 → 调用LLM → 返回响应
↑
模板管理库
6.2 核心模板实现
knowledge_base.jinja2:
jinja2复制根据以下知识库内容回答问题:
{% for item in knowledge %}
- {{ item }}
{% endfor %}
用户问题:{{ question }}
{% if history %}
对话历史:
{{ history }}
{% endif %}
请用{{ tone }}的语气回答,限制在{{ max_length }}字以内。
6.3 动态模板加载
python复制from langchain.prompts import load_prompt
from jinja2 import Environment, FileSystemLoader
class PromptManager:
def __init__(self):
self.env = Environment(
loader=FileSystemLoader('./templates'),
autoescape=True
)
def get_prompt(self, name, **kwargs):
template = self.env.get_template(f"{name}.jinja2")
return template.render(**kwargs)
# 使用示例
manager = PromptManager()
prompt_text = manager.get_prompt(
"knowledge_base",
knowledge=["产品保修期1年", "支持7天无理由退货"],
question="我的设备买了10个月出现故障怎么办?",
tone="专业友好",
max_length=200
)
6.4 效果评估指标
建立模板评估体系:
- 响应相关性(0-5分)
- 风格符合度(0-5分)
- 平均响应时间
- 用户满意度调查
通过A/B测试持续优化模板设计。
