====== 第三章:核心组件详解 - Prompts ======
在 LangChain 框架中,Prompt(提示词)是与大语言模型(LLM)交互的核心桥梁。精心设计的提示词可以显著提升模型的输出质量和准确性。本章将深入探讨 LangChain 中的提示词工程、PromptTemplate 的使用、Few-shot Prompting 技术以及动态提示词的管理方法。
===== 3.1 提示词工程基础 =====
==== 3.1.1 什么是提示词工程 ====
提示词工程(Prompt Engineering)是指通过设计和优化输入提示词,引导大语言模型生成高质量、符合预期的输出内容的技术。它是与大语言模型交互的艺术和科学。
**提示词工程的重要性:**
* **输出质量**:好的提示词能让模型产生更准确、更有用的回答
* **一致性**:通过结构化提示词,可以确保模型输出的格式一致
* **可控性**:提示词工程让我们能更好地控制模型的行为和输出风格
* **效率**:优化后的提示词可以减少不必要的迭代和修正
==== 3.1.2 提示词的基本结构 ====
一个完整的提示词通常包含以下几个部分:
**1. 指令(Instruction)**
告诉模型需要执行什么任务。这是最核心的部分。
instruction = "请用简洁的语言总结以下文章的主要内容:"
**2. 上下文(Context)**
提供完成任务所需的背景信息。
context = """
这篇文章发布于2024年,作者是人工智能领域的专家。
目标读者是技术初学者,因此需要使用通俗易懂的语言。
"""
**3. 输入数据(Input Data)**
模型需要处理的具体内容。
input_data = """
人工智能(AI)正在改变我们的生活方式...
[文章内容]
"""
**4. 输出指示(Output Indicator)**
明确说明期望的输出格式。
output_indicator = """
请输出:
1. 核心观点(50字以内)
2. 三个关键要点
3. 结论
"""
**完整的提示词示例:**
full_prompt = f"""
{instruction}
{context}
文章:
{input_data}
{output_indicator}
"""
==== 3.1.3 提示词设计原则 ====
**原则一:清晰明确**
提示词应该清楚地表达你想要什么。模糊的要求会导致模糊的答案。
# ❌ 不好的示例
bad_prompt = "写点东西关于苹果"
# ✅ 好的示例
good_prompt = """
请写一篇200字的产品描述,介绍 iPhone 15 的主要特点:
- 钛金属设计
- A17 Pro 芯片
- 专业级摄像系统
语气要求:专业、吸引人、面向科技爱好者
"""
**原则二:提供上下文**
模型不知道你知道什么。提供足够的背景信息可以帮助模型更好地理解任务。
# ❌ 缺乏上下文
no_context = "分析这个数据"
# ✅ 提供上下文
with_context = """
背景:你是一位数据分析专家,正在为一家电商公司工作。
数据:2024年第一季度销售数据
- 总销售额:1000万元
- 订单数量:5000单
- 退货率:5%
请分析这些数据,并给出改进建议。
"""
**原则三:指定输出格式**
如果你想要特定格式的输出,一定要在提示词中明确说明。
format_prompt = """
请根据以下客户反馈,生成一份报告。
反馈内容:
"产品很好用,但是配送太慢了,希望改进物流服务。"
输出格式(JSON):
{{
"sentiment": "正面/负面/中性",
"category": "产品/物流/服务/其他",
"key_points": ["要点1", "要点2"],
"urgency": "高/中/低"
}}
"""
**原则四:使用分隔符**
使用清晰的分隔符(如###、---、""")来区分提示词的不同部分。
structured_prompt = """
### 指令
总结以下文章的主要观点。
### 文章
"""
{article_content}
"""
### 要求
- 不超过100字
- 使用 bullet points
- 突出关键数据
"""
**原则五:给出示例(Few-shot)**
当任务比较复杂或格式要求严格时,提供示例是最好的方式。
few_shot_prompt = """
将以下描述转换为结构化数据。
示例1:
输入:张三,25岁,软件工程师,北京
输出:{{"name": "张三", "age": 25, "job": "软件工程师", "city": "北京"}}
示例2:
输入:李四,30岁,产品经理,上海
输出:{{"name": "李四", "age": 30, "job": "产品经理", "city": "上海"}}
现在请处理:
输入:王五,28岁,数据分析师,深圳
输出:
"""
==== 3.1.4 常见的提示词模式 ====
**模式一:角色扮演(Role Prompting)**
让模型扮演特定角色,以获得更专业的回答。
role_prompt = """
你是一位经验丰富的心理咨询师,拥有15年的临床经验。
一位来访者说:"我最近工作压力很大,晚上睡不着,不知道该怎么办。"
请给出专业的建议,注意:
1. 语气温和、同理心强
2. 提供具体可行的建议
3. 如果情况严重,建议寻求专业帮助
"""
**模式二:思维链(Chain-of-Thought)**
引导模型逐步思考,而不是直接给出答案。
cot_prompt = """
问题:一个农场有鸡和兔子,一共有35个头,94只脚。鸡和兔子各有多少只?
请按以下步骤思考并回答:
1. 设鸡的数量为x,兔子的数量为y
2. 根据头的数量列出方程
3. 根据脚的数量列出方程
4. 解方程组
5. 验证答案
详细展示你的思考过程。
"""
**模式三:零样本思维链(Zero-shot CoT)**
即使没有示例,也可以通过简单的指令引导模型逐步思考。
zero_shot_cot = """
问题:如果5台机器5分钟生产5个零件,那么100台机器生产100个零件需要多少分钟?
让我们一步一步思考:
"""
**模式四:自洽性检查(Self-Consistency)**
让模型从多个角度思考问题,然后选择最一致的答案。
self_consistency_prompt = """
问题:解释为什么天空是蓝色的。
请从以下三个角度分别解释,然后综合得出最准确的答案:
1. 物理学角度(光的散射)
2. 生物学角度(人眼感知)
3. 大气科学角度(大气成分)
最后,整合以上观点,给出简洁准确的解释。
"""
===== 3.2 PromptTemplate 详解 =====
==== 3.2.1 PromptTemplate 概述 ====
PromptTemplate 是 LangChain 中用于创建和管理提示词的核心组件。它允许你定义带有变量的模板,并在运行时动态填充这些变量。
**为什么使用 PromptTemplate?**
* **可复用性**:定义一次模板,多次使用
* **类型安全**:变量可以被验证和处理
* **灵活性**:轻松切换不同的模型和配置
* **维护性**:集中管理提示词,易于修改
==== 3.2.2 基础使用 ====
**安装和导入:**
# 安装依赖
# pip install langchain
from langchain.prompts import PromptTemplate
**创建基础模板:**
from langchain.prompts import PromptTemplate
# 定义一个简单的模板
template = "请用{style}的风格,写一段关于{topic}的描述,大约{length}字。"
# 创建 PromptTemplate 对象
prompt_template = PromptTemplate(
input_variables=["style", "topic", "length"],
template=template
)
# 查看模板结构
print(prompt_template.input_variables) # ['style', 'topic', 'length']
**格式化模板:**
# 方式1:使用 format 方法
prompt = prompt_template.format(
style="幽默",
topic="人工智能",
length=100
)
print(prompt)
# 输出:请用幽默的风格,写一段关于人工智能的描述,大约100字。
# 方式2:使用 format_prompt 方法(返回 PromptValue 对象)
prompt_value = prompt_template.format_prompt(
style="专业",
topic="区块链",
length=200
)
print(prompt_value.to_string())
==== 3.2.3 高级模板功能 ====
**部分变量(Partial Variables)**
有时你希望预先填充部分变量,创建一个"半成品"模板。
from langchain.prompts import PromptTemplate
# 定义完整模板
full_template = PromptTemplate(
template="你是一位{role},请用{language}回答:{question}",
input_variables=["role", "language", "question"]
)
# 部分填充:预先设定角色
partial_template = full_template.partial(role="Python专家")
# 使用时只需提供剩余变量
prompt1 = partial_template.format(
language="中文",
question="什么是装饰器?"
)
print(prompt1)
# 输出:你是一位Python专家,请用中文回答:什么是装饰器?
# 可以再次部分填充
chinese_template = partial_template.partial(language="中文")
prompt2 = chinese_template.format(question="解释列表推导式")
print(prompt2)
**使用函数作为部分变量:**
from datetime import datetime
def get_current_date():
return datetime.now().strftime("%Y年%m月%d日")
template_with_date = PromptTemplate(
template="今天是{date},请根据以下信息生成报告:{content}",
input_variables=["content"],
partial_variables={"date": get_current_date}
)
prompt = template_with_date.format(content="销售数据...")
print(prompt)
# 输出:今天是2024年01月15日,请根据以下信息生成报告:销售数据...
==== 3.2.4 模板验证 ====
PromptTemplate 会自动验证输入变量,确保所有必需变量都已提供。
from langchain.prompts import PromptTemplate
# 定义模板
template = PromptTemplate(
template="你好{name},欢迎{action}!",
input_variables=["name", "action"]
)
# ✅ 正确:提供所有变量
try:
prompt = template.format(name="张三", action="加入我们")
print("成功:", prompt)
except Exception as e:
print("错误:", e)
# ❌ 错误:缺少变量
try:
prompt = template.format(name="张三") # 缺少 action
except KeyError as e:
print(f"缺少变量: {e}")
==== 3.2.5 自定义输入解析器 ====
有时输入变量需要特殊的处理,你可以自定义输入变量的解析方式。
from langchain.prompts import PromptTemplate
from langchain.prompts.base import StringPromptValue
class MyPromptTemplate(PromptTemplate):
def format(self, **kwargs) -> str:
# 自定义格式化逻辑
if "name" in kwargs:
kwargs["name"] = kwargs["name"].upper()
return super().format(**kwargs)
template = MyPromptTemplate(
template="尊敬的{name},{message}",
input_variables=["name", "message"]
)
prompt = template.format(name="alice", message="欢迎光临")
print(prompt) # 尊敬的ALICE,欢迎光临
===== 3.3 Few-shot Prompting =====
==== 3.3.1 什么是 Few-shot Prompting ====
Few-shot Prompting(少样本提示)是一种通过提供少量示例来指导模型完成任务的技术。它特别适用于:
* 需要特定输出格式的任务
* 复杂的推理任务
* 风格转换或语气调整
* 特定的命名实体识别
==== 3.3.2 基础示例 ====
from langchain.prompts import FewShotPromptTemplate, PromptTemplate
# 定义示例
examples = [
{
"input": "今天天气真好,我想去公园散步。",
"output": "积极"
},
{
"input": "这部电影太糟糕了,完全浪费时间。",
"output": "消极"
},
{
"input": "会议定在下午三点。",
"output": "中性"
}
]
# 定义示例的格式
example_template = """
文本:{input}
情感:{output}
"""
example_prompt = PromptTemplate(
template=example_template,
input_variables=["input", "output"]
)
# 创建 FewShotPromptTemplate
few_shot_prompt = FewShotPromptTemplate(
examples=examples,
example_prompt=example_prompt,
prefix="请判断以下文本的情感倾向(积极/消极/中性):",
suffix="\n文本:{input}\n情感:",
input_variables=["input"],
example_separator="\n---\n"
)
# 生成提示词
prompt = few_shot_prompt.format(input="这家餐厅的饭菜真好吃!")
print(prompt)
**输出示例:**
请判断以下文本的情感倾向(积极/消极/中性):
文本:今天天气真好,我想去公园散步。
情感:积极
---
文本:这部电影太糟糕了,完全浪费时间。
情感:消极
---
文本:会议定在下午三点。
情感:中性
---
文本:这家餐厅的饭菜真好吃!
情感:
==== 3.3.3 示例选择器(Example Selectors) ====
当示例很多时,你可能只想选择最相关的几个示例。LangChain 提供了多种示例选择器。
**长度限制选择器:**
from langchain.prompts import FewShotPromptTemplate
from langchain.prompts.example_selector import LengthBasedExampleSelector
examples = [
{"input": "happy", "output": "sad"},
{"input": "tall", "output": "short"},
{"input": "energetic", "output": "lethargic"},
{"input": "sunny", "output": "gloomy"},
{"input": "windy", "output": "calm"},
]
example_prompt = PromptTemplate(
template="输入: {input}\n输出: {output}",
input_variables=["input", "output"]
)
# 根据长度选择示例
example_selector = LengthBasedExampleSelector(
examples=examples,
example_prompt=example_prompt,
max_length=100 # 最大长度限制
)
dynamic_prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=example_prompt,
prefix="请找出以下单词的反义词:",
suffix="输入: {input}\n输出:",
input_variables=["input"]
)
# 输入越长,选择的示例越少
short_prompt = dynamic_prompt.format(input="big")
print("短输入的提示词长度:", len(short_prompt))
long_prompt = dynamic_prompt.format(input="这是一个非常长的输入,包含很多字符...")
print("长输入的提示词长度:", len(long_prompt))
**语义相似度选择器:**
from langchain.prompts import FewShotPromptTemplate
from langchain.prompts.example_selector import SemanticSimilarityExampleSelector
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
examples = [
{"question": "谁活了最长?", "answer": "艾德加多·阿尔瓦伦加活了138年。"},
{"question": "2023年谁是美国总统?", "answer": "乔·拜登是2023年的美国总统。"},
{"question": "《呼啸山庄》的作者是谁?", "answer": "艾米莉·勃朗特是《呼啸山庄》的作者。"},
{"question": "谁发明了相对论?", "answer": "阿尔伯特·爱因斯坦发明了相对论。"},
{"question": "《哈利·波特》的作者是谁?", "answer": "J.K.罗琳是《哈利·波特》的作者。"},
]
example_prompt = PromptTemplate(
template="问题: {question}\n答案: {answer}",
input_variables=["question", "answer"]
)
# 使用语义相似度选择器
example_selector = SemanticSimilarityExampleSelector.from_examples(
examples,
OpenAIEmbeddings(),
Chroma,
k=2 # 选择最相似的2个示例
)
similar_prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=example_prompt,
prefix="请根据示例回答以下问题:",
suffix="问题: {input}\n答案:",
input_variables=["input"]
)
# 输入与示例相关的问题
prompt = similar_prompt.format(input="《指环王》是谁写的?")
print(prompt)
# 可能会选择《哈利·波特》和《呼啸山庄》相关的示例
==== 3.3.4 Few-shot 最佳实践 ====
**选择合适的示例数量:**
# 通常 3-5 个示例效果最好
# 太多示例可能导致:
# 1. Token 消耗增加
# 2. 上下文窗口限制
# 3. 模型注意力分散
# 平衡策略
examples = [
# 示例1:展示基本格式
{...},
# 示例2:展示边界情况
{...},
# 示例3:展示复杂场景
{...},
]
**示例的质量要求:**
# ✅ 好的示例特点:
# 1. 清晰明确
# 2. 覆盖不同场景
# 3. 格式一致
# 4. 输入输出相关
good_examples = [
{
"instruction": "总结以下文章",
"input": "[一篇500字的文章]",
"output": "[简洁的50字总结]"
},
{
"instruction": "提取关键信息",
"input": "[同样的文章]",
"output": "[关键数据列表]"
}
]
# ❌ 避免:
# 1. 示例之间矛盾
# 2. 格式不一致
# 3. 示例与任务不相关
===== 3.4 Prompt 组合与管理 =====
==== 3.4.1 组合多个 Prompt ====
在实际应用中,你可能需要组合多个提示词模板来构建复杂的工作流。
**使用 PipelinePromptTemplate:**
from langchain.prompts import PipelinePromptTemplate, PromptTemplate
# 定义各个阶段的模板
introduction_template = """你是一个{role}。
你的任务是:{task}
"""
introduction_prompt = PromptTemplate(
template=introduction_template,
input_variables=["role", "task"]
)
example_template = """以下是几个示例:
{examples}
"""
example_prompt = PromptTemplate(
template=example_template,
input_variables=["examples"]
)
question_template = """现在请回答以下问题:
{question}
"""
question_prompt = PromptTemplate(
template=question_template,
input_variables=["question"]
)
# 组合成完整的 pipeline
full_template = """{introduction}
{example}
{question}"""
full_prompt = PromptTemplate(
template=full_template,
input_variables=["introduction", "example", "question"]
)
# 创建 pipeline
pipeline_prompt = PipelinePromptTemplate(
final_prompt=full_prompt,
pipeline_prompts=[
("introduction", introduction_prompt),
("example", example_prompt),
("question", question_prompt)
]
)
# 使用 pipeline
result = pipeline_prompt.format(
role="数据分析师",
task="分析销售数据并给出建议",
examples="示例1: ...\n示例2: ...",
question="Q1季度的销售额下降了10%,可能是什么原因?"
)
print(result)
==== 3.4.2 使用 ChatPromptTemplate ====
对于聊天模型,LangChain 提供了专门的 ChatPromptTemplate,可以定义不同角色的消息。
from langchain.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
# 系统消息模板
system_template = """你是一个{role}助手。
你的专长领域是:{expertise}
回答时请遵循以下规则:
{rules}
"""
system_message_prompt = SystemMessagePromptTemplate.from_template(system_template)
# 人类消息模板
human_template = "{user_input}"
human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)
# 组合成聊天提示词
chat_prompt = ChatPromptTemplate.from_messages([
system_message_prompt,
human_message_prompt
])
# 格式化
messages = chat_prompt.format_prompt(
role="编程",
expertise="Python 和数据分析",
rules="1. 代码必须可运行\n2. 添加详细注释",
user_input="写一个计算斐波那契数列的函数"
).to_messages()
for message in messages:
print(f"{message.type}: {message.content}")
**输出:**
system: 你是一个编程助手。
你的专长领域是:Python 和数据分析
回答时请遵循以下规则:
1. 代码必须可运行
2. 添加详细注释
human: 写一个计算斐波那契数列的函数
==== 3.4.3 多轮对话模板 ====
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.schema import HumanMessage, AIMessage
# 创建包含对话历史的模板
chat_prompt = ChatPromptTemplate.from_messages([
("system", "你是一个有帮助的助手。"),
MessagesPlaceholder(variable_name="history"), # 占位符,用于插入对话历史
("human", "{input}")
])
# 模拟对话历史
history = [
HumanMessage(content="你好"),
AIMessage(content="你好!有什么我可以帮助你的吗?"),
HumanMessage(content="我想了解 Python"),
AIMessage(content="Python 是一门很棒的编程语言...")
]
# 格式化
messages = chat_prompt.format_prompt(
history=history,
input="如何安装 Python?"
).to_messages()
for msg in messages:
print(f"{msg.type}: {msg.content}")
==== 3.4.4 Prompt 管理最佳实践 ====
**使用配置文件管理提示词:**
# prompts.yaml
# prompts_config.yaml
greeting:
template: "你好{name},欢迎{action}!"
input_variables: ["name", "action"]
summarization:
template: |
请总结以下内容:
{content}
要求:
- {style}
- 大约{length}字
input_variables: ["content", "style", "length"]
import yaml
from langchain.prompts import load_prompt
# 加载 YAML 配置
with open("prompts_config.yaml", "r", encoding="utf-8") as f:
config = yaml.safe_load(f)
# 动态创建模板
templates = {}
for name, cfg in config.items():
templates[name] = PromptTemplate(
template=cfg["template"],
input_variables=cfg["input_variables"]
)
# 使用
greeting_prompt = templates["greeting"]
print(greeting_prompt.format(name="张三", action="访问"))
**版本控制提示词:**
from dataclasses import dataclass
from datetime import datetime
import json
@dataclass
class PromptVersion:
version: str
template: str
variables: list
description: str
created_at: str
performance_score: float = 0.0
class PromptRegistry:
def __init__(self):
self.prompts = {}
def register(self, name: str, version: PromptVersion):
if name not in self.prompts:
self.prompts[name] = []
self.prompts[name].append(version)
def get_latest(self, name: str):
if name in self.prompts and self.prompts[name]:
return sorted(self.prompts[name],
key=lambda x: x.version)[-1]
return None
def save(self, filepath: str):
data = {}
for name, versions in self.prompts.items():
data[name] = [
{
"version": v.version,
"template": v.template,
"variables": v.variables,
"description": v.description,
"created_at": v.created_at,
"performance_score": v.performance_score
}
for v in versions
]
with open(filepath, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
# 使用示例
registry = PromptRegistry()
v1 = PromptVersion(
version="1.0.0",
template="总结:{content}",
variables=["content"],
description="基础总结模板",
created_at=datetime.now().isoformat(),
performance_score=0.85
)
registry.register("summarization", v1)
registry.save("prompt_registry.json")
===== 3.5 动态提示词 =====
==== 3.5.1 条件提示词 ====
根据输入动态调整提示词内容。
from langchain.prompts import PromptTemplate
from typing import Optional
def create_conditional_prompt(
content: str,
audience: str = "general",
include_examples: bool = True
) -> str:
"""根据受众和选项创建条件提示词"""
# 基础模板
base = "请对以下内容进行解释:\n\n{content}"
# 根据受众调整
audience_instructions = {
"general": "使用通俗易懂的语言,避免专业术语。",
"technical": "可以使用专业术语,提供技术细节。",
"executive": "重点关注商业价值和高层次概念。",
"beginner": "从最基础的概念开始,逐步深入。"
}
instruction = audience_instructions.get(audience, audience_instructions["general"])
# 条件添加示例
example_section = ""
if include_examples:
example_section = """
请提供具体的例子帮助理解。
示例格式:
- 场景描述
- 具体应用
- 预期结果"""
template = f"""{base}
受众:{audience}
要求:{instruction}{example_section}
"""
return template.format(content=content)
# 使用示例
prompt_general = create_conditional_prompt(
content="区块链是一种分布式账本技术...",
audience="beginner",
include_examples=True
)
print("初学者版本:")
print(prompt_general)
prompt_technical = create_conditional_prompt(
content="区块链是一种分布式账本技术...",
audience="technical",
include_examples=False
)
print("\n技术版本:")
print(prompt_technical)
==== 3.5.2 模板继承与组合 ====
class PromptBuilder:
"""动态提示词构建器"""
def __init__(self):
self.sections = []
self.variables = set()
def add_instruction(self, text: str):
"""添加指令部分"""
self.sections.append(("指令", text))
return self
def add_context(self, text: str, **vars):
"""添加上下文部分"""
self.sections.append(("上下文", text))
self.variables.update(vars.keys())
return self
def add_constraints(self, constraints: list):
"""添加约束条件"""
constraint_text = "约束条件:\n" + "\n".join(f"- {c}" for c in constraints)
self.sections.append(("约束", constraint_text))
return self
def add_output_format(self, format_spec: str):
"""添加输出格式说明"""
self.sections.append(("输出格式", format_spec))
return self
def build(self) -> str:
"""构建最终提示词"""
parts = []
for section_type, content in self.sections:
parts.append(f"### {section_type}\n{content}")
return "\n\n".join(parts)
# 使用示例
builder = PromptBuilder()
prompt = (builder
.add_instruction("请分析以下用户反馈并提供改进建议。")
.add_context(
"用户反馈:{feedback}\n产品类型:{product_type}",
feedback="",
product_type=""
)
.add_constraints([
"分析必须客观",
"建议必须可行",
"考虑用户情绪"
])
.add_output_format("""
JSON格式:
{
"sentiment": "正面/负面/中性",
"issues": ["问题1", "问题2"],
"suggestions": ["建议1", "建议2"]
}
""")
.build())
print(prompt)
==== 3.5.3 动态 Few-shot 示例 ====
根据查询动态选择最相关的示例。
from typing import List, Dict
import numpy as np
class DynamicExampleSelector:
"""动态示例选择器"""
def __init__(self, examples: List[Dict], embeddings_model=None):
self.examples = examples
self.embeddings = embeddings_model
self.example_embeddings = None
if embeddings_model:
# 预计算示例的嵌入向量
texts = [ex["input"] for ex in examples]
self.example_embeddings = embeddings_model.embed_documents(texts)
def select_by_similarity(self, query: str, k: int = 3) -> List[Dict]:
"""基于相似度选择示例"""
if not self.embeddings or not self.example_embeddings:
# 如果没有嵌入模型,随机选择
import random
return random.sample(self.examples, min(k, len(self.examples)))
# 计算查询的嵌入向量
query_embedding = self.embeddings.embed_query(query)
# 计算相似度
similarities = [
np.dot(query_embedding, ex_emb) /
(np.linalg.norm(query_embedding) * np.linalg.norm(ex_emb))
for ex_emb in self.example_embeddings
]
# 选择最相似的 k 个
top_k_indices = np.argsort(similarities)[-k:][::-1]
return [self.examples[i] for i in top_k_indices]
def select_by_category(self, category: str) -> List[Dict]:
"""基于类别选择示例"""
return [ex for ex in self.examples if ex.get("category") == category]
def create_few_shot_prompt(
self,
query: str,
k: int = 3,
template: str = "输入: {input}\n输出: {output}\n---"
) -> str:
"""创建 few-shot 提示词"""
selected = self.select_by_similarity(query, k)
example_texts = []
for ex in selected:
example_texts.append(template.format(**ex))
return "\n".join(example_texts) + f"\n输入: {query}\n输出:"
# 使用示例
examples = [
{"input": "天气怎么样?", "output": "询问天气", "category": "weather"},
{"input": "今天会下雨吗?", "output": "询问天气", "category": "weather"},
{"input": "附近有什么餐厅?", "output": "询问餐厅", "category": "food"},
{"input": "推荐一家好吃的火锅店", "output": "询问餐厅", "category": "food"},
{"input": "怎么去机场?", "output": "询问路线", "category": "navigation"},
]
selector = DynamicExampleSelector(examples)
prompt = selector.create_few_shot_prompt("明天需要带伞吗?", k=2)
print(prompt)
==== 3.5.4 自适应提示词长度 ====
根据上下文窗口自动调整提示词长度。
from langchain.prompts import PromptTemplate
class AdaptivePromptTemplate:
"""自适应提示词模板"""
def __init__(
self,
base_template: str,
max_tokens: int = 2000,
tokenizer=None
):
self.base_template = base_template
self.max_tokens = max_tokens
self.tokenizer = tokenizer
def estimate_tokens(self, text: str) -> int:
"""估计 token 数量"""
if self.tokenizer:
return len(self.tokenizer.encode(text))
# 简单估计:中文字符约1个token,英文单词约1.3个token
return len(text) // 2
def format_adaptive(
self,
**kwargs
) -> str:
"""自适应格式化,确保不超过最大 token 数"""
# 尝试完整格式化
prompt = self.base_template.format(**kwargs)
tokens = self.estimate_tokens(prompt)
if tokens <= self.max_tokens:
return prompt
# 需要压缩内容
compression_ratio = self.max_tokens / tokens * 0.9 # 留一些余量
compressed_kwargs = {}
for key, value in kwargs.items():
if isinstance(value, str) and len(value) > 100:
# 截断长文本
keep_length = int(len(value) * compression_ratio)
compressed_kwargs[key] = value[:keep_length] + "... [内容已截断]"
else:
compressed_kwargs[key] = value
return self.base_template.format(**compressed_kwargs)
# 使用示例
template = """请分析以下长篇文档:
文档内容:
{document}
分析要求:
{requirements}
输出格式:
{format}
"""
adaptive = AdaptivePromptTemplate(template, max_tokens=1000)
long_document = "这是一篇非常长的文档..." * 500 # 模拟长文档
result = adaptive.format_adaptive(
document=long_document,
requirements="提取关键信息",
format="JSON格式"
)
print(f"生成的提示词长度: {len(result)}")
==== 3.5.5 实时提示词优化 ====
根据模型反馈实时调整提示词。
class PromptOptimizer:
"""提示词优化器"""
def __init__(self, llm):
self.llm = llm
self.history = []
def optimize_prompt(
self,
original_prompt: str,
target_output: str = None,
feedback: str = None
) -> str:
"""基于反馈优化提示词"""
optimization_instruction = f"""
你是一位提示词工程专家。请优化以下提示词,使其产生更好的结果。
原始提示词:
{original_prompt}
"""
if target_output:
optimization_instruction += f"""
目标输出示例:
{target_output}
"""
if feedback:
optimization_instruction += f"""
当前问题反馈:
{feedback}
"""
optimization_instruction += """
请提供优化后的提示词,并说明:
1. 改进了哪些方面
2. 为什么这些改进会产生更好的结果
3. 优化后的完整提示词
优化后的提示词:
"""
optimized = self.llm.predict(optimization_instruction)
return optimized
def iterative_improvement(
self,
base_prompt: str,
test_cases: list,
iterations: int = 3
) -> str:
"""通过多次迭代改进提示词"""
current_prompt = base_prompt
for i in range(iterations):
print(f"\n=== 迭代 {i+1}/{iterations} ===")
# 在当前提示词上测试
results = []
for test in test_cases:
result = self.llm.predict(current_prompt.format(**test["input"]))
results.append({
"input": test["input"],
"expected": test["expected"],
"actual": result
})
# 分析结果
feedback = self._analyze_results(results)
print(f"反馈: {feedback}")
# 优化提示词
if i < iterations - 1: # 最后一次不需要优化
current_prompt = self.optimize_prompt(
current_prompt,
feedback=feedback
)
print(f"优化后的提示词预览: {current_prompt[:200]}...")
return current_prompt
def _analyze_results(self, results: list) -> str:
"""分析测试结果并生成反馈"""
correct = sum(1 for r in results if r["expected"] in r["actual"])
total = len(results)
feedback = f"准确率: {correct}/{total}\n"
for i, result in enumerate(results):
if result["expected"] not in result["actual"]:
feedback += f"\n测试用例 {i+1} 失败:"
feedback += f"\n 输入: {result['input']}"
feedback += f"\n 期望: {result['expected']}"
feedback += f"\n 实际: {result['actual'][:100]}..."
return feedback
# 使用示例(伪代码)
"""
from langchain.llms import OpenAI
llm = OpenAI()
optimizer = PromptOptimizer(llm)
test_cases = [
{"input": {"text": "今天天气很好"}, "expected": "积极"},
{"input": {"text": "这部电影太糟糕了"}, "expected": "消极"},
]
final_prompt = optimizer.iterative_improvement(
base_prompt="分析文本情感:{text}",
test_cases=test_cases,
iterations=3
)
"""
===== 3.6 本章小结 =====
本章深入探讨了 LangChain 中的提示词工程,涵盖了以下核心内容:
**提示词工程基础:**
* 提示词的基本结构:指令、上下文、输入数据、输出指示
* 设计原则:清晰明确、提供上下文、指定输出格式、使用分隔符、给出示例
* 常见模式:角色扮演、思维链、自洽性检查
**PromptTemplate 详解:**
* 基础使用方法
* 部分变量(Partial Variables)技巧
* 模板验证机制
* 自定义输入解析器
**Few-shot Prompting:**
* 基础示例和实现
* 示例选择器(长度限制、语义相似度)
* 最佳实践和注意事项
**Prompt 组合与管理:**
* PipelinePromptTemplate 的使用
* ChatPromptTemplate 处理多轮对话
* 使用配置文件管理提示词
* 提示词版本控制
**动态提示词:**
* 条件提示词
* 模板继承与组合
* 动态 Few-shot 示例选择
* 自适应提示词长度
* 实时提示词优化
掌握这些提示词技术,将帮助你构建更高质量、更可控的 LLM 应用。在实际项目中,建议:
* 建立统一的提示词管理规范
* 使用版本控制跟踪提示词变化
* 收集用户反馈持续优化提示词
* 根据具体场景选择合适的提示词策略
在下一章中,我们将学习 LangChain 的 Chains 组件,了解如何将多个操作串联起来构建复杂的工作流。
===== 练习 =====
1. **基础练习**:创建一个 PromptTemplate,用于生成产品描述。模板应包含产品名称、特点列表、目标受众等变量。
2. **进阶练习**:实现一个 Few-shot 情感分析提示词,包含5个示例,并使用长度限制选择器。
3. **综合项目**:设计一个动态提示词系统,能够根据用户输入的复杂度自动选择不同数量和类型的示例。
===== 参考资源 =====
* [[https://python.langchain.com/docs/modules/model_io/prompts|LangChain 官方文档 - Prompts]]
* [[https://www.promptingguide.ai/zh|提示工程指南]]
* [[https://github.com/f/awesome-chatgpt-prompts|Awesome ChatGPT Prompts]]