19 Prompt 进阶——系统讲解

Prompt 进阶------系统讲解

为什么要"Prompt 进阶"?

普通调用方式:llm.invoke("你好") 够用,但有三个问题:

  • 格式不稳定:LLM 每次输出格式可能不同
  • 推理质量差:复杂逻辑题容易出错
  • 示例复用难:想让 LLM 模仿某种风格,只能每次手写

Prompt 进阶就是解决这三个问题的工程方法。

Chat 模型吃的是「消息列表」

像 GPT 这类 Chat 模型,输入不是一段纯文本,而是一组有角色的消息:

json 复制代码
[
    {"role": "system",    "content": "你是助手"},
    {"role": "user",      "content": "苹果什么颜色?"},
    {"role": "assistant", "content": "通常是红色"},
    {"role": "user",      "content": "草莓呢?"},   # 当前问题
]

LangChain 里对应的消息类型是:

  • SystemMessage
  • HumanMessage(用户)
  • AIMessage(助手)

ChatPromptTemplate 的作用就是:用模板描述「消息列表应该长什么样」,运行时填入变量,生成真正的消息列表。

ChatPromptTemplate.from_messages

hatPromptTemplate 是「对话式 Prompt 模板」;from_messages 是它的工厂方法,用消息列表来定义模板结构。

参数格式

from_messages 接受一个列表,每个元素可以是:

写法 含义
("system", "固定文本") 系统消息,内容写死
("human", "{变量名}") 用户消息,带占位符
("ai", "{变量名}") 助手消息(用于 few-shot 示例里的「标准答案」)
子模板对象 如 FewShotChatMessagePromptTemplate、MessagesPlaceholder
它做什么?
  • 定义结构:这段对话由哪些消息、什么顺序组成
  • 声明变量:{question} 这类占位符,调用时要传入
  • 生成消息:invoke({"question": "草莓什么颜色?"}) 时,把变量填进去,返回 ChatPromptValue(本质是消息列表)
简单例子
py 复制代码
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是颜色助手"),
    ("human", "{question}"),
])
# 调用
messages = prompt.invoke({"question": "草莓什么颜色?"})
# 实际生成(概念上):
# [
#   SystemMessage("你是颜色助手"),
#   HumanMessage("草莓什么颜色?"),
# ]
常见用途
  • 系统指令 + 用户问题(最常见)
  • 多轮对话(配合 MessagesPlaceholder 插入历史)
  • 作为 FewShotChatMessagePromptTemplate 的 example_prompt(定义单条示例格式)

FewShotChatMessagePromptTemplate

专门用于 Few-shot(少样本)提示 的子模板。

它不负责整段 prompt,只负责:把多条示例数据,批量展开成多轮 human/ai 对话消息。

核心参数
py 复制代码
FewShotChatMessagePromptTemplate(
    examples=[...],           # 示例数据列表
    example_prompt=...,       # 单条示例的格式模板
    example_selector=None,    # 可选:动态挑选示例(如语义相似)
)
参数 作用
examples 示例库,每条是 dict,字段名要和 example_prompt 里的 {变量} 对应
example_prompt 一条示例怎么排版(通常也是 ChatPromptTemplate.from_messages 建的)
example_selector 不传则用全部 examples;传了可以按相似度只选几条

Few-shot Prompting(少样本提示)

原理:给 LLM 几个"输入 → 输出"的例子,让它照着模仿。

两种形式:

text 复制代码
# Zero-shot(无示例)
用户:苹果是什么颜色?
AI:苹果有红色、绿色、黄色。

# Few-shot(有示例,输出更规范)
示例1 - 用户:香蕉是什么颜色?AI:香蕉通常是黄色。
示例2 - 用户:西瓜是什么颜色?AI:西瓜外皮绿色,果肉红色。
用户:苹果是什么颜色?      ← 真实问题
AI:苹果通常是红色或绿色。  ← 自动模仿上面的简洁风格

LangChain 中用 FewShotChatMessagePromptTemplate 实现,它把示例列表自动展开成多轮 human/ai 对话消息塞进 prompt。

Chain-of-Thought(思维链,CoT)

原理:让模型在给答案前先写推理过程,就像让人"列草稿"。

实验证明:对数学/逻辑题,CoT 比直接问答准确率高 20-40%。

两种 CoT:

