模块2:反思设计模式「Andrew Ng:Agentic AI」

2.1 通过反思改进任务输出

反思设计模式是我在许多应用中使用过的一种方法,而且它的实现惊人地简单。让我们来看一看。

正如人类有时会反思自己的成果并找到改进的方法一样,大型语言模型(LLM)也可以。例如,我可能会写这样一封电子邮件,如果我打字很快,我可能会写出一份不太好的初稿。如果我通读一遍,我可能会说:“嗯,‘下个月’这个说法对于 Tommy 什么时候有空吃晚饭来说不够清楚”,而且我还有一个拼写错误,也忘了署名。这会让我修改草稿,说得更具体一些:“嘿,Tommy,你 5 号到 7 号有空吃晚饭吗?”

类似的过程也能让 LLM 改进它们的输出。你可以提示一个 LLM 写出邮件的初稿,得到邮件版本1(email v1)后,你可以把它传递给也许是同一个模型,同一个大型语言模型,但使用不同的提示,告诉它进行反思并写一个改进的第二稿,从而得到最终的输出,即邮件版本2(email v2)。在这里,我只是硬编码了这个工作流:提示 LLM 一次,然后再次提示它进行反思和改进,从而得到邮件 v2。

事实证明,类似的过程可以用来改进其他类型的输出。例如,如果你让一个 LLM 编写代码,你可能会提示它为某个任务编写代码,它可能会给你代码的 v1 版本。然后,你可以将它传递给同一个或另一个 LLM,要求它检查错误并编写一个改进的代码第二稿。不同的 LLM 有不同的优势,所以我有时会为撰写初稿和进行反思改进选择不同的模型。例如,事实证明,推理模型(有时也称为思考模型)非常擅长发现错误,所以我有时会通过直接生成来编写代码的初稿,但随后使用一个推理模型来检查错误。

现在,事实证明,如果你能获得外部反馈,即来自 LLM 外部的新信息,而不仅仅是让 LLM 反思代码,那么反思会变得更加强大。在代码的例子中,你可以做的一件事就是直接执行代码,看看代码做了什么。通过检查输出,包括代码的任何错误信息,这对于 LLM 反思并找到改进其代码的方法来说,是极其有用的信息。

所以在这个例子中,LLM 生成了代码的初稿,但当我运行它时,它产生了一个语法错误。当你将这个代码输出和错误日志传回给 LLM,并要求它根据反馈进行反思并撰写新稿时,这给了它大量非常有用的信息,从而能够得出一个好得多的代码版本2。

所以,反思设计模式并非魔术。它不会让 LLM 每次都百分之百地做对所有事情,但它通常能给性能带来适度的提升。但需要记住的一个设计考虑是,当有新的、额外的外部信息可以被纳入反思过程时,反思会变得更加强大。所以在那个例子中,如果你能运行代码,并将代码输出或错误信息作为反思步骤的额外输入,那确实能让 LLM 更深入地反思,并找出可能出了什么问题(如果有的话),从而产生一个比没有这些可供吸收的外部信息时好得多的代码第二版。所以要记住一点,每当反思有机会获得额外信息时,它都会变得更加强大。

现在,让我们进入下一个视频,我想与你分享一个更系统的比较,关于使用反思与直接生成(我们有时称之为零样本提示)的对比。让我们进入下一个视频。

2.2 为什么不直接生成?

让我们来看一看,为什么我们可能更倾向于使用反思工作流,而不是仅仅提示一次 LLM,让它直接生成答案然后就此结束。

使用直接生成时,你只需用一条指令来提示 LLM,然后让它生成一个答案。所以你可以要求一个 LLM 写一篇关于黑洞的文章,然后让它直接生成文本;或者让它编写计算复利的 Python 函数,然后让它直接写出代码。你在这里看到的提示示例也被称为零样本提示(zero-shot prompting)。

让我解释一下“零样本”是什么意思。与零样本提示相对的一种相关方法是,在你的提示中包含一个或多个你希望输出看起来像的例子。这被称为单样本提示(one-shot prompting),如果你在提示中包含了一个期望的输入输出对的例子;或者双样本或少样本提示(two-shot or few-shot prompting),取决于你在提示中包含了多少这样的例子。因此,零样本提示指的是如果你包含了零个例子,也就是不包含任何你想要的期望输出的例子。但如果你还不熟悉这些术语,也别担心。重要的是,在你看到的这些例子中,你只是提示 LLM 一次性直接生成答案,我也称之为零样本提示,因为我们包含了零个例子。

