提示词工程完全指南:从零基础到高级优化
从零样本基础到思维树、DSPy 和自动化优化,掌握提示词工程。包含基准测试、代码示例和调试工具箱。
同样的模型,两种问法在小学数学题上的准确率分别为 17% 和 78%——这种差别并非玄学,而是提示词工程的结果。本文将系统梳理那些真正有效的技巧及其原因,并探讨如何在生产环境中优化提示词。
你将学到什么#
- 基础:零样本、少样本、多样本、任务分解,以及“五块结构”的提示词骨架。
- 推理: Chain-of-Thought、 Self-Consistency、 Tree of Thoughts、 Graph of Thoughts、 ReAct。
- 自动化: APE、 DSPy、 LLMLingua 压缩。
- 实战模板:结构化输出、代码生成、数据抽取、多轮对话。
- 评估与调试:指标选择、 A/B 测试、错误归因、调试工具箱。
前置要求:会用 Python,调用过任何一家的 LLM API。不需要数学背景。
为什么提示词工程值得花时间#
2020 年 GPT-3 刚推出时,研究者很快发现了一个反常识的现象:同一个模型,不同问法会导致截然不同的结果。写得不好的提示词只能换来胡言乱语,而精心设计的提示词却能解决复杂的推理任务。这并不是 bug,而是这类模型学习语言的方式所决定的。
传统编程是确定性的:写函数、定义入参出参,机器照做;而语言模型不是这样工作的。它本质上是在根据万亿级 token 训练出的模式预测“下一个最有可能的词”;提示词不是在给模型下命令,而是在为它构造一个上下文,把它的概率分布往你想要的方向“推”一下。
把它想成米其林餐厅的菜单:如果只写一句“给我做点好吃的”,厨师只能凭直觉发挥;写清楚“清蒸鲈鱼,少盐,配芦笋”,他才知道怎么端盘。提示词工程的本质,就是写出让模型不需要猜的菜单。
这项工作的回报显著:优化后的提示词既能将 API 调用成本压缩至原来的十分之一(得益于更紧凑的上下文),又能将复杂推理任务的准确率从 40% 提升至 90%。对于每月百万级请求的生产系统,这是真金白银。
一个生产级提示词的解剖#

几乎所有上线的提示词都能拆成同样的五块:角色(role)、上下文(context)、指令(instruction)、示例(examples)、输出格式(format)。把它当骨架,每个任务换“血肉”,骨架保持不变——这种统一结构能显著简化评估、缓存和版本控制。
基础技巧#
零样本提示#
零样本就是不给任何示例,直接让模型做事,完全依靠它在预训练中见过的世界来理解意图。
Yao 等人 (2023) 的对照:
| 任务 | CoT | ToT | Δ |
|---|---|---|---|
| 24 点 | 7.3% | 74% | +66pp |
| 创意写作 | 7.3 | 7.9 | +0.6 |
| 填字游戏 | 15.6% | 78% | +62pp |
代价。breadth=3、depth=4 一道题就要约 80 次 LLM 调用。ToT 真正划算的前提有两个:解空间里确实存在多条可行路径,模型能可靠地给中间状态打分。组合优化、规划、约束满足类问题适合用;普通问答则不适合。
生产里推荐用带优先队列的最佳优先搜索,配一个硬性的调用次数上限,单题不能炸预算。
Graph of Thoughts (GoT):从树到 DAG#
GoT(Besta 等人, 2023)将 ToT 推广为任意有向无环图。思想可以合并(多分支汇总成一个),也可以迭代(一个思想跨轮次反复打磨),表达能力比树更强。
最经典的例子是多文档摘要:
每个单文档摘要互相独立,可以并行。合并步把它们汇总。这是图,不是树。
| |
在 32 个数字的排序任务上, Besta et al. 报告 GoT 准确率 89%,成本比 ToT 低 62%——合并节点把重复探索砍掉了。
ReAct:边想边做#
ReAct(Yao 等人, 2022)将推理和行动交错起来。模型在思考和工具调用之间反复切换,每一步行动的结果都被观察并反馈。
| |
ReAct 解决了纯语言模型的三个老问题:训练数据有截止时间(无法回答最新信息)、不擅长精确计算、不能访问私有数据。一个最小的 agent:
| |
HotpotQA (多跳问答)上:标准提示 28.7% → CoT 32.9% → ReAct 37.4%。 AlfWorld (交互环境)上: 12% → 34%。
实战要点:
- 工具描述要写好。模型靠 docstring 选工具。
- 观察结果要截断。搜索引擎可能返回几千字,会撑爆上下文。
- 强制步数上限。防止死循环。
- 错误信息要描述性。让模型有机会自我纠正,而不是一崩到底。
提示词默认是脆弱的#

