题接上回,
前两天同事提了个需求,被我怼了。他说现在有一些客服数据,和对应的分类,直接用deepseek分类感觉分的不是很准确,能不能做个东西让分类更精确。
我说:咱们自己调一个模型。
他:能不能用提示词工程???
我: 你还是不懂原理啊。
因此我觉得我又该出山写点东西给他们科普一下子了。
提示词工程
提示词工程(Prompt Engineering) ,也叫做上下文提示(In-Context Prompting) ,是指通过设计输入的提示词来引导语言模型(LLM)做出我们想要的反应,而不需要改变模型本身的参数。简单来说,就是怎么通过"问对问题"来让模型更好地完成任务。
提示词工程更多的是一种经验科学,不同的模型对于同一个提示的回复会有很大差异,因此需要大量的实验和调整。
提示词工程为什么在某些情况下不能使用:
-
模型参数不变: 知识只是作为输入给模型,模型并不能像传统训练或者微调那样,通过数据的不断训练优化自身的内部参数。也就是说,模型的内部表示和参数没有得到更新,因此无法改变模型本身的分类精度。
如果模型本身的知识不包含这些内容,提示词写的再好也没用! 模型本身学过这些知识,使用提示词工程才好使!
-
提示词的局限性:提示词工程还依赖于你提供的示例数据是否能够覆盖任务的多样性。以及模型本身的处理能力。
如果数据多样性较高,而模型在面对复杂或不常见的情况时没有足够的训练,提示词设计虽然有帮助,但它的效果会受到很大限制。
如果提示词过长,超出模型本身的处理能力,也不会得到想要的结果。
接下来看一下常见的提示词工程:
基础提示词(Basic Prompting)
在提示词工程中,最基本的两种方法就是零样本学习(Zero-shot) 和 少样本学习(Few-shot) ,早期提出提示词工程不需要微调的模型,比如gpt系列。
注意 这里的"学习"是带引号的,因为提示词工程不会改变模型本身的参数,并不会导致真正意义上的模型学习,知识将其翻译为零样本学习和小样本学习。
零样本学习(Zero-shot)
零样本学习就是直接给模型一个任务描述,要求它直接给出结果。也就是说,我们不提供任何示例或上下文,仅仅提供任务要求。
举个例子:

模型会根据任务给出情感分析结果(比如:正面、负面或者其他答案)。
👉 零样本的特点:简单直接,但由于没有示例,模型的理解可能不够准确,结果也可能较为一般。
小样本学习(Few-shot)
小样本学习是提供一组高质量的示例,每个示例包括输入和期望的输出。通过这些示例,模型可以更好地理解我们希望它如何回答,从而得到更符合我们要求的结果。
比如下面我和chatGPT交谈,先给他一些示例,然后再提问。

