第02章:Prompt 工程 —— 用语言精准指挥 AI

0 阅读7分钟

本章目标:理解 Prompt 的设计原则,掌握 LangChain 模板系统、Few-Shot 提示和思维链(CoT)三大核心技术,能够写出让 AI 稳定输出你期望结果的提示词。


一、为什么 Prompt 如此重要?

很多人学会调用 LLM API 后,第一个困惑是:”为什么同一个问题,有时答得很好,有时答得一塌糊涂?”

答案几乎都在 Prompt 里。

同一个任务,两种 Prompt 的差距:

# ❌ 模糊的 Prompt
prompt_bad = "总结一下这篇文章"
# AI 可能输出:3 句话,也可能输出 3 段,风格不定,不知道要点

# ✅ 精准的 Prompt
prompt_good = """
请对以下文章进行总结。要求:
1. 用不超过 150 字
2. 必须包含:核心观点、关键数据、适用结论
3. 使用简洁的中文,避免专业术语
4. 格式为:【核心观点】... 【关键数据】... 【结论】...

文章内容:
{article}
"""

Prompt 工程的黄金公式:

高质量 Prompt = 清晰角色 + 明确任务 + 具体约束 + 期望格式

二、LangChain 为什么需要 Prompt 模板?

在真实项目里,Prompt 通常不是写死的字符串,而是根据用户输入动态生成的。LangChain 的 Prompt 模板解决了以下问题:

  1. 复用性:同一个提示结构可以填入不同的变量值
  2. 可测试性:模板和数据分离,单独测试模板逻辑
  3. 与 Chain 集成:模板可以无缝接入 LCEL 链(下一章讲解)

添加图片注释,不超过 140 字(可选)


三、Step 1:基础 Prompt 模板

PromptTemplate(纯文本模板)

from langchain_core.prompts import PromptTemplate

# ── 创建模板,用 {} 标记变量 ──────────────────────────────────
template = PromptTemplate(
    input_variables=["topic", "audience", "length"],
    template="""你是一位经验丰富的科普作家。

请为【{audience}】写一篇关于"{topic}"的科普文章。
要求:
- 文章长度:{length}字以内
- 语言:通俗易懂,避免专业术语
- 结构:先讲是什么,再讲为什么重要,最后给出实际例子
"""
)

# ── 填充变量生成最终 Prompt ──────────────────────────────────
formatted = template.format(
    topic="区块链",
    audience="初中生",
    length="300"
)
print(formatted)

ChatPromptTemplate(对话模板,推荐使用)

from langchain_core.prompts import ChatPromptTemplate

# ── 多角色对话模板 ────────────────────────────────────────────
chat_prompt = ChatPromptTemplate.from_messages([
    # system:定义 AI 的专业身份和行为准则
    ("system", """你是一位专业的{role},拥有{years}年从业经验。
回答时遵循以下规范:
- 先给出明确结论,再解释原因
- 如果问题超出专业范围,直接告知
- 用中文回答,简洁专业"""),
    
    # human:用户的问题
    ("human", "{question}"),
])

# ── 格式化为消息列表 ──────────────────────────────────────────
messages = chat_prompt.format_messages(
    role="心脏病学医生",
    years=15,
    question="心率超过 100 次/分钟算正常吗?",
)

# messages 是一个列表:[SystemMessage(...), HumanMessage(...)]
for msg in messages:
    print(f"[{msg.__class__.__name__}]: {msg.content}")

与 LLM 直接调用:

from langchain_openai import ChatOpenAI
import os

