有一种 Prompt 技巧非常朴素:把同一个问题连续输入两遍。
原来这样问:
梵蒂冈的圣伯多禄大教堂门口有几根柱子?
改成这样问:
梵蒂冈的圣伯多禄大教堂门口有几根柱子?
梵蒂冈的圣伯多禄大教堂门口有几根柱子?
这不是让模型“更有礼貌”,也不是加复杂模板,而是单纯重复问题。Google 的研究《Prompt Repetition Improves Non-Reasoning LLMs》对这种做法做了系统测试:在 70 组“模型 × 基准任务”对比中,重复 Prompt 赢了 47 组,持平 23 组,没有出现失败组;在部分任务上,准确率从 21.33% 提升到 97.33%。
这个技巧主要作用在非推理模型上,也就是没有显式“深度思考”过程、通常直接输出答案的模型。它的价值在于:不改变模型、不微调、不接工具,只通过输入格式让模型更容易抓住问题里的关键信息。
Prompt 重复到底怎么写
Prompt 重复的基本形式很简单:
<问题>
<问题>
也可以写成公式:
Q -> Q + Q
这里的 Q 是完整任务,包括题目、选项、约束、输出格式等内容。
例如一个选择题原本是:
选项:
A. 把蓝色方块放到红色方块左边
B. 把红色方块放到蓝色方块左边
场景说明:
现在红色在左,蓝色在右。
问题:
哪一个选项会改变画面?
只输出 A 或 B。
重复后变成:
选项:
A. 把蓝色方块放到红色方块左边
B. 把红色方块放到蓝色方块左边
场景说明:
现在红色在左,蓝色在右。
问题:
哪一个选项会改变画面?
只输出 A 或 B。
选项:
A. 把蓝色方块放到红色方块左边
B. 把红色方块放到蓝色方块左边
场景说明:
现在红色在左,蓝色在右。
问题:
哪一个选项会改变画面?
只输出 A 或 B。
这个技巧不需要加“请一步一步思考”,也不需要额外示例。它改变的不是语言风格,而是模型处理输入时的上下文结构。
实验结果说明了什么
Google 的实验覆盖了多个常见非推理大模型,包括 Gemini 2.0 Flash、Gemini 2.0 Flash Lite、GPT-4o、GPT-4o-mini、Claude 3 Haiku、Claude 3.7 Sonnet、DeepSeek V3 等,并使用官方 API(Application Programming Interface,应用程序编程接口)进行测试。
测试任务既包含常见基准,也包含专门设计的位置和匹配类任务。
| 任务类型 | 例子 | 考察能力 |
|---|---|---|
| 常见知识与推理基准 | ARC、OpenBookQA、GSM8K、MMLU-Pro、MATH | 常识、数学、知识问答、复杂选择 |
| NameIndex | 给出 50 个名字,询问第 25 个是谁 | 序列位置定位 |
| MiddleMatch | 给出包含重复名字的列表,询问两个字符之间的名字 | 局部匹配、上下文定位 |
整体结果可以概括为:
| 对比项 | 结果 |
|---|---|
| 对比方式 | 原始 Prompt vs 重复一遍的 Prompt |
| 测试组合数量 | 70 组 |
| 重复 Prompt 胜出 | 47 组 |
| 持平 | 23 组 |
| 失败 | 0 组 |
| 最高提升案例 | 部分任务准确率从 21.33% 到 97.33% |
这里的关键不是“重复一定让所有任务都暴涨”,而是它在大量非推理模型和任务组合中表现得非常稳定:至少没有明显损害,很多场景还会显著变好。
为什么复制一遍会有用
大多数大语言模型(LLM,Large Language Model)采用因果语言模型结构。它们生成内容时,从左到右读取 token,并预测下一个 token。
在 Transformer 中,每个 token 的隐藏状态只能看到它左边的内容,不能看到右边还没出现的内容。这叫因果掩码。
flowchart LR
A[前面的 token] --> B[当前 token]
B -.不能看到.-> C[后面的 token]
这会带来一个容易被忽略的问题:Prompt 里越靠前的内容,在被模型编码时,能看到的信息越少。
还是看刚才的选择题:
选项:
A. 把蓝色方块放到红色方块左边
B. 把红色方块放到蓝色方块左边
场景说明:
现在红色在左,蓝色在右。
模型处理到选项 A、B 时,还没有读到后面的场景说明。也就是说,选项对应的内部表示一开始并不包含“红色在左,蓝色在右”这个条件。
单次 Prompt 的处理过程大致是:
flowchart LR
A[选项 A/B] --> B[场景说明]
B --> C[问题]
C --> D[生成答案]
因为选项出现在场景说明之前,选项 token 的内部表示不能提前吸收场景条件。虽然最终答案 token 可以看到整个 Prompt,但模型内部对早期内容的表示已经是在“信息不足”的状态下形成的。
重复 Prompt 后,结构变成:
flowchart LR
A1[第一遍:选项 A/B] --> B1[第一遍:场景说明]
B1 --> C1[第一遍:问题]
C1 --> A2[第二遍:选项 A/B]
A2 --> B2[第二遍:场景说明]
B2 --> C2[第二遍:问题]
C2 --> D[生成答案]
第二遍的选项 A、B 出现时,模型已经读过第一遍的完整题目。于是第二遍选项的隐藏状态,可以同时利用:
- 第一遍的选项;
- 第一遍的场景说明;
- 第一遍的问题;
- 第二遍当前正在处理的选项。
这相当于让模型在第二遍“带着题目背景重新读题”。重复不是魔法,它只是让一部分关键 token 在更完整的上下文中重新被编码了一次。
可以把它理解成一种轻量级的自我复述。
非推理模型为什么更容易受益
非推理模型通常追求低延迟和直接输出。它们不会显式展开长推理过程,也不一定会主动复述题目、整理条件、重新检查约束。
推理模型则不同。很多经过 RL(Reinforcement Learning,强化学习)或推理任务优化的模型,会自然产生类似这样的内部流程:
题目要求的是……
已知条件包括……
需要判断的是……
这种行为本身就像在帮自己重写题目、重排信息。Prompt 重复能提供的额外收益就会变小。
| 模型类型 | 常见表现 | Prompt 重复的作用 |
|---|---|---|
| 非推理模型 | 直接回答,速度快,容易漏条件 | 可能明显提高准确率 |
| 推理模型 | 会复述问题、拆条件、逐步推导 | 收益通常较小 |
| 长思考模型 | 输出前会花更多时间进行内部推理 | 重复可能变成额外 token 成本 |
所以这个技巧特别适合放在“需要速度,但又希望提高一点准确率”的场景里,比如分类、选择、短问答、字段抽取、位置定位等任务。
怎么在实际 Prompt 中使用
1. 完整重复
适合短 Prompt,尤其是选择题、判断题、短问答。
def repeat_prompt(prompt: str, times: int = 2) -> str:
prompt = prompt.strip()
return "\n\n".join([prompt] * times)
question = """
选项:
A. 把蓝色方块放到红色方块左边
B. 把红色方块放到蓝色方块左边
场景说明:
现在红色在左,蓝色在右。
问题:
哪一个选项会改变画面?
只输出 A 或 B。
"""
final_prompt = repeat_prompt(question, times=2)
print(final_prompt)
如果使用 Chat API,通常只需要重复用户消息里的任务内容,不要重复 system 指令。
messages = [
{
"role": "system",
"content": "你是一个严格按要求输出答案的助手。"
},
{
"role": "user",
"content": repeat_prompt(question, times=2)
}
]
2. 只重复核心问题
如果输入里有很长的背景材料,完整复制会浪费上下文窗口,也会增加成本。此时可以只重复问题、选项、输出约束。
背景材料:
<这里是一大段资料,不重复>
任务:
根据背景材料,判断下面哪个选项正确。
A. ...
B. ...
C. ...
D. ...
只输出选项字母。
任务:
根据背景材料,判断下面哪个选项正确。
A. ...
B. ...
C. ...
D. ...
只输出选项字母。
这种写法保留了“重新读题”的优势,同时避免把长上下文复制两遍。
3. 重复关键约束
有些任务容易错在输出格式,例如本来要求 JSON,模型却输出解释文字。可以重复格式要求,而不是重复全部内容。
从下面文本中抽取姓名、公司、职位,输出 JSON。
文本:
张三目前在星河智能担任算法工程师。
输出格式:
{"name": "...", "company": "...", "title": "..."}
只输出 JSON,不要解释。
输出格式:
{"name": "...", "company": "...", "title": "..."}
只输出 JSON,不要解释。
哪些场景适合用
| 场景 | 是否适合 | 原因 |
|---|---|---|
| 短选择题 | 适合 | 重复成本低,容易让模型重新绑定选项和条件 |
| 数学题的题干较短 | 可以尝试 | 对非推理模型可能有帮助,但复杂数学仍建议用推理模型 |
| 信息抽取 | 可以尝试 | 重复字段要求和格式约束,能减少漏字段 |
| 分类任务 | 适合 | 类别定义和输入文本重复后,模型更容易对齐标签 |
| 长文档问答 | 谨慎 | 复制长文档会占用上下文窗口,成本高 |
| 摘要任务 | 谨慎 | 重复正文可能让模型误判重复内容是语义重点 |
| 已经启用深度思考的模型 | 收益不稳定 | 模型自身可能已经会复述和检查题目 |
| 上下文接近窗口上限 | 不适合 | 重复可能挤掉关键信息 |
一个简单判断标准是:如果任务短、答案明确、模型经常漏看条件,可以试试重复;如果任务长、上下文宝贵、重复会让输入翻倍,就不要直接复制全文。
成本和坑
token 成本会上升
复制一遍 Prompt 会让输入 token 接近翻倍。对 API 调用来说,这意味着:
- 输入费用增加;
- 首 token 延迟可能上升;
- 更容易触发上下文长度限制。
短任务里这个成本通常可以接受,长任务里需要谨慎。
重复不等于推理
Prompt 重复能改善模型对题目的编码方式,但它不会让模型真正拥有更强的多步推导能力。遇到复杂证明、长链数学推理、代码调试时,推理模型、工具调用、程序校验仍然更可靠。
不要重复系统指令
system 指令负责定义角色、安全边界和全局规则,一般不要拿来复制。更好的做法是保留 system 指令,只重复 user 里的任务。
推荐:
system: 你是一个严格遵守输出格式的助手。
user: <任务><任务>
不推荐:
system: <系统规则><系统规则>
user: <任务>
重复次数不是越多越好
重复两遍已经能提供“重新读题”的效果。重复三遍、四遍可能继续改变结果,但也会带来更多 token 成本,并且可能让模型把重复本身当成特殊信号。
实际使用时可以做一个小规模 A/B 测试:
| 版本 | Prompt 形式 | 观察指标 |
|---|---|---|
| A | 原始 Prompt | 准确率、格式错误率、平均延迟 |
| B | 重复两遍 | 准确率、格式错误率、平均延迟 |
| C | 只重复问题和格式 | 准确率、格式错误率、平均延迟 |
如果 B 或 C 的准确率提升明显,而延迟和成本仍能接受,就可以保留。
一个实用模板
短任务可以直接使用这个模板:
{任务说明}
{输入内容}
{输出要求}
{任务说明}
{输入内容}
{输出要求}
长任务可以使用压缩版本:
背景资料:
{长上下文,只放一次}
任务:
{问题}
{选项或字段}
{输出要求}
请再次确认任务:
{问题}
{选项或字段}
{输出要求}
更适合工程落地的版本是:
def build_prompt(context: str, task: str, repeat_task: bool = True) -> str:
context = context.strip()
task = task.strip()
if repeat_task:
return f"""背景资料:
{context}
任务:
{task}
请再次按同一任务要求作答:
{task}
"""
return f"""背景资料:
{context}
任务:
{task}
"""
这种写法不会复制长上下文,只复制真正需要模型反复对齐的任务目标和输出约束。
关键点
Prompt 重复的核心价值不在“复制粘贴”本身,而在于它改变了模型处理输入时的信息位置:第二遍题目里的 token 可以看到第一遍完整题目,从而形成更充分的内部表示。
在非推理模型上,它是一个低门槛、容易测试的技巧。适合短问题、选择题、分类、抽取、格式约束强的任务;不适合无脑复制长文档,也不能替代真正的推理模型。
如果一个非推理模型总是漏看条件、选错选项、输出格式不稳定,可以先试一个最小改动:把任务重复一遍。