事实证明,多项研究表明,在各种任务上,反思都能提升直接生成的性能。这张图改编自 Madaan 等人的研究论文,它展示了一系列不同的任务,在有和没有反思的情况下,使用不同模型实现的结果。解读这张图的方法是看这些相邻的浅色条和深色条对,其中浅色条显示的是零样本提示,深色条显示的是同一模型但带有反思。蓝色、绿色和红色的颜色显示了使用不同模型(如 GPT-3.5 和 GPT-4)进行的实验。你所看到的是,对于许多不同的应用,深色条(即带有反思)比浅色条要高出不少。但当然,具体到你的应用,你的情况可能会有所不同。

这里还有一些反思可能有所帮助的例子。

  • 生成结构化数据:如果你在生成结构化数据,比如一个 HTML 表格,有时输出的格式可能会不正确。所以一个验证 HTML 代码的反思提示可能会有帮助。如果是基础的 HTML,这可能帮助不大,因为 LLM 对基础 HTML 已经很擅长了。但特别是如果你有更复杂的结构化输出,比如一个有很多嵌套的 JSON 数据结构,那么反思就更有可能发现错误。
  • 生成指令序列:或者,如果你要求一个 LLM 生成一系列构成指令的步骤,比如如何冲泡一杯完美的茶,有时 LLM 可能会遗漏步骤,一个要求检查指令的连贯性和完整性的反思提示可能有助于发现错误。
  • 创意性任务:还有一些我实际做过的事情,就是使用 LLM 来生成域名,但有时它生成的名字有意外的含义或者很难发音。所以我用过反思提示来再次检查域名是否有任何有问题的内涵或意义,或者名字是否难读。我们实际上在我的一个团队 AI Fund 中用这个方法来为我们正在做的创业公司头脑风暴域名。

我想给你看几个反思提示的例子。

  • 为头脑风暴域名:你可能会要求它“审查你建议的域名”,然后要求它“检查每个名字是否容易发音”,“检查每个名字在英语或其他语言中是否可能有负面含义”,然后“输出一个只包含满足这些标准的名字的简短列表”。
  • 为改进电子邮件:你可以写一个反思提示,告诉它“审查邮件初稿”,“检查语气”,“验证所有陈述的事实和承诺是否准确”(这在 LLM 被输入了大量事实和日期等信息以撰写邮件草稿的背景下是有意义的,所有这些都会作为 LLM 上下文的一部分提供),然后“根据它可能发现的任何问题,撰写邮件的下一稿”。

所以,这里有一些编写反思提示的技巧。明确指出你希望它“审查”或“反思”输出的初稿,这很有帮助。如果你能指定一套明确的标准,比如域名是否易于发音、是否有负面含义,或者对于邮件,检查语气和验证事实,那么这能更好地引导 LLM 在你最关心的标准上进行反思和批判。我发现,我学会写出更好提示的方法之一,就是阅读大量其他人写的提示。有时我甚至会下载开源软件,去找到我认为做得特别好的软件中的提示,就为了去读读作者们写的提示。

希望你现在对如何编写一个基本的反思提示有了一定的了解,甚至可能会在你自己的工作中尝试一下,看看它是否能帮助你获得更好的性能。在下一个视频中,我想与你分享一个有趣的例子,我们将开始研究多模态的输入和输出。我们会让一个算法反思一个正在生成的图像或图表。让我们去看一看。

2.3 图表生成工作流

在本模块你将看到的编码实验中,你会体验一个图表生成工作流,在其中你使用一个代理来生成漂亮的图表。事实证明,反思可以显著提高这种输出的质量。让我们来看一看。

在这个例子中,我有一台咖啡机的数据,显示了拿铁、咖啡、热巧克力、卡布奇诺等不同饮品的销售时间以及价格。我们想让一个代理创建一个图表,比较2024年和2025年第一季度的咖啡销售情况。

一种方法是编写一个提示,要求一个 LLM“使用存储在电子表格(CSV文件,即逗号分隔值文件)中的数据,创建一个比较2024年和2025年第一季度咖啡销售情况的图表”。一个 LLM 可能会写出像这样的 Python 代码来生成图表。使用这个 v1 版本的代码,如果你执行它,它可能会生成一个像这样的图表。当我运行 LLM 输出的代码时,它第一次就生成了这个。这是一个堆叠条形图,这并不是一种非常容易可视化的方式,而且这个图看起来不太好。