👉 少样本的特点:通常能得到比零样本更好的表现,但需要更多的计算资源(更多的token)并且如果示例过多,可能会触及上下文长度的限制。
提示词的设计对模型表现的影响
目前本多人都在学习如何写prompt(提示词),如何用合适的提示词让模型获得更好的输出结果。但是[2102.09690] Calibrate Before Use: Improving Few-Shot Performance of Language Models研究小样本时候,发现选取不同的样本会,模型输出会存在偏差:
- 多数标签偏差:如果提示的示例样本中标签分布不均衡,模型容易倾向于预测多数类标签。
- 近期偏差:模型有时会倾向于重复示例中的最后一个标签。
- 常见词偏差:倾向于输出常见的词汇,而不是较为罕见的词汇。
在提示词工程中,有效的示例选择 和示例排序 对于提升语言模型的性能至关重要,尤其是在上下文学习任务中。通过确保示例的语义相关性 和多样性,并精心考虑示例的呈现顺序,可以帮助模型生成更加准确和平衡的结果。为了克服这些偏差,研究者们也讨论了很多方法。
示例选择
在进行上下文学习时,选择合适的示例至关重要,目的是确保所选示例既相关 又多样化,以提升模型的性能。以下是几种选择高质量示例的方法和策略:
-
语义相似度(KNN聚类) :
一种方法是通过使用嵌入空间中的最近邻聚类 来选择与测试示例语义相似的示例。这种方法有助于选择与当前任务语境更相近的示例,从而帮助模型更好地泛化。[2101.06804] What Makes Good In-Context Examples for GPT- <math xmlns="http://www.w3.org/1998/Math/MathML"> 3 3 </math>3?
-
基于图的方法来增加多样性 :
[2209.01975] Selective Annotation Makes Language Models Better Few-Shot Learners提出了一种基于图的选择方法,用于选择多样且具有代表性的示例:-
首先,使用嵌入模型 (比如 SBERT)来计算不同样本之间的余弦相似度 ,然后根据相似度,构建一个有向图 <math xmlns="http://www.w3.org/1998/Math/MathML"> G = ( V , E ) G=(V, E) </math>G=(V,E):
- 图中的每个节点代表一个样本。
- 每个节点指向它最相近的 <math xmlns="http://www.w3.org/1998/Math/MathML"> k k </math>k 个邻居(也就是最相似的其他样本)。
-
接下来,把所有样本分成两部分:
- 已经选中的集合 <math xmlns="http://www.w3.org/1998/Math/MathML"> L \mathcal{L} </math>L(一开始是空的,即 <math xmlns="http://www.w3.org/1998/Math/MathML"> L = ∅ \mathcal{L} = \emptyset </math>L=∅)
- 还没选的集合 <math xmlns="http://www.w3.org/1998/Math/MathML"> U \mathcal{U} </math>U
-
对于每一个还没被选中的样本 <math xmlns="http://www.w3.org/1998/Math/MathML"> u u </math>u,计算一个得分(score) :
-
找到所有从 <math xmlns="http://www.w3.org/1998/Math/MathML"> u u </math>u 出发、指向 <math xmlns="http://www.w3.org/1998/Math/MathML"> U \mathcal{U} </math>U 中其他节点的连边。
-
对于每一个连边的终点 <math xmlns="http://www.w3.org/1998/Math/MathML"> v v </math>v,计算一个值 <math xmlns="http://www.w3.org/1998/Math/MathML"> s ( v ) s(v) </math>s(v)。
- 如果 <math xmlns="http://www.w3.org/1998/Math/MathML"> v v </math>v 的邻居中已经有很多被选中了,那么 <math xmlns="http://www.w3.org/1998/Math/MathML"> s ( v ) s(v) </math>s(v) 会很小;
- 公式是 <math xmlns="http://www.w3.org/1998/Math/MathML"> s ( v ) = ρ − ∣ ℓ ∈ L ∣ ( v , ℓ ) ∈ E ∣ s(v) = \rho^{-|{\ell \in \mathcal{L} \mid (v, \ell) \in E}|} </math>s(v)=ρ−∣ℓ∈L∣(v,ℓ)∈E∣,这里 <math xmlns="http://www.w3.org/1998/Math/MathML"> ρ \rho </math>ρ 是一个大于1的常数(比如可以取2)。
-
然后,把所有这些 <math xmlns="http://www.w3.org/1998/Math/MathML"> s ( v ) s(v) </math>s(v) 加起来,得到 <math xmlns="http://www.w3.org/1998/Math/MathML"> u u </math>u 的总得分。
-
-
选择多样化的样本
- 得分越低,说明 <math xmlns="http://www.w3.org/1998/Math/MathML"> u u </math>u 周围的样本大多已经被选过了,所以不太需要再选它。
- 得分高的样本,说明周围还有很多没被覆盖的区域,优先选它。
- 这样就能逐步挑出一组既有代表性又足够多样化的示例。
-
-
......
示例排序
选好示例后,如何排列它们的顺序同样重要,合理的示例排序能帮助模型更好地做出预测,减少偏差。以下是一些关于如何组织示例的建议:
- 多样性和相关性 :
确保所选的示例既是多样化 的(涵盖多种可能的情况),又是相关的(与当前任务紧密相关)。这可以帮助模型学习到广泛的有效模式,而不会过拟合到某一类输入。 - 随机化顺序 :
为了避免多数标签偏差 (模型可能会过拟合到最常见的标签)和最近标签偏差 (模型可能会过度强调最近的示例),通常建议对示例顺序进行随机化。这有助于模型在不同情况下更好地泛化。 - ......
指令提示(Instruction Prompting)
小样本学习,本质上是用展示的方式来描述任务,告诉模型需要他处理什么形式的数据,如何回答。但 小样本学习方法有一个比较致命的问题:太耗tokens,而且因为上下文长度有限,能放的内容也受限制。
所以,有人就想了:干脆直接告诉模型要做什么不就好了?
这就诞生了"指令提示"这种思路。
Instructed Language Models (比如 InstructGPT、Natural Instructions 项目)是把已经预训练好的大模型,使用用高质量的指令-输入-输出三元组 进一步微调,让模型能更好理解人类的意图 ,更听话。见的微调方法比如instructGPT就开始的 人类反馈强化学习(RLHF) 。
这种"教模型听指令"的方法,好处是:
- 让模型跟人类意图更加一致;
- 让人和模型交流成本大幅下降,不用再绕弯子说话。
使用指令模型时的小技巧:
要尽量详细、具体地描述任务,明确告诉模型"要做什么"。
举个例子,情感分类任务可以这么写:

对比一下,输出的是我们想要的答案,积极或者消极,如果不加限制可能输出的就是正面或者反面。并且没了那一段不需要的解释,大量节省token。

扩展
也有工作将二者结合,Investigating the Effectiveness of Task-Agnostic Prefix Prompt for Instruction Following中提出的 In-Context Instruction Learning 是把 few-shot 和 instruction prompting 结合起来的做法:
在提示词里,放入多个不同任务的示范 ,每个示范都包括任务指令、输入、输出。
(不过他们的实验只在分类任务上做了测试,而且每条指令里都包含了所有可选标签。)
举个例子:
定义 :判断对话中是谁在说话,"agent"(客服)还是 "customer"(客户)。
输入:I have successfully booked your tickets.
输出:agent
定义 :判断问题属于哪个类别,"数量"还是"地点"。输入:What's the oldest building in US?
输出:Location
定义 :判断电影评论是正面还是负面。输入:i'll bet the video game is a lot more fun than the film.
输出:
链式思考提示(Chain-of-Thought)
链式思考提示(CoT) 是一种通过生成一系列简短的句子来描述推理过程的方式,帮助模型一步步地推理,最终得出正确答案。这种方法的优势在于它能够帮助模型在面对复杂推理任务时做出更加准确的回答,尤其是在使用大模型(例如参数超过50B的模型)时。
CoT是一种非常适合复杂问题的推理方法,通过一步步的推理链帮助模型更好地理解并回答问题。对于简单问题,使用这种方法的效果不明显,但对于需要复杂推理的任务,CoT能显著提高模型的准确性。
Zero-shot CoT(零样本)
这种方法不提供任何示范,而是直接通过自然语言提示模型生成推理链,并要求模型基于这些推理生成最终答案。
直接在提示中要求分步回答:
"小明有5个苹果,吃了2个,又买了4个。他现在有多少苹果?请逐步计算并解释每一步。 "
模型输出:
- 初始苹果:5个
- 吃掉后剩余:5 - 2 = 3个
- 购买后总数:3 + 4 = 7个
→ 最终答案:7个苹果
Few-shot CoT(少量示例)
通过提供几个示范案例来引导模型的推理,每个案例都包含手工编写(或者由模型生成的)高质量推理链。
问题: 一个花园有12朵玫瑰,园丁摘了3朵,又种了5朵。现在有多少朵?
解答:
初始玫瑰:12朵
摘除后:12 - 3 = 9朵
新种后:9 + 5 = 14朵
答案:14朵
问题:书架上有20本书,借出6本,归还4本。还剩多少本?
解答:
方法优化
在前面,我们了解了链式思考提示,它通过让模型分步骤推理,显著提升了面对复杂问题时的推理能力。但在实际应用中,仅仅使用基本的CoT提示还远远不够。为了让模型推理得更准确、更稳定,研究者们又提出了一些针对性的扩展方法,我们简单举几个例子:
推理链复杂度
[2210.00720] Complexity-Based Prompting for Multi-Step Reasoning认为提供高复杂度推理示例的提示词效果更好。具体表现为,增加推理步骤数量,且用换行符分隔步骤比逗号或句号更有效。
比如:
- 步骤越多(如5步>3步),模型表现越好。
- 格式建议:用换行符(
\n
)而非标点分隔步骤。
自洽推理器
如果手上只有正确答案,但没有详细推理过程怎么办?[2203.14465] STaR: Bootstrapping Reasoning With Reasoning提出STaR,可以:
第一步:让模型自己想推理过程,只保留那些推理后能得出正确答案的。
第二步:用这些正确推理的过程来继续训练模型,反复循环,直到模型推理越来越稳定。
比如:小明知道答案是"米饭",但不知道怎么做,于是让他自己摸索做饭的步骤,只保留那些最后真能做出米饭的方法。
自提问与检索增强链式思考
[2210.03350] Measuring and Narrowing the Compositionality Gap in Language Models提出了Self-Ask,让模型在思考过程中,不断自己向自己提后续小问题,再一步步解答。遇到不会的问题,可以去查百度、搜狗等搜索引擎补充资料。
比如:

这张图展示了Self-Ask(自己提问题)的方法是怎么工作的。
简单来说,Self-Ask 就是让模型在遇到复杂问题时,不是一下子硬想答案,而是自己动手拆分问题、提出后续小问题,一步步搞清楚细节,最后再得出总答案。
先通过fewshot给出第一个样例:
- 问题是:"Theodor Haecker 和 Harry Vaughan Watkins 谁活得更久?"
- 模型自己判断:要回答这个问题,还得先分别知道两个人各活了多少岁。
- 于是它自动提出两个小问题:
- Theodor Haecker 死时多少岁?
- Harry Vaughan Watkins 死时多少岁?
- 然后分别找到答案,最后总结得出谁活得更久。
然后抛出第二个问题:
- 问题是:"目前最高的木质格子塔是什么时候建成的?"
- 模型判断,这问题有点绕,得先知道:"目前最高的木质格子塔是哪一座?"
- 于是提一个后续小问题,用搜索引擎查到了答案是"格利维采广播塔"(Radio Tower Gliwice)。
- 接着又问:"那格利维采广播塔是什么时候建成的?"
- 再通过搜索找到答案"1935年",最终给出正确答复。
Tree of Thoughts(思维树)
[2305.10601] Tree of Thoughts: Deliberate Problem Solving with Large Language Models提出的Tree of Thoughts进一步扩展了CoT的想法:在每一个推理步骤上,不止想一种可能性,而是想好几种,像分叉一样,形成一棵树。然后可以用广度优先搜索(BFS) 或 深度优先搜索(DFS) 的方法,在这些想法中探索最优解。每走一步都需要用投票或者分类器来判断是不是走对了。
比如:像下围棋一样,不只想一手棋,而是考虑多种走法,每条路都探索一段,看哪条最可能赢。

提示词工程是我们和大模型"对话"的核心技巧,虽然它不能直接让模型变得更聪明,但通过精心设计输入 ,就能让模型的输出效果大幅提升。不管是简单的零样本、少样本提示,还是更复杂的指令设计,甚至是各种进阶玩法,本质上都是为了一个目标:让模型更清楚、更高效地完成我们想要的任务。
以上介绍的只是提示词工程中一部分常见且基础的技术。随着大模型的发展,各种新套路也在不断出现。这篇文章也是希望大家能了解提示词工程的原理,知道提示词工程的适用范围,更好的运用大模型赋能我们的日常生活。