本节摘要:提示模板把提示词从代码里解耦成带占位符的图纸,LangChain 提供普通模板、对话消息模板、少样本模板三种形态,配合输出解析器可以把自由文本约束成结构化数据。本节覆盖四种写法与工艺单的工程化管理。
发动机再好,喂料规格混乱也产不出合格件。裸写提示词的三大病灶——字符串拼接不可维护、变量遗漏靠肉眼查、对话角色手工排——在 PromptTemplate 这里有三种解:
from langchain_core.prompts import PromptTemplate # 写法一:普通模板,大括号即占位器 prompt = PromptTemplate.from_template( "请告诉我知道关于{topic}的一些有趣事实") print(prompt.format(topic="太阳系")) # 输出:请告诉我知道关于太阳系的一些有趣事实 # 变量清单由模板自动推导,不用手工维护 print(prompt.input_variables) # 输出:['topic']
对话场景要用消息结构而非一大段字符串。系统消息定人设、历史消息带上下文、用户消息装当次输入——三各司其职:
from langchain_core.prompts import ChatPromptTemplate # 写法二:对话消息模板,元组列表声明角色 chat_prompt = ChatPromptTemplate.from_messages([ ("system", "你是一位{name},回答保持{style}的风格"), ("human", "{question}"), ]) msgs = chat_prompt.format_messages(name="资深电工", style="严谨", question="家里跳闸先查什么") print(msgs[0].content) # 输出:你是一位资深电工,回答保持严谨的风格 print(msgs[1].content) # 输出:家里跳闸先查什么
带历史接入时,模板里放一个占位段落,记忆模块(2.5 节)会把裁剪好的历史填进这里:
# placeholder 是留给记忆工位的历史插槽 prompt_with_history = ChatPromptTemplate.from_messages([ ("system", "你是耐心的一对一辅导老师"), ("placeholder", "{chat_history}"), ("human", "{question}"), ])
模型更擅长模仿而不是理解抽象规则。与其写十条要求,不如直接给三个输入输出对:
from langchain_core.prompts import FewShotChatMessagePromptTemplate from langchain_core.prompts import ChatPromptTemplate examples = [ {"word": "快乐", "antonym": "悲伤"}, {"word": "高", "antonym": "矮"}, {"word": "潮湿", "antonym": "干燥"}, ] # 样品段:告诉模型"照这个样子干" example_prompt = ChatPromptTemplate.from_messages([ ("human", "{word}"), ("ai", "{antonym}"), ]) few_shot = FewShotChatMessagePromptTemplate( examples=examples, example_prompt=example_prompt) # 样品段拼进完整工艺单 final_prompt = ChatPromptTemplate.from_messages([ ("system", "请输出输入词的反义词,只输出词本身"), few_shot, ("human", "{word}"), ]) print(final_prompt.format_messages(word="复杂")[-1].content) # 输出:复杂(模型看到三个样品后,会回:简单)
工艺单管进口,出口的规整靠解析器。没有它,下游拿到的是一坨自由文本;有了它,产线末端交付的是结构化数据:
from langchain_core.output_parsers import JsonOutputParser from pydantic import BaseModel, Field # 声明交付规格:字段名即合同 class Product(BaseModel): name: str = Field(description="产品名") slogan: str = Field(description="12字以内标语") parser = JsonOutputParser(pydantic_object=Product) # 把格式说明自动注入工艺单 prompt = ChatPromptTemplate.from_messages([ ("human", "为{product}想一个品牌名与标语\n{format_instructions}"), ]).partial(format_instructions=parser.get_format_instructions()) from langchain_openai import ChatOpenAI llm = ChatOpenAI(temperature=0.3) # 工艺单 + 动力单元 + 出口夹具,三件锁成传送带 chain = prompt | llm | parser print(chain.invoke({"product": "彩色袜子"})) # 输出示例:{'name': '彩织坊', 'slogan': '让每一天从彩色开始'}
工艺单多了就要进图纸柜:模板支持序列化成 JSON 存盘,团队协作时评审的是图纸而不是散在代码里的字符串。四种形态怎么选,一张表说清:
| 形态 | 适用工序 | 好处 |
|---|---|---|
| 普通模板 | 单轮文本任务 | 最轻,字符串即图纸 |
| 对话消息模板 | 聊天、有角色的指令 | 角色结构化,历史有插槽 |
| 少样本模板 | 格式敏感的抽取与改写 | 用样品替代冗长规则 |
| 解析器组合 | 需要结构化交付的工序 | 进出口都有合同 |
import json # 序列化:把图纸存进图纸柜(这里直接演示存取往返) pic = PromptTemplate.from_template("总结{doc}的三个要点") saved = json.dumps({"template": pic.template, "variables": pic.input_variables}, ensure_ascii=False) print(saved) # 输出:{"template": "总结{doc}的三个要点", "variables": ["doc"]}
⚠️ 占位符撞车是高频事故:模板里已经用了双大括号转义的字面量大括号,又混入单大括号变量,格式化时才报错。复杂模板先单独 format 一遍再上产线,两分钟排查省两小时返工。
写完任何一张工艺单,过三问:变量是否都由 input_variables 覆盖;角色与历史插槽是否放对位置;出口是自由文本还是结构化、下游知道吗。三问答完,这张工艺单就能挂上 2.3 节的传送带了。