目录

第三章:核心组件详解 - 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 详解:

Few-shot Prompting:

Prompt 组合与管理:

动态提示词:

掌握这些提示词技术,将帮助你构建更高质量、更可控的 LLM 应用。在实际项目中,建议:

在下一章中,我们将学习 LangChain 的 Chains 组件,了解如何将多个操作串联起来构建复杂的工作流。

练习

1. 基础练习:创建一个 PromptTemplate,用于生成产品描述。模板应包含产品名称、特点列表、目标受众等变量。

2. 进阶练习:实现一个 Few-shot 情感分析提示词,包含5个示例,并使用长度限制选择器。

3. 综合项目:设计一个动态提示词系统,能够根据用户输入的复杂度自动选择不同数量和类型的示例。

参考资源