但你可以做的是,将代码的 v1 版本以及这段代码生成的图表,都提供给一个多模态模型——也就是一个也能接受图像输入的 LLM——然后要求它检查这段代码生成的图像,接着对图像进行批判,找到一种方法来提出更好的可视化方案,并更新代码以生成一个更清晰、更好的图表。多模态 LLM 可以使用视觉推理,所以它实际上可以从视觉上审视图表,以找到改进它的方法。当我这样做时,它实际上生成了一个条形图,不是那种堆叠条形图,而是一种更常规的条形图,它将2024年和2025年的咖啡销售分开,我认为这种方式更美观、更清晰。当你进入编码实验时,请随意尝试这些问题,看看你是否能得到比这些更好看的图表。

因为不同的 LLM 有不同的优点和缺点,我有时会为初始生成和反思使用不同的 LLM。例如,你可能会用一个 LLM 来生成初始代码,也许是像 GPT-4o 或 GPT-5 这样的模型,然后用一个像这样的提示来让它编写 Python 代码生成可视化等等。然后,反思的提示可能是这样的,你告诉 LLM 扮演一个“提供建设性反馈的专家数据分析师”的角色,然后给它代码的版本1,即生成的部分,也许还有代码生成过程中的计算历史,并要求它根据特定的标准进行批判。记住,当你给出像“可读性”、“清晰度”和“完整性”这样的具体标准时,它能帮助 LLM 更好地弄清楚该做什么。然后要求它编写新的代码来实现你的改进。

你可能会发现的一件事是,有时使用一个推理模型进行反思,可能比使用一个非推理模型效果更好。所以,当你为初始生成和反思尝试不同的模型时,这些都是你可能需要调整或尝试不同组合的配置。

所以,当你进入编码实验时,我希望你在可视化咖啡销售数据时玩得开心。现在,当你构建一个应用时,你可能会想,反思是否真的能提高你特定应用的性能?从各种研究来看,反思在某些应用上能稍微提高性能,在另一些应用上能大幅提高,而在其他一些应用上可能几乎没有任何提升。因此,了解它对你应用的影响会很有用,这也能指导你如何调整初始生成或反思的提示,以尝试获得更好的性能。在下一个视频中,让我们来看一看针对反思工作流的评估(evals)。让我们进入下一个视频。

2.4 评估反思的影响

反思通常能提高系统的性能,但在我决定保留它之前,我通常会想再次确认它到底能提升多少性能,因为它确实需要一个额外的步骤,会稍微拖慢系统。让我们来看一看针对反思工作流的评估。

让我们看一个使用反思来改进 LLM 编写的数据库查询的例子,这个查询用于获取数据来回答问题。假设你经营一家零售店,你可能会收到这样的问题:“哪种颜色的产品总销售额最高?” 要回答这样的问题,你可能会让一个 LLM 生成一个数据库查询。如果你听说过像 SQL 这样的数据库语言,它可能会生成那种语言的查询。但如果你不熟悉 SQL,也别担心。但是在编写了数据库查询后,你可能不会直接用它从数据库中获取信息,而是让一个 LLM(同一个或不同的 LLM)反思版本一的数据库查询,并将其更新为可能改进过的版本,然后对数据库执行那个查询以获取信息,最后让一个 LLM 来回答问题。

所以问题是,使用第二个 LLM 来反思和改进数据库或 SQL 查询,是否真的能改善最终的输出?

为了评估这一点,我可能会收集一组问题或提示,以及它们的标准答案。所以可能有一个问题是:“2025年5月卖出了多少件商品?”“库存中最贵的商品是什么?”“我的店里有多少种款式?” 然后我为大约10到15个提示写下标准答案。

然后,你可以运行这个不带反思的工作流。不带反思意味着,取第一个 LLM 生成的 SQL 查询,然后直接看它给出了什么答案。而带反思则意味着,取第二个 LLM 反思后生成的数据库查询,看看它从数据库中获取了什么答案。然后我们可以测量在没有反思和有反思的情况下,正确答案的百分比。在这个例子中,没有反思时,答案的正确率为87%,而有反思时,正确率为95%。这表明,反思正在有意义地提高我能得到的数据库查询的质量,从而提取出正确的答案。

