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 混合消息定义
支持多种消息定义方式混用:- 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] 问题:这个商品支持货到付款吗?
- 背景:商品:智能手表,配送地区:北京市
- 情感分析提示词:
- 评论:这个手机续航超强,一天充一次电就够了!
- 情感:正面
- 理由:续航能力强
- 评论:价格太贵了,性能却一般般,不太值
- 情感:负面
- 理由:价格高、性能一般
- 评论:手机外观很好看,但系统有点卡顿
- 情感:中性
- 理由:外观好但系统卡顿
- 评论:这个耳机很垃圾,声音太小了
- 情感分析:
复制代码 这个示例展示了:
- ChatPromptTemplate - 构建多角色对话
- MessagesPlaceholder - 动态插入对话历史
- FewShotPromptTemplate - 情感分析任务
- 多种 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 内置的 SemanticSimilarityExampleSelector 或 MaxMarginalRelevanceExampleSelector,效果会更好。
LangChain 1.2.0 的提示词系统非常灵活,建议在实际项目中根据需求选择合适的组件组合使用。掌握这些技巧,将帮助你构建更高效、更强大的 AI 应用。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |