对应 Maps to:CMU 17-630 Prompt Engineering(Prof. Travis Breaux),第 6 周的第一个主题 · Task Decomposition。原课 Schedule 对这一讲的说明(意译):把复杂任务分解为简单任务以提高整体表现,以 least-to-most 和 successive prompting 为例。 依据说明:原课的 slides 和阅读清单不公开。Schedule 点名的两种方法——Least-to-Most(§3)和 Successive Prompting(§4)——各占一节;§2(组合泛化与 SCAN)是理解”为什么分解有效”所需的背景,§5(Decomposed Prompting)、§6(提示链与错误传播)和 §7(分解的成本)是讲义补充的,对应原课作业 2 的”多阶段任务分解”。材料来自 Lake & Baroni(2018,SCAN)、Anil et al.(2022)、Zhou et al.(2023,Least-to-Most)、Dua et al.(2022,Successive Prompting)、Press et al.(2023,Self-Ask)、Khot et al.(2023,Decomposed Prompting)、Wu et al.(2022,AI Chains)等;引用时按原文的说法转述结论,具体数字以原文为准。全部实验是本讲义自己设计的,不代表课堂上讲了什么。 配套代码 Code:
code/lecture09/(PyTorch CPU;1 个模块 + 5 个脚本;不需要 API key;第一次运行会从零训练两个迷你 SCAN 模型各约 15 秒、三个”不太可靠”的模型共约 5 秒,之后读缓存;每个脚本都在 20 秒以内) 预计阅读时间:3.5 小时(含运行代码)
0. 本讲导航 · How to read this lecture
0.1 学习目标 Learning goals
学完这一讲,你应该能够:
- 解释分解为什么有效:每个子问题更像模型训练时见过的样子,错误也更容易定位。
- 说清楚组合泛化和长度泛化是什么,以及 SCAN 基准上神经网络的两种著名失败。
- 写出 Least-to-Most 的两个阶段(分解、依次求解),并知道它的效果依赖于分解本身的质量。
- 区分 Least-to-Most、Successive Prompting、Self-Ask、Decomposed Prompting,说出各自适合什么样的任务。
- 用 估计一条提示链的可靠性,并在链的中间设计便宜的检查;区分随机错误和系统性错误。
- 判断一个任务该不该分解、拆到多细——分解有收益也有代价。
0.2 先修与本讲范围 Prerequisites & scope
| 默认你已经会 Assumed | 本讲只给直觉,后面再展开 Previewed only |
|---|---|
| 第 1 讲:迷你 GPT 的结构(可学习的绝对位置嵌入) | 自动生成提示与自我验证(第 10 讲) |
| 第 4 讲:思维链、组合性差距、Self-Ask、程序分解、self-consistency | 规划与搜索:Tree of Thoughts(第 13 讲) |
| 第 8 讲:单阶段 vs 多阶段、先规划 vs 边做边看、工具 | 多智能体分工(第 15 讲) |
| 前置讲 P3:区间、样本量 |
0.3 本讲地图 Map
§1 为什么分解有效
§2 组合泛化与 SCAN:长度划分、新原语划分;迷你 SCAN 上的失败 ← 代码 01
§3 Least-to-Most:先分解、再依次求解;分解本身也会错 ← 代码 01、02
§4 Successive Prompting 与 Self-Ask:分解和回答交错进行 ← 代码 02
§5 Decomposed Prompting:拆到多细、交给谁 ← 代码 03
§6 提示链与错误传播:p^k、检查与重试、随机错误 vs 系统性错误 ← 代码 04
§7 分解的成本:什么时候不该分解
§8 在真实模型上 ← 代码 05(可选)
§9 小结 §10 常见误区 §11 练习 §12 延伸阅读 §13 下一讲预告
0.4 记号与术语 Notation & terms
| 术语 | 本讲义里的意思 |
|---|---|
| 分解(decomposition) | 把一个问题拆成若干个更简单的子问题 |
| 组合泛化(compositional generalization) | 学会了部件和组合规则之后,能处理部件的新组合 |
| 长度泛化(length generalization) | 在比训练时更长的输入 / 输出上仍然正确 |
| 系统性(systematicity) | 会理解 “jump” 和 “walk twice”,就会理解 “jump twice”(Fodor & Pylyshyn 1988) |
| 提示链(prompt chaining) | 把多次模型调用串起来,上一步的输出是下一步的输入 |
| 处理器(handler) | 负责一个子任务的组件:一个提示、一个模型、一段程序或一个工具 |
| 错误传播(error propagation) | 链上某一步的错误导致后面所有步骤、乃至最终结果出错 |
0.5 本讲的实验装置 · The setup
一个迷你 SCAN(scan_lab.py)。SCAN(Lake & Baroni 2018)把简单的导航命令翻译成动作序列:
jump left twice and walk → LTURN JUMP LTURN JUMP WALK
run thrice after look right → RTURN LOOK RUN RUN RUN ("A after B" = 先 B 后 A)
4 个动词 × 3 种方向(无 / left / right)× 3 种重复(无 / twice / thrice)= 36 种子句,用 and / after 连接。我们从零训练一个和第 1 讲同样结构的迷你 GPT(词表只有 22 个词,约 15 秒),两种训练集:
- 长度划分:全部 1–2 个子句的命令(2628 条),测试时用 3–5 个子句;
- 新原语划分:所有不含 jump 的 1–2 子句命令 + 光秃秃的 “jump” → JUMP,测试含 jump 的组合(“jump twice”、“jump left and walk”)。
分解由一个规则分解器完成(代表”分解做对了”),02 号脚本再看分解出错时会怎样。
1. 为什么分解有效 · Why decomposition works
第 4 讲的组合性差距已经给出了一个例子:模型能分别答对”X 的首都是什么”和”Y 的领导人是谁”,却答不对”X 的首都所在国家的领导人是谁”;把问题拆成两步,就答对了。第 8 讲的单阶段 vs 多阶段是另一个例子:步骤越多,一次性回答越差;每一步交给工具,就都对了。
分解有效,有三个互相关联的原因:
- 每一步更像训练分布。模型在”简单问题”上见过大量数据,在”把五个简单问题揉成一个”的复杂问题上见得少。分解把一个分布外的问题变成几个分布内的问题。
- 每一步更短。长输入、长输出、需要同时记住很多中间结果,都是模型的弱点。分解让每次调用只处理一小块。
- 错误可以被定位。一次性回答错了,你只知道”错了”;分解之后,你知道是哪一步错了,可以只改那一步的提示,或者在那一步后面加检查(§6)。
但分解也有代价:更多的调用、更长的延迟、新的出错环节(分解本身可能错,§3.3)。这一讲的实验会让你看到收益和代价都是真实的。
2. 组合泛化与 SCAN · Compositional generalization
2.1 一个”人一看就会”的任务
SCAN 的规则写出来只有几行,人看几个例子就会。可是 Lake & Baroni(2018)发现,当时的序列到序列模型在两种划分上几乎全军覆没:
- 长度划分:训练时只见过动作序列较短的命令,测试时的命令更长;
- 新原语划分:训练时 “jump” 只单独出现过,测试时要和 twice、left、and 组合。
他们的论文标题就是结论:“没有系统性的泛化”——网络学到的是训练数据里的模式,而不是可以自由组合的规则。之后的 COGS(Kim & Linzen 2020)等基准在更像自然语言的任务上得到了类似的结论。
2.2 在迷你 SCAN 上
01_length_generalization.py 演示 1:迷你模型只在 1–2 个子句的命令上训练过。
| 子句数 | 标准答案平均长度 | 一次性翻译的平均长度 | 一次性翻译 | Least-to-Most |
|---|---|---|---|---|
| 1 | 3.2 | 3.2 | 100.0% | 100.0% |
| 2 | 6.6 | 6.6 | 100.0% | 100.0% |
| 3 | 10.4 | 9.8 | 2.0% | 100.0% |
| 4 | 13.4 | 12.4 | 0.0% | 100.0% |
| 5 | 16.7 | 14.2 | 0.0% | 100.0% |
规则一点都没变,变的只是长度,一次性翻译就从 100% 掉到 2%、0%。
它错在哪里?演示 2 把 3 个子句的 200 条错误分类:105 条的输出比标准答案短,但其中只有 6 条是”把前两个子句翻译对了,然后停下”;更常见的是把后面的子句揉在一起。例如 “jump twice and walk left thrice” 被翻成 JUMP JUMP JUMP——第二个子句的动词配上了第三个子句的 thrice。另有 63 条长度对、内容错,28 条反而更长。模型像是把一条长命令硬塞进它见过的”两个子句”的模板里:它学到的不只是”怎样翻译”,还有”命令长什么样”。
一个技术上的原因是位置编码。这个迷你模型用的是可学习的绝对位置嵌入(第 1 讲):训练时没出现过的位置,嵌入几乎没有被训练过。Anil et al.(2022)系统地研究过大语言模型的长度泛化,发现无论微调还是少样本提示,模型在比训练(或示范)更长的问题上普遍表现变差;位置编码的设计(相对位置、RoPE、ALiBi……)能缓解,但不能消除这个问题。分解绕开了它:分解之后,模型从来不需要生成比训练时更长的东西。
3. Least-to-Most:先分解,再依次求解 · Least-to-Most prompting
3.1 两个阶段
Zhou et al.(2023)的 Least-to-Most 提示分两个阶段,每个阶段都用少样本提示:
- 分解:示范”怎样把一个问题拆成一串从易到难的子问题”,让模型对新问题也这样拆。
- 依次求解:从最简单的子问题开始,一个一个地解;每个子问题的提示里包含前面子问题的问答,所以后面的子问题可以用前面的答案。
名字里的”由少到多”(least to most)指的就是这个顺序:先解最简单的,再用它的答案去解更难的。论文在三类任务上评估:符号操作(把一串单词的最后一个字母连起来,测试的单词数比示范多得多)、组合泛化(SCAN 的长度划分)、数学推理(GSM8K 等)。在 SCAN 长度划分上,他们用 code-davinci-002 + Least-to-Most 提示得到了接近完美的准确率,而标准的思维链提示远远达不到;在”最后一个字母”任务上,示范只有很短的列表,Least-to-Most 在长得多的列表上仍然准确,思维链则随长度明显下降。
和思维链(第 4 讲)比:思维链在一次生成里写出中间步骤,中间步骤的长度和格式仍然要模型自己泛化;Least-to-Most 把问题真正拆成多次调用,每次调用都是一个短的、示范里见过的形式。
3.2 在迷你 SCAN 上
上面那张表的最后一列就是 Least-to-Most:规则分解器把命令拆成子句(“A after B” 拆成 [B, A]),模型逐个翻译子句,程序把结果拼起来。所有长度上都是 100%——每个子问题都是一个子句,正是训练时见过的样子。
(SCAN 的子问题互相独立,后面的子句不需要前面的答案,所以”依次求解”退化成了”分别求解再拼接”。在数学应用题这类任务上,后面的子问题要用到前面的答案,Least-to-Most 的”依次”才真正起作用。)
3.3 分解本身也会错
01 号脚本里的分解器是一个正确的规则。真实的 Least-to-Most 用 LLM 做分解,分解本身可能出错。02_decomposition_quality.py 演示 1 换上一个”粗心”的分解器:从左到右切开,不处理 “A after B” 的顺序反转。
| 连接词 | Least-to-Most + 正确的分解器 | + 粗心的分解器 | 一次性翻译 |
|---|---|---|---|
| and | 100.0% | 100.0% | 100.0% |
| after | 100.0% | 9.0% | 100.0% |
子问题个个都解对了,拼起来却是错的(剩下 9% 对的,是两个子句的”动作单元”恰好相同,例如 walk 和 walk twice,先后顺序无所谓)。**分解错了,后面做得再好也没用。**注意最后一列:两个子句是训练时见过的长度,一次性翻译本来就是 100%——在模型已经会的地方,分解没有带来好处,只带来了新的出错机会。
演示 2 用一个”有噪声”的分解器模拟会出错的 LLM:在每个连接处以概率 ε 出错(漏掉一个子句,或者弄反相邻两个的顺序)。
| ε | 2 个子句:实测(预测) | 3 个子句 | 4 个子句 | 5 个子句 |
|---|---|---|---|---|
| 0.05 | 95%(95%) | 92%(90%) | 82%(86%) | 80%(81%) |
| 0.10 | 92%(90%) | 80%(81%) | 72%(73%) | 70%(66%) |
| 0.20 | 77%(80%) | 66%(64%) | 50%(51%) | 42%(41%) |
括号里是 的预测:k 个子句有 k − 1 个连接处,每一处都是一次出错的机会。实测和预测基本一致(上下差几个百分点是 200 条的抽样噪声)。命令越长、连接越多,分解越容易出错——分解器成了新的瓶颈。所以分解提示的少样本示范、分解结果的检查(例如”子问题的数量对不对”、“有没有漏掉原问题里的某个部分”),和求解提示同样重要。
4. Successive Prompting 与 Self-Ask:边分解边回答 · Interleaved decomposition
Least-to-Most 先一次性写出全部子问题,再依次求解。Successive Prompting(Dua et al. 2022)把两件事交错进行:
Q1:要回答原问题,第一个需要知道的是什么? A1:……
Q2:根据 A1,下一个需要知道的是什么? A2:……
……
Q:还需要别的吗? 不需要 → 给出最终答案
每一步先问”下一个子问题是什么”,回答它,再根据目前的进展决定下一个子问题。它的好处是后面的分解可以依赖前面的答案;论文还提出可以用合成数据分别训练”提问”和”回答”两个部分,在 DROP 这类需要多步推理的阅读理解上取得了提升。第 4 讲的 Self-Ask(Press et al. 2023)是同一个思路的提示版本:“Are follow up questions needed here? Yes. Follow up: …… Intermediate answer: ……”,并且可以把每个 follow-up 问题交给搜索引擎回答。
02_decomposition_quality.py 演示 3 打印了一条这样的轨迹(4 个子句,一次问一个、答一个)。在 SCAN 上两种方式等价,因为子问题互相独立;在多跳问答上(“X 的首都所在国家的领导人是谁”),第二个子问题必须用到第一个的答案,交错的方式更自然。这和第 8 讲”先规划 vs 边做边看”是同一个取舍:
| 先分解(Least-to-Most、ReWOO) | 边分解边回答(Successive Prompting、Self-Ask、ReAct) | |
|---|---|---|
| 优点 | 调用少、可以并行、分解可以先被检查 | 后面的步骤可以根据前面的结果调整 |
| 缺点 | 后面的子问题依赖前面的答案时只能猜 | 调用多、串行、延迟高 |
| 适合 | 子问题互相独立、结构可预知 | 多跳、需要中间结果才能决定下一步 |
5. Decomposed Prompting:拆到多细、交给谁 · Decomposed Prompting
5.1 分解的粒度
03_decomposed_prompting.py 用 SCAN 的新原语划分:模型训练时只见过光秃秃的 “jump”,其他三个动词的所有组合都见过。
| 测试 | 条数 | 一次性翻译 | 按子句分解(Least-to-Most) | 更细:动词交给模型,方向和重复交给程序 |
|---|---|---|---|---|
| 含 jump 的组合(1 个子句) | 8 | 0.0% | 0.0% | 100.0% |
| 含 jump 的组合(2 个子句) | 496 | 0.0% | 0.0% | 100.0% |
| (对照)不含 jump 的 2 个子句 | 81 | 100.0% | 100.0% | 100.0% |
一次性翻译全错。看它的输出(演示 2):一个 JUMP 都没有,“jump twice” 被翻成了 LOOK、RUN 这些在组合里见过的动作——在训练数据里,“jump” 后面只跟过 “end”,模型把它学成了”只能单独出现的特殊命令”。
按子句分解没有帮助:‘jump twice’ 本身就是一个子句,分解到子句这一级,子问题仍然不在训练分布里。再细一层——把子句拆成”动词 / 方向 / 重复”,动词交给模型(它见过 “jump” 单独出现),方向和重复交给一个小程序——就全对了。
**分解有用的前提是:分解出来的每一块,模型都会做。**拆到哪一级,要看模型的能力边界在哪里。
5.2 交给谁:模块化的处理器
Decomposed Prompting(DecomP,Khot et al. 2023)把这个想法做成了一个框架:一个”分解器”提示把任务拆成子任务,每个子任务交给一个处理器(handler):
- 另一个专门的少样本提示(例如”从一个词里取出第 k 个字母”);
- 再分解一次的提示(递归地处理太难的子任务);
- 一段符号程序或一个外部工具(例如检索、计算器——第 8 讲)。
处理器可以单独调试、单独替换、在不同的任务之间复用。论文在符号操作(例如对多个单词取第 k 个字母再连接)、多跳问答等任务上报告,DecomP 优于一次性的思维链提示,尤其是在需要泛化到更长、更复杂的输入时。我们的迷你实验正是它的极简版本:动词处理器是模型,方向和重复处理器是程序。
这也解释了为什么”程序 + 模型”的组合在实践中如此常见:能写成确定规则的部分交给程序,需要语言理解的部分交给模型。程序那部分永远不会出现长度泛化或组合泛化的问题。
6. 提示链与错误传播 · Prompt chaining and error propagation
6.1 p^k
一条提示链有 k 个环节,每个环节以概率 p 成功,环节之间的错误互相独立,那么整条链成功的概率是 。04_error_propagation.py 演示 1 用训练得不够充分的迷你模型(110 / 130 / 150 步)做子句求解器:
| 训练步数 | 单个子句的准确率 p | 1 个子句 | 2 个 | 3 个 | 4 个 | 5 个 |
|---|---|---|---|---|---|---|
| 110 | 66.7% | 66%(67%) | 44%(44%) | 33%(30%) | 24%(20%) | 14%(13%) |
| 130 | 80.6% | 80%(81%) | 62%(65%) | 56%(52%) | 46%(42%) | 34%(34%) |
| 150 | 94.4% | 96%(94%) | 88%(89%) | 85%(84%) | 84%(80%) | 79%(75%) |
(括号里是 的预测。)单个子句 94% 看起来不错,5 个子句的命令就只剩 79%。实测和 很接近,因为这里的错误几乎是独立的:每个子句是独立抽的,而求解器对某一种子句要么总对、要么总错。如果错误是相关的(例如某一类问题系统性地错,又常常一起出现), 就不准了。
这个简单的公式对设计提示链有直接的含义:
| 每一环的准确率 | 3 环 | 5 环 | 10 环 |
|---|---|---|---|
| 99% | 97% | 95% | 90% |
| 95% | 86% | 77% | 60% |
| 90% | 73% | 59% | 35% |
与其把链做长,不如先把每一环做稳——或者在环与环之间加检查。
6.2 在中间加检查
演示 2 在每个子句的输出之后加一个结构校验:‘jump left twice’ 的输出必须是两组”LTURN + 一个动作”——它检查形状,不知道 jump 对应哪个动作。不通过就按温度 1 重新采样,最多 5 次。
| 训练步数 | 子句数 | 不校验 | 校验 + 重试 | 每个子句平均求解次数 | 校验通过但答案仍错的子句 |
|---|---|---|---|---|---|
| 110 | 5 | 14% | 25% | 2.20 | 22 |
| 130 | 5 | 34% | 66% | 1.69 | 0 |
| 150 | 5 | 79% | 79% | 1.23 | 0 |
三行讲了三个道理:
- 130 步:便宜的检查让 5 个子句的准确率几乎翻倍,平均每个子句只多求解 0.69 次。检查放在每一环之后,让错误在传播之前就被拦下。
- 110 步:提高得少。很多错误是”形状对、动作错”(22 个子句),只看形状的检查拦不住。检查越便宜,漏掉的越多;能完全确认答案的检查,往往和重新做一遍一样贵。
- 150 步:完全没有提高。它错的那些子句,重新采样 5 次也找不到正确的形状——错误是系统性的(它根本不会这种子句),不是偶然的。重试只能修随机错误,修不了系统性错误——第 4 讲的 self-consistency 也是如此。系统性错误需要换一个处理器(更好的提示、更强的模型、一段程序),或者把这一步再分解。
真实的提示链里,这样的检查到处都是:输出是不是合法的 JSON(第 8 讲的 Schema 校验)、数字是不是在合理范围、代码能不能编译和通过单元测试(第 11 讲)、引用的段落是否支持这个说法(第 7 讲的归因)、LLM 判官的打分(第 10 讲)。Wu et al.(2022)的 AI Chains 研究从人机交互的角度发现:把一个任务拆成可见的链条,用户更容易理解、调试和控制模型的行为——可检查性本身就是分解的一个重要收益。
7. 分解的成本:什么时候不该分解 · The cost of decomposition
分解不是免费的:
- 调用次数和延迟:Least-to-Most 在 k 个子句的命令上至少要 k + 1 次调用(1 次分解 + k 次求解);交错的方式还必须串行。
- token:每次调用都要重发示范和指令(第 8 讲 §3.1)。
- 新的出错环节:分解本身会错(§3.3),拼接也可能错。
- 在模型已经会的地方没有收益:两个子句的命令,一次性翻译本来就是 100%(§3.3 的表)。
一个实用的决策顺序:
- 先测一次性的表现,按问题的复杂度(长度、步骤数)分组。分解的收益集中在一次性做不好的那些组。
- 如果要分解,先确定每个子问题模型是否都会做(§5.1);不会的就再拆,或者交给程序 / 工具。
- 估计整条链的可靠性(),在最容易错、最便宜检查的地方加检查(§6.2)。
- 子问题互相独立时并行执行;只有在后面依赖前面时才串行(§4)。
- 分别评估分解的质量和求解的质量(05 号脚本的 B 列 vs C 列),这样才知道该改进哪一个提示。
更进一步的方向——让模型在多种分解或多条推理路径之间搜索(Tree of Thoughts),或者让多个智能体分工——在第 13、15 讲。
8. 在真实模型上 · On a real model(可选)
05_real_llm.py 把迷你 SCAN 的设置搬到真实的 Claude 上:提示里只给 8 个 1–2 子句命令的翻译示范,测试 3–6 个子句的命令(每种长度 6 条)。三种做法:A 直接翻译;B Least-to-Most(模型自己分解,再逐个翻译,程序拼接);C 用规则分解代替 B 的第一阶段——B 和 C 的差别就是分解错误的代价,C 和 100% 的差别就是子命令翻译错误的代价。--toy 模式把 01 号脚本的迷你模型当作”LLM”(它的 A 列在 3–6 个子句上是 16.7% / 0% / 0% / 0%,B、C 都是 100%)。前沿模型在这个规则简单的玩具任务上可能已经不需要分解;如果 A 在 6 个子句时仍然全对,可以把命令加长到 10–15 个子句,看它从哪里开始出错。编写环境里没有安装 anthropic SDK,真实模型的结果没有报告。
9. 小结 · Summary
- 为什么分解有效:每个子问题更像训练分布、更短、错误可定位。
- 组合泛化与长度泛化:迷你 SCAN 模型在训练过的长度上 100%,3 个子句就只剩 2%;错误主要是把长命令硬塞进”两个子句”的模板。位置编码和训练分布都限制了长度泛化。
- Least-to-Most:先分解、再依次求解,每个子问题都是见过的形式——所有长度上 100%。但分解本身会错:粗心的分解器让 after 命令从 100% 掉到 9%;每个连接处以 ε 出错,整条管线约以 成功。
- Successive Prompting / Self-Ask:分解和回答交错,适合后面依赖前面的多跳问题;和”先规划 vs 边做边看”是同一个取舍。
- Decomposed Prompting:拆到每一块模型都会做为止,并把能写成规则的部分交给程序——新原语划分上,按子句分解 0%,拆到”动词 / 方向 / 重复”100%。
- 错误传播:k 环、每环 p,整条约 (94% 的子句准确率 → 5 个子句 79%)。便宜的检查 + 重试能修随机错误(34% → 66%),修不了系统性错误(79% → 79%)。
- 分解有成本:更多调用、更长延迟、新的出错环节;在模型已经会的地方没有收益。先测一次性的表现,再决定拆不拆、拆多细、在哪里检查。
10. 常见误区 · Common pitfalls
| 误区 | 实际情况 |
|---|---|
| “分解总是有帮助” | 在模型已经会的地方没有收益,只增加调用和出错环节 |
| “分解成几步就行,拆到哪一级无所谓” | 只有当每一块模型都会做时分解才有用;按子句拆解决不了 ‘jump twice’ |
| “分解是模型的事,只要求解做对就行” | 分解本身会错,而且错误会让后面全部白做;分解的提示和结果都要评估 |
| “每一步 95% 已经很好了” | 10 步的链只有 60%;长链要求每一环极其可靠,或者在中间加检查 |
| “出错了就多试几次” | 重试只能修随机错误;系统性错误要换处理器或再分解 |
| “检查就是让模型再做一遍” | 好的检查要比重做便宜:格式、形状、范围、单元测试;但便宜的检查会漏掉一部分错误 |
| “思维链和 Least-to-Most 是一回事” | 思维链在一次生成里写出中间步骤;Least-to-Most 真正拆成多次调用,每次都是短的、见过的形式 |
| “能用模型做的都用模型做” | 能写成确定规则的部分交给程序,永远不会有泛化问题 |
11. 练习 · Exercises
练习 1(计算) 一条提示链有 6 步,每步的准确率分别是 0.99、0.97、0.95、0.99、0.90、0.98,错误互相独立。(a)整条链的准确率是多少?(b)你只有改进一步的预算,改进哪一步最划算?(c)如果在第 5 步之后加一个检查,能拦下它 80% 的错误并重试成功,整条链变成多少?
参考答案:(a)0.99 × 0.97 × 0.95 × 0.99 × 0.90 × 0.98 ≈ 0.797。(b)乘积里最小的因子是 0.90(第 5 步),把它提高到 0.98 能让整条链变成约 0.867;同样提高别的步骤收益都更小——改进最弱的一环最划算。(c)第 5 步的有效准确率变成 0.90 + 0.10 × 0.8 = 0.98,整条链约 0.867(和 b 一样):一个好的检查等价于把那一步做稳。
练习 2(设计分解) 任务:“把一段会议记录整理成按负责人分组的待办事项列表,并标出截止日期。“设计一个分解方案:子任务、每个子任务交给谁(提示 / 程序)、在哪里加检查。
参考答案(一种可能):(1)抽取:从记录里找出所有”谁要做什么、什么时候”的句子(LLM,少样本示范,输出 JSON 列表)。检查:JSON Schema 校验;每条都有负责人和任务。(2)规范化:把”下周三""月底”转换成具体日期(程序 + 会议日期作为输入;程序做不了的交给 LLM 并要求输出 ISO 日期,再用程序校验格式)。(3)人名归一:把”小王""王工”合并(LLM 或一张名单)。检查:每个负责人都在参会名单里。(4)分组与排版(程序)。分解的理由:抽取和人名归一需要语言理解,日期计算和分组是确定的规则;每一步的输出都能被便宜地检查,出错时能定位。
练习 3(解释) §5.1 里,按子句分解在新原语划分上是 0%,而在长度划分上是 100%。用”每一块模型都会做”解释这两个结果。
参考答案:长度划分的训练集里有全部 36 种子句,按子句分解之后,每个子问题(一个子句)都是训练时见过的——所以 100%。新原语划分的训练集里,“jump” 只单独出现过,“jump twice""jump left” 这些子句从没见过;按子句分解之后,这些子句仍然是分布外的子问题,模型照样不会——所以 0%。只有再拆一级(动词 / 方向 / 重复),每一块(“jump” 单独出现、重复和方向的规则)才都落在模型或程序的能力范围内。
练习 4(判断) 下面几种情况,你会选 Least-to-Most(先分解)还是 Successive Prompting / Self-Ask(交错)?为什么?(a)把一份长合同的每一条款分别翻译成中文;(b)“某公司现任 CEO 的母校在哪个城市?”;(c)计算一个购物车的总价(每件商品的单价要查价格表、再乘数量、最后加运费)。
参考答案:(a)先分解:条款互相独立,可以一次切分、并行翻译。(b)交错:要先知道 CEO 是谁,才能问他的母校,再问母校所在的城市——后面的子问题依赖前面的答案。(c)先分解(ReWOO 式):查每件商品的价格互相独立,可以并行;乘法、加法用计算器或程序。只有在价格表里查不到某件商品、需要改变后续步骤时,才需要交错的方式处理异常。
练习 5(动手) 修改 scan_lab.check_clause,让它也检查”动作词是否和动词一致”(需要知道 VERBS 的映射)。重新运行 04 号脚本的演示 2,训练 110 步的模型会怎样?这还算”便宜的检查”吗?
参考答案:加上动作词的检查后,“形状对、动作错”的错误也会被拦下并重试,110 步的模型提高会更多(前提是重新采样能采到正确的动作)。但这个检查已经知道了完整的答案——它本身就是一个解释器,可以直接生成正确的输出,重试就没有意义了。这说明检查的”强度”和”成本”是连续的:最便宜的检查只看格式,最贵的检查等于重做一遍。实际系统里要找的是”便宜但能拦下大部分错误”的检查。
练习 6(批判) 有人说:“我们把一个复杂的报告生成任务拆成了 12 个步骤,每一步都用 LLM,每一步的准确率都在 95% 以上,所以整个系统很可靠。“指出这个推理的问题,并提出至少两个改进。
参考答案:问题:12 步、每步 95%,独立时整条链只有约 54%;而且”每一步 95%“可能是在各自的测试集上测的,没有考虑上一步的错误输入。改进:(1)端到端地评估整条链,并按步骤记录错误来源;(2)合并或去掉不必要的步骤,把能写成规则的步骤换成程序;(3)在最容易错的步骤之后加便宜的检查(格式、范围、一致性),不通过就重试或回退;(4)对系统性的错误换处理器(更强的模型、更好的示范),而不是重试。
12. 延伸阅读 · Further reading
组合泛化与长度泛化
- Lake & Baroni (2018). Generalization without Systematicity: On the Compositional Skills of Sequence-to-Sequence Recurrent Networks. ICML.(SCAN)
- Fodor & Pylyshyn (1988). Connectionism and Cognitive Architecture: A Critical Analysis.(系统性)
- Kim & Linzen (2020). COGS: A Compositional Generalization Challenge Based on Semantic Interpretation.
- Anil et al. (2022). Exploring Length Generalization in Large Language Models. NeurIPS.
分解的提示方法
- Zhou et al. (2023). Least-to-Most Prompting Enables Complex Reasoning in Large Language Models. ICLR.
- Dua et al. (2022). Successive Prompting for Decomposing Complex Questions. EMNLP.
- Press et al. (2023). Measuring and Narrowing the Compositionality Gap in Language Models(Self-Ask)。
- Khot et al. (2023). Decomposed Prompting: A Modular Approach for Solving Complex Tasks. ICLR.
- Drozdov et al. (2023). Compositional Semantic Parsing with Large Language Models(动态的 least-to-most,用在语义解析上)。
- Perez et al. (2020). Unsupervised Question Decomposition for Question Answering;Wolfson et al. (2020). Break It Down: A Question Understanding Benchmark(问题分解的表示与数据集)。
提示链
- Wu et al. (2022). AI Chains: Transparent and Controllable Human-AI Interaction by Chaining Large Language Model Prompts. CHI.
13. 下一讲预告 · Next lecture
这一讲的分解、检查都是人设计的:我们写分解的示范、选检查的方式。第 10 讲继续原课第 6 周的第二个主题:自动提示与自我验证——让模型自己写提示、自己检查自己。
- 指令归纳与自动提示工程师(APE):给模型一些输入—输出的例子,让它猜出”任务指令”,再在开发集上挑最好的;OPRO:把优化过程本身交给 LLM;
- 提示优化里的过拟合:在开发集上挑出来的提示,在测试集上还有多好(前置讲 P3 的”赢家诅咒”再次登场);
- 自我纠正(Self-Refine)与它的边界:第 8 讲已经看到,没有外部反馈时”再想一遍”常常没用;
- 验证链(CoVe):把回答拆成一条条可以单独核对的说法——第 5 讲”稳定的幻觉”的一个对策;
- LLM-as-a-judge:用模型给模型打分,以及它的位置偏差、长度偏差、自我偏好——怎样测量、怎样修正。