开发者们通常还会做的一件事是重写反思提示。例如,你是否想在反思提示中加入一条指令,让数据库查询运行得更快或更清晰?或者你可能对如何重写初始生成提示或反思提示有不同的想法。一旦你建立了像这样的评估体系,你就可以快速尝试关于这些提示的不同想法,并在你改变提示时测量你系统的正确率,从而了解哪些提示对你的应用效果最好。所以,如果你在尝试很多提示,建立评估体系是很重要的。它真的能帮助你有一种系统性的方法,来在你可能考虑的不同提示之间做出选择。

但这个例子是在你可以使用客观评估的情况下,因为有一个正确的答案。卖出的商品数量是1301件,答案要么对要么错。那么对于需要更主观而非客观评估的应用呢?

在我们上一个视频中看到的绘图例子里,没有反思我们得到了堆叠条形图,有反思我们得到了这个图。但我们怎么知道哪个图实际上更好呢?我知道我更喜欢后一个,但对于在不同维度上变化的各种图表,我们如何判断哪个更好?衡量这些图中哪个更好,更多的是一个主观标准,而不是一个纯粹的黑白分明的客观标准。

所以对于这些更主观的标准,你可以做的一件事是使用一个 LLM 作为评判者。也许一个基本的方法是,将两个图都输入到一个 LLM 中,一个可以接受两张图片作为输入的多模态 LLM,然后直接问它哪张图片更好。事实证明,这样做效果不是很好。我稍后会分享一个更好的主意。但你可以做的一件事可能是,也给它一些评估这两个图的标准,比如清晰度、美观度等等。

但事实证明,使用 LLM 来比较两个输入并告诉你哪个更好,存在一些已知的问题。首先,事实证明答案通常不是很好。它可能对作为评判者的 LLM 的提示的确切措辞很敏感,而且有时排名顺序与人类专家的判断并不十分吻合。这方面的一个表现是,许多 LLM 会有位置偏见。事实证明,许多 LLM 通常会更频繁地选择第一个选项而不是第二个选项。实际上,我用过的很多 LLM,给定两个选择,无论我把哪个选择放在前面,它都会说第一个选择更好。也许有些 LLM 偏爱第二个选项,但我认为大多数 LLM 偏爱第一个选项。

与其让一个 LLM 比较一对输入,不如使用评分标准进行评分能得到更一致的结果。例如,你可以提示一个 LLM,告诉它:“给定一张图片,根据质量评分标准评估附上的图片。” 这个评分标准或评分准则可能包含明确的标准,比如:图表是否有清晰的标题?坐标轴标签是否存在?图表类型是否合适?等等,包含几个类似这样的标准。事实证明,与其让 LLM 在1到5的等级上给某样东西打分(它在这方面往往校准得不好),不如给它比如说5个二元标准,5个0-1标准,让它给出5个二元分数,然后你把这些分数加起来得到一个从1到5的数字,或者如果你有10个二元标准,就是从1到10,这样做往往能得到更一致的结果。

因此,如果我们收集一些(比如10-15个)用户关于他们可能希望看到的咖啡机销售数据可视化的查询,那么你可以让它在没有反思的情况下生成图像,或者在有反思的情况下生成图像,然后用这样的评分标准给每张图像打分,从而检查有反思生成的图像在多大程度上或是否真的比没有反思生成的要好。然后,一旦你建立了这样一套评估体系,如果你想改变初始生成提示或反思提示,你也可以重新运行这个评估,看看比如说更新你的一个提示是否能让系统生成根据这个评分标准得分更高的图像。所以这也给了你一种方法,可以不断调整你的提示,以获得越来越好的性能。

你在为反思或其他 agentic 工作流构建评估时可能会发现,当存在客观标准时,基于代码的评估通常更容易管理。在我们看到的数据库查询的例子中,我们建立了一个标准答案和标准输出的数据库,然后直接编写代码来看系统生成正确答案的频率,这是一个非常客观的评估指标。相比之下,对于小型的主观任务,你可能会使用一个 LLM 作为评判者,但这通常需要多一点的调整,比如需要思考你可能想用什么评分标准来让作为评判者的 LLM 被很好地校准,或者输出可靠的评估结果。

