1. 本章目标 #

前几章里,提示词多是写死在代码字符串里。业务一复杂就会出现:

本章目标:

用 Prompt / ChatPrompt 模板把提示词做成可维护的一层:变量填充、少样本、系统提示设计。

学完你应能:

参考(API 在 langchain_core.prompts):

2. 为什么需要 Prompt 层 #

硬编码:
  model.invoke("你是客服……用户说:" + user_text)

模板化:
  prompt = ChatPromptTemplate.from_messages([...])
  messages = prompt.format_messages(user_text=user_text, ...)
  model.invoke(messages)

模板层的价值:

收益 说明
结构清晰 system / human / ai 角色分开写
变量安全 缺变量会报错,少拼字符串 bug
可复用 同一模板服务多个入口(API、脚本、Agent)
可组合 历史占位、少样本、partial 预填

和 Agent 的关系:create_agent(system_prompt=...) 管 harness 级人设;业务里更复杂的「翻译模板 / 抽工单模板 / 带历史的问答」仍常用 ChatPromptTemplate

3. PromptTemplate:字符串模板 #

适合最终是一段文本的场景(简单补全、非聊天协议、或拼进某个字段)。

3.1. 1.PromptTemplate.py #

from langchain_core.prompts import PromptTemplate
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv

load_dotenv(override=True)

# {name} 是待填变量;缺变量 format 会报错
prompt = PromptTemplate.from_template(
    "你是简洁助手。用户叫{name},请用一句话打招呼。"
)

text = prompt.format(name="张三")
print(text)

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
print(model.invoke(text).content)

要点:

4. ChatPromptTemplate:对话模板(主推) #

按「多条带角色的消息」组织提示,每条都可含 {变量}

4.1. 2.ChatPromptTemplate.py #

from langchain_core.prompts import ChatPromptTemplate
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv

load_dotenv(override=True)

prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是乐于助人的助手,名字叫{name}。回答要短。"),
        ("human", "你好!"),
        ("ai", "你好,我是{name},有什么可以帮你?"),
        ("human", "{user_input}"),
    ]
)

# 填充全部变量 → 消息列表
messages = prompt.format_messages(
    name="小助",
    user_input="你叫什么名字?",
)
for m in messages:
    print(type(m).__name__, ":", m.content)

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
print(model.invoke(messages).content)

也可用 invoke 得到 ChatPromptValue,再 .to_messages()

value = prompt.invoke({"name": "小助", "user_input": "你叫什么名字?"})
print(value.to_messages())

4.2. 管道写法预览(LCEL) #

第 7 章会系统讲 Runnable。这里先建立直觉:prompt | model 把「填模板 → 调模型」串起来。

4.2.1. 3.prompt_pipe_model.py #

from langchain_core.prompts import ChatPromptTemplate
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv

load_dotenv(override=True)

prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "把用户句子翻译成{target_lang},只输出译文。"),
        ("human", "{text}"),
    ]
)
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)

chain = prompt | model
ai = chain.invoke({"target_lang": "英文", "text": "生活不止眼前的代码"})
print(ai.content)

5. MessagesPlaceholder:插入历史消息 #

普通 {var} 填充的是字符串;多轮历史需要插入一整段消息对象列表,用 MessagesPlaceholder

5.1. 4.MessagesPlaceholder.py #

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

load_dotenv(override=True)

prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是简洁助手,结合历史回答。"),
        MessagesPlaceholder("history"),
        ("human", "{question}"),
    ]
)

history = [
    HumanMessage("我叫李雷。"),
    AIMessage("好的,李雷。"),
]

messages = prompt.format_messages(
    history=history,
    question="我叫什么名字?",
)

for m in messages:
    print(type(m).__name__, ":", m.content)

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
print(model.invoke(messages).content)

注意:history 必须是 Message 列表,不能是普通字符串列表。第 11 章 Memory 会把「自动维护 history」接上。

可选:MessagesPlaceholder("history", optional=True),允许调用时不传历史。

6. 少样本(Few-shot) #

少样本 = 在提示里放若干「输入 → 期望输出」例子,让模型模仿格式与风格。常比单纯口头描述规则更稳。

6.1. 聊天消息里直接写死示例(最简单) #

适合示例少、固定不变:

6.1.1. 5.few_shot_inline.py #

from langchain_core.prompts import ChatPromptTemplate
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv

load_dotenv(override=True)

prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "将情感分类为:正面 / 负面 / 中性。只输出标签。"),
        # 示例 1
        ("human", "今天天气真好,心情不错。"),
        ("ai", "正面"),
        # 示例 2
        ("human", "快递又丢了,太糟心了。"),
        ("ai", "负面"),
        # 真正的用户输入
        ("human", "{text}"),
    ]
)

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
chain = prompt | model
print(chain.invoke({"text": "这部电影一般般,不好不坏。"}).content)

6.2. FewShotPromptTemplate(字符串少样本) #

示例多、要统一格式时,用「示例列表 + 单例模板 + 前后缀」拼成一段文本:

6.2.1. 6.FewShotPromptTemplate.py #

from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv

load_dotenv(override=True)

examples = [
    {"word": "开心", "antonym": "难过"},
    {"word": "高", "antonym": "矮"},
]