llm = ChatOpenAI(
    model="qwen-plus",
    openai_api_key=os.getenv("DASHSCOPE_API_KEY"),
    openai_api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

# 方式一:先格式化,再调用
messages = chat_prompt.format_messages(role="营养师", years=10, question="早餐吃什么最健康?")
response = llm.invoke(messages)
print(response.content)

# 方式二:用链(推荐,下一章深讲)
chain = chat_prompt | llm
response = chain.invoke({"role": "营养师", "years": 10, "question": "早餐吃什么最健康?"})
print(response.content)

四、Step 2:Few-Shot 提示(示例驱动)

什么是 Few-Shot?

Few-Shot 是”给几个例子”的意思。你给模型展示几个”输入→输出”的例子,模型就能理解你期望的格式和风格,然后对新输入产生类似的输出。

适用场景:

  • 需要特定格式的输出(JSON、表格、特定句式)
  • 需要特定风格(文言文、专业术语、儿童语言)
  • 模型不擅长的小众任务(情感分类、命名实体识别)
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
from langchain_openai import ChatOpenAI

# ── 定义示例:每个示例都是一个 dict ──────────────────────────
examples = [
    {
        "text": "这款手机电池续航太差了,一天充两次电,差评!",
        "sentiment": "负面",
        "reason": "用户对电池续航不满"
    },
    {
        "text": "物流超快,下单第二天就到了,包装也很严实。",
        "sentiment": "正面",
        "reason": "用户对物流速度和包装满意"
    },
    {
        "text": "质量还行,但价格有点贵,性价比一般。",
        "sentiment": "中性",
        "reason": "质量满意但价格不满意,整体中立"
    },
]

# ── 单个示例的展示模板 ────────────────────────────────────────
example_prompt = PromptTemplate(
    input_variables=["text", "sentiment", "reason"],
    template="评论:{text}\n情感:{sentiment}\n原因:{reason}"
)

# ── Few-Shot 模板 ─────────────────────────────────────────────
few_shot_prompt = FewShotPromptTemplate(
    examples=examples,
    example_prompt=example_prompt,
    prefix="你是一个电商评论情感分析专家。请分析以下评论的情感倾向(正面/负面/中性)并说明原因:\n\n参考示例:",
    suffix="\n评论:{text}\n情感:",  # 最后留空,让模型填写
    input_variables=["text"],
    example_separator="\n---\n",     # 示例之间的分隔符
)

# ── 调用 ─────────────────────────────────────────────────────
llm = ChatOpenAI(
    model="qwen-plus",
    openai_api_key=os.getenv("DASHSCOPE_API_KEY"),
    openai_api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

test_reviews = [
    "屏幕显示效果非常清晰,色彩还原准确,专业摄影师推荐!",
    "买了之后根本用不了,联系客服也没人回,气死了。",
]

for review in test_reviews:
    formatted = few_shot_prompt.format(text=review)
    response = llm.invoke(formatted)
    print(f"评论:{review}")
    print(f"分析:{response.content}")
    print("-" * 50)

Few-Shot 的本质原理:


五、Step 3:思维链提示(Chain of Thought)

为什么需要思维链?

直接问 AI 复杂问题时,它可能跳步推理导致出错。思维链(CoT)的思路是:强制模型一步步思考,每个步骤都显式写出来,这样错误更少,推理更可靠。

研究发现:在复杂推理任务上,CoT 提示可以将准确率提升 20-50%。

from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

# ── 不带思维链(容易出错)────────────────────────────────────
no_cot_prompt = ChatPromptTemplate.from_messages([
    ("system", "你是数学老师,直接给出答案。"),
    ("human", "{question}"),
])

# ── 带思维链(逐步推理)──────────────────────────────────────
cot_prompt = ChatPromptTemplate.from_messages([
    ("system", """你是数学老师。解题时必须按以下格式逐步推理:
    
【分析】:理解题目,找出已知条件
【步骤1】:...(写出每一步的计算)
【步骤N】:...
【答案】:最终结果

绝不能跳过中间步骤,必须展示完整推理过程。"""),
    ("human", "{question}"),
])

llm = ChatOpenAI(
    model="qwen-plus",
    openai_api_key=os.getenv("DASHSCOPE_API_KEY"),
    openai_api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
    temperature=0,  # 数学题用 0,确保结果稳定
)

question = """
一家餐厅周一到周五每天接待 80 位顾客,周六接待 120 位,周日不营业。
每位顾客平均消费 85 元,月均营业天数按 4.3 周计算。
请计算该餐厅每月的营业额。
"""

print("=" * 50)
print("不带思维链的回答:")
chain_no_cot = no_cot_prompt | llm
print(chain_no_cot.invoke({"question": question}).content)

print("\n" + "=" * 50)
print("带思维链的回答:")
chain_cot = cot_prompt | llm
print(chain_cot.invoke({"question": question}).content)

Zero-Shot CoT(无示例,直接触发推理):

# 只需要在问题末尾加上这句话,就能激活模型的推理模式
question_with_cot = f"{question}\n\n让我们一步一步地思考。"

💡 “让我们一步一步地思考” 这句话已被研究证明是一个非常有效的 CoT 触发词,适用于各种 LLM。


六、Prompt 最佳实践速查

原则 1:具体优于模糊

# ❌ 模糊
"帮我改进这段代码"

# ✅ 具体
"检查以下 Python 代码的潜在 bug 和性能问题,
按【问题】【位置】【修复建议】格式输出,
重点关注:空指针、资源泄漏、时间复杂度"

原则 2:使用分隔符隔离数据

# ❌ 容易被用户内容干扰(Prompt 注入风险)
f"总结这篇文章:{user_input}"

# ✅ 用 XML 标签或分隔符隔离
f"""总结以下<article>标签内的文章:

<article>
{user_input}
</article>

要求:100字以内,提取核心观点。"""

原则 3:角色设定要具体

# ❌ 过于泛泛
("system", "你是专家")

# ✅ 明确背景和能力
("system", """你是一位拥有 10 年经验的全栈工程师,
擅长 Python、React 和 AWS 架构。
当回答技术问题时,优先给出可直接运行的代码示例,
并标注关键参数的作用。""")

原则 4:控制输出格式

# 要求输出 JSON
("system", """以 JSON 格式输出,字段包括:
- "summary": 文章摘要(50字以内)
- "keywords": 关键词列表(3-5个)
- "sentiment": 情感倾向(positive/negative/neutral)
不要输出任何 JSON 之外的文字。""")

七、常见 Prompt 问题与调试方法

问题现象可能原因调试方法
输出格式不稳定格式要求不够明确在 prompt 中加入示例输出
答案偏离主题系统提示太弱或无系统提示强化 system 消息的约束
输出截断max_tokens 设置太小增大 max_tokens
数学计算出错没有使用 CoT加入逐步推理要求
多语言混杂没有语言约束在 prompt 中明确”用中文回答”

调试技巧:先 print(prompt.format(...)) 看格式化后的完整 Prompt,确认是否符合预期。


📌 下一章预告:学会了 Prompt 之后,下一章我们学习如何用 LCEL(LangChain 表达语言)把 Prompt、LLM、输出解析器串联成”链”,实现更复杂的多步骤任务。

作者:阿聪谈架构

公众号:阿聪谈架构(分享后端架构 / AI / Java 技术文章)

相关代码关注公众号:【阿聪谈架构】 回复:AI专栏代码