Lecture 9 · 任务分解 · Task Decomposition

Prompt Engineering · Lecture 09。个人学习讲义与配套实验,按主题整理并持续修订。

课程源码 ZIP ↓

个人整理的学习笔记,非官方讲义;课程来源与整理说明见课程介绍。配套示例未逐一运行验证。

本篇目录

对应 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

学完这一讲,你应该能够:

  1. 解释分解为什么有效:每个子问题更像模型训练时见过的样子,错误也更容易定位。
  2. 说清楚组合泛化和长度泛化是什么,以及 SCAN 基准上神经网络的两种著名失败。
  3. 写出 Least-to-Most 的两个阶段(分解、依次求解),并知道它的效果依赖于分解本身的质量。
  4. 区分 Least-to-Most、Successive Prompting、Self-Ask、Decomposed Prompting,说出各自适合什么样的任务。
  5. 用 pkp^k 估计一条提示链的可靠性,并在链的中间设计便宜的检查;区分随机错误和系统性错误。
  6. 判断一个任务该不该分解、拆到多细——分解有收益也有代价。

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 多阶段是另一个例子:步骤越多,一次性回答越差;每一步交给工具,就都对了。

分解有效,有三个互相关联的原因:

  1. 每一步更像训练分布。模型在”简单问题”上见过大量数据,在”把五个简单问题揉成一个”的复杂问题上见得少。分解把一个分布外的问题变成几个分布内的问题。
  2. 每一步更短。长输入、长输出、需要同时记住很多中间结果,都是模型的弱点。分解让每次调用只处理一小块。
  3. 错误可以被定位。一次性回答错了,你只知道”错了”;分解之后,你知道是哪一步错了,可以只改那一步的提示,或者在那一步后面加检查(§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
13.23.2100.0%100.0%
26.66.6100.0%100.0%
310.49.82.0%100.0%
413.412.40.0%100.0%
516.714.20.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 提示分两个阶段,每个阶段都用少样本提示:

  1. 分解:示范”怎样把一个问题拆成一串从易到难的子问题”,让模型对新问题也这样拆。
  2. 依次求解:从最简单的子问题开始,一个一个地解;每个子问题的提示里包含前面子问题的问答,所以后面的子问题可以用前面的答案。

名字里的”由少到多”(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 + 正确的分解器+ 粗心的分解器一次性翻译
and100.0%100.0%100.0%
after100.0%9.0%100.0%

子问题个个都解对了,拼起来却是错的(剩下 9% 对的,是两个子句的”动作单元”恰好相同,例如 walk 和 walk twice,先后顺序无所谓)。**分解错了,后面做得再好也没用。**注意最后一列:两个子句是训练时见过的长度,一次性翻译本来就是 100%——在模型已经会的地方,分解没有带来好处,只带来了新的出错机会。

演示 2 用一个”有噪声”的分解器模拟会出错的 LLM:在每个连接处以概率 ε 出错(漏掉一个子句,或者弄反相邻两个的顺序)。

ε2 个子句:实测(预测)3 个子句4 个子句5 个子句
0.0595%(95%)92%(90%)82%(86%)80%(81%)
0.1092%(90%)80%(81%)72%(73%)70%(66%)
0.2077%(80%)66%(64%)50%(51%)42%(41%)

括号里是 (1−ε)k−1(1-\varepsilon)^{k-1} 的预测: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 个子句)80.0%0.0%100.0%
含 jump 的组合(2 个子句)4960.0%0.0%100.0%
(对照)不含 jump 的 2 个子句81100.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 成功,环节之间的错误互相独立,那么整条链成功的概率是 pkp^k。04_error_propagation.py 演示 1 用训练得不够充分的迷你模型(110 / 130 / 150 步)做子句求解器:

训练步数单个子句的准确率 p1 个子句2 个3 个4 个5 个
11066.7%66%(67%)44%(44%)33%(30%)24%(20%)14%(13%)
13080.6%80%(81%)62%(65%)56%(52%)46%(42%)34%(34%)
15094.4%96%(94%)88%(89%)85%(84%)84%(80%)79%(75%)

(括号里是 pkp^k 的预测。)单个子句 94% 看起来不错,5 个子句的命令就只剩 79%。实测和 pkp^k 很接近,因为这里的错误几乎是独立的:每个子句是独立抽的,而求解器对某一种子句要么总对、要么总错。如果错误是相关的(例如某一类问题系统性地错,又常常一起出现),pkp^k 就不准了。

这个简单的公式对设计提示链有直接的含义:

每一环的准确率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 次。

训练步数子句数不校验校验 + 重试每个子句平均求解次数校验通过但答案仍错的子句
110514%25%2.2022
130534%66%1.690
150579%79%1.230

三行讲了三个道理:

  1. 130 步:便宜的检查让 5 个子句的准确率几乎翻倍,平均每个子句只多求解 0.69 次。检查放在每一环之后,让错误在传播之前就被拦下。
  2. 110 步:提高得少。很多错误是”形状对、动作错”(22 个子句),只看形状的检查拦不住。检查越便宜,漏掉的越多;能完全确认答案的检查,往往和重新做一遍一样贵。
  3. 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 的表)。

一个实用的决策顺序:

  1. 先测一次性的表现,按问题的复杂度(长度、步骤数)分组。分解的收益集中在一次性做不好的那些组。
  2. 如果要分解,先确定每个子问题模型是否都会做(§5.1);不会的就再拆,或者交给程序 / 工具。
  3. 估计整条链的可靠性(pkp^k),在最容易错、最便宜检查的地方加检查(§6.2)。
  4. 子问题互相独立时并行执行;只有在后面依赖前面时才串行(§4)。
  5. 分别评估分解的质量和求解的质量(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

  1. 为什么分解有效:每个子问题更像训练分布、更短、错误可定位。
  2. 组合泛化与长度泛化:迷你 SCAN 模型在训练过的长度上 100%,3 个子句就只剩 2%;错误主要是把长命令硬塞进”两个子句”的模板。位置编码和训练分布都限制了长度泛化。
  3. Least-to-Most:先分解、再依次求解,每个子问题都是见过的形式——所有长度上 100%。但分解本身会错:粗心的分解器让 after 命令从 100% 掉到 9%;每个连接处以 ε 出错,整条管线约以 (1−ε)k−1(1-\varepsilon)^{k-1} 成功。
  4. Successive Prompting / Self-Ask:分解和回答交错,适合后面依赖前面的多跳问题;和”先规划 vs 边做边看”是同一个取舍。
  5. Decomposed Prompting:拆到每一块模型都会做为止,并把能写成规则的部分交给程序——新原语划分上,按子句分解 0%,拆到”动词 / 方向 / 重复”100%。
  6. 错误传播:k 环、每环 p,整条约 pkp^k(94% 的子句准确率 → 5 个子句 79%)。便宜的检查 + 重试能修随机错误(34% → 66%),修不了系统性错误(79% → 79%)。
  7. 分解有成本:更多调用、更长延迟、新的出错环节;在模型已经会的地方没有收益。先测一次性的表现,再决定拆不拆、拆多细、在哪里检查。

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:用模型给模型打分,以及它的位置偏差、长度偏差、自我偏好——怎样测量、怎样修正。
← 返回课程目录