Langchain学习笔记(八):Prompt工程与模板设计

:本文是Langchain框架的学习笔记,内容可能有所疏漏,欢迎交流指正。后续将持续更新学习笔记,分享我的学习心得和实践经验。


引言

在大语言模型应用开发中,提示词(Prompt)的设计是决定应用成败的关键因素。一个精心设计的提示词能够:

  • 提高输出质量:清晰的指令和上下文能让模型更准确地理解任务需求
  • 确保输出一致性:标准化的模板能保证不同输入下输出格式的统一
  • 降低开发成本:可复用的模板减少重复工作,提高开发效率
  • 便于维护优化:集中管理的模板便于后续调优和版本控制

Langchain框架提供了完整的提示模板管理体系,从基础的变量替换到复杂的多轮对话,为开发者提供了强大而灵活的工具集。


1. PromptTemplate核心概念深度解析

1.1 什么是PromptTemplate?

PromptTemplate是Langchain中的核心组件,它解决了传统字符串拼接方式的诸多问题:

传统方式的问题:

  • 字符串拼接容易出错,难以维护
  • 缺乏变量验证机制
  • 无法实现模板复用和组合
  • 难以处理复杂的格式化需求

PromptTemplate的优势:

  • 类型安全:自动验证变量名称和类型
  • 可复用性:一次定义,多处使用
  • 可组合性:支持模板嵌套和组合
  • 易维护性:集中管理,便于版本控制

1.2 核心组件详解

PromptTemplate由三个核心组件构成:

  1. input_variables(输入变量)

    • 定义模板中所有可变参数的名称列表
    • 用于运行时验证,确保所有必需变量都被提供
    • 支持中文变量名,便于国内开发者使用
  2. template(模板字符串)

    • 包含固定文本和变量占位符的字符串
    • 使用{变量名}格式标记可替换部分
    • 支持复杂的格式化语法
  3. partial_variables(可选的部分变量)

    • 预设某些变量的默认值
    • 支持动态计算的变量值
from langchain_core.prompts import PromptTemplate

# 基础示例:文章生成模板
article_template = PromptTemplate(
    input_variables=["主题", "字数", "风格"],
    template="请写一篇关于{主题}的{字数}字文章,风格要求{风格}。"
)

# 使用模板
prompt = article_template.format(
    主题="人工智能发展趋势", 
    字数="1000", 
    风格="深入浅出,适合大众阅读"
)

1.3 模板验证机制

Langchain内置了严格的验证机制,能够在模板创建时就发现潜在问题:

验证内容包括:

  • 模板中的变量是否都在input_variables中声明
  • input_variables中的变量是否都在模板中使用
  • 变量名格式是否正确(不能包含特殊字符)
# 验证示例:这会抛出异常
try:
    invalid_template = PromptTemplate(
        input_variables=["主题", "字数"],  # 缺少"语言"变量声明
        template="请用{语言}写一篇关于{主题}的{字数}字文章。"
    )
except ValueError as e:
    print(f"模板验证失败: {e}")

1.4 部分变量填充的应用场景

部分变量填充(partial)是一个强大的功能,特别适用于以下场景:

场景1:分阶段构建复杂提示词

# 创建基础模板
base_template = PromptTemplate(
    input_variables=["角色", "任务", "输出格式", "具体问题"],
    template="你是一位{角色}。{任务}请以{输出格式}格式回答:{具体问题}"
)

# 第一阶段:设定角色和任务
role_template = base_template.partial(
    角色="资深Python开发工程师",
    任务="请根据你的专业知识和经验,"
)

# 第二阶段:设定输出格式
formatted_template = role_template.partial(输出格式="详细的步骤说明")

# 最终使用
final_prompt = formatted_template.format(具体问题="如何优化Python代码性能?")

场景2:动态变量计算

from datetime import datetime

def get_current_time():
    return datetime.now().strftime("%Y年%m月%d日 %H:%M")

# 使用函数作为部分变量
time_template = PromptTemplate(
    input_variables=["任务"],
    template="当前时间:{时间}\n请完成以下任务:{任务}",
    partial_variables={"时间": get_current_time}
)

2. 变量插入与格式化进阶技巧

2.1 为什么需要高级格式化?

在实际应用中,我们经常需要处理复杂的数据结构:

  • 列表数据:如示例集合、选项列表等
  • 字典数据:如用户信息、配置参数等
  • 嵌套结构:如多层级的数据组织

传统的简单变量替换无法满足这些需求,因此需要更强大的格式化机制。


2.2 复杂数据结构处理策略

策略1:预处理数据
将复杂数据转换为字符串后再传入模板

# 处理示例数据
examples = [
    {"问题": "什么是机器学习?", "答案": "机器学习是人工智能的一个分支"},
    {"问题": "深度学习的特点?", "答案": "深度学习使用多层神经网络"}
]

def format_examples(examples):
    return "\n".join([f"Q: {ex['问题']}\nA: {ex['答案']}" for ex in examples])

