当前位置: 首页 > news >正文

LangChain 学习记录 03:提示词模板 PromptTemplate 与 ChatPromptTemplate 详解

LangChain 学习记录 03:提示词模板 PromptTemplate 与 ChatPromptTemplate 详解

摘要

在大模型应用开发里,Prompt 不是随手拼出来的一段字符串,而是影响模型输出质量、代码可维护性和项目稳定性的核心资产。

本文围绕 LangChain 的提示词模板展开,重点讲清楚:

  • 为什么不要长期依赖 f-string 手动拼 Prompt
  • PromptTemplateChatPromptTemplate分别适合什么场景
  • partialpartial_variables如何减少重复传参
  • MessagesPlaceholder为什么是多轮对话的关键
  • FewShotPromptTemplate如何用示例引导模型输出

一句话概括:

Prompt Template 的作用,是把“怎么问模型”这件事标准化、参数化、可复用。

文章目录

  • LangChain 学习记录 03:提示词模板 PromptTemplate 与 ChatPromptTemplate 详解
    • 摘要
    • 一、为什么需要提示词模板
    • 二、Prompt Template 的底层逻辑
    • 三、PromptTemplate:最基础的字符串模板
      • 常见写法对比
    • 四、ChatPromptTemplate:聊天模型的主力模板
      • system、human、ai 的区别
    • 五、partial 与 partial_variables
      • partial 的核心价值
      • partial 和 partial_variables 区别
    • 六、MessagesPlaceholder:给历史对话留位置
    • 七、FewShotPromptTemplate:用示例教模型做事
    • 八、项目中怎么选
    • 九、常见坑
      • 1. 模板变量名和 invoke 输入不一致
      • 2. 把 ChatPromptTemplate 当成普通字符串
      • 3. MessagesPlaceholder 传入的不是消息列表
      • 4. Few-shot 示例太多
    • 十、小结
    • 参考资料

一、为什么需要提示词模板

刚开始调用大模型时,我们很容易这样写:

topic="向量数据库"style="通俗易懂"prompt=f"请用{style}的方式解释什么是{topic}"

这当然能跑。

但真实项目里的 Prompt 往往不是一句话,而是包含:

  • 角色设定
  • 任务目标
  • 输入数据
  • 输出格式
  • 约束条件
  • 示例
  • 历史对话
  • 安全边界

如果全部用字符串拼接,代码会慢慢变成这样:

prompt=("你是一个专业助手。\n"f"请分析下面内容:{text}\n""要求:\n""1. 输出 JSON\n""2. 不要添加解释\n"f"3. 语气要{style}\n")

一开始看着还行,后面需求一改,Prompt 四处散落,维护起来就像在旧项目里找一个没有注释的全局变量,心态非常容易裂开。

提示词模板解决的就是这个问题:

把 Prompt 的固定部分和可变部分分离。

也就是:

固定模板 + 动态变量 = 最终 Prompt

二、Prompt Template 的底层逻辑

先看一张逻辑图。

LangChain 中的提示词模板大概遵循这个流程:

变量输入 ↓ PromptTemplate / ChatPromptTemplate ↓ PromptValue ↓ Chat Model 或 LLM

这里有一个容易忽略的点:

PromptTemplate 调用后,不一定直接得到普通字符串。

它可能生成一个PromptValue,这个中间对象可以适配不同模型:

  • 给普通 LLM 时,可以转成字符串
  • 给 Chat Model 时,可以转成消息列表

这就是 LangChain 组件能无缝接入 LCEL 链的原因。

后面你会经常看到:

chain=prompt|model|parser

这里的prompt能接到model前面,就是因为它们都遵循统一的输入输出协议。

三、PromptTemplate:最基础的字符串模板

PromptTemplate适合简单文本生成场景。

比如:

fromlangchain_core.promptsimportPromptTemplate prompt=PromptTemplate.from_template("请用{style}的风格解释什么是{topic}。")result=prompt.invoke({"style":"通俗易懂","topic":"向量数据库"})print(result)

这里的{style}{topic}是占位符。

调用时传入:

{"style":"通俗易懂","topic":"向量数据库"}

LangChain 会把变量填进模板,生成最终 Prompt。

你也可以把它理解成一个函数:

defprompt(style,topic):returnf"请用{style}的风格解释什么是{topic}。"

只不过 LangChain 的模板更适合后续接入模型、解析器和 Chain。

常见写法对比

写法说明适合场景
f-stringPython 原生字符串拼接临时实验、小脚本
PromptTemplateLangChain 字符串模板单轮文本生成
ChatPromptTemplate聊天消息模板对话模型、多轮问答
FewShotPromptTemplate示例驱动模板需要给模型示范输出模式

如果你只是写一个极小 demo,f-string 没问题。

但只要准备做项目,就建议尽早换成 Prompt Template。

四、ChatPromptTemplate:聊天模型的主力模板