类型 方法 适用场景
Zero-shot CoT system 里写"请一步步思考" 快速,不需要准备示例
Few-shot CoT 示例里包含完整推理步骤 更稳定,适合固定题型
text 复制代码
# Zero-shot CoT 示例
system: "请先写推理步骤,再给答案"
user: "鸡兔同笼35头94脚"

# 模型会输出:
# 推理:设鸡x只,兔y只
# x + y = 35
# 2x + 4y = 94 → x=21, y=14
# 答案:鸡21只,兔14只

动态 Few-shot(SemanticSimilarityExampleSelector)

问题:示例库有100条,每次全塞进 prompt → token 暴增,费钱还慢。

解决:用向量相似度,自动从示例库里找最相关的 K 条塞进 prompt。

text 复制代码
示例库(100条)
    ↓ 向量化存入 Qdrant
用户问"蓝莓什么颜色"
    ↓ 向量检索
命中最近邻:葡萄颜色、草莓颜色(都是水果颜色类问题)
    ↓
只把这2条塞进 prompt

关键类:SemanticSimilarityExampleSelector,内部封装了向量数据库的存储和检索。会向量化所有示例并创建存储

MessagesPlaceholder(消息占位符)

原理:在 prompt 模板中预留一个"插槽",运行时把任意消息列表插进去。

典型用途:多轮对话时注入历史记录。

text 复制代码
# 模板定义时:
[system, <<chat_history占位>>, human当前问题]

# 运行时传入 chat_history:
[system, human"我叫小明", ai"你好小明", human"我叫什么?"]
#                ↑ 这部分由 MessagesPlaceholder 动态填充

partial()------模板预填充

原理:提前固定模板中的部分变量,生成可复用的"专用模板"。

py 复制代码
# 原始模板:两个变量
translate_prompt = "将{text}翻译成{language}"

# partial 固定 language,生成两个子模板
to_english = translate_prompt.partial(language="英文")  # 只剩 text 变量
to_japanese = translate_prompt.partial(language="日文")

# 调用时只需传 text
to_english.invoke({"text": "你好"})

类比:像函数的 functools.partial,固定部分参数。

结构化输出(with_structured_output)

问题:LLM 返回的是字符串,需要手动解析 JSON,容易出错。

解决:用 Pydantic 定义期望格式,LangChain 自动让 LLM 返回符合格式的对象。

py 复制代码
class MovieReview(BaseModel):
    title: str
    rating: float
    pros: list[str]

structured_llm = llm.with_structured_output(MovieReview)
result = structured_llm.invoke("评价《星际穿越》")
# result 是 MovieReview 对象,直接 result.rating 取值

底层:LangChain 把 Pydantic schema 转成 OpenAI 的 function_call 或 json_schema,强制模型按格式输出。

「必要性」对照表

能力 Chain 时代 LangGraph 实战 是否值得学
zero-shot / few-shot 常用 常用(常在 node 里拼 prompt) 必学
SemanticSimilarityExampleSelector 大示例库时常用 常用(可独立成 node) 必学
with_structured_output 常用 非常常用(路由、评分、抽取) 必学
MessagesPlaceholder 多轮 Chain 的标准写法 基本不用 了解即可
partial() 复用模板时有点用 很少用 了解即可

代码示例

py 复制代码
# 准备示例库:每个 dict 包含 input(用户问题)和 output(期望回答)
FEW_SHOT_EXAMPLES = [
    {
        "input": "苹果是什么颜色?",
        "output": "苹果通常是红色、绿色或黄色。",
    },
]
cot_examples = [
    {
        "input": "一共有5只苹果,吃掉2只,还剩几只?",
        "output": (
            "推理:\n"
            "1. 初始苹果数量 = 5\n"
            "2. 吃掉 2 只\n"
            "3. 剩余 = 5 - 2 = 3\n"
            "答案:3"
        ),
    },
]
example_prompt = ChatPromptTemplate.from_messages(
    [
        ("human", "{input}"),
        ("ai", "{output}"),
    ]
)

zero_shot_prompt

py 复制代码
cot_prompt = ChatPromptTemplate.from_messages(
[
    (
        "system",
        "你是数学解题助手。请先写出完整的推理步骤,最后单独一行写【答案:X】。",
    ),
    ("human", "{question}"),
]
)
chain = cot_prompt | llm
result = chain.invoke({"question": "鸡兔同笼:共35个头,94只脚,鸡和兔各有几只?"})
return result.content.strip()