qa_template = PromptTemplate(
    input_variables=["examples", "question"],
    template="参考以下示例:\n{examples}\n\n现在请回答:{question}"
)

策略2:条件格式化
根据不同条件生成不同的模板结构

def create_adaptive_template(difficulty_level):
    """根据难度级别创建不同的模板"""
    if difficulty_level == "初级":
        return PromptTemplate(
            input_variables=["question"],
            template="请用简单易懂的语言回答:{question}\n要求:避免专业术语,多用比喻"
        )
    elif difficulty_level == "高级":
        return PromptTemplate(
            input_variables=["question"],
            template="请从技术角度深入分析:{question}\n要求:包含原理解释和实现细节"
        )

2.3 格式化最佳实践

  1. 数据预处理原则:在模板外部处理复杂逻辑
  2. 模板简洁原则:保持模板结构清晰易读
  3. 错误处理原则:对格式化过程添加异常处理
  4. 性能考虑原则:避免在模板中进行重复计算



3. 模板组合与继承的设计模式

3.1 为什么需要模板组合?

在复杂应用中,单一模板往往无法满足需求:

  • 功能模块化:不同功能需要不同的提示结构
  • 代码复用:避免重复定义相似的模板片段
  • 维护便利:修改一个组件影响所有使用它的模板
  • 灵活组装:根据需要动态组合不同的模板片段

3.2 组合模式的实现策略

策略1:字符串级别组合

# 定义可复用的模板片段
ROLE_PROMPT = "你是一位{role},具有{expertise}的专业能力。"
TASK_PROMPT = "请完成以下任务:{task}"
FORMAT_PROMPT = "输出格式要求:{format}"

# 组合成完整模板
combined_template = PromptTemplate(
    input_variables=["role", "expertise", "task", "format"],
    template=f"{ROLE_PROMPT}\n\n{TASK_PROMPT}\n\n{FORMAT_PROMPT}"
)

策略2:模板对象组合

class TemplateBuilder:
    def __init__(self):
        self.components = []
        self.variables = set()
    
    def add_component(self, template_str, variables):
        self.components.append(template_str)
        self.variables.update(variables)
    
    def build(self):
        combined_template = "\n\n".join(self.components)
        return PromptTemplate(
            input_variables=list(self.variables),
            template=combined_template
        )

# 使用构建器
builder = TemplateBuilder()
builder.add_component("你是{role}", ["role"])
builder.add_component("任务:{task}", ["task"])
template = builder.build()

3.3 继承模式的应用

通过继承可以创建具有共同基础结构但细节不同的模板族:

class BasePromptTemplate:
    def __init__(self, role, task_type):
        self.role = role
        self.task_type = task_type
    
    def get_base_template(self):
        return f"你是{self.role},专门处理{self.task_type}类任务。"
    
    def create_template(self, specific_instruction):
        base = self.get_base_template()
        return PromptTemplate(
            input_variables=["input"],
            template=f"{base}\n\n{specific_instruction}\n\n输入:{{input}}"
        )

# 创建特定类型的模板
code_reviewer = BasePromptTemplate("代码审查专家", "代码质量评估")
code_template = code_reviewer.create_template("请分析以下代码的质量并提出改进建议:")



4. Few-shot Learning提示设计深度解析

4.1 Few-shot Learning的工作原理

Few-shot Learning(少样本学习)是一种通过提供少量示例来引导模型理解任务的技术:

核心原理:

  • 模式识别:模型通过示例识别输入输出的模式
  • 上下文学习:利用模型的上下文理解能力
  • 任务泛化:从少量示例推广到新的输入

适用场景:

  • 格式化输出任务(如数据转换、格式标准化)
  • 分类任务(如情感分析、文本分类)
  • 生成任务(如翻译、摘要)

4.2 示例选择策略

策略1:代表性示例
选择能够覆盖主要情况的典型示例

# 情感分析的代表性示例
sentiment_examples = [
    {"text": "这个产品质量很好,我很满意", "sentiment": "正面"},
    {"text": "服务态度太差了,很失望", "sentiment": "负面"},
    {"text": "价格还可以,没什么特别的", "sentiment": "中性"},
    {"text": "功能不错但价格偏贵", "sentiment": "混合"}
]

策略2:边界案例示例
包含容易混淆或特殊情况的示例

# 包含边界案例的示例
boundary_examples = [
    {"text": "还行吧", "sentiment": "中性"},  # 模糊表达
    {"text": "不是不好", "sentiment": "正面"},  # 双重否定
    {"text": "简直了", "sentiment": "需要上下文"},  # 语境依赖
]

4.3 动态示例选择的实现

from langchain.prompts.example_selector import LengthBasedExampleSelector

# 创建示例选择器
example_selector = LengthBasedExampleSelector(
    examples=sentiment_examples,
    example_prompt=PromptTemplate(
        input_variables=["text", "sentiment"],
        template="输入: {text}\n输出: {sentiment}"
    ),
    max_length=1000  # 控制总长度
)

