1.Langchain 1.2.0 学习 --- 提示词

LangChain 1.2.0 提示词工程学习笔记

前言

在 LLM 应用开发中,提示词(Prompt)是连接人类意图与大语言模型能力的桥梁。LangChain 1.2.0 提供了强大而灵活的提示词构建工具,帮助开发者更高效地构建 AI 应用。本文将详细介绍 LangChain 中的各种提示词模板和示例选择器,让你从入门到实战全面掌握。


一、PromptTemplate - 基础字符串模板

PromptTemplate 是 LangChain 中最基础的提示词构建工具,用于创建简单的字符串模板,支持变量插值。

1.1 自动推断变量

使用 from_template() 方法时,LangChain 会自动从模板字符串中提取变量名:

from langchain_core.prompts import PromptTemplate

template = "介绍{product}的{feature}功能"
prompt = PromptTemplate.from_template(template)

print(prompt.input_variables)
# 输出: ['feature', 'product']

1.2 显式指定变量

通过直接实例化方式,需要显式指定 input_variables

prompt = PromptTemplate(
    input_variables=["name"],
    template="你好{name}"
)

这种方式的优势:

  • 编译时就能发现变量名拼写错误
  • 明确声明模板的依赖关系
  • 支持更复杂的模板引擎(如 Jinja2)

1.3 format() 方法

使用 format() 方法填充变量值:

result = prompt.format(
    product="智能手环",
    feature="24小时健康监测",
    benefit="随时掌握身体状况,预防疾病"
)

二、ChatPromptTemplate - 聊天消息模板

聊天模型需要特殊的消息格式,ChatPromptTemplate 用于构建符合 OpenAI/Anthropic 等聊天 API 的消息列表。

2.1 基础用法

from langchain_core.prompts import ChatPromptTemplate

chat_template = ChatPromptTemplate.from_messages([
    ("system", "你是一个专业的{role},擅长{skill}。"),
    ("human", "请帮我{task}"),
    ("ai", "好的,我很乐意帮助您。请告诉我更多细节。"),
    ("human", "{detail}")
])

messages = chat_template.format_messages(
    role="营养师",
    skill="制定健康饮食计划",
    task="制定一个减肥餐计划",
    detail="我是一位25岁的女性,平时喜欢运动,希望减脂增肌。"
)

2.2 Messages 概述

Messages 是 LangChain 中用于与 LLM 交互的基本单元。每个消息包含:

  • Role(角色): 标识消息类型(system、user、assistant、tool)
  • Content(内容): 消息的实际内容(文本、图片、音频等)
  • Metadata(元数据): 附加信息如消息ID、token使用量等

2.3 为什么使用 Messages

使用场景 推荐方式
单次独立请求 字符串或 HumanMessage
多轮对话 Message 列表
多模态输入 content_blocks
工具调用 ToolMessage

2.4 Message 类型详解

1. SystemMessage

系统消息,用于设置 AI 的行为、角色和指导方针。

from langchain.messages import SystemMessage

# 基本用法
msg = SystemMessage("你是一个友好的AI助手。")

# 详细角色设定
msg = SystemMessage("""
你是一位资深的Python开发者。
- 始终提供代码示例
- 解释你的思考过程
- 保持简洁但全面的回答
""")

2. HumanMessage

用户消息,代表用户的输入和交互。

from langchain.messages import HumanMessage

# 文本内容
msg = HumanMessage("你好,请介绍一下Python。")

# 带元数据
msg = HumanMessage(
    content="紧急问题:服务器宕机了!",
    name="alice",
    id="msg_123"
)

# 多模态内容
msg = HumanMessage(content=[
    {"type": "text", "text": "描述这张图片"},
    {"type": "image", "url": "https://example.com/img.jpg"}
])

3. AIMessage

AI 消息,模型生成的响应。

from langchain.messages import AIMessage

# 接收模型响应
response = model.invoke("你好")
print(response.content)  # AI的回复文本

# 手动创建(用于对话历史)
ai_msg = AIMessage("Python是一种高级编程语言。")

# 带工具调用
ai_msg = AIMessage(
    content="",
    tool_calls=[{
        "name": "get_weather",
        "args": {"location": "北京"},
        "id": "call_123"
    }]
)

4. ToolMessage

工具消息,传递工具执行结果给模型。

from langchain.messages import ToolMessage

# 基本用法
tool_msg = ToolMessage(
    content="北京今天晴朗,28度",
    tool_call_id="call_123",
    name="get_weather"
)