example_prompt = PromptTemplate.from_template(
    "词语:{word}\n反义词:{antonym}"
)

few_shot = FewShotPromptTemplate(
    examples=examples,
    example_prompt=example_prompt,
    prefix="给出词语的反义词。参照示例格式作答。\n",
    suffix="\n词语:{input}\n反义词:",
    input_variables=["input"],
)

text = few_shot.format(input="冷")
print(text)

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
print(model.invoke(text).content)

6.3. FewShotChatMessagePromptTemplate(聊天少样本) #

把示例也做成 human/ai 消息对,更贴 Chat Model:

6.3.1. 7.FewShotChatMessagePromptTemplate.py #

from langchain_core.prompts import (
    ChatPromptTemplate,
    FewShotChatMessagePromptTemplate,
)
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv

load_dotenv(override=True)

examples = [
    {"input": "2+2", "output": "4"},
    {"input": "2+3", "output": "5"},
]

example_prompt = ChatPromptTemplate.from_messages(
    [
        ("human", "{input}"),
        ("ai", "{output}"),
    ]
)

few_shot_prompt = FewShotChatMessagePromptTemplate(
    examples=examples,
    example_prompt=example_prompt,
)

final_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是算术助手。只输出最终数字。"),
        few_shot_prompt,
        ("human", "{input}"),
    ]
)

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
chain = final_prompt | model
print(chain.invoke({"input": "10+8"}).content)

示例很多、需要按长度或相似度动态挑选时,可接 example_selector(如 LengthBasedExampleSelector)。本课先掌握静态 few-shot;动态选择属于进阶优化。

7. partial:预填部分变量 #

把模板里很少变的变量先锁死,调用时只传业务变量。

7.1. 8.partial.py #

from langchain_core.prompts import ChatPromptTemplate
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv

load_dotenv(override=True)

base = ChatPromptTemplate.from_messages(
    [
        ("system", "你是{role},面向用户{user_name},回答要短。"),
        ("human", "{question}"),
    ]
)

# 预填角色与用户名,只剩 question
prompt = base.partial(role="售后顾问", user_name="王女士")

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
chain = prompt | model
print(chain.invoke({"question": "订单两天了还没发货,怎么办?"}).content)

PromptTemplate.partial(...) 同理。适合:租户名、产品名、固定人设等多处复用同一骨架。

8. 系统提示设计(实战清单) #

系统提示写得好,比堆 few-shot 更划算。可用下面结构:

1. 角色:你是谁
2. 目标:要完成什么
3. 能力与边界:能做什么 / 不能做什么
4. 工具策略:何时必须调工具(若有)
5. 输出格式:长短、语言、是否 JSON
6. 失败行为:信息不足时怎么问澄清

8.1. 9.system_prompt_design.py #

from langchain_core.prompts import ChatPromptTemplate
from langchain.chat_models import init_chat_model
from dotenv import load_dotenv

load_dotenv(override=True)

SYSTEM = """
你是电商售后助手。

目标:用最短步骤帮用户解决问题。

边界:
- 不承诺未核实的退款时效
- 不知道物流状态时,请用户提供订单号

输出:
- 先给结论(1 句)
- 再给 1~3 条可执行步骤
- 使用中文
""".strip()

prompt = ChatPromptTemplate.from_messages(
    [
        ("system", SYSTEM),
        ("human", "{question}"),
    ]
)

model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
print((prompt | model).invoke(
    {"question": "我的快递一直显示运输中,已经五天了。"}
).content)

接到 Agent 时,把同一段文字放进 system_prompt 即可:

from langchain.agents import create_agent

agent = create_agent(
    model="deepseek:deepseek-v4-flash",
    tools=[],  # 实战中挂查询订单等工具
    system_prompt=SYSTEM,
)

9. 可维护性建议 #

做法 说明
模板与业务代码分离 模板集中定义,入口只 invoke 变量
先 format 再调模型 出问题先 print(messages),别先怪模型
少样本要「像真输出」 示例格式必须和你期望的最终格式一致
变量命名稳定 {question} / {context} 全项目统一
敏感信息不进 prompt 密钥、身份证等不要拼进模板
用 LangSmith 对比版本 改 prompt 前后看 trace / 评测集

10. 练习 #

  1. 翻译模板:用 ChatPromptTemplate 做「源语言 / 目标语言 / 文本」三变量翻译链。
  2. 带历史客服MessagesPlaceholder("history") + 两轮假历史,问第三轮是否记得用户名。
  3. 情感分类 few-shot:至少 3 条示例,测 5 句新文本,看标签是否稳定。
  4. partial 多租户:同一模板 partial(product_name=...) 出两套「不同产品」的助手,对比回答。

11. 本章小结 #

  1. Prompt 层把提示词从散落字符串变成可复用组件。
  2. 聊天场景主用 ChatPromptTemplate;需要整段文本时用 PromptTemplate
  3. MessagesPlaceholder 插入消息历史;few-shot 用示例教格式。
  4. partial 预填固定变量;系统提示写清角色、边界、输出格式。
  5. prompt | model 是后续 LCEL 的起点。

下一章:结构化输出——用 Pydantic / Structured Output 抽出订单、工单等稳定结构。