# 创建动态Few-shot模板
dynamic_template = FewShotPromptTemplate(
    example_selector=example_selector,
    example_prompt=PromptTemplate(
        input_variables=["text", "sentiment"],
        template="输入: {text}\n输出: {sentiment}"
    ),
    prefix="以下是情感分析的示例:",
    suffix="输入: {input}\n输出:",
    input_variables=["input"]
)



5. ChatPromptTemplate与对话系统设计

5.1 对话系统的特殊需求

与单轮问答不同,对话系统需要处理:

  • 多轮上下文:保持对话历史和上下文连贯性
  • 角色管理:区分系统消息、用户消息和助手消息
  • 状态维护:跟踪对话状态和用户意图
  • 个性化:根据用户特点调整回复风格

5.2 消息类型与作用

SystemMessage(系统消息)

  • 设定AI的角色和行为准则
  • 提供背景知识和约束条件
  • 通常在对话开始时设置,影响整个对话过程

HumanMessage(用户消息)

  • 用户的输入和问题
  • 对话的主要驱动力
  • 可以包含文本、图片等多种内容

AIMessage(AI消息)

  • AI的回复和响应
  • 用于构建对话历史
  • 影响后续对话的上下文

5.3 实用的对话模板设计

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

# 专业咨询对话模板
consultation_template = ChatPromptTemplate.from_messages([
    ("system", """你是一位{domain}领域的专业顾问,具有{experience}年经验。
    
    你的特点:
    - 专业知识丰富,能提供准确建议
    - 善于倾听,理解客户真实需求
    - 回答简洁明了,重点突出
    - 必要时会询问更多细节
    
    请始终保持专业和友好的态度。"""),
    
    MessagesPlaceholder(variable_name="chat_history"),
    ("human", "{input}")
])

# 使用示例
messages = consultation_template.format_messages(
    domain="软件架构",
    experience="10",
    chat_history=[],
    input="我想了解微服务架构的优缺点"
)



6. 与Ollama集成的完整应用实例

6.1 集成架构设计

from langchain_community.chat_models import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
import json

class SmartAssistant:
    def __init__(self, model_name="deepseek-r1:1.5b"):
        self.model = ChatOllama(model=model_name)
        self.templates = self._init_templates()
    
    def _init_templates(self):
        return {
            "analyze": ChatPromptTemplate.from_messages([
                ("system", "你是数据分析专家,擅长从文本中提取关键信息。"),
                ("human", "请分析以下内容:{content}\n分析角度:{aspect}")
            ]),
            
            "summarize": ChatPromptTemplate.from_messages([
                ("system", "你是内容总结专家,能够提取核心要点。"),
                ("human", "请将以下内容总结为{length}字以内:{content}")
            ]),
            
            "translate": ChatPromptTemplate.from_messages([
                ("system", "你是专业翻译,确保翻译准确且符合目标语言习惯。"),
                ("human", "请将以下{source_lang}翻译为{target_lang}:{text}")
            ])
        }
    
    def process(self, task_type, **kwargs):
        if task_type not in self.templates:
            raise ValueError(f"不支持的任务类型: {task_type}")
        
        template = self.templates[task_type]
        messages = template.format_messages(**kwargs)
        return self.model.invoke(messages)

# 使用示例
assistant = SmartAssistant()

# 文本分析
result = assistant.process(
    "analyze",
    content="今天的销售数据显示,产品A销量增长30%,但产品B下降了15%",
    aspect="销售趋势"
)

# 内容总结
summary = assistant.process(
    "summarize",
    content="长篇文章内容...",
    length="100"
)

6.2 错误处理与优化

class RobustAssistant(SmartAssistant):
    def __init__(self, model_name="deepseek-r1:1.5b", max_retries=3):
        super().__init__(model_name)
        self.max_retries = max_retries
    
    def process_with_retry(self, task_type, **kwargs):
        for attempt in range(self.max_retries):
            try:
                return self.process(task_type, **kwargs)
            except Exception as e:
                if attempt == self.max_retries - 1:
                    raise e
                print(f"尝试 {attempt + 1} 失败,重试中...")
        
    def validate_input(self, task_type, **kwargs):
        """输入验证"""
        required_params = {
            "analyze": ["content", "aspect"],
            "summarize": ["content", "length"],
            "translate": ["text", "source_lang", "target_lang"]
        }
        
        if task_type in required_params:
            for param in required_params[task_type]:
                if param not in kwargs:
                    raise ValueError(f"缺少必需参数: {param}")

7. 最佳实践与性能优化

模板设计原则

  1. 单一职责原则:每个模板专注于一个特定任务
  2. 可读性原则:模板结构清晰,易于理解和维护
  3. 可扩展原则:设计时考虑未来的扩展需求
  4. 性能原则:避免不必要的复杂计算和重复操作

你可能感兴趣的:(LangChain,langchain,学习,笔记)