同一个模型、同一组示例、同一个任务。仅仅改变示例的格式(Q:/A: 还是 Input/Output)或者它们的顺序,准确率可能波动 20+ 个百分点(Lu et al., 2022; Sclar et al., 2024)。这就是为什么经验性评估不能省。在宣布“找到最优提示词”之前,至少在多种顺序下都跑一遍。
自动化与工程化#
手工调提示词不可持续。下面这些方法把这件事工程化。
自动提示工程(APE)#
APE (Zhou et al., 2022)把“找最优提示词”当成一个搜索问题:
- 生成:让 LLM 根据任务描述和少量示例,自动生成几十个候选提示词。
- 评估:每个候选在验证集上跑一遍。
- 选择:留下表现最好的那一个。
| |
Zhou et al. 发现 APE 找到的提示词常常比人写的高 3–8 个百分点。原因不复杂: APE 探索人类不会想到的措辞,直接在你的数据上优化,而且能轻松测试上百个候选。
迭代版会把当前最优作为基准再生成变体,相当于在提示词空间里做爬山。
DSPy:把提示词当代码写#
DSPy (Khattab et al., 2023)把提示词工程当成编程问题。你不再手写提示词字符串,而是写一个生成提示词的程序,由编译器自动调优。
三个核心抽象:
- Signature:带类型标注的输入输出签名。
- Module:可组合的提示词模板。
- Optimizer:自动选择 demonstration 和 instruction 的优化器。
一个情感分类器:
| |
DSPy 能从训练集自动 bootstrap 出 demonstration:
| |
多阶段流水线也很自然:
| |
DSPy 编译器会同时优化三个子提示词。代价也是真实的:有学习曲线,对最终措辞的控制力会下降,前期需要一笔编译预算。当你已经有稳定的训练集和靠谱的评估指标时, DSPy 是最划算的选择。
LLMLingua:给提示词“瘦身”#
LLMLingua (Jiang et al., 2023)通过删除信息量低的 token 来压缩提示词。一个小模型给每个 token 打一个“重要性分”,分数低的删掉。
| |
底层用的是条件困惑度:删掉某个 token,看下一个 token 的预测困惑度增加多少;增加越多,说明这个 token 越重要,越该保留。
实测影响:
- 问答任务 2x 压缩:准确率掉 2–3%,成本省 50%,延迟降 1.4x。
- RAG 任务 4x 压缩:准确率掉 5–7%,成本省 75%。
适合:长上下文场景(RAG、文档分析),且能接受一点准确率损失。慎用:法律、医疗等每个字都可能有责任的场景。
一个自适应压缩器的草图,按重要性给不同段分配预算:
| |
实战模板#