# 带附加数据(artifact)
tool_msg = ToolMessage(
    content="查询结果...",
    tool_call_id="call_456",
    name="search",
    artifact={"document_id": "doc_123", "page": 1}
)

参数说明

BaseMessage 通用参数
参数 类型 必填 说明 推荐
content str 消息的实际内容 根据实际需求
name str 发送者名称,用于区分不同用户 在多用户场景使用
id str 消息唯一标识符,用于追踪 大型应用中使用
HumanMessage 参数
参数 类型 必填 说明 推荐 可选形式
content str 消息内容 简洁明确的提问 字符串、列表
name str 发送者名称 用户标识 -
id str 消息ID 追踪用途 -
content_blocks list 标准内容块 多模态输入 标准内容块格式
AIMessage 额外参数
参数 类型 说明 推荐
tool_calls list[dict] 模型触发的工具调用 工具调用场景
usage_metadata dict Token使用统计 需要统计成本时
response_metadata dict 响应元数据 调试和分析
content_blocks list 标准内容块 多模态输出
ToolMessage 参数
参数 类型 必填 说明 推荐 可选形式
content str 工具执行结果 清晰的返回信息 字符串
tool_call_id str 关联的工具调用ID 必须与AI的tool_calls匹配 字符串
name str 工具名称 与绑定时的名称一致 字符串
artifact dict 附加数据 需要传递额外元数据 字典
SystemMessage 参数
参数 类型 必填 说明 推荐
content str 系统指令 清晰、具体的角色定义
消息内容格式(Content)
  1. 字符串格式(最简单)
HumanMessage("Hello!")
  1. 字典格式(OpenAI兼容)
{"role": "user", "content": "Hello!"}
  1. 列表格式(多模态)
HumanMessage(content=[
    {"type": "text", "text": "描述图片"},
    {"type": "image", "url": "https://..."}
])
  1. 标准内容块(LangChain v1+)
HumanMessage(content_blocks=[
    {"type": "text", "text": "Hello"},
    {"type": "image", "url": "https://..."}
])

内容块类型(Content Blocks)

类型 用途 必需参数
text 文本内容 text
reasoning 推理过程 reasoning
image 图片 urlbase64 + mime_type
audio 音频 urlbase64 + mime_type
video 视频 urlbase64 + mime_type
file 文件(PDF等) urlbase64 + mime_type
tool_call 工具调用 name, args, id
tool_call_chunk 流式工具调用 name, args, id, index

2.5 混合消息定义

支持多种消息定义方式混用:

from langchain_core.prompts import (
    ChatPromptTemplate,
    SystemMessagePromptTemplate,
    HumanMessagePromptTemplate
)

chat_prompt = ChatPromptTemplate.from_messages([
    SystemMessage(content="你是一个有帮助的助手。"),
    ("ai", "你好!我能为你做什么?"),
    HumanMessagePromptTemplate.from_template("{user_input}"),
])

三、MessagesPlaceholder - 消息占位符

用于在模板中插入动态数量的消息,特别适合处理对话历史。

3.1 基础用法

from langchain_core.prompts import MessagesPlaceholder

chat_template = ChatPromptTemplate.from_messages([
    SystemMessagePromptTemplate.from_template("你是一个客服助手,以下是与客户的对话历史:"),
    MessagesPlaceholder(variable_name="history"),
    HumanMessagePromptTemplate.from_template("{input}"),
])

history_messages = [
    HumanMessage(content="我收到了错误的商品。"),
    AIMessage(content="非常抱歉,请问您的订单号是多少?"),
    HumanMessage(content="订单号是12345。"),
]

messages = chat_template.format_messages(
    history=history_messages,
    input="我的订单号。"
)

3.2 optional 参数

optional=True 使占位符变为可空,适合对话第一回合:

MessagesPlaceholder(variable_name="history", optional=True)

四、FewShotPromptTemplate - 少样本提示模板

通过提供示例引导模型完成特定任务,适合格式要求严格的场景。

4.1 基础结构

from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate
examples = [
   {

       "input": "1939年9月,希特勒在德国闪击波兰",

       "output": "{{'name': '希特勒', 'event': '闪击波兰', 'time': '1939年9月', 'location': '德国'}}"

   },

   {

       "input": "爱因斯坦于1905年提出了相对论",

       "output": "{{'name': '爱因斯坦', 'event': '提出了相对论', 'time': '1905年', 'location': ''}}"

   }
]