few_shot_prompt

py 复制代码
example_prompt = ChatPromptTemplate.from_messages(
    [
        ("human", "{input}"),
        ("ai", "{output}"),
    ]
)
few_shot_prompt = FewShotChatMessagePromptTemplate(
    examples=cot_examples,
    example_prompt=example_prompt,
)
# 调用方式如 zero_shot_prompt

SemanticSimilarityExampleSelector

py 复制代码
selector = SemanticSimilarityExampleSelector.from_examples(
    examples=FEW_SHOT_EXAMPLES,# 加入有很多例子
    embeddings=embeddings,
    vectorstore_cls=QdrantVectorStore,
    location=":memory:",
    collection_name="dynamic_few_shot_examples",
    k=2,  # 每次选最相似的2个示例
)
dynamic_few_shot = FewShotChatMessagePromptTemplate(
    example_selector=selector,
    example_prompt=example_prompt,
)
final_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是颜色描述助手,参考示例风格回答。"),
        dynamic_few_shot,
        ("human", "{question}"),
    ]
)
# 调用方式如 zero_shot_prompt

MessagesPlaceholder

py 复制代码
prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是一个友好的助手,记住之前的对话内容。"),
        (MessagesPlaceholder(variable_name="chat_history")),
        ("human", "{question}"),
    ]
)
chain = prompt | llm

history: list = []
q1 = "我叫小明,今年20岁。"
r1 = chain.invoke({"chat_history": history, "question": q1})
history.append(HumanMessage(content=q1))
history.append(AIMessage(content=r1.content))

partial_prompt

py 复制代码
transalte_prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "你是翻译助手,请将文本翻译成{language},只输出翻译结果。"),
        ("human", "{text}"),
    ]
)

to_endlish = transalte_prompt.partial(language="英文")
text = "人工智能正在改变世界。"
cn_result = cn_chain.invoke({"text": text}).content.strip()
return {"english": en_result, "chinese": cn_result}

with_structured_output

py 复制代码
structured_llm = llm.with_structured_output(MovieReview, method="function_calling")
prompt = .ChatPromptTemplatefrom_messages(
    [
        ("system", "你是专业影评人,请对电影进行结构化评价。"),
        ("human", "请评价电影《{movie}》"),
    ]
)
# 代码中 chain 等价于:
# messages = prompt.invoke({"movie": "东邪西毒"})只把模板渲染成消息,不会调用模型
# result = structured_llm.invoke(messages)
chain = prompt | structured_llm
result: MovieReview = chain.invoke({"movie": "东邪西毒"})
return result
相关推荐
来让爷抱一个2 小时前
拯救我的“烂尾“项目:我用MonkeyCode把五个AI热点实践了个遍
网络·数据库·人工智能·prompt·ai编程
Eric.462 天前
AI漫剧量产Prompt参数实操手册:Stable Diffusion+ComfyUI+OpenClaw通用复制即用配置
人工智能·深度学习·stable diffusion·prompt·ai漫剧
精彩AI说2 天前
ChatGPT生成内容总是不按要求怎么办?格式、字数和指令失效的6个解决方法
chatgpt·prompt·ai写作·提示词·使用技巧·chatgpt教程
Eric.462 天前
Stable Diffusion+ComfyUI+OpenClaw AI漫剧量产提示词工程:结构化Prompt、负面词脱敏、权重锁定防画面崩坏全方案
大数据·人工智能·stable diffusion·prompt·comfyui·ai漫剧
SHIPKING3932 天前
【Harness Engineering】07_多代理与验证:用分工和验证管理不稳定性
prompt·harness
LayZhangStrive2 天前
提示词沉淀 - 使用豆包时平时提问题
面试·职场和发展·prompt·提示词·豆包
ZGi.ai2 天前
ZGI:工作流分支失控,先把规则拆出 Prompt
prompt·prompt工程·workflow·aiagent·zgi
赵大仁2 天前
Prompt 缓存与上下文压缩:把 Token 账单砍一刀的实操清单
ai·大模型·prompt·token·成本优化
Elias不吃糖3 天前
Langfuse 入门:Trace、Prompt、Dataset、Experiment、Evaluator
前端·python·prompt·langfuse