希望这能让你对如何构建评估来评价反思,或者更普遍地,评价不同的 agentic 工作流,有了一定的了解。知道如何做好评估,对于你如何有效地构建 agentic 工作流非常重要,你也会在后面的视频中听到我更多地谈论这一点。但现在你已经对如何使用反思有了一定的了解,我希望在下一个视频中深入探讨它的一个方面,那就是当你能从外部获得额外信息时,这会使反思的效果好得多。所以在本模块的最后一个视频中,让我们来看一看那个能让你的反思工作流效果好得多的技术。我们下一个视频见。

2.5 使用外部反馈

带有外部反馈的反思,如果你能得到的话,比仅使用 LLM 作为唯一反馈来源的反思要强大得多。让我们来看一看。

当我构建一个应用时,如果我只是为直接生成(即零样本提示)进行提示工程,随着时间的推移,性能可能是这样的:一开始,随着我调整提示,性能会提升一段时间,但过了一段时间后,它就趋于平稳或停滞不前了,尽管我进一步设计提示,也很难再获得更好的性能水平。

所以,与其把所有时间都浪费在调整提示上,不如早一点在流程中开始加入反思,有时这会给性能带来一次提升。有时提升较小,有时较大,但这增加了复杂性。但如果我在流程的这个节点开始加入反思,然后开始调整反思提示,那么我最终得到的性能可能就是这样。但事实证明,如果我能获得外部反馈,使得新信息的来源不仅仅是 LLM 对它之前已有的相同信息进行反思,而是有一些新的外部信息,那么有时,随着我继续调整提示和外部反馈,你最终会得到一个高得多的性能水平。

所以,如果你正在进行提示工程,并且你觉得你的努力正在看到递减的回报,你调整了很多提示,但性能就是没有变得更好,那么可以考虑一下是否可以加入反思,或者更好的是,是否有某种外部反馈可以注入,从而让性能曲线摆脱那条趋于平缓的红线,走向一个可能更高的性能提升轨道。

提醒一下,我们之前看到,如果你在编写代码,一个反馈来源就是,如果你直接执行代码,看看它会生成什么输出——无论是输出结果还是错误信息——然后将该输出反馈给 LLM,让它获得新的信息进行反思,然后利用这些信息来编写新版本的代码。

这里还有几个例子,说明软件代码或工具何时可以创建新信息来帮助反思过程。

  • 模式匹配:如果你使用 LLM 来写邮件,而它有时会提到竞争对手的名字,那么如果你编写代码或构建一个软件工具来进行模式匹配(也许通过正则表达式模式匹配)来搜索输出中的竞争对手名字,那么每当你找到一个竞争对手的名字,你就把它作为批评或输入反馈给 LLM。这是非常有用的信息,可以告诉它在不提及那些竞争对手的情况下重写文本。
  • 事实核查:另一个例子,你可以使用网络搜索或查看其他可信来源来对一篇文章进行事实核查。所以,如果你的研究代理说“泰姬陵建于1648年”,技术上讲,泰姬陵实际上是在1631年受命建造,并于1648年完工。所以也许这不完全是错的,但它也没有准确地反映历史。为了更准确地表述这座美丽建筑的建造时间,如果你进行一次网络搜索,找到一段准确解释泰姬陵建造时期的片段,并将其作为额外的输入提供给你的反思代理,那么它也许能够利用这些信息来写出一个关于泰姬陵历史的更好版本的文本。
  • 约束检查:最后一个例子,如果你使用 LLM 来撰写文案,也许是为了一篇博客文章或一篇研究论文的摘要,但它写的内容有时会超过字数限制。LLM 在遵守确切的字数限制方面仍然不是很好。那么,如果你实现一个字数统计工具,就是编写代码来计算确切的单词数量,如果它超过了字数限制,那么就把这个字数反馈给 LLM,并让它再试一次。这有助于它更准确地达到你希望它生成的输出的期望长度。

在这三个例子中,你都可以编写一段代码来帮助找到关于初始输出的额外事实,然后将这些事实——无论是你找到了竞争对手的名字,还是网络搜索到的信息,或者是确切的字数——提供给进行反思的 LLM,以帮助它更好地思考如何改进输出。

反思是一个强大的工具,我希望你在自己的许多工作中都能发现它的用处。在下一个模块中,我们将在此基础上讨论工具使用,在那里,除了你看到的少数几个工具例子外,你将学会如何系统地让你的 LLM 调用不同的函数,这将使你的 agentic 应用变得更加强大。希望你喜欢学习关于反思的内容。我现在要反思一下你刚刚学到的东西。希望在下一个视频中见到你。