现在大多数大模型接口都属于 Chat Model。

Chat Model 不只是接收一段字符串,而是接收一组消息:

system:系统规则 human:用户输入 ai:模型历史回复

所以更常用的是ChatPromptTemplate

fromlangchain_core.promptsimportChatPromptTemplate prompt=ChatPromptTemplate.from_messages([("system","你是一名耐心的技术导师,擅长用简单例子解释复杂概念。"),("human","请解释:{question}")])prompt_value=prompt.invoke({"question":"LangChain 中的 PromptTemplate 有什么用?"})print(prompt_value)

这段模板里有两条消息:

system:给模型设定身份和行为规则 human:用户真正要问的问题

system、human、ai 的区别

角色作用示例
system设定模型身份、规则、边界你是一个严谨的代码审查助手
human用户当前输入请解释这段代码
ai模型之前的回复,常用于示例或历史这段代码的作用是…

一个更贴近项目的例子:

review_prompt=ChatPromptTemplate.from_messages([("system","你是一个资深 Python 代码审查助手。"),("human","请审查下面代码,指出潜在问题:\n{code}")])

这个模板比普通字符串更清晰:

  • 系统身份放在system
  • 用户任务放在human
  • 变量{code}独立传入

代码更像在描述业务流程,而不是乱拼字符串。

五、partial 与 partial_variables

有时模板里有些变量是固定的。

比如:

prompt=PromptTemplate.from_template("请用{style}风格解释{topic},要求不超过{max_words}字。")

如果style总是“通俗易懂”,我们不想每次都传:

prompt=prompt.partial(style="通俗易懂")

之后调用时只需要传剩下的变量:

result=prompt.invoke({"topic":"RAG","max_words":100})

partial 的核心价值

提前填充一部分变量,让模板变成更具体的模板。

这有点像函数默认参数:

defexplain(topic,style="通俗易懂"):...

partial 和 partial_variables 区别

方式使用时机特点
partial()模板创建之后再填充变量灵活,适合运行时决定
partial_variables模板创建时就固定变量更自描述,适合固定配置

示例:

prompt=PromptTemplate(template="请用{style}风格解释{topic}",input_variables=["topic"],partial_variables={"style":"通俗易懂"})

如果这个固定值来自用户选择、配置文件或运行时上下文,优先用partial()

如果这个固定值就是模板定义的一部分,可以用partial_variables

六、MessagesPlaceholder:给历史对话留位置

多轮对话中,我们需要把历史聊天记录插入 Prompt。

这时就要用:

MessagesPlaceholder

示例:

fromlangchain_core.promptsimportChatPromptTemplate,MessagesPlaceholder prompt=ChatPromptTemplate.from_messages([("system","你是一个耐心的学习助手。"),MessagesPlaceholder(variable_name="history"),("human","{input}")])

这里的:

MessagesPlaceholder(variable_name="history")

可以理解为:

这里先空着,运行时把历史消息列表塞进来。

运行时可以传:

prompt.invoke({"history":[("human","我叫张三"),("ai","你好,张三")],"input":"我叫什么?"})

最终模型看到的上下文大概是:

system:你是一个耐心的学习助手 human:我叫张三 ai:你好,张三 human:我叫什么?

重点:

MessagesPlaceholder 本身不保存历史,它只是给历史消息留一个插槽。

真正自动保存和读取历史,后面通常会配合RunnableWithMessageHistory

七、FewShotPromptTemplate:用示例教模型做事

有些任务光靠一句指令不够稳定。

比如你希望模型把情绪词转换成近义词:

高兴 -> 开心 难过 -> 悲伤 生气 -> 愤怒

这时可以用 Few-shot prompting,也就是给模型几个示例。

fromlangchain_core.promptsimportFewShotPromptTemplate,PromptTemplate examples=[{"input":"高兴","output":"开心"},{"input":"难过","output":"悲伤"},{"input":"生气","output":"愤怒"},]example_prompt=PromptTemplate.from_template("输入:{input}\n输出:{output}")prompt=FewShotPromptTemplate(examples=examples,example_prompt=example_prompt,prefix="请把用户输入的情绪词转换成一个近义词。",suffix="输入:{word}\n输出:",input_variables=["word"])result=prompt.invoke({"word":"开心"})print(result)

生成的 Prompt 大致是:

请把用户输入的情绪词转换成一个近义词。 输入:高兴 输出:开心 输入:难过 输出:悲伤 输入:生气 输出:愤怒 输入:开心 输出:

Few-shot 的底层逻辑是:

不是只告诉模型规则,而是给模型看“输入和输出应该长什么样”。

这对以下任务特别有用:

  • 分类
  • 格式转换
  • 风格模仿
  • 固定结构输出
  • 少量业务规则示范

八、项目中怎么选

可以用这张表快速判断。

