从零开始学LangChain(三):Prompts 提示词工程
本系列教程将带你从零开始学习LangChain框架,构建强大的AI应用程序。
什么是提示词工程?
提示词工程(Prompt Engineering)是指设计和优化输入给大语言模型的文本,以获得更准确、更符合预期的输出。
想象一下,LLM就像一个非常聪明但需要明确指令的助手。如果你给的指令模糊不清,输出的结果可能不尽如人意。而好的提示词能让LLM准确理解你的意图,给出高质量的回复。
提示词有五个核心要素需要关注。角色设定告诉AI扮演什么角色,比如”你是一位资深Python工程师”。任务描述清晰说明要完成什么,比如”请解释什么是装饰器”。输入数据提供必要的上下文信息,比如具体的代码片段。输出格式指定期望的形式,比如”请用JSON格式返回”。约束条件限制输出的范围或方式,比如”不超过100字”。
提示词模板基础
LangChain提供了强大的提示词模板功能,让提示词的管理和复用变得简单。
PromptTemplate
最基础的提示词模板类是PromptTemplate。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| from langchain.prompts import PromptTemplate
template = """ 请解释以下{language}概念:{concept} 要求: 1. 用简单的比喻 2. 提供一个代码示例 3. 字数不超过{word_limit}字 """
prompt = PromptTemplate( template=template, input_variables=["language", "concept", "word_limit"] )
formatted_prompt = prompt.format( language="Python", concept="装饰器", word_limit=200 )
print(formatted_prompt)
|
更简洁的方式是使用from_template。
1 2 3 4 5 6 7 8 9 10 11 12 13
| from langchain.prompts import PromptTemplate
prompt = PromptTemplate.from_template( "请用{style}风格解释{topic},不超过{max_words}字" )
prompt_input = prompt.format( style="幽默", topic="递归", max_words=100 )
|
ChatPromptTemplate
专为对话场景设计的提示词模板。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| from langchain.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([ ("system", "你是一位资深的{role},擅长用{method}教学。"), ("human", "请解释:{topic}") ])
messages = prompt.format_messages( role="Python导师", method="比喻", topic="生成器" )
print(messages)
|
也可以创建更复杂的对话模板,包含对话历史占位符。
1 2 3 4 5 6 7 8 9 10
| from langchain.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([ ("system", "你是一位专业的客服代表。"), ("placeholder", "{chat_history}"), ("human", "{user_input}"), ("messages", "additional", [ ("ai", "请问还有什么可以帮您的吗?") ]) ])
|
少样本提示
给模型提供示例,让它理解你想要的输出格式,这被称为少样本提示(Few-shot prompting)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| from langchain.prompts import FewShotPromptTemplate from langchain.prompts.prompt import PromptTemplate
examples = [ { "question": "苹果是什么颜色的?", "answer": "苹果通常是红色、绿色或黄色的。" }, { "question": "天空是什么颜色的?", "answer": "晴朗的天空是蓝色的。" } ]
example_prompt = PromptTemplate( input_variables=["question", "answer"], template="问题:{question}\n答案:{answer}\n" )
few_shot_prompt = FewShotPromptTemplate( examples=examples, example_prompt=example_prompt, prefix="以下是一些问答示例:\n\n", suffix="\n\n现在请回答:\n问题:{input}", input_variables=["input"], example_separator="\n" )
final_prompt = few_shot_prompt.format(input="香蕉是什么颜色的?") print(final_prompt)
|
还可以动态选择示例,根据输入长度自动选择最相关的示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| from langchain.prompts import FewShotPromptTemplate from langchain.prompts.example_selector import LengthBasedExampleSelector
all_examples = [ {"input": "2+2=?", "output": "4"}, {"input": "3*3=?", "output": "9"}, {"input": "10/2=?", "output": "5"}, {"input": "5+5=?", "output": "10"}, ]
example_selector = LengthBasedExampleSelector( examples=all_examples, example_prompt=PromptTemplate( input_variables=["input", "output"], template="输入:{input} 输出:{output}" ), max_length=20 )
dynamic_prompt = FewShotPromptTemplate( example_selector=example_selector, example_prompt=PromptTemplate( input_variables=["input", "output"], template="输入:{input} 输出:{output}" ), suffix="\n输入:{input} 输出:", prefix="数学计算示例:", input_variables=["input"] )
print(dynamic_prompt.format(input="100/10=?"))
|
部分变量填充
有时候你想先填充部分变量,稍后再填充剩余变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| from langchain.prompts import PromptTemplate
prompt = PromptTemplate( template="请用{language}编写一个{task},要求:{requirements}", input_variables=["language", "task", "requirements"] )
partial_prompt = prompt.partial( language="Python", task="计算斐波那契数列的函数" )
final_prompt = partial_prompt.format( requirements="使用递归实现,添加类型注解" )
print(final_prompt)
|
还可以使用函数进行部分填充,每次调用都会获取新的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| from datetime import datetime
def get_current_date(): """获取当前日期""" return datetime.now().strftime("%Y-%m-%d")
prompt = PromptTemplate( template="今天是{date},请生成一份{report_type}报告。", input_variables=["date", "report_type"] )
partial_prompt = prompt.partial( date=get_current_date )
print(partial_prompt.format(report_type="销售"))
|
输出解析器
控制LLM的输出格式,使其更易于程序处理。
CSV解析器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| from langchain.prompts import PromptTemplate from langchain.output_parsers import CommaSeparatedListOutputParser
parser = CommaSeparatedListOutputParser()
format_instructions = parser.get_format_instructions()
prompt = PromptTemplate( template="列出5个{topic}。\n{format_instructions}", input_variables=["topic"], partial_variables={"format_instructions": format_instructions} )
input_prompt = prompt.format(topic="Python Web框架") print(input_prompt)
parsed_output = parser.parse("Django, Flask, FastAPI, Tornado, Pyramid") print(parsed_output)
|
结构化输出解析器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| from langchain.prompts import PromptTemplate from langchain.output_parsers import StructuredOutputParser from pydantic import BaseModel, Field
class MovieReview(BaseModel): title: str = Field(description="电影标题") rating: float = Field(description="评分(0-10)") summary: str = Field(description="剧情简介") pros: list[str] = Field(description="优点列表") cons: list[str] = Field(description="缺点列表")
parser = StructuredOutputParser.from_pydantic(MovieReview)
prompt = PromptTemplate( template="请对电影《{movie_name}》进行评价。\n{format_instructions}", input_variables=["movie_name"], partial_variables={"format_instructions": parser.get_format_instructions()} )
formatted_prompt = prompt.format(movie_name="肖申克的救赎") print(formatted_prompt)
|
提示词优化技巧
明确角色和任务
清晰的定义比模糊的指令效果更好。
1 2 3 4 5 6 7 8 9 10 11
| bad_prompt = "写一个排序算法"
good_prompt = """ 你是一位计算机科学教授,擅长用通俗易懂的方式解释算法。 请用Python写一个快速排序算法,要求: 1. 添加详细的注释 2. 解释算法的时间复杂度 3. 提供一个使用示例 """
|
提供上下文信息
完整的上下文信息帮助模型更好理解你的需求。
1 2 3 4 5 6
| bad_prompt = "这段代码有什么问题?\n代码:x = [1, 2, 3]"
good_prompt = """ 以下代码试图从列表中移除偶数,但运行结果不正确:
|
x = [1, 2, 3, 4, 5]
for i in x:
if i % 2 == 0:
x.remove(i)
print(x) # 期望输出 [1, 3, 5],实际输出 [1, 3, 4]
使用思维链
让模型逐步推理,得到更准确的答案。
1 2 3 4 5 6 7 8 9 10 11 12
| chain_prompt = """ 请解答以下问题,要求一步步思考:
问题:一个农场有鸡和兔子共35只,腿共94条,问鸡和兔子各多少只?
请按以下步骤回答: 1. 设未知数 2. 列出方程 3. 解方程 4. 验证答案 """
|
实战示例:智能提示词生成器
创建一个工具,帮助生成高质量的提示词。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| from langchain_openai import ChatOpenAI from langchain.prompts import ChatPromptTemplate
class PromptOptimizer: """提示词优化器"""
def __init__(self): self.chat = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.3)
def optimize_prompt( self, original_prompt: str, context: str = "", requirements: str = "" ) -> str: """优化提示词"""
system_msg = """你是一位提示词工程专家,擅长将简单的需求转换为高质量的提示词。 优化后的提示词应该: 1. 角色设定清晰 2. 任务描述明确 3. 包含必要的约束条件 4. 提供输出格式要求"""
user_msg = f"""原始提示词:{original_prompt}
上下文信息:{context} 特殊要求:{requirements}
请优化这个提示词,使其更加有效。"""
prompt = ChatPromptTemplate.from_messages([ ("system", system_msg), ("human", user_msg) ])
chain = prompt | self.chat return chain.invoke({}).content
optimizer = PromptOptimizer()
original = "写代码" context = "需要实现一个用户登录功能" requirements = "使用Flask框架,包含密码哈希"
optimized = optimizer.optimize_prompt(original, context, requirements) print("=== 优化后的提示词 ===") print(optimized)
|
常见错误
变量名不匹配
症状是出现KeyError: 'missing_input_variable'错误。解决方案是确保模板中的变量名与format时使用的变量名一致。
1 2 3 4 5 6 7 8
| from langchain.prompts import PromptTemplate
prompt = PromptTemplate.from_template("解释{topic}") prompt.format(subject="Python")
prompt.format(topic="Python")
|
忘记转义大括号
症状是出现ValueError: Invalid interpolation错误。解决方案是使用双大括号转义,或者使用jinja2模板格式。
1 2 3 4 5 6 7 8 9 10 11
| template = "使用{{变量}}"
template = "使用{{{prefix}_变量}}"
prompt = PromptTemplate( template="使用 {{ 变量 }}", template_format="jinja2" )
|
系列导航