这张图展示了同一套五块骨架在六类常见任务上的特化。复用骨架的好处不仅仅是看起来整齐——它让评估、缓存、版本控制都变得容易。
结构化输出#
让 LLM 稳定输出合法 JSON 是一件出名的麻烦事。三种策略,鲁棒性递增:
| |
策略 2 是给几个合法 JSON 示例做 few-shot——简单 schema 下足够。策略 3 是用 OpenAI/Anthropic 的原生 function/tool calling,由 API 保证 JSON 合法,是最稳的。
代码生成 + 自检#
| |
模式是 生成 → 测试 → 修复。修复 prompt 把失败的测试反馈给模型,原始指令保持不变。
多轮对话管理#
长对话会撑爆上下文。常见做法:保留最近若干轮的滑动窗口,老对话用 LLM 自己生成的摘要替换。
class ConversationManager:
def __init__(self, model, system_prompt: str, max_tokens: int = 4000):
self.model = model
self.system_prompt = system_prompt
self.max_tokens = max_tokens
self.history: list[dict] = []
def chat(self, user_message: str) -> str:
self.history.append({"role": "user", "content": user_message})
context = self._get_context()
response = self.model.generate(self._build_prompt(context))
self.history.append({"role": "assistant", "content": response})
return response
def _get_context(self) -> list[dict]:
ctx = [{"role": "system", "content": self.system_prompt}]
used = count_tokens(self.system_prompt)
for msg in reversed(self.history):
t = count_tokens(msg["content"])
if used + t > self.max_tokens:
break
ctx.insert(1, msg)
used += t
return ctx
def summarize_old_turns(self) -> None:
if len(self.history) < 10:
return
old = self.history[:6]
summary = self.model.generate(
f"简明地总结以下对话:
{format_messages(old)}
总结:"
)
self.history = (
[{"role": "system", "content": f"早期对话摘要:{summary}"}]
+ self.history[6:]
)
评估与调试#
提示词工程是经验科学,没有指标就只能凭感觉。
几种指标,按成本排序:
def exact_match(pred, truth):
return normalize(pred) == normalize(truth)
def f1(pred, truth):
p = set(normalize(pred).split())
t = set(normalize(truth).split())
if not p or not t:
return float(p == t)
common = p & t
if not common:
return 0.0
prec = len(common) / len(p)
rec = len(common) / len(t)
return 2 * prec * rec / (prec + rec)
def semantic_similarity(pred, truth, model="all-MiniLM-L6-v2"):
from sentence_transformers import SentenceTransformer, util
m = SentenceTransformer(model)
return util.cos_sim(m.encode(pred), m.encode(truth)).item()
def llm_as_judge(pred, truth, criteria):
return parse_score(llm_call(f"""请评估以下输出。
任务:{criteria['task']}
期望:{truth}
实际:{pred}
请给 0-10 分并简要说明:"""))
挑能反映你真正在意的东西的最便宜的指标。
系统化的 A/B 测试#
class PromptExperiment:
def __init__(self, test_set, metrics):
self.test_set = test_set
self.metrics = metrics
def evaluate(self, prompt_fn):
return {
m.__name__: sum(
m(llm_call(prompt_fn(x), temperature=0), y)
for x, y in self.test_set
) / len(self.test_set)
for m in self.metrics
}
def compare(self, variants: dict):
import pandas as pd
rows = [{"prompt_name": name, **self.evaluate(fn)}
for name, fn in variants.items()]
df = pd.DataFrame(rows)
return df.sort_values(by=df.columns[1], ascending=False)
测试集一定要是提示词作者没看过的数据。
提示词调试清单#
| 现象 | 可能原因 | 解法 |
|---|---|---|
| 输出含糊、跑题 | 指令模糊 | 加具体约束和示例 |
| 某条要求被忽略 | 指令互相矛盾 | 解决冲突,明确优先级 |
| 即使尝试也答错 | 缺少上下文 | 提供事实或检索段落 |
| 输出格式不对 | 没指定格式 | 给 schema 和示例 |
| 每次答案不一样 | 任务对单次推理太复杂 | 拆步骤 |
一个能扫出常见问题的小检查器:
class PromptDebugger:
AMBIGUOUS = {"相关", "合适", "好", "差", "一些", "若干", "大量",
"stuff", "things"}
CONFLICT = {"但", "然而", "尽管", "除了"}
def check_ambiguity(self, prompt: str) -> bool:
return any(w in prompt for w in self.AMBIGUOUS)
def check_conflicts(self, prompt: str) -> bool:
return sum(w in prompt for w in self.CONFLICT) >= 2
def infer_format(self, text: str) -> str:
t = text.strip()
if t.startswith("{") and t.endswith("}"): return "json_object"
if t.startswith("[") and t.endswith("]"): return "json_array"
if "
-" in t or "
*" in t or "
1." in t: return "list"
return "prose"
错误归因#
把失败案例分桶。下面这套类别能覆盖大多数错误模式:
def categorize_error(pred: str, truth: str) -> str:
p, t = normalize(pred), normalize(truth)
if not p:
return "empty_output"
if p in t or t in p:
return "partial_match"
p_w, t_w = set(p.split()), set(t.split())
overlap = len(p_w & t_w) / max(len(p_w), len(t_w))
if overlap > 0.5:
return "semantic_error"
if overlap > 0:
return "partial_hallucination"
return "complete_hallucination"
然后从最大的桶开始改。
常见坑与解法#
| 坑 | 解法 |
|---|---|
| 指令模糊 | 列出要优化的具体维度:清晰度、长度、语气、格式。 |
| 假设模型知道你脑子里的东西 | 把代码、文档、数据补到提示词里。 |
| 提示词太长 | 拆段、摘要,或用 RAG。“lost in the middle"是真实的。 |
| 不指定输出格式 | 显式给 schema、单位、语言,并配示例。 |
| 没有验证 | 包一层“验证 → 重试 → 失败”循环。 |
常见问题#
温度参数怎么选?#
0:分类、抽取、数学、代码——所有要求一致性的场景。 0.7–0.8:写作、头脑风暴、营销文案。 1.0+ 几乎用不上。结构化任务默认 0,创意任务默认 0.7。
少样本应该给几个例子?#
简单任务 2–3 个;多数任务 5–7 个最合适; 10+ 只有在示例足够多样时才有用。超过 50 就该考虑微调了。
什么时候该微调?#
已经有 1000+ 高质量标注数据、任务高度专业化、延迟/成本是关键约束、提示词工程已经触顶。其他情况都先用提示词,因为迭代速度快一千倍。
怎么减少幻觉?#
用检索的上下文 grounding,明确告诉模型“不知道就说不知道”,要求带引用,把温度调到 0,加一道验证。
长文档怎么处理?#
chunk + map-reduce、检索增强生成、分层摘要,或者直接用大上下文模型(Claude 3 200K, Gemini 1.5 1M)。 RAG 通常是默认正确答案。
提示词能在不同模型间复用吗?#
通用技巧(清晰指令、少样本、格式说明、 CoT)能复用;具体措辞、格式偏好(Claude 偏爱 XML)、 tool calling 语法不能。务必在目标模型上重新测。
能自动优化吗?#
能。 APE、 DSPy、遗传搜索都可以。先手工调到能用,再自动化。
XML、 JSON、纯文本怎么选?#
简单提示词用纯文本;结构化输入输出用 JSON;复杂多块提示词用 XML (特别是 Claude)。三个都行,取决于下游解析器。
CoT、 ToT、 GoT 什么时候用哪个?#
| 技术 | 结构 | 适合 | 成本 |
|---|---|---|---|
| CoT | 线性链 | 多步推理、数学、逻辑 | 1–2x |
| ToT | 树形搜索 | 多解空间、规划、谜题 | 5–50x |
| GoT | 任意 DAG | 并行处理、合并洞察 | 视情况而定 |
未来方向?#
多模态提示词、自动化更紧(DSPy、 APE)、激进压缩、 meta-prompting (用提示词生成提示词)、具身 agent。这门技能不会消失,只会从手工措辞往优化目标设计、评估框架搭建、系统编排迁移。
收束#
提示词工程从最初的试错,已经成长为一个有研究支撑、有可复用工具的工程学科。基础——清晰的指令、合适的示例、稳定的输出格式——放之四海而皆准。 Chain-of-Thought、 Tree of Thoughts 这类进阶技术,把“裸跑”做不到的能力解锁出来。 APE、 DSPy 这类框架把这些实践搬上生产。
但仅有技术不够。真正有效的提示词工程需要四样东西:
- 经验主义:什么都要测。在某个模型某个任务上有效的方法,换一个未必有效。
- 迭代:第一版几乎不可能是最好的版本。基于真实失败修。
- 评估:没有指标就只是在猜。
- 背景判断:理解模型擅长什么、任务要什么、在成本/延迟/质量之间怎么取舍。
简单开始。持续度量。反复迭代。最好的提示词,不是最巧妙的,而是能稳定解决你问题的那一个。
参考文献#
- Brown et al., 2020. Language Models are Few-Shot Learners. NeurIPS.
- Wei et al., 2022. Chain-of-Thought Prompting Elicits Reasoning in Large Language Models. NeurIPS.
- Kojima et al., 2022. Large Language Models are Zero-Shot Reasoners. NeurIPS.
- Wang et al., 2022. Self-Consistency Improves Chain of Thought Reasoning in Language Models. ICLR.
- Yao et al., 2023. Tree of Thoughts: Deliberate Problem Solving with Large Language Models. NeurIPS.
- Besta et al., 2023. Graph of Thoughts: Solving Elaborate Problems with Large Language Models. AAAI.
- Yao et al., 2022. ReAct: Synergizing Reasoning and Acting in Language Models. ICLR.
- Zhou et al., 2022. Large Language Models Are Human-Level Prompt Engineers. ICLR.
- Khattab et al., 2023. DSPy: Compiling Declarative Language Model Calls into Self-Improving Pipelines. arXiv.
- Jiang et al., 2023. LLMLingua: Compressing Prompts for Accelerated Inference of Large Language Models. EMNLP.
- Liu et al., 2021. What Makes Good In-Context Examples for GPT-3? arXiv.
- Lu et al., 2022. Fantastically Ordered Prompts and Where to Find Them. ACL.
- Sclar et al., 2024. Quantifying Language Models’ Sensitivity to Spurious Features in Prompt Design. ICLR.
- Min et al., 2022. Rethinking the Role of Demonstrations: What Makes In-Context Learning Work? EMNLP.