场景推荐模板原因
简单文本生成PromptTemplate轻量,适合字符串 Prompt
调用聊天模型ChatPromptTemplate支持 system/human/ai 角色
多轮对话ChatPromptTemplate + MessagesPlaceholder可以插入历史消息
需要示例引导FewShotPromptTemplate用样例稳定模型行为
固定部分变量partial()减少重复传参

我的建议:

如果你现在主要用 ChatOpenAI、ChatOllama 等聊天模型,优先掌握 ChatPromptTemplate。

PromptTemplate要会,但真实项目里ChatPromptTemplate更常用。

九、常见坑

1. 模板变量名和 invoke 输入不一致

模板里写的是:

"请解释:{topic}"

调用时却传:

{"question":"LangChain"}

这样会报错。

正确写法:

{"topic":"LangChain"}

变量名要对上,程序不认“差不多”。

2. 把 ChatPromptTemplate 当成普通字符串

ChatPromptTemplate生成的是消息结构,不是简单字符串。

它的优势正是能区分:

system / human / ai

不要强行把它当普通字符串拼来拼去。

3. MessagesPlaceholder 传入的不是消息列表

错误思路:

{"history":"用户:你好\nAI:你好"}

更推荐:

{"history":[("human","你好"),("ai","你好,有什么可以帮你?")]}

因为聊天模型更容易理解结构化消息。

4. Few-shot 示例太多

示例不是越多越好。

示例太多会:

  • 增加 token 成本
  • 挤占上下文窗口
  • 让 Prompt 变难维护

通常先从 2 到 5 个高质量示例开始。

十、小结

这一篇主要讲了 LangChain 的提示词模板。

你可以这样记:

PromptTemplate:普通字符串模板。 ChatPromptTemplate:聊天消息模板。 MessagesPlaceholder:给历史消息留插槽。 FewShotPromptTemplate:用示例引导模型。 partial:提前填充部分变量。

最重要的是这句话:

提示词模板不是为了少写字符串,而是为了让 Prompt 变得可复用、可维护、可组合。

下一篇继续学习:Output Parser 输出解析器
如果说 Prompt Template 负责“怎么问”,那么 Output Parser 就负责“怎么把模型回答变成程序能用的数据”。

参考资料

  • LangChain Python 官方文档:https://docs.langchain.com/oss/python/langchain
  • LangChain Core API Reference:https://reference.langchain.com/python/langchain-core/
http://www.cnnetsun.cn/news/3379917.html

相关文章:

  • ChatGPT实体识别从零到上线:手把手教会你构建高准确率NER流水线(F1≥92.6%实测验证)
  • Mac钥匙串访问:管理与同步已保存Wi-Fi密码的进阶指南
  • 收藏必备!小白程序员也能快速上手学习大模型,打造智能病历生成神器
  • 大模型微调实战:从LoRA到QLoRA的完整流程与避坑指南
  • 多智能体强化学习与生物智能融合的技术探索
  • 内容7 内存管理 1​​
  • pandas字符串操作太慢?用Polars和PyArrow提速20倍实战指南
  • 鸿蒙应用开发 @Builder 使用教程:从入门到精通
  • 执行 git clone,而 SSH 拒绝了你的连接,因为它无法找到有效的 publickey 来完成身份验证。
  • LangChain单页速查表:LCEL编码直觉与RAG/Agent生产实践
  • 基于ADC12DJ5200SE的宽带射频采样接收机设计实战指南
  • 【Java零基础连载20】Java 集合框架全集|底层原理、源码解析、面试对比、避坑实战
  • CPM4OSSP-PROXY安全防护:构建企业级软件包管理安全体系
  • 从递推公式到数值计算:深入解析sin^n x与cos^n x的定积分
  • 零基础学pcie--Bus / Device / Function 编号规则(PCIe 的“身份证”)
  • C++17多态内存资源(PMR)实战:原理、性能优化与工程应用
  • 视觉-语言大模型在图像地理定位中的创新应用
  • QiLinkOS 产品合规与风险控制框架(V1.0)运行中
  • HDFS 读、写流程
  • DeepSeek思维链显示深度逆向工程:基于v3.2.0源码级分析,解锁未公开的trace_mode参数
  • 告别系统激活困扰!KMS智能激活工具的完整解决方案
  • 基于YOLOv8的安全锥检测系统:从算法原理到工程部署
  • 从零搭建 OPC 一人公司:Python + AI Agent 全栈自动化实战(附完整代码)
  • 129.2026年国家级科研痛点 燃气轮机转子动力学与临界转速精密计算
  • 基于三菱FX2NPLC的锅炉温度控制系统设计231(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_
  • MrFlow:无需训练的扩散模型10倍加速技术解析
  • ISTA 3B随机振动测试是啥,3B测试中的随机振动一定是4h吗
  • YOLOv8-face人脸检测与关键点识别架构深度解析
  • agent面试必备35-AI Agent 核心进阶:4 大常见工具(Tools)
  • Java 30 组技术知识点表格对比