# 创建示例模板
# 这个模板定义了单个示例的格式
# {input} 和 {output} 会被 examples 中的值替换
example_template = """
输入:{input}
输出:{output}
"""

# 创建单个示例的 PromptTemplate
# input_variables 必须与 examples 字典的键一致
example_prompt = PromptTemplate(
    input_variables=["input", "output"],
    template=example_template
)

# 创建 FewShotPromptTemplate
# 结构:prefix + [示例1, 示例2, ...] + suffix
few_shot_prompt = FewShotPromptTemplate(
    examples=examples,              # 示例列表,会被逐个渲染
    example_prompt=example_prompt,  # 单个示例的模板
    suffix="文本:{user_input}\n提取结果:",  # 后缀模板,包含实际输入变量
    input_variables=["user_input"],   # 最终模板需要的变量(在后缀中定义)
    example_separator="\n",       # 示例之间的分隔符
    prefix="请从文本中提取人名、事件、时间、地点,输出JSON格式(无则留空):\n"  # 前缀说明
)

# 使用 format() 渲染最终提示词
# 只需要提供 suffix 中定义的变量
# examples 会自动展开插入到 prefix 和 suffix 之间
result = few_shot_prompt.format(user_input="1945年8月15日,鬼子在密苏里号上投降")

4.2 重要提示:花括号转义

⚠️ 注意:在示例的 output 字段中,如果包含花括号,需要双写转义:

# 错误写法(会导致 KeyError)
"output": "{'name': '爱因斯坦', ...}"

# 正确写法
"output": "{{'name': '爱因斯坦', ...}}"

这是因为 Python 的 str.format() 会将 {name} 识别为格式化占位符。

4.3 ExampleSelector - 示例选择器

当示例库很大时,全量放入提示词会超出 token 限制。ExampleSelector 根据策略动态选择最相关的子集,既能引导模型理解任务,又不会超出上下文限制。

使用 example_selector 参数替代 examples 参数即可启用动态选择:

few_shot_prompt = FewShotPromptTemplate(
    example_selector=my_selector,  # 使用选择器替代静态 examples
    example_prompt=example_prompt,
    suffix="文本:{input}\n结果:",
    input_variables=["input"],
    prefix="请根据示例完成任务:\n"
)

4.3.1 LengthBasedExampleSelector

根据输入长度动态选择示例数量,输入越长,选择的示例越少:

from langchain_core.example_selectors import LengthBasedExampleSelector

example_selector = LengthBasedExampleSelector(
    examples=examples,
    example_prompt=example_prompt,
    max_length=50  # 最大长度阈值
)

工作原理

  • 短输入 → 选择更多示例(节省空间)
  • 长输入 → 选择更少示例(控制 token 数量)

4.3.2 关键词相似度选择器

基于关键词重叠度选择最相关的示例(简单使用Jaccard相似度对比):

import re
from langchain_core.example_selectors import BaseExampleSelector

def extract_keywords(text: str) -> set:
    """提取文本中的关键词"""
    chinese = re.findall(r'[\u4e00-\u9fff]+', text)
    english = re.findall(r'[a-zA-Z]+', text)
    keywords = set(chinese) | set(english)
    return {w for w in keywords if len(w) >= 2}

class KeywordSimilarityExampleSelector(BaseExampleSelector):
    def __init__(self, examples, k=2):
        self.examples = examples
        self.k = k
        self.example_keywords = {}
        for i, ex in enumerate(examples):
            text = ex.get("input", "")
            self.example_keywords[i] = extract_keywords(text)

    def select_examples(self, input_variables):
        input_text = input_variables.get("input", "")
        input_words = extract_keywords(input_text)

        # 使用 Jaccard 相似度
        scores = []
        for i, keywords in self.example_keywords.items():
            overlap = len(input_words & keywords)
            union = len(input_words | keywords)
            score = overlap / union if union > 0 else 0
            scores.append((i, score))

        scores.sort(key=lambda x: x[1], reverse=True)
        return [self.examples[x[0]] for x in scores[:self.k]]

4.3.3 多样化选择器

确保选中的示例来自不同类别,增加模型看到多样化示例的机会:

class DiverseExampleSelector(BaseExampleSelector):
    def __init__(self, examples, k=3):
        self.examples = examples
        self.k = k
        self.example_categories = {}
        for i, ex in enumerate(examples):
            self.example_categories[i] = ex.get("output", "")

    def select_examples(self, input_variables):
        category_count = {}
        for cat in self.example_categories.values():
            category_count[cat] = category_count.get(cat, 0) + 1

        selected = []
        categories = list(category_count.keys())

        idx = 0
        while len(selected) < self.k:
            cat = categories[idx % len(categories)]
            for i, c in self.example_categories.items():
                if c == cat and self.examples[i] not in selected:
                    selected.append(self.examples[i])
                    if len(selected) >= self.k:
                        break
            idx += 1
            if idx > len(categories) * 3:
                break

        return selected

4.3.4 随机选择器

每次随机选择 K 个示例,适用于调试或演示:

import random

class RandomExampleSelector(BaseExampleSelector):
    def __init__(self, examples, k=2):
        self.examples = examples
        self.k = k

    def select_examples(self, input_variables):
        return random.sample(self.examples, min(self.k, len(self.examples)))

五、实战示例:智能客服助手

综合应用 ChatPromptTemplate + MessagesPlaceholder + FewShotPromptTemplate,构建一个智能客服助手:

from langchain_core.prompts import PromptTemplate, ChatPromptTemplate, MessagesPlaceholder, FewShotPromptTemplate
import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage

load_dotenv()

# 初始化模型
llm = init_chat_model(
    model="*************",
    model_provider="openai",
    base_url=os.getenv("*************"),
    api_key=os.getenv("*************"),
)

# ============================================
# 第一部分:情感分析提示词 (FewShotPromptTemplate)
# ============================================

# 少样本示例 - 情感分析
sentiment_examples = [
    {"comment": "这个手机续航超强,一天充一次电就够了!", "sentiment": "正面", "reason": "续航能力强"},
    {"comment": "价格太贵了,性能却一般般,不太值", "sentiment": "负面", "reason": "价格高、性能一般"},
    {"comment": "手机外观很好看,但系统有点卡顿", "sentiment": "中性", "reason": "外观好但系统卡顿"},
]

sentiment_prompt = FewShotPromptTemplate(
    examples=sentiment_examples,
    example_prompt=PromptTemplate(
        input_variables=["comment", "sentiment", "reason"],
        template="评论:{comment}\n情感:{sentiment}\n理由:{reason}\n"
    ),
    suffix="评论:{user_comment}\n情感分析:",
    input_variables=["user_comment"]
)

# ============================================
# 第二部分:客服对话提示词 (ChatPromptTemplate + MessagesPlaceholder)
# ============================================

# 客服对话模板 - 根据情感调整回复策略
customer_service_prompt = ChatPromptTemplate.from_messages([
    ("system", """你是一位专业的电商客服助手,根据用户的情感调整回复策略:
- 正面评价:感谢用户,保持热情
- 负面评价:耐心倾听,提供解决方案
- 中性评价:客观介绍,引导用户了解更多
- 回复礼貌、专业、耐心
- 回复简洁,控制在100字以内"""),

    # 对话历史占位符 - 支持多轮对话
    MessagesPlaceholder(variable_name="history", optional=True),

    # 当前用户输入
    ("human", "用户评论:{comment}\n分析的情感:{sentiment}\n情感理由:{reason}")
])

# ============================================
# 串联使用:情感分析 + 客服回复
# ============================================

def process_user_comment(user_comment: str):
    """
    处理用户评论的完整流程:
    1. 使用 FewShotPromptTemplate 分析情感
    2. 使用 ChatPromptTemplate 生成回复
    """
    print(f"\n{'='*60}")
    print(f"用户评论:{user_comment}")
    print(f"{'='*60}")

    # Step 1: 情感分析
    sentiment_prompt_text = sentiment_prompt.format(user_comment=user_comment)
    sentiment_result = llm.invoke(sentiment_prompt_text)

    # 解析情感结果
    sentiment_content = sentiment_result.content.strip()
    # 简单解析结果(假设格式为:情感 理由)
    lines = sentiment_content.split('\n')
    sentiment = lines[0].strip() if lines else "中性"
    reason = lines[1].strip() if len(lines) > 1 else ""

    print(f"\n【情感分析结果】")
    print(f"情感:{sentiment}")
    print(f"理由:{reason}")

    # Step 2: 生成客服回复
    messages = customer_service_prompt.format_messages(
        comment=user_comment,
        sentiment=sentiment,
        reason=reason,
        history=[]  # 首次对话,无历史
    )

    # 调用模型生成回复
    response = llm.invoke(messages)

    print(f"\n【客服回复】")
    print(response.content)

    return {
        "sentiment": sentiment,
        "reason": reason,
        "response": response.content
    }

# 测试几条评论
test_comments = [
    "这个手机续航超强,一天充一次电就够了!",
    "价格太贵了,性能却一般般,不太值",
    "手机外观很好看,但系统有点卡顿"
]

for comment in test_comments:
    result = process_user_comment(comment)
    print()

运行效果

生成的对话消息:
[system] 你是一位专业的电商客服助手...
[human] 问题:这个商品支持货到付款吗?
背景:商品:智能手表,配送地区:北京市

情感分析提示词:
评论:这个手机续航超强,一天充一次电就够了!
情感:正面
理由:续航能力强

评论:价格太贵了,性能却一般般,不太值
情感:负面
理由:价格高、性能一般

评论:手机外观很好看,但系统有点卡顿
情感:中性
理由:外观好但系统卡顿

评论:这个耳机很垃圾,声音太小了
情感分析:

这个示例展示了:

  1. ChatPromptTemplate - 构建多角色对话
  2. MessagesPlaceholder - 动态插入对话历史
  3. FewShotPromptTemplate - 情感分析任务
  4. 多种 ExampleSelector - 根据场景选择合适的示例

六、模板保存与加载

LangChain 支持将提示词模板序列化为 JSON:

import json

# 创建模板
# ============================================
# 概念:模板的序列化与持久化
# --------------------------------------------
# LangChain 1.2.0 支持将 PromptTemplate 序列化为 JSON
# 使用 to_json() 方法可以将模板配置保存到文件
# 便于:版本控制、团队协作、生产环境部署
# ============================================

import json

# 创建模板
template = PromptTemplate(
    input_variables=["product", "feature"],
    template="为{product}撰写文案,突出{feature}。"
)

# 保存为JSON
# to_json() 方法返回模板的字典表示
# 包含 template、input_variables 等关键配置
template_dict = template.to_json()

# 写入 JSON 文件
with open("prompt_template.json", "w", encoding="utf-8") as f:
    json.dump(template_dict, f, ensure_ascii=False, indent=2)

print("模板已保存到 prompt_template.json")
print(f"\n模板内容:{template_dict}")
# ============================================
# 概念:从 JSON 加载模板
# --------------------------------------------
# 从文件加载之前保存的模板配置
# 反序列化后重新创建 PromptTemplate 实例
# 实现提示词模板的版本管理和复用
# ============================================

# 从 JSON 文件加载模板配置
with open("prompt_template.json", "r", encoding="utf-8") as f:
    loaded_dict = json.load(f)
    print("加载的字典:", loaded_dict)

# 使用加载的配置重新创建 PromptTemplate
# 注意:LangChain 1.2.0 的 JSON 格式中,配置在 kwargs 里面
loaded_template = PromptTemplate(
    input_variables=["input_variables"],
    template=loaded_dict["kwargs"]["template"]
)

# 使用加载的模板进行渲染
# 验证模板功能正常
result = loaded_template.format(product="新手机", feature="超长续航")
print("\n加载的模板格式化结果:", result)

总结

组件 用途 适用场景
PromptTemplate 基础字符串模板 简单的变量替换
ChatPromptTemplate 聊天消息模板 多角色对话构建
MessagesPlaceholder 动态消息插入 对话历史管理
FewShotPromptTemplate 少样本提示 格式严格的任务
ExampleSelector 动态示例选择 控制 token 数量/语义匹配/多样化/随机等

常见的 ExampleSelector 类型

选择器 原理 适用场景
LengthBasedExampleSelector 根据输入长度动态选择 输入长度变化大,需要控制 token 数量
关键词相似度选择器 基于关键词重叠度计算相似度 没有嵌入 API 时使用,简单高效
多样化选择器 确保选中的示例来自不同类别 需要示例覆盖不同类型/类别
随机选择器 随机选择示例 调试、演示、特殊需求

提示:如果需要更强大的语义相似度选择,可以配置嵌入 API(如 OpenAI、BAAI、百度千帆等),使用 LangChain 内置的 SemanticSimilarityExampleSelectorMaxMarginalRelevanceExampleSelector,效果会更好。


LangChain 1.2.0 的提示词系统非常灵活,建议在实际项目中根据需求选择合适的组件组合使用。掌握这些技巧,将帮助你构建更高效、更强大的 AI 应用。

📝 个人笔记:本文档基于 LangChain 1.2.0 版本,不同版本可能存在 API 差异,请以官方文档为准。

posted @ 2026-03-16 22:30  刘莫语  阅读(174)  评论(0)    收藏  举报