Voyager:结合大语言模型的创新虚拟智能体 [译]

Guanzhi Wang, Yuqi Xie, Yunfan Jiang, Ajay Mandlekar, Chaowei Xiao, Yuke Zhu, Linxi Fan, Anima Anandkumar

NVIDIA, Caltech, UT Austin, Stanford, ASU

共同贡献    共同指导

https://voyager.minedojo.org

摘要

让我们欢迎 Voyager 的加入——这是 Minecraft 游戏中首个以大语言模型(LLM)为核心的虚拟学习智能体。它能够不断地探索虚拟世界,自主掌握各种技能,并在没有人为介入的情况下,不断做出新的发现。Voyager 的核心由三部分组成:1) 一个自动优化探索路径的课程设计,2) 一个持续扩展的技能库,该库用可执行代码存储和调用复杂行为,3) 一种新型的迭代提示机制,通过融入环境反馈、执行错误和自我核验来不断优化程序。Voyager 通过所谓的黑箱查询与 GPT-4 进行交互,从而免去了对模型参数细微调整的需求。Voyager 所发展的技能不仅能跨越时间使用,还易于解释和组合,这显著加速了智能体的学习能力,同时有效减少了所谓的灾难性遗忘现象。从实践效果来看,Voyager 展现出了卓越的在场景中的终身学习能力,并在 Minecraft 游戏中展示了超群的技能。与以往最好的技术相比,它获得的独特物品多出了 3.3×3.3\times,旅行的距离也增加了 2.3×2.3\times,而在解锁关键的技术树里程碑方面,速度更是快了多达 15.3×15.3\times。Voyager 甚至能够在全新的 Minecraft 世界中,利用其学习的技能库,从零开始解决新的任务,而其他技术在这方面则显得力不从心。

请查看图片说明
请查看图片说明

图 1: Voyager 通过自发的探索活动,持续不断地在 Minecraft 中发现新的物品和技能,其性能远远超过基准水平。X 轴显示的是提示迭代的轮数。

1 引言

AI 领域面临着一个巨大的挑战:打造能在不断变化的世界中探险、制定计划、习得新技能的具身智能体 kolve2017ai2thor; savva2019habitat; zhu2020robosuite; xia2019igibson0.5; shen2020igibson。经典的解决方案,比如强化学习 (RL) kober2013reinforcement; arulkumaran2017deep 和模仿学习 openai2022vpt; team2021creating; vinyals2019alphastar,依赖于基础动作来进行操作,但在系统性地探索 ecoffet2019goexplore; huizinga2022evolving; wang2020enhanced; kanitscheider2021multitask; dennis2020paired,提升可理解性 liang2022code; sun2020program; zhao2021proto,以及加强泛化能力 jiang2022vima; shridhar2021cliport; fan2021secant 这些方面存在挑战。最新的进展表明,大语言模型 (LLM) 所驱动的智能体可以利用预先训练好的 LLM 中蕴含的世界知识来制定一致性的行动方案或可操作的策略 liang2022code; singh2022progprompt; jiang2022vima。这些智能体不仅可用于游戏和机器人这类需要“身体”的任务 fan2022minedojo; zeng2022socratic; ahn2022saycan; huang2022inner; huang2022language,也能胜任那些不需“身体”的自然语言处理 (NLP) 任务 autogpt; yao2022react; shinn2023reflexion

尽管有些先进的智能体正在探索新的知识领域,但它们还没有达到能像人类一样,不断学习和累积知识,并能够随时间不断进步和应用这些知识的程度 parisi2023continualwang2023comprehensive

我们不妨拿 Minecraft 这款游戏来说事。Minecraft 与 AI 领域研究的其他多数游戏不同 mnih2013playingopenai2019dotavinyals2019alphastar,它没有既定的终点或固定的剧情,相反,它提供了一个充满无限可能性的开放世界 fan2022minedojo。在 Minecraft 里,玩家需要探索由程序生成的广阔三维地形,并利用搜集来的资源来逐步解锁技术树。通常,玩家需要从挖掘木头和烹饪食物等基本技能做起,然后逐步学习更为复杂的技能,比如对抗怪物、制作钻石工具等。我们认为,一个高效的终身学习智能体也应该具备类似于人类玩家的技能:(1)根据当前的技能水平和世界状况,提出合适的任务——比如在沙漠而不是森林里时,先学会采集沙子和仙人掌,而不是铁;(2)根据环境反馈进行技能的精细调整,并能够将熟练技能储存在记忆中,以便在未来遇到相似情况时重复使用——例如,打僵尸和打蜘蛛有类似之处;(3)不断自主探索新世界,自发地寻找新任务。

参考图片说明
参考图片说明

图 2: Voyager 的构成包括三个核心部分:一个面向无限探索的自动化课程安排,一个涵盖越来越复杂行为的技能库,以及一个迭代式的提示机制,其通过编码作为执行动作的空间。

迈向这些宏伟目标,我们推出了 Voyager,它是首个由 LLM 驱动的自主式终身学习智能体,在 Minecraft 游戏中自我推动探索,掌握各种技能,并不断进行新的发现,全过程无需人类干预。Voyager 能够实现,多亏了三大关键模块(见图 2):1)一个促进最大化探索的自动课程系统;2)一个用于储存和调用复杂行为的技能库;3)一种创新的迭代式提示机制,它能够生成用于实体控制的可执行代码。我们采用代码而非基础的机械指令作为操作界面,是因为代码能够更自然地表现出延时性和组合性的动作 liang2022code; singh2022progprompt,这对于 Minecraft 中众多长期性的任务至关重要。Voyager 通过提示和上下文学习与一个黑盒 LLM(GPT-4 openai2023gpt4)进行互动 wei2022emergent; brown2020gpt3; raffel2020t5,这样我们就无需直接访问模型参数或进行显式的梯度训练或微调了。

更详细地说,Voyager 要挑战的是一系列由自动课程系统设计出的难度逐级提升的任务,这个系统会根据探索的进度和智能体的状态不断优化任务难度。这些课程由 GPT-4 根据一个宏大的目标——“发现尽可能多的多样化事物”来生成,类似于一种场景化的新奇性搜索 eysenbach2018diversity; conti2018novelty。Voyager 会逐渐构建起一个技能库,将解决问题成功的动作程序存储起来,每个程序都能通过其描述的关键词被索引并在将来相似的场景中被调用。通过组合简单的程序来构建复杂技能,这样不仅迅速提升了 Voyager 的能力,而且有效避免了其他连续学习方法中的灾难性遗忘问题 parisi2023continual; wang2023comprehensive

虽然大语言模型(LLM)在首次尝试中很难稳定输出正确的动作代码 openai2021codex,我们提出了一种迭代式提示机制来克服这一难题。这个机制首先执行生成的代码,通过 Minecraft 模拟获取反馈信息,如背包物品列表和周围生物情况,以及代码执行中的错误追踪;然后把这些反馈整合进 GPT-4 的提示中,进行下一轮代码的优化;最后重复这个流程,直到一个自我验证模块确认任务已完成。这时候,我们会把这段程序存入技能库(比如 craftStoneShovel() 和 combatZombieWithSword()),并查看自动课程表以确定下一个目标(见图 2)。

实际操作中,Voyager 证明了自身强大的即时和终身学习能力。它能构建一个持续扩充的动作程序库,这个库内的程序不仅可重复使用、易于解释,还可以适用于全新的任务。我们在开源的 Minecraft AI 框架 MineDojo fan2022minedojo 中对 Voyager 进行了系统评估,并与其他基于 LLM 的智能体技术(例如 ReAct yao2022react,Reflexion shinn2023reflexion,AutoGPT autogpt)进行了对比。结果显示,Voyager 在获取独特物品的数量上是先前技术的 3.3×3.3\times,解锁关键技术树里程碑的速度快达 15.3×15.3\times,且穿越的距离是先前技术的 2.3×2.3\times。我们还证明了 Voyager 能够在一个全新的 Minecraft 世界中,利用其学习到的技能库从头解决新的任务,而其他技术则在这方面显得力不从心。

2 方法

Voyager 的设计包括三个创新部分:(1) 自动课程(第 2.1 节),它提出了开放探索的目标;(2) 技能库(第 2.2 节),用于培养越来越复杂的动作行为;(3) 迭代提示机制(第 2.3 节),用于生成可以直接控制角色行动的代码。详细的提示内容见附录第 A 节。

2.1 自动课程安排

请参考图片说明
请参考图片说明

图 3: 自动课程安排出的任务。为简化描述,我们这里只展示了部分任务提示。完整的任务结构请参考附录部分的章节 A.3

在一个充满无限可能的环境中,智能体会遇到各种复杂性各异的目标任务。一个精心设计的自动课程能够为自由探索带来巨大益处:它不仅确保了学习过程既具有挑战性又易于管理,还能激发智能体由好奇心驱动的内在求知欲,促使它们学习并探索新事物,并且培养了它们解决问题的通用性和灵活性 wang2019paired; portelas2020automatic; forestier2022intrinsically。我们基于 GPT-4 巨大的互联网知识库来制定自动课程,通过不断地提出新的任务或挑战,引导智能体进步。这样的课程设计采取自下而上的方式,既能灵活适应,也能及时响应智能体探索的进度和当前状态(参见图 3)。随着 "Voyager" 这个程序逐步挑战更高难度的目标,它自然而然地掌握了各种技能,比如“开采钻石”。

向 GPT-4 提出的输入提示包括以下几个方面:

  1. (1) 鼓励多样化行为并设置限制的指令,例如:“我的终极目标是发现尽可能多的不同事物……下一个任务不应该太过困难,因为我可能还不具备完成它所需的资源或技能。”;

  2. (2) 智能体的当前状态,涵盖了库存、装备、附近的方块和实体、生态类型、时间、健康和饥饿指标、位置等信息;

  3. (3) 之前已完成和未完成的任务,这反映了智能体目前探索的进度和能力范围;

  4. (4) 补充背景:我们还使用 GPT-3.5 基于智能体当前的状态和探索进度来自我提问,并利用维基知识库 digminecraft 来自我回答问题,以此为 GPT-4 提供更多的背景信息。考虑到预算因素,我们选择了 GPT-3.5 而非 GPT-4 来执行标准的自然语言处理任务。

2.2 技能库

图解参考
图解参考

图 4: 技能库展示。上图:新增技能。每当 GPT-4 创建并确认一项新技能时,我们便将其加入到代表着技能储藏的向量数据库中。这里面,“键”指的是程序描述的嵌入向量(由 GPT-3.5 生成),而“值”即程序本身。下图:技能检索。当自动课程安排了新的任务时,我们会先使用 GPT-3.5 来出一个大概的解决方案,并将其与环境反馈结合作为查询的上下文。然后,我们检索数据库,找出五个最为匹配的技能。

随着自动课程不断提出更加复杂的挑战,建立一个可以供学习和进化参考的技能库显得尤为重要。借鉴了程序设计中的通用性、可解释性和普适性 ellis2020dreamcoder,我们用可执行的代码片段来表征每项技能,这样的代码能够串联起一系列的动作,帮助完成自动课程所布置的特定任务。

向 GPT-4 提出的输入提示包括以下几部分:

  1. (1)代码生成的指导原则,比如“你的函数会被用来构建更加复杂的函数,所以它应当是通用且可复用的。”;

  2. (2)控制原语 API 以及技能库中检索到的相关技能,这对于上下文学习至关重要,能够使学习过程更加顺畅 wei2022emergentbrown2020gpt3raffel2020t5

  3. (3)前一轮的代码产出、环境反馈、执行中的错误以及批评意见,GPT-4 依据这些信息进行自我优化(详见第 2.3 节);

  4. (4)智能体目前的状态,包括所持物品、装备、周围的方块和生物、生物群系、时间、生命值和饥饿值,以及位置信息;(5)在生成代码之前进行的连贯思考提示 wei2022chain,助力逻辑推理过程。

我们采用了一种创新的迭代提示机制,不断优化程序(详见第 2.3 节),并将其作为新技能纳入技能库中,通过其描述内容的嵌入方式进行归档(参见 图 4 上图)。在技能检索过程中,我们将自我生成的任务计划和环境反馈的嵌入数据作为查询依据(参见 图 4 下图)。通过不停地扩充和改进技能库,Voyager 能在各种各样的任务中持续学习和适应,不断提高其在不断变化的世界中的能力边界。

2.3 迭代式提示机制

参照图片说明
参照图片说明

图 5: 左侧:环境反馈。GPT-4 认识到在制造木棒之前需要额外的 2 块木板。右侧:执行出错。GPT-4 明白它应该制作木斧,而不是不存在于 Minecraft 中的金合欢斧。这里为了简洁,我们只显示了部分提示内容。完整的编码生成提示结构见附录,第 A.4 节。

我们提出了一种循环反馈机制来实现自我改善,这涉及到以下三种反馈:

  1. (1) 环境反馈,它展示了程序执行的中间步骤(见图 5,左侧)。比如,提示“我无法制作铁胸甲,因为我还需要 7 个铁锭”,明确指出了造成制作失败的具体原因。我们在控制原语 API 中利用 bot.chat() 功能生成这种环境反馈,并且在编码过程中也鼓励 GPT-4 使用这一功能;

  2. (2) 执行错误,是由程序解释器反馈的,它揭示了代码中的任何非法操作或语法错误,对于调试和修正错误至关重要(见图 5,右侧);

  3. (3) 自我验证,用来确认任务是否成功完成。我们没有为自动化教学大纲中提出的每个新任务手动编写成功检查器,而是创建了另一个 GPT-4 智能体来进行自我验证。我们将 Voyager 的当前状态和待完成的任务提供给 GPT-4,让它充当评判者 mnih2016asynchronousschulman2017proximallillicrap2016continuous,并反馈程序是否达成了目标。此外,如果任务失败,它还会提供建议,指出如何完成任务(见图 6)。因此,我们的自我验证方式比单纯的自我反省 shinn2023reflexion 要全面得多,它不仅检查是否成功,还会对错误进行反思。

在每一轮的代码生成中,我们会运行所编写的程序,收集代码解释器提供的环境反馈和执行中的错误信息,然后把这些信息反馈进 GPT-4 下一轮代码改进的提示中。这样的迭代过程会不断重复,直到一个自我验证的机制确认了任务已经完成,接着我们便将这项新技能加入到技能库,并且向自动课程系统申请下一个学习目标(见图 2)。如果智能体在进行了四轮代码生成之后仍然无法解决问题,我们就会向课程系统寻求另一个任务。这种迭代式的提示流程显著提升了用于实体控制的程序合成效果,让 Voyager 无需外部干预,就能不断学习到各种各样的新技能。

请参考图片说明
请参考图片说明

图 6: 自我验证的例子。这里只显示了部分提示语以便简洁。完整的提示结构请参考附录,第 A.5 节。

3 实验

3.1 实验设置

我们使用了 OpenAI 提供的 gpt-4-0314 openai2023gpt4 和 gpt-3.5-turbo-0301 chatgpt API 来完成文本生成,以及 text-embedding-ada-002 embedding API 来进行文本嵌入处理。所有实验中的“温度”参数我们都设为 0,唯独自动课程表的温度设为 0.1,以此来促进任务的多样化。我们的模拟环境是建立在 MineDojo fan2022minedojo 之上的,并且使用 Mineflayer mineflayer 的 JavaScript API 来控制移动。更多的实验细节,可以查看附录中的第 B.1 节。

3.2 对照组

鉴于市面上还没有能够直接应用于 Minecraft 的 LLM(大语言模型)智能体,我们精心挑选了几种有代表性的算法作为我们研究的对照基准。这些算法最初是为了非实体化的自然语言处理(NLP)任务设计的,我们不得不重新将其解读,以便它们能在 MineDojo 环境中运行,同时兼容我们的实验环境:

ReAct yao2022react 运用链式思维提示 wei2022chain 的技术,利用 LLM 生成推理路径和行动计划。我们向其提供环境反馈和智能体的状态信息作为观察数据。

Reflexion shinn2023reflexion 在 ReAct yao2022react 的基础上增加了自省功能,以便更直观地推断未来的动作。我们还提供了执行错误数据和自身的验证模块以供其使用。

AutoGPT autogpt 是一个广受欢迎的软件工具,它将一个高层次的目标分解为多个子目标,并在类似 ReAct 的循环中执行这些子目标。我们使用 GPT-4 重新实现了 AutoGPT 的任务分解功能,并且为其提供智能体状态、环境反馈和执行错误等观察数据以辅助子目标的实施。与 Voyager 相比,AutoGPT 不具备积累知识的技能库、用于评估任务成败的自我验证机制,以及用于无限探索的自动课程编排。

需要指出的是,我们并没有直接与那些以 Minecraft 屏幕像素作为输入,生成底层控制指令的早期方法进行比较 nottingham2023embodied; cai2023openworld; wang2023describe,因为这不是一种公平的比较。我们所依赖的是高级 Mineflayer mineflayer API 来指挥智能体,我们研究的重点是探索 GPT-4 在终身学习的实体智能体领域的潜能,而非解决三维感知或传感器 - 运动控制的难题。Voyager 方法是独立的,可以与如 VPT openai2022vpt 这样的基于梯度的方法结合,只要控制器能够提供代码接口。

3.3 评测成果

我们对 Voyager 以及其他基准模型进行了全方位的评测,考察了它们在探险效能、科技发展树熟练度、地图覆盖面,以及在全新世界对未知任务的快速适应能力。

探险能力突出。Voyager 在探险方面的卓越表现如图 1 所示。在完成 160 轮指令迭代后,它能发现 63 种不同的项目,是其它同类系统发现新项目数量的 3.3 倍。相比之下,AutoGPT 在新发现方面明显落后,而 ReAct 和 Reflexion 在面对如此抽象的开放式探索任务时也显得力不从心,因为没有合适的学习路线图,这样的任务难以取得实质进展。

科技树精通度持续提升。在 Minecraft 的科技树中,评估了智能体制作和使用不同级别工具的能力。智能体需要依次掌握木工具、石工具、铁工具、钻石工具的制作技能,这考验了它们的系统性和综合性技能。相较于基准模型,Voyager 解锁木级工具的速度是其它模型的 15.3 倍,石级是 8.5 倍,铁级是 6.4 倍,并且 Voyager 是唯一能解锁钻石级别的模型(参见图 2 和表 1)。这突显了自动学习路线图的有效性,它能够持续提供适当难度的挑战,帮助智能体不断进步。

地图探索范围广泛。Voyager 能在多样的地形中行进更远的距离,是基准模型的 2.3 倍。而基准模型的智能体们往往只局限于一个小区域,这大大限制了它们探索新知识的能力(见图 7)。

表 1: 科技树精通度。分数显示的是三次运行中成功解锁的次数。0/3 表示在最多 160 轮指令迭代内,模型未能解锁科技树的某一级别。数字是三次运行的平均指令迭代次数,迭代次数越少表示方法越有效。

方法木制工具石制工具铁制工具钻石工具
ReAct yao2022react不适用(0/3)不适用(0/3)不适用(0/3)不适用(0/3)
Reflexion shinn2023reflexion不适用(0/3)不适用(0/3)不适用(0/3)不适用(0/3)
AutoGPT autogpt92±72(满分)94±72(满分)135±103(满分)不适用(0/3)
Voyager 不带技能库7 ± 29 ± 429 ± 11不适用 (0/3)
Voyager(我们的)6 ± 2 (3/3)11 ± 2 (3/3)21 ± 7 (3/3)102 (1/3)

请参阅图片说明
请参阅图片说明

图 7: 地图展示:从空中俯瞰 Minecraft 的地图。Voyager 在穿越各种地形时,能够行进的距离是传统基准的 2.3×2.3\times 倍。

高效应对未知任务的即时泛化能力。为了测试这种即时泛化能力,我们将智能体的物品栏清空,并将其放置于一个全新的世界中,以此来检验其处理从未见过的任务的能力。不论是 Voyager 还是 AutoGPT,我们都依靠 GPT-4 把任务细分为若干个子目标。表 2 和图 8 显示 Voyager 能够稳定解决所有新任务,而其他基线方法则无法在 50 轮提示迭代中完成任何一个任务。有趣的是,我们通过终身学习构建的技能库不仅增强了 Voyager 的能力,也对 AutoGPT 产生了正面影响。这证明了技能库是一个多用途的工具,其他方法可以轻松应用,有效地作为提高性能的加强插件。

表 2: 对未知任务的即时泛化能力。分数指的是三次尝试中成功的次数。0/3 意味着该方法在最大迭代次数(50 次提示)内未能完成任务。这里的数字表示平均三次尝试中的迭代次数,迭代次数越少,方法越显高效。

方法钻石镐金剑熔岩桶指南针
ReAct yao2022react不适用  (\nicefrac03)(\nicefrac{0}{3})不适用  (\nicefrac03)(\nicefrac{0}{3})不适用  (\nicefrac03)(\nicefrac{0}{3})不适用  (\nicefrac03)(\nicefrac{0}{3})
Reflexion shinn2023reflexion不适用  (\nicefrac03)(\nicefrac{0}{3})不适用  (\nicefrac03)(\nicefrac{0}{3})不适用  (\nicefrac03)(\nicefrac{0}{3})不适用  (\nicefrac03)(\nicefrac{0}{3})
AutoGPT autogpt不适用  (\nicefrac03)(\nicefrac{0}{3})不适用  (\nicefrac03)(\nicefrac{0}{3})不适用  (\nicefrac03)(\nicefrac{0}{3})不适用  (\nicefrac03)(\nicefrac{0}{3})
AutoGPT autogpt 结合我们的技能库39 (\nicefrac13)(\nicefrac{1}{3})30 (\nicefrac13)(\nicefrac{1}{3})不适用  (\nicefrac03)(\nicefrac{0}{3})30 (\nicefrac23)(\nicefrac{2}{3})
Voyager  无技能库36 ( \nicefrac23 )( 30\pm 9 ) ( \nicefrac33 )( 27\pm 9 ) ( \nicefrac33 )( 26\pm 3 ) ( \nicefrac33 )
Voyager(我们的)19 ± 3 (满分 3/3)18 ± 7 (满分 3/3)21 ± 5 (满分 3/3)18 ± 2 (满分 3/3)

图 8: 在未曾遇见的任务上实现零次学习的泛化能力。本图展示了各种方法在两个任务中的执行过程。关于另外两个任务的详情,请参阅附录中的第 A.3 节。由于 ReAct 和 Reflexion 在这些任务上没有显著进展,所以没有在图中展示它们的进程。

3.4 实验探究

在 Voyager 的设计中,我们测试了 6 个关键元素(包括自动课程、技能库、环境反馈、执行错误、自我验证和用于代码生成的 GPT-4)对于探索任务效率的影响(详细信息请参见附录 B.3 节)。研究结果如图 9 所示,我们总结了几个主要发现:

  • •  自动规划学习课程对于智能体持续提升至关重要。若随机安排课程,发现的物品数量会骤降 9393%,原因在于一些任务如果不按正确顺序来,难度会大大增加。相比之下,手动设计的课程需要深厚的 Minecraft 游戏知识,且不能根据智能体的即时情况做出调整,因此在实验中表现不如自动课程。

  • •  缺乏技能库的 Voyager 在任务后期容易遇到发展瓶颈。这凸显了技能库对于 Voyager 的重要性,它不仅能够帮助构建更复杂的行为,还能通过在已有技能基础上增添新技能,不断拓展智能体的能力边界。

  • •  自我验证是所有反馈形式中最关键的一个环节。取消这一机制会导致发现的物品数量大幅下降(73-73%),自我验证对于决定何时着手新任务或者重新尝试未成功的任务发挥着决定性作用。

  • • 在代码生成方面,GPT-4 的表现远超 GPT-3.5,能够发现的独特物品数量多出 5.7×5.7\times,GPT-4 在编程能力上的巨大飞跃得到了本次发现的支持,这也与最近的研究结果相吻合 bubeck2023sparksliu2023summary

请参考图片说明
请参考图片说明

图 9: 左侧:针对自动课程、技能库和 GPT-4 进行的实验探究。其中 GPT-3.5 代表使用 GPT-3.5 来替代 GPT-4 进行代码生成。Voyager 在所有其他方案中的表现都是最优的,凸显了每个环节的重要性。右侧:对迭代式提示机制进行的实验探究。Voyager 在各项比较中表现最佳,突显了迭代式提示机制中各类反馈的必要性。

请参考图片说明
请参考图片说明

图 10: Voyager 在人类的指导下构建 3D 结构。图中从左至右显示了结合了人类反馈的建筑设计过程。

3.5 人类的多模态反馈

Voyager 目前尚不能支持视觉感知,因为本文撰写之时,可用的 GPT-4 版本仅限于文本处理。尽管如此,Voyager 可以通过整合多模态感知模型 liu2023prismerdriess2023palme 来完成更加复杂的任务。我们已经证明了在人类反馈的帮助下,Voyager 能够在 Minecraft 中创建出复杂的三维结构,如地狱门和住宅(参见图 10)。整合人类反馈主要有两种途径:

  1. (1)将人作为评审(相当于 Voyager 的自我验证模块):人类向 Voyager 提出视觉上的建议,帮助其修改前一次的编码。这种反馈对 Voyager 来说至关重要,它可以借此更正那些自身无法直接察觉的三维结构空间细节错误。

  2. (2)将人作为教程(相当于 Voyager 的自动课程模块):人类把复杂的建筑任务拆解成小步骤,引导 Voyager 一步步地完成。这种方法能显著提升 Voyager 应对复杂三维建筑任务的能力。

4 局限性与未来展望

成本。使用 GPT-4 API 相当昂贵,其成本比 GPT-3.5 高出了 15×15\times。然而,Voyager 项目依赖于 GPT-4 在代码生成质量上的巨大进步(参见图 9),这是 GPT-3.5 和开源的大语言模型所不能达到的 touvron2023llama

误差。即使加入了迭代式提示机制,仍然有时智能体程序无法正确生成所需的技能。不过,自动课程安排能够灵活地选择稍后重新尝试。有时,自我校验模块也可能出错,例如没能正确识别 spider string 作为击败蜘蛛的成功标志。

错觉。自动课程有时会提出一些根本无法完成的任务。比如,它可能会指示智能体去打造一个“铜剑”或“铜胸甲”,这些在游戏中根本不存在的物品。在代码生成过程中也会出现错觉,例如 GPT-4 有时会错误地使用鹅卵石作为燃料,尽管它在游戏里并不是有效的燃料。它还可能调用不存在于控制原语 API 中的函数,导致代码执行时出错。

我们相信,随着 GPT API 模型的持续改进和开源大语言模型微调技术的创新,这些问题将在不久的将来得到解决。

5 相关研究

Minecraft 中的智能决策体。

Minecraft 是一个自由度极高的三维世界,它灵活的游戏机制支持玩家进行各种各样的活动。依托于一系列著名的 Minecraft 基准测试 fan2022minedojo; guss2019minerl; guss2019minerl2; guss2021minerl; kanervisto2022minerl; johnson16malmo,Minecraft 的学习算法被分为两大阵营:1) 基础控制器:很多研究通过分层强化学习,借鉴人类示范进行学习 lin2021juewumc; mao2021seihai; skrynnik2021hierarchical。Kanitscheider 等人 kanitscheider2021multitask 设计了基于成功率的训练课程,但它只限于特定的项目。MineDojo fan2022minedojo 和 VPT openai2022vpt 则是利用 YouTube 视频进行大规模的预训练。而 DreamerV3 hafner2023mastering 则通过学习世界模型,去探索环境并搜集钻石。2) 高阶规划器:Volum 等人 volum2022craft 尝试用 Codex openai2021codex 进行小规模的提示以产生可执行的策略,尽管它们还需要人类的额外参与。近期的研究则是将 LLMs 应用于 Minecraft 的高阶规划中,通过将一个复杂任务分解为多个子目标,并按照 Minecraft 的配方执行 wang2023describe; nottingham2023embodied,这样做虽然在一定程度上牺牲了探索的自由度。像这些研究一样,Voyager 也应用了 LLMs 作为高阶规划器,通过激发 GPT-4 的潜能,并结合使用 Mineflayer mineflayer 作为基础控制器,继承了 Volum 等人的做法 volum2022craft。不过与之前的研究不同,Voyager 开发了一种自动化的学习课程,这种课程是由好奇心推动的自下而上的发展方式,为游戏世界的自由探索提供了可能。

利用大语言模型进行智能体规划。

在大语言模型(LLMs)展示出如单次指令完成任务(zero-shot prompting)和复杂推理等惊人能力的激发下bommasani2021opportunities; brown2020gpt3; raffel2020t5; wei2022emergent; chowdhery2022palm; chung2022flan,具身智能体技术的研究领域duan2022survey; batra2020rearrangement; ravichandar2020recent; collins2021review 也因应用这些模型来进行规划任务而迎来了发展高峰。这些新近的研究大致上可以分为两大类:1) 应用于机器人学习的大语言模型:一些早期研究将 LLMs 用来产生机器人规划的子目标huang2022language; huang2022language; ahn2022saycan。例如,“内部独白”项目(Inner Monologue)(huang2022inner,) 把环境反馈融合到 LLMs 驱动的机器人规划中。“代码即策略”(Code as Policies)liang2022code 和 ProgPrompt singh2022progprompt 项目直接运用 LLMs 制定可实行的机器人策略。而 VIMA jiang2022vima 和 PaLM-E driess2023palme 则是通过调优预先训练好的 LLMs 来适应多种形式的提示需求。2) 用于文本智能体的大语言模型:ReAct yao2022react 项目通过“连锁思维提示”(chain-of-thought prompting)wei2022chain 来一并生成推理过程和针对特定任务的行动方案。而 Reflexion shinn2023reflexion 项目则在 ReAct yao2022react 的基础上加入了自我反思机制来提升推理能力。AutoGPT autogpt 则是一个受欢迎的自动化自然语言处理任务工具,它设计了一系列的子目标教学大纲来帮助实现更宏大的目标,并融入了 ReAct yao2022react 的推理和行动反馈循环。

DERA nair2023dera 把任务设定为两个 GPT-4 openai2023gpt4 智能体之间的对话。Generative Agents park2023generative 利用 ChatGPT chatgpt 来模拟人的行为,通过保存智能体的经历作为记忆并利用这些记忆来规划未来的行动,不过这些智能体的动作还不能实际执行。这些研究都还没有包含一个技能库,而这样的库对于构建更加复杂的行为至关重要,对 Voyager 这类致力于终生学习的系统而言尤其如此。

代码生成与执行。

在自然语言处理领域,代码生成一直是个老大难问题 openai2021codex; nijkamp2022conversational; le2022coderl; chowdhery2022palm; brown2020gpt3,众多研究通过执行结果来提升程序合成的能力。有的方法通过执行过程中产生的中间结果来指导编程搜索 chen2019execution; chen2021latent; ellis2019write,另一些研究则通过多数投票来选出表现最佳的代码候选 li2022competitionlevel; cobbe2021training。LEVER [ni2023lever](#bib.bib88] 则通过训练一个验证器来识别并排除执行结果显示有误的程序。而 CLAIRIFY skreta2023errors 则生成代码以规划化学实验,并运用一套基于规则的验证器来对 LLM 进行错误反馈和迭代学习。Voyager 则通过一个迭代式的提示机制,结合环境反馈、执行中的错误以及自我验证(来评估任务是否成功),在体现控制方面与众不同。

6 结论

我们在这项工作中介绍的 Voyager,是首个利用 LLM 技术的体现终身学习智能体。它通过 GPT-4 来不断探索这个世界,发展越来越复杂的技能,并在没有人类干预的情况下持续不断地取得新的发现。Voyager 在探索新物品、解开 Minecraft 技术之谜、穿梭各种地形,以及在全新的世界中应对前所未有的任务上表现出卓越的能力。Voyager 的问世,标志着我们向开发出不需要调整模型参数的强大全能智能体迈出了坚实的一步。

7 致谢

在此,我们对 Ziming Zhu, Kaiyu Yang, Rafał Kocielnik, Colin White, Or Sharir, Sahin Lale, De-An Huang, Jean Kossaifi, Yuncong Yang, Charles Zhang, Bochao Huang 等许多同事和朋友的宝贵建议及深入讨论表示衷心的感谢。这项研究成果是 Guanzhi Wang 在 NVIDIA 实习期间所完成的。同时,Guanzhi Wang 的研究也得到了 Caltech Kortschak 计算与数学科学奖学金的支持。

附录 A 方法

A.1 Voyager 算法解析

伪代码 1: 实现 Voyager 算法的步骤。

def voyager(
environment, # 这是一个环境,它将代码作为执行动作的空间
curriculum_agent, # 课程代理,它的职责是规划接下来的学习任务
action_agent, # 行动代理,主要负责编写代码
critic_agent, # 批评代理,负责自我检查与反馈
skill_manager, # 技能管理器,用于增加新技能和回顾已有技能
):
agent_state = environment.reset() # 初始化代理状态
while True: # 持续进行任务直到达成目标
# 获取代理的探索进展,包括已完成和失败的任务
exploration_progress = (
curriculum_agent.get_exploration_progress(
curriculum_agent.get_completed_tasks(),
curriculum_agent.get_failed_tasks(),
)
)
# 代理根据当前状态和进展情况提出下一个任务
task = curriculum_agent.propose_next_task(
agent_state, exploration_progress
)
# 初始化变量
code = None
environment_feedback = None
execution_errors = None
critique = None
success = False
# 对每个任务最多进行 4 次尝试,如果不成功则继续下一个任务
for i in range(4):
# 根据任务和环境反馈获取技能
skills = skill_manager.retrieve_skills(
task, environment_feedback
)
# 生成代码,包括新任务、旧代码、环境反馈、执行错误和批判性意见
code = action_agent.generate_code(
task,
code,
environment_feedback,
execution_errors,
critique,
skills,
)
# 执行代码并获取新的状态、环境反馈和执行错误
(
agent_state,
environment_feedback,
execution_errors,
) = environment.step(code)
# 检查任务是否成功,并得到批评代理的反馈
success, critique = critic_agent.check_task_success(
task, agent_state
)
if success: # 如果成功,跳出循环
break
if success: # 如果任务成功,添加新技能并记录已完成任务
skill_manager.add_skill(code)
curriculum_agent.add_completed_task(task)
else: # 如果任务失败,记录失败任务
curriculum_agent.add_failed_task(task)

A.2 提示方式

在 GPT-4 和 GPT-3.5 中,用户可以根据三种不同的角色来设定每条提示信息:

  • •  系统:这是一个高层次的指令,用于在整个对话过程中指导模型的行为。它确定了交流的整体风格和目标。

  • •  用户:这是一个具体的指令,用于指引助手生成下一个直接的响应。

  • •  助理:这是由模型生成的响应消息。

更多信息,请参阅 https://platform.openai.com/docs/guides/chat/introduction。

为了减少代币(令牌)的使用,我们通常不进行多轮对话,而是将系统提示和用户提示组合在一起,以此来得到助手的每一个回应。

A.3 自动课程体系

A.3.1 提示中的各个组件

向 GPT-4 提出的指示由若干部分构成:

  1. (1) 指令旨在激发不同的行为并设置限制(确保所提任务既可行又可检验):完整的提示请参阅 A.3.4 节;

  2. (2) 智能体目前的状态包括:

    • • 库存:一个记录物品及其数量的清单,比如,{'cobblestone': 4, 'furnace': 1, 'stone_pickaxe': 1, 'oak_planks': 7, 'dirt': 6, 'wooden_pickaxe': 1, 'crafting_table': 1, 'raw_iron': 4, 'coal': 1}
    • • 装备:智能体所穿戴的防护装备或武器;
    • • 附近的方块:在智能体周围 32 个方块内的方块名称,如 'dirt', 'water', 'spruce_planks', 'grass_block', 'dirt_path', 'sugar_cane', 'fern';
    • • 最近看到的其他方块:不在智能体附近或库存中的方块;
    • • 附近的生物:在智能体周围 32 个方块内的生物名称,例如 'pig', 'cat', 'villager', 'zombie';
    • • 智能体见到的箱子列表:箱子是智能体可以放置物品的地方。如果某个箱子之前未开启,其内容标记为“未知”。否则,每个箱子里的物品都会展示给智能体;
    • • 生物群落:例如 'plains', 'flower_forest', 'meadow', 'river', 'beach', 'forest', 'snowy_slopes', 'frozen_peaks', 'old_growth_birch_forest', 'ocean', 'sunflower_plains', 'stony_shore';
    • • 时间:可能是 'sunrise', 'day', 'noon', 'sunset', 'night', 'midnight' 中的任何一个;
    • • 生命值与饥饿指示条:最大值均为 20;
    • • 位置:智能体在 Minecraft 游戏世界中的三维坐标 (x,y,z)(x,y,z)
  3. (3) 已经完成和未完成的任务记录;

  4. (4) 更多的背景信息:详见 A.3.2 节;

  5. (5) 在对话链式思考过程中  [47],我们邀请 GPT-4 对目前的进度进行分析,并提出接下来可能的任务方向。

A.3.2 补充背景

我们使用 GPT-3.5 向自己提问,以此来丰富对话的背景信息。每个问题都与一个概念相关联,通过这个概念我们能够从 wiki 知识库检索到最相关的文献  [23]。然后我们将这些文献内容输入给 GPT-3.5,让它自己来找答案。实践中,虽然可以不依赖 wiki 知识库,因为 GPT-3.5 对《Minecraft》的游戏机制已经有了深入的了解,但如果它没有在特定的领域得到预先的训练,那么外部的知识库就显得尤为重要了。

A.3.3 热身阶段

在实际操作中,我们会根据智能体完成的任务数量来逐步启动一个热身阶段,将智能体的状态和补充的背景信息融入到提示中。这样可以确保随着探索进度的增加,提示内容也会包含越来越多的信息,智能体因此能够从掌握基础技能逐步过渡到学会更复杂、更多样的技能。我们在所有实验中都使用了以下的热身设置,具体展示在表 A.1 中。

表 A.1:自动课程表的热身计划。

提示包含的信息完成任务数量要求
核心库存(仅包含原木、木板、棍子、工作台、炉子、泥土、煤、镐、剑和斧头)0
装备工具0
周围的方块0
当前位置0
附近的实体5
全部库存7
近期看到的其它方块10
生物群系10
生命值指示条15
饥饿度指示条15
当前游戏时间15
额外的背景信息15

A.3.4 完整提示

提示 1: 针对自动课程系统,这是一个全面的引导提示。所列的问题和答案对为你提供了附加信息。

作为一个助手,我的职责是指导你在《Minecraft》中接下来应当采取的具体行动。你的终极梦想是探索尽可能多的新奇事物,完成各种各样的任务,努力成为世界上最出色的《Minecraft》玩家。

以下是你提供的信息:

问题 1: …

回答:…

问题 2: …

回答:…

问题 3: …

回答:…

...

生物群系:…

时间:…

附近的方块:…

最近见到的其他方块:…

附近的生物(由近及远排列): …

健康指数:高于 15 表示我身体状况良好。

饥饿指数:高于 15 表示我并不感到饥饿。

我的位置:…

我的装备:如果我背包里有更优质的盔甲,你应该提醒我换上它。

背包空间(xx/36): …

箱子:你可以建议我往箱子里存放或取出物品。偶尔会遇到未知的箱子,需要你提醒我去打开它,查看里面有什么物品。

迄今为止我完成的任务:…

因难度太高而失败的任务:…

在指导我进行下一步行动时,请遵循以下准则:

  1. 作为我的导师,根据我当前的学习进展,为我规划下一步的任务。

  2. 请具体指导我需要搜集哪些资源、制作什么工具或武器,或者击败哪些怪物。

  3. 下一步的行动应该简明扼要,比如:“挖掘 [数量] [方块]”,“制作 [数量] [物品]”,“熔炼 [数量] [物品]”,“击败 [数量] [生物]”,“烹饪 [数量] [食物]”,“装备 [物品]”等。确保是一条简单的指令,避免一次性布置多项任务或提及无关内容。

  4. 下一项任务不应过于复杂,考虑到我可能尚未拥有完成它所需的资源或技能。

  5. 下一项任务应该具有新颖性和趣味性,我需要去寻找罕见的资源,用更优质的材料升级我的装备和工具,探索未知的事物,而非重复单调的活动。

  1. 如果为了完成更高难度的任务而需要更多资源,我可能不得不重复某些任务。但这种重复只能在确实必要时才进行。
  1. 不要建议我搭建或挖掘避难所,即使夜幕已至。我渴望探索这个世界,发现新奇的事物,而不是局限于一个固定的地方。

  2. 应避免那些需要超出玩家当前状态验证信息的任务。比如,“放置 4 个火把”或“挖一个 2x1x2 的洞”就不太适合,因为完成这些任务需要在屏幕上进行视觉的确认。所有涉及放置、建造、种植、交易的任务都应该避免提出,尤其是以这些动作为开头的指令。

回应格式:

推理:根据我之前列出的信息,进行逻辑推导,确定下一个任务应当是什么。

任务:接下来的行动计划。

这儿有一个回答示例:

解释:库存空无一物,砍伐一棵树可以获取一些木材。

任务:获取一根木头原木。

提示 2: 完整系统提示,用于提问辅导。我们提供了优秀和不够好的示例作为少数示例。

你是一个帮忙的助手,你的提问将帮助我决定在《Minecraft》游戏中接下来应立即执行的任务。我的终极目标是尽可能多地探索,完成尽可能多的任务,成为世界上最出色的 Minecraft 玩家。

我会向你提供以下信息:

生态群系:...

时间:...

附近的方块:...

最近看到的其他方块:...

附近的实体(由近及远):...

健康状况:...

饥饿度:...

位置:...

装备:...

库存(xx/36):...

箱子:...

迄今为止完成的任务:...

失败了的、过于困难的任务:...

你必须遵循以下准则:

  1. 你需要至少提出 5 个问题(但不超过 10 个问题)来帮助我决定接下来立即要做的任务。每个问题后应该附带该问题所涉及的概念。

  2. 你的问题应该针对 Minecraft 中的具体概念。

不佳示例(问题过于宽泛):

问题:Minecraft 最佳游玩策略是什么?

概念:未知

不佳示例(斧头的范畴还是太宽泛,你应当具体到某种类型的斧头,比如木斧):

问题:使用斧子采集资源有哪些好处?

概念:斧子

佳例:

问题:如何制作一把木镐?

概念:木镐

  1. 你提出的问题应该是独立的,不需要任何背景知识。

以下是一些不推荐的提问示例,因为这些问题需要特定情境的背景信息:

问题:我现在所在的生物群落能找到哪些方块?

概念:未知

不推荐的提问(需要了解我的当前库存情况):

问题:目前你最缺的资源是什么?

概念:未知

问题:你有金子或者绿宝石资源吗?

概念:金子

不推荐的提问(需要知道我周围的实体情况):

问题:附近有没有可以捕猎吃的动物?

概念:食物

问题:附近有水源吗?

概念:水

以下是一个推荐的提问示例:

问题:稀疏丛林里,我能找到哪些方块?

概念:稀疏丛林

4) 请不要询问关于建筑任务的问题(比如建造避难所),因为这对我来说太复杂了。

比如说,如果你现在处在一个稀疏丛林生物群落,你可以这样提问:

问题:稀疏丛林里我能找到哪些物品?

概念:稀疏丛林

问题:稀疏丛林里有哪些怪物?

概念:稀疏丛林

比如说,你附近有一个爬行者,而你之前没有打败过它。你可以这样提问:

问题:如何打败爬行者?

概念:爬行者

如果你最后完成的任务是“制作一把木质镐”,你可以这样提问:

问题:制作完木镐后,有哪些建议的活动可以进行?

概念:木镐

这里还有一些其他的问题和概念示例:

问题:在稀疏丛林里,我能找到哪些种类的矿石?

概念:稀疏丛林

(上述概念不应该是“矿石”,因为我需要翻阅“稀疏丛林”条目来确定在稀疏丛林里能发现哪些矿石)

问题:在稀疏丛林中,如何获取食物?

概念:稀疏丛林

(上述概念不应该是“食物”,因为我需要查找“稀疏丛林”条目来了解在稀疏丛林中能得到哪些食物)

问题:如何使用熔炉来升级装备和制作有用物品?

概念:熔炉

问题:怎样才能找到钻石矿石?

概念:钻石矿石

问题:相比木镐,石镐有哪些优势?

概念:石镐

问题:使用木板和棒子能制作哪些工具?

概念:木板

回答问题时,请严格遵守以下的回答格式:

回答格式:

推理:…

问题 1:…

概念 1:…

问题 2:…

概念 2:…

问题 3:…

概念 3:…

问题 4:…

概念 4:…

问题 5:…

概念 5:…

提示 3:完整的系统提示用于回答问题。上下文是指可选的来自维基知识库的内容。

作为一个助手,你需要回答关于《Minecraft》的问题。

我会向你提供以下信息:

问题:…

根据提供的上下文(如果可用并且有帮助的话)以及你自身对《Minecraft》的了解来回答问题。

1) 以 "回答:"开头。

2) 如果不知道答案,请回答 "回答:未知"。

A.4Skill Library

A.4.1 提示中的组成部分

GPT-4 接收的输入提示由以下几个关键部分构成:

  1. (1) 代码生成指南:详细内容见 A.4.2 节,提供了完整的提示信息;

  2. (2) 我们开发的控制基础 API:这些 API 有两个作用:首先,它们示范了如何使用 Mineflayer 的 API;其次,GPT-4 可以直接调用这些接口进行操作。

    • •  exploreUntil(bot, direction, maxTime = 60, callback):指导智能体朝一个方向探索,直到 maxTime 时间。callback 是智能体设定的一个终止条件,用来判断探索何时结束;

    • •  mineBlock(bot, name, count = 1):在 32 方块的范围内,挖掘并收集特定数量的方块;

    • •  craftItem(bot, name, count = 1):在工作台附近制造指定的物品;

    • •  placeItem(bot, name, position):把方块放置在特定的位置;

    • •  smeltItem(bot, itemName, fuelName, count = 1):利用指定的燃料熔炼物品。必须有熔炉在附近;

    • •  killMob(bot, mobName, timeout = 300):攻击生物并收集它们掉落的物品;

    • •  getItemFromChest(bot, chestPosition, itemsToGet):走到特定位置的箱子那里,取出里面的物品;

    • •  depositItemIntoChest(bot, chestPosition, itemsToDeposit):把物品存入位于特定位置的箱子里;

  3. (3) Mineflayer 提供的控制基础 API:

    • •  await bot.pathfinder.goto(goal):导航至特定的位置。设置目标的具体方法见后文说明;

    • •  new GoalNear(x, y, z, range):引导机器人移动至指定方块周围的特定范围内;

    • •  new GoalXZ(x, z):用于设定没有特定垂直位置(Y 轴)要求的远程目标;

    • •  new GoalGetToBlock(x, y, z):目的不是进入某个方块,而是到达其旁边。这在钓鱼、耕作、装桶和使用床时非常有用;

    • •  new GoalFollow(entity, range):在一定范围内跟随指定的实体移动;

    • •  new GoalPlaceBlock(position, bot.world, ):摆放机器人以便在特定位置放置方块;

    • •  new GoalLookAtBlock(position, bot.world, ):规划路径至某个位置,使得该位置的方块可以被直接看到;

  • •  bot.isABed(bedBlock): 如果 bedBlock 是一个床,则返回 true;

  • •  bot.blockAt(position): 返回位于 position 位置的方块;

  • •  await bot.equip(item, destination): 装备指定位置的物品。destination 应是“hand”, “head”, “torso”, “legs”, “feet”, “off-hand”中的一个;

  • •  await bot.consume(): 使用 bot 手中的物品。在使用前,必须先装备该物品,适用于食物、药水等;

  • •  await bot.fish(): 开始让 bot 进行钓鱼。使用这个功能前,必须要找到水源并装备好钓鱼竿。bot 会在钓到鱼之后自动停止;

  • •  await bot.sleep(bedBlock): 睡到日出。首先需要找到一张床;

  • •  await bot.activateBlock(block): 等同于在游戏中对一个方块进行右键操作。适用于操作按钮、开关门等。必须要先走到方块旁边;

  • •  await bot.lookAt(position): 注视特定位置。在注视前,需要先走到那个位置附近。例如要装水进桶,你得先看准水;

  • •  await bot.activateItem(): 等同于用右键使用 bot 手中的物品。适用于使用桶等物品。使用之前,需要先装备该物品;

  • •  await bot.useOn(entity): 等同于在游戏中对一个实体进行右键操作。适用于比如给羊剪毛。必须先接近那个实体;

  1. (4) 从技能库中提取技能;

  2. (5) 编写上一轮的代码;

  3. (6) 环境反馈:提示框中的聊天记录;

  4. (7) 执行过程中的错误;

  5. (8) 自我验证模块的反馈意见;

  6. (9) 智能体当前的状态:详见 Sec. A.3.1 中关于智能体状态的每一项详细内容;

  7. (10) 自动课程安排的任务;

  8. (11) 任务上下文:我们让 GPT-3.5 提供解决任务的通用建议。通常这一部分由自动课程系统负责,它拥有一套系统性的答疑机制(见 Sec. A.3.2);

  9. (12)链式思考提示 [47] 应用实例:我们指导 GPT-4 首先阐释上一次尝试中代码未能成功的原因,接着提出一步步实现目标的方案,并在最后编写出代码。有关完整的提示内容,详见节 A.4.2

A.4.2 完整引导

引导 4: 完整的代码生成系统引导。

你是一个编写 Mineflayer javascript 代码的助手,能够帮我完成指定的任意 Minecraft 任务。
下面是一些利用 Mineflayer API 编写的实用程序示例。
/\*
探索直到找到铁矿石。由于铁矿石一般位于地下,因此使用 Vec3(0, -1, 0) 来定位。
await exploreUntil(bot, new Vec3(0, -1, 0), 60, () => {
const iron_ore = bot.findBlock({
matching: mcData.blocksByName["iron_ore"].id,
maxDistance: 32,
});
return iron_ore;
});
探索直到找到一头猪。由于猪通常在地面出没,所以使用 Vec3(1, 0, 1) 进行搜索。
let pig = await exploreUntil(bot, new Vec3(1, 0, 1), 60, () => {
const pig = bot.nearestEntity((entity) => {
return (
entity.name === "pig" &&
entity.position.distanceTo(bot.entity.position) < 32
);
});
return pig;
});
\*/
async exploreUntil(bot, direction, maxTime = 60, callback) {
/\*
此函数的具体实现在此省略。
direction: Vec3,其值只能是 -1, 01 中的一个。
maxTime: number,最长探索时间。
callback: function,提前终止探索的条件,每秒执行一次。如果该函数返回非空值,则停止探索。
返回值:如果探索超时则返回 null,否则返回 callback 函数的返回值。
\*/
}
// 挖掘 3 块鹅卵石:mineBlock(bot, "stone", 3);
async mineBlock(bot, name, count = 1) {
const blocks = bot.findBlocks({
matching: (block) => {
return block.name === name;
},
maxDistance: 32,
count: count,
});
// 在距离不超过 32 的范围内,采集数量为 count 的资源:
const targets = [];
for (let i = 0; i < Math.min(blocks.length, count); i++) {
targets.push(bot.blockAt(blocks[i]));
}
// 让机器人无视路径不可达的情况,收集所有目标方块
await bot.collectBlock.collect(targets, { ignoreNoPath: true });
}
// 利用 2 个橡木原木制作 8 个橡木木板(重复配方 2 次):craftItem(bot, "oak_planks", 2);
// 使用此功能前,你必须先放置一个工作台
async function craftItem(bot, name, count = 1) {
// 获取指定物品的信息
const item = mcData.itemsByName[name];
// 寻找最近的工作台
const craftingTable = bot.findBlock({
matching: mcData.blocksByName.crafting_table.id,
maxDistance: 32,
});
// 导航至工作台位置并进行制作
await bot.pathfinder.goto(
new GoalLookAtBlock(craftingTable.position, bot.world)
);
// 获取配方并进行制作
const recipe = bot.recipesFor(item.id, null, 1, craftingTable)[0];
await bot.craft(recipe, count, craftingTable);
}
// 在玩家旁边放置一个工作台,Vec3(1, 0, 0) 仅是一个例子,实际操作时不应总是使用这个坐标:placeItem(bot, "crafting_table", bot.entity.position.offset(1, 0, 0));
async function placeItem(bot, name, position) {
// 在背包中找到指定物品
const item = bot.inventory.findInventoryItem(mcData.itemsByName[name].id);
// 找到一个参考方块
const faceVectors = [
new Vec3(0, 1, 0),
new Vec3(0, -1, 0),
new Vec3(1, 0, 0),
new Vec3(-1, 0, 0),
new Vec3(0, 0, 1),
new Vec3(0, 0, -1),
];
let referenceBlock = null;
let faceVector = null;
// 循环检查各个方向的向量
for (const vector of faceVectors) {
const block = bot.blockAt(position.minus(vector));
if (block?.name !== "air") {
referenceBlock = block;
faceVector = vector;
break;
}
// 你必须首先走到你想要放置方块的位置
await bot.pathfinder.goto(new GoalPlaceBlock(position, bot.world, {}));
// 放置方块之前,你必须先装备好物品
await bot.equip(item, "hand");
await bot.placeBlock(referenceBlock, faceVector);
}
// 使用 1 个橡木木板作为燃料,将 1 个生铁熔炼成 1 个铁锭: smeltItem(bot, "raw_iron", "oak_planks");
// 在调用此函数之前,你必须放置一个熔炉
async function smeltItem(bot, itemName, fuelName, count = 1) {
const item = mcData.itemsByName[itemName];
const fuel = mcData.itemsByName[fuelName];
const furnaceBlock = bot.findBlock({
matching: mcData.blocksByName.furnace.id,
maxDistance: 32,
});
await bot.pathfinder.goto(
new GoalLookAtBlock(furnaceBlock.position, bot.world)
);
const furnace = await bot.openFurnace(furnaceBlock);
for (let i = 0; i < count; i++) {
await furnace.putFuel(fuel.id, null, 1);
await furnace.putInput(item.id, null, 1);
// 等待 12 秒钟让熔炉熔炼物品
await bot.waitForTicks(12 * 20);
await furnace.takeOutput();
}
await furnace.close();
}
// 杀死一只猪并收集掉落的物品: killMob(bot, "pig", 300);
async function killMob(bot, mobName, timeout = 300) {
const entity = bot.nearestEntity(
(entity) =>
entity.name === mobName &&
entity.position.distanceTo(bot.entity.position) < 32
);
await bot.pvp.attack(entity);
await bot.pathfinder.goto(
new GoalBlock(entity.position.x, entity.position.y, entity.position.z)
);
}
// 从位于 (30, 65, 100) 的箱子中获取火把:getItemFromChest(bot, new Vec3(30, 65, 100), {"torch": 1});
// 无论机器人与箱子的距离有多远,这个功能都能正常工作。
async function getItemFromChest(bot, chestPosition, itemsToGet) {
await moveToChest(bot, chestPosition);
const chestBlock = bot.blockAt(chestPosition);
const chest = await bot.openContainer(chestBlock);
for (const name in itemsToGet) {
const itemByName = mcData.itemsByName\[name];
const item = chest.findContainerItem(itemByName.id);
await chest.withdraw(item.type, null, itemsToGet\[name]);
}
await closeChest(bot, chestBlock);
}
// 将火把存放到位于 (30, 65, 100) 的箱子中:depositItemIntoChest(bot, new Vec3(30, 65, 100), {"torch": 1});
// 无论机器人与箱子的距离有多远,这个功能都能正常工作。
async function depositItemIntoChest(bot, chestPosition, itemsToDeposit) {
await moveToChest(bot, chestPosition);
const chestBlock = bot.blockAt(chestPosition);
const chest = await bot.openContainer(chestBlock);
for (const name in itemsToDeposit) {
const itemByName = mcData.itemsByName\[name];
const item = bot.inventory.findInventoryItem(itemByName.id);
await chest.deposit(item.type, null, itemsToDeposit\[name]);
}
await closeChest(bot, chestBlock);
}
// 检查位于 (30, 65, 100) 箱子中的物品:checkItemInsideChest(bot, new Vec3(30, 65, 100));
// 只需要调用一次这个函数,无需任何操作即可完成检查箱子内部物品的任务。
async function checkItemInsideChest(bot, chestPosition) {
await moveToChest(bot, chestPosition);
const chestBlock = bot.blockAt(chestPosition);
await bot.openContainer(chestBlock);
// 如果你被要求打开一个箱子,打开它之后必须关闭它
await closeChest(bot, chestBlock);
}
await bot.pathfinder.goto(goal); // 这是一个非常有用的功能。这个函数可能会改变你的主手装备。
// 以下是一些你可以使用的 Goals:
new GoalNear(x, y, z, range); // 将机器人移动到指定范围内指定方块的位置。‘x‘, ‘y‘, ‘z‘和‘range‘都是‘number‘类型
new GoalXZ(x, z); // 对于没有具体 Y 级别的长距离目标非常有用。‘x‘和‘z‘都是‘number‘类型
new GoalGetToBlock(x, y, z); // 不是进入方块,而是直接相邻。对于钓鱼、农业、装水桶和床非常有用。‘x‘, ‘y‘和‘z‘都是‘number‘类型
new GoalFollow(entity, range); // 跟随指定实体在指定范围内。‘entity‘是‘Entity‘类型,‘range‘是‘number‘类型
new GoalPlaceBlock(position, bot.world, {}); // 为了放置方块而定位机器人。‘position‘是‘Vec3‘类型
new GoalLookAtBlock(position, bot.world, {}); // 路径到一个位置,在那里可以看见位于‘position‘的方块的一个方块面。‘position‘是‘Vec3‘类型
// 这些是你可以使用的其他 Mineflayer 函数:
id:8kolr7ondalqmcxb
bot.isABed(bedBlock); // 如果‘bedBlock‘是床,返回真(true)
bot.blockAt(position); // 返回‘position‘位置的方块,其中‘position‘是‘Vec3‘坐标
// 以下是其他你可以使用的 Mineflayer async:
await bot.equip(item, destination); // 在指定的部位装备物品‘item‘,‘item‘为物品对象,‘destination‘可选项包括 "hand", "head", "torso", "legs", "feet", "off-hand"
await bot.consume(); // 吃掉或喝下机器人手上的物品。需先装备该物品,常用于进食或饮用
await bot.fish(); // 指令机器人开始钓鱼。开始此动作前,需确保机器人已经靠近水源并装备了钓鱼竿。钓到鱼后,机器人会自动停下
await bot.sleep(bedBlock); // 让机器人睡觉直到天亮。需确保机器人已经在床旁
await bot.activateBlock(block); // 相当于在游戏中对一个方块进行右键操作。用于操作按钮、开关门、用锄头等。操作之前需要站在方块旁
await bot.lookAt(position); // 让机器人注视指定坐标的位置,需要机器人先走到那个位置附近。比如要用桶装水,首先要执行 lookAt 动作。‘position‘是‘Vec3‘坐标
await bot.activateItem(); // 类似于使用机器人手中物品的右键功能。适用于用桶等物品时。使用之前需先装备该物品
通过 await bot.useOn(entity); 指令,可以实现类似于在游戏中右键点击一个实体的操作。这个功能很实用,比如用于给羊剪毛、给马匹装备马具等场景。在此之前,你需要首先走到该实体面前。
\{retrieved\_skills\}
在每轮对话中,我都会提供以下信息给你:
上一轮的代码: ...
执行出错: ...
聊天日志: ...
当前生物群系: ...
当前时间: ...
周围的方块: ...
周围的实体(由近至远):
健康状况: ...
饥饿值: ...
当前位置: ...
装备情况: ...
背包容量(xx/36): ...
宝箱情况: ...
当前任务: ...
场景上下文: ...
问题点评: ...
你需要据此回复我:
解释(如有必要): 你的方案中是否有遗漏的步骤?代码为何没有完成任务?聊天记录和执行错误又暗示了什么问题?
计划:详细阐述一步步如何完成任务。你需要重点关注背包情况,因为它会告诉你拥有哪些物品。最终的任务完成情况也将以你的背包内容为准。
编写代码:
1) 编写一个async,这个函数只接收 bot 作为参数。
2) 尽可能复用已有的实用程序代码。
- 用 'mineBlock(bot, name, count)' 来采集方块,避免直接使用 'bot.dig'。
- 用 'craftItem(bot, name, count)' 来制作物品,避免直接使用 'bot.craft'。
- 用 'smeltItem(bot, name, count)' 来冶炼物品,避免直接使用 'bot.openFurnace'。
- 用 'placeItem(bot, name, position)' 来放置方块,避免直接使用 'bot.placeBlock'。
- 用 'killMob(bot, name, timeout)' 来攻击怪物,避免直接使用 'bot.attack'。
3) 你设计的函数将用于打造更高级的程序。所以,务必使它通用且易于复用。别对存货(inventory)做太多设定,因为将来它可能会有变动。这就意味着,在动手用之前,得先确认下手头有没有需要的物品。如果发现缺货,就得先搜集齐全,再来利用前面那些实用的程序。
4) 在 “上一轮代码” 部分的函数,将不会被保留或运行。因此,列在那的函数请勿重新利用。
5) 函数外面定义的任何东西都会被无视,记得把所有变量都放在函数内定义。
6) 使用 ‘bot.chat‘ 来显示过程中的各个阶段。
7) 当你找不到某物品时,用 ‘exploreUntil(bot, direction, maxDistance, callback)‘ 来探索。在挖掘或打怪之前,常常要调用这个函数。而且每次探索的方向要随机选择,不要老是用同一个方向 (1, 0, 1)。
8) 无论是用 ‘bot.findBlocks‘ 还是 ‘bot.findBlock‘,‘maxDistance‘ 的值都应该固定是 32,切忌作弊。
9) 别写出无限循环或是递归函数。
10) 不要用 ‘bot.on‘ 或 ‘bot.once‘ 来设定事件监听器,你其实根本不需要这些。
11) 给你的函数起一个好名字,能够让人一看名字就知道它的用途。
回答时,请严格按照下述格式:
回答格式:
解释: …
计划:
1\) …
2\) …
3\) …
代码:
\`\`\`javascript
// 如果确实需要,才添加辅助函数(但尽量少用)
// 在所有辅助函数之后,编写你的主要函数
async function yourMainFunctionName(bot) {
// …
}
\`\`\`

提示 5: 为编写功能描述的完整系统提示。当你需要为技能库新增一项技能时,你会用到这个提示。我们提供了一个示例来展示如何使用。

你是一个编写 Mineflayer JavaScript 代码功能描述的助手。

1) 请勿提及功能名称。

2) 切勿提及任何有关‘bot.chat’或辅助功能的信息。

3) 主功能之前可能会有一些辅助功能,但你只需描述主功能。

4) 努力用不超过 6 句话来概括这个功能。

5) 你的回答应当是一行文本。

例如,对于以下功能:

async mineCobblestone(bot) {
// 检查库存中是否有木制镐,如果没有,先制作一个
let woodenPickaxe = bot.inventory.findInventoryItem(mcData.itemsByName\["wooden_pickaxe"].id);
if (!woodenPickaxe) {
bot.chat("Crafting a wooden pickaxe.");
await craftWoodenPickaxe(bot);
woodenPickaxe = bot.inventory.findInventoryItem(mcData.itemsByName\["wooden_pickaxe"].id);
}
// 如果找到木制镐,则装备它
if (woodenPickaxe) {
await bot.equip(woodenPickaxe, "hand");
// 探索直到找到石头方块
await exploreUntil(bot, new Vec3(1, -1, 1), 60, () => {
const stone = bot.findBlock({
matching: mcData.blocksByName\["stone"].id,
maxDistance: 32
});
if (stone) {
return true;
}
});
// 使用木镐开采 8 个石头方块
bot.chat("Found a stone block. Mining 8 cobblestone blocks.");
await mineBlock(bot, "stone", 8);
bot.chat("成功挖到了 8 块圆石。");
// 记录挖到 8 块圆石的情况
bot.save("cobblestone\_mined");
} 否则 {
bot.chat("没能成功做出木头镐,挖不了圆石。");
}
}

核心的功能是‘mineCobblestone‘。

接下来你可以这么描述:

这个功能的核心是用木头镐去挖掘 8 块圆石。先检查一下我们的工具箱里有没有木头镐。如果没有,我们就得先做一个。如果已经有了木头镐,就拿在手里准备使用。然后在周围环境中寻找,直到发现石头。找到石头后,就用木头镐挖取,直到集齐 8 块圆石。

A.4.3 示例

技能库示例 1:制作木板。

async function craftWoodenPlanks(bot) {
// 木头日志数组
const logNames = [
"oak_log",
"birch_log",
"spruce_log",
"jungle_log",
"acacia_log",
"dark_oak_log",
"mangrove_log",
];
// 木板名称数组
const plankNames = [
"oak_planks",
"birch_planks",
"spruce_planks",
"jungle_planks",
"acacia_planks",
"dark_oak_planks",
"mangrove_planks",
];
// 检查库存中是否有木头
const logInInventory = logNames.find(
(logName) => bot.inventory.count(mcData.itemsByName[logName].id) > 0
);
// 如果没有木头,先去挖
if (!logInInventory) {
bot.chat("库存中没有木头日志。正在采集木头日志...");
await mineWoodLog(bot);
}
// 获取木头对应的木板名称
const logIndex = logNames.indexOf(logInInventory);
const plankName = plankNames[logIndex];
// 开始制作木板
bot.chat(`制作 4 个 ${plankName}...`);
await craftItem(bot, plankName, 1);
bot.chat(`已制作 4 个 ${plankName}`);
}

技能库示例 2:在 Y=0 以下挖掘十块圆石深板岩。

async function mineTenCobbledDeepslateBelowY0(bot) {
// 首先,装备铁镐
const ironPickaxe = bot.inventory.findInventoryItem(mcData.itemsByName["iron_pickaxe"].id);
await bot.equip(ironPickaxe, "hand");
// 寻找 Y=0 以下的圆石深板岩方块
const cobbledDeepslateBlocks = await exploreUntil(bot, new Vec3(1, -1, 1), 60, () => {
const cobbledDeepslate = bot.findBlock({
matching: mcData.blocksByName["cobbled_deepslate"].id,
maxDistance: 32,
position: pos => pos.y < 0
});
return cobbledDeepslate;
});
// 接下来,挖掘 10 块圆石深板岩
等待挖掘方块(bot, "cobbled_deepslate", 10;
bot.chat("在 Y=0 以下挖掘了 10 块 cobbled_deepslate。");
}
const water = bot.findBlock({
matching: mcData.blocksByName.water.id,
maxDistance: 32
});
return water;
});
if (!waterBlock) {
bot.chat("Could not find water.");
return;
}
// Go to the water block
const adjacentBlock= waterBlock.position.offset(0, 1, 0);
await bot.pathfinder.goto(
new GoalGetToBlock(adjacentBlock.x, adjacentBlock.y, adjacentBlock.z)
);
// Look at the water block
await bot.lookAt(waterBlock.position, true);
// Equip the bucket
const bucket = bot.inventory.findInventoryItem(mcData.itemsByName.bucket.id);
await bot.equip(bucket, "hand");
// Activate the bucket to collect water
await bot.activateItem();
bot.chat("Filled the bucket with water.");
}
Skill library example 5: catchFiveFishSafely.
async function catchFiveFishSafely(bot) {
// Check if the bot has a fishing rod in its inventory
let fishingRod = bot.inventory.findInventoryItem(mcData.itemsByName.fishing_rod.id);
if (!fishingRod) {
await craftFishingRod(bot);
fishingRod = bot.inventory.findInventoryItem(mcData.itemsByName.fishing_rod.id);
}
// Find a nearby water block
let waterBlock;
while (!waterBlock) {
waterBlock = await exploreUntil(bot, new Vec3(1, 0, 1), 60, () => {
const foundWaterBlock = bot.findBlock({
matching: mcData.blocksByName.water.id,
maxDistance: 32
});
return foundWaterBlock;
});
if (!waterBlock) {
bot.chat("No path to the water block. Trying to find another water block...");
}
}
// Move to a block adjacent to the water block
const adjacentBlock = waterBlock.position.offset(0, 1, 0);

技能库示例 3:smeltFiveRawIronV2。

async findSuitablePosition(bot) {
偏移量为 [new Vec3(1, 0, 0), new Vec3(-1, 0, 0), new Vec3(0, 0, 1), new Vec3(0, 0, -1)];
对于偏移量中的每一个 {
定位为 bot.entity.position.offset(offset.x, offset.y, offset.z);
块为 bot.blockAt(定位);
如果 块.name === "air" {
返回 定位;
}
}
返回 null;
}
async smeltFiveRawIron(bot) {
// 检查背包中是否有熔炉
熔炉物品为 bot.inventory.findInventoryItem(mcData.itemsByName.furnace.id);
// 如果没有,使用现有的圆石来制作熔炉
如果 (!熔炉物品) {
等待 craftFurnace(bot);
}
// 找到一个适合放置熔炉的位置
熔炉位置为等待 findSuitablePosition(bot);
如果 (!熔炉位置) {
bot.chat("找不到适合放置熔炉的位置。");
返回;
}
// 在合适的位置放置熔炉
等待 placeItem(bot, "furnace", 熔炉位置);
// 使用可用的煤作为燃料,熔炼 5 块生铁
等待 smeltItem(bot, "raw_iron", "coal", 5);
bot.chat("已熔炼 5 块生铁。");
}
技能库示例 4:fillBucketWithWater。
async fillBucketWithWater(bot) {
// 寻找附近的水块
await bot.pathfinder.goto(new GoalBlock(adjacentBlock.x, adjacentBlock.y, adjacentBlock.z));
// 观察水块
await bot.lookAt(waterBlock.position);
// 装备钓鱼竿
await bot.equip(fishingRod, "hand");
// 在水中钓鱼 5 次
for (let i = 0; i < 5; i++) {
try {
await bot.fish();
bot.chat(‘Fish ${+ 1} caught.);
} catch (error) {
if (error.message === "Fishing cancelled") {
bot.chat("Fishing was cancelled. Trying again...");
i--; // 重试相同的迭代
} else {
throw error;
}
}
}

A.5 自我检验

A.5.1 提示的组成部分

向 GPT-4 提供的输入包含以下几个部分:

  1. (1) 代理状态:我们不考虑代理最近看到的其他方块和附近的实体,因为它们对完成任务的评估没有帮助。每个代理状态的元素详见第 [A.3.1](#A1

.SS3.SSS1) 节;

  1. (2) 自动课程提出的任务;

  2. (3) 任务背景:我们让 GPT-3.5 提出解决任务的通用建议。实际操作中,这一部分由自动课程负责,因为它有一套系统的问答机制(详见第 A.3.2 节);

  3. (4) 思维链式提示回应 [47]:我们让 GPT-4 初步推理任务的成败,随后输出一个表征任务成败的布尔值,并且在任务失败时给予代理反馈和批评。

  4. (5) 为了实现情境学习而提供的少数示例 [363738]。

A.5.2 完整提示

提示 6: 用于自我检验的详细系统说明。

作为一个助手,我的任务是评估你在 Minecraft 游戏中的进展,并提供实用的建议。

你需要根据以下信息判断我是否完成了任务要求。超出任务要求也算成功,如果没能达到要求,你需要给出指导意见帮我提高。

信息如下:

生物群系(Biome): 完成任务后所处的生物群落类型。

时间(Time): 当前的游戏时间。

附近的方块(Nearby blocks): 我周围的方块。这些方块还没有被收集,但对某些放置或种植任务很有帮助。

健康值(Health): 我目前的生命值。

饥饿值(Hunger): 我目前的饥饿程度。如果进食任务中我的饥饿值达到了 20.0,那么表示我成功吃到了食物。

位置(Position): 我当前在游戏中的坐标。

装备(Equipment): 我任务结束时的装备。在制作任务中,我有时会使用我制作的物品。

库存(Inventory (xx/36)): 我最终的物品栏情况。对于挖矿和熔炼任务,你只需检查我的库存。

箱子(Chests): 如果任务要求我把物品放进箱子,这里会提供箱子的信息。

任务(Task): 我需要达成的目标。

上下文(Context): 任务进行的背景信息。

你需要以下面的 JSON 格式给出响应:

{
"reasoning": "推理",
"success": 布尔值,
"critique": "批评",
}

确保这个响应可以被 Python 的‘json.loads’正确解析,比如:不要有多余的逗号,不要用单引号等。

这里有一些例子:

输入:

库存(Inventory (2/36)): {'oak_log':2, 'spruce_log':2}

任务(Task): 挖掘 3 块木头

响应:

{
"推理": "你需要采集三块木头。你的背包里有两块橡树木和两块云杉木,加起来总共四块木头哦。",
"结果": "成功",
"点评": ""
}

输入:

背包 (3/36): {'工作台': 1, '云杉木板': 6, '棍子': 4}

任务:制作一把木镐

回应:

{
"推理": "看起来你的材料足够制作一把木镐,但你似乎忘了真正去做这件事。",
"结果": "失败",
"点评": "用工作台、三块云杉木板和两根棍子来制作一把木镐吧。"
}

输入:

背包 (2/36): {'生铁': 5, '石镐': 1}

任务:采集 5 块铁矿石

回应:

{
"推理": "在《我的世界》里,挖铁矿石会得到生铁。你的背包里已经有五块生铁了。",
"结果": "成功",
"点评": ""
}

输入:

生物群落:平原

周围方块:石头,泥土,草地,草,农田,小麦

背包 (26/36): …

任务:种下一颗小麦种子。

回应:

{
"推理": "说到种植,背包里有什么其实并不重要。看周围,有农田和小麦,说明你已经成功种下了小麦种子。",
"结果": "成功",
"点评": ""
}

输入:

背包 (11/36): …,'腐肉': 1}

任务:杀死一个僵尸

上下文:…

回应:

{
"推理": "背包里有腐肉,看来你确实干掉了一只僵尸。",
"结果": "成功",
"点评": ""
}

输入:

饥饿度:20.0/20.0

背包 (11/36): …

任务:吃掉一个 …

上下文:…

回应

{
"reasoning": "在我们的进食任务中,如果玩家的饥饿值达到了满分 20.0,那么这顿大餐可以说是相当成功了。",
"success": true,
"critique": ""
}

输入:

周围环境:箱子

背包(28/36 个空位):{‘铁轨’: 1, ‘煤炭’: 2, ‘橡木板’: 13, ‘铜块’: 1, ‘闪长岩’: 7, ‘熟牛肉’: 4, ‘花岗岩’: 22, ‘深板岩圆石’: 23, ‘羽毛’: 4, ‘皮革’: 2, ‘熟鸡肉’: 3, ‘白羊毛’: 2, ‘木棍’: 3, ‘黑羊毛’: 1, ‘石剑’: 2, ‘石锄’: 1, ‘石斧’: 2, ‘石铲’: 2, ‘熟羊肉’: 4, ‘圆石墙’: 18, ‘工作台’: 1, ‘熔炉’: 1, ‘铁镐’: 1, ‘石镐’: 1, ‘生铜’: 12}

箱子:

(81, 131, 16):{‘安山岩’: 2, ‘泥土’: 2, ‘圆石’: 75, ‘木镐’: 1, ‘木剑’: 1}

任务:把不需要的物品存放到(81, 131, 16)坐标的箱子里

上下文:…

RESPONSE

{
"reasoning": "在你把物品放入箱子后,你的背包里还有 28 件物品,超出了 20 件的理想状态。你需要继续清理背包,把更多物品放入箱子。",
"success": false,
"critique": "建议你继续清理那些铜块、闪长岩、花岗岩、深板岩圆石、羽毛和皮革等不太有用的物品,以确保你的背包里只占用了 20 个空位。"
}

附录 B 实验

B.1 实验环境搭建

我们基于 MineDojo [23] 打造了一个模拟环境,并结合 Mineflayer [53] 提供的 JavaScript API 来控制机器人动作(详见 A.4.2 节)。在 Mineflayer 的功能中,我们增加了大量的 bot.chat() 功能,这样可以获得更多环境的反馈,并通过多种条件检测以及异常处理保证程序可以不间断运行。如果机器人不慎“挂掉”,它会自动在最近的地面点重生,并且它的物品栏会被完好地保存下来,这样就能够无缝地继续探险。每次探险结束后,机器人还会聪明地回收它的工作台和炉子。想了解更多技术细节,不妨瞧瞧我们的代码库。

B.2 比较基准

ReAct [29] 利用链式提示 [47] 的方式,结合大语言模型(LLMs),既生成推理过程,也规划行动步骤。我们为它提供了模拟环境中的反馈和代理的状态作为输入信息。ReAct 会先进行一轮全新的代码编写,接着是三次代码优化过程,这个循环会一直重复,直到触发最大次数的提示。

Reflexion [30] 在 ReAct [29] 的基础上增加了自我反省机制,这样能够更直观地预测和规划未来的动作。我们不仅给它提供了环境的反馈,代理的状态,执行过程中出现的错误,还有我们自己开发的自我验证模块。和 ReAct 类似,Reflexion 也是一轮全新的编码,然后是三轮的代码改进,如此循环,直到达到最大的提示次数。

AutoGPT [28] 是一个广受欢迎的软件工具,它能够自动处理自然语言处理(NLP)任务,方法是把一个复杂的目标拆分成多个小目标,并像 ReAct 那样循环执行。我们用 GPT-4 重新实现了 AutoGPT,它会对任务进行分解,并且根据代理的状态、环境反馈和执行中的错误进行子目标的执行。相较于 Voyager,AutoGPT 缺少一个积累知识的技能库,没有自我验证机制来确认任务是否成功,也没有自动化的课程设计来引导开放式的探索。在执行每个小目标时,如果没有错误发生,我们就认为该目标已完成,然后进行下一个;如果出错了,则会对程序进行三轮的优化(相当于进行四轮的编码),之后再继续下一个小目标。如果连续三个小目标都没有获得新的物品,我们就会重新分析任务,并再次进行任务分解。

对于所有的基准测试来说,任务目标都是一样的:“探索这个世界,尽可能多地收集物品”。

B.3 消融实验

在 Voyager 的实验中,我们选择了 6 个关键设计来进行单独测试:自动课程、技能库、环境反馈、执行错误、自我检验和利用 GPT-4 进行代码生成,分析它们各自对探索任务性能的具体影响。

  • •  手动课程:我们尝试用人工制定的教程来替换自动化的学习流程,比如挖矿任务就设计成:先挖 3 根木头,再造 1 个工作台,然后是 1 个木镐,接着挖 11 块圆石,制作 1 个石镐,再来 1 个熔炉,挖 3 块铁矿,熔炼这些铁矿,造 1 个铁镐,最后挖到 1 颗钻石。这种手动课程需要人力去精心设计,面对不断变化的探索任务就显得不够灵活了。

  • •  随机课程:我们从 Voyager 收集到的 101 个项目中随机挑选项目,组成一个随机任务序列。

  • •  不带技能库:在这个设置中,我们取消了技能库的使用,这意味着代码生成时无法回溯查找以前的技能。

  • • 不带环境反馈:这里不考虑环境反馈(例如聊天记录),让代码生成时没有了这些线索。

  • • 不带执行错误提示:生成代码时,不会考虑之前的执行错误,代码生成就缺少了这种错误指导。

  • • 不带自我验证:我们试着在每个任务中生成代码,但不进行自我验证,只是简单地连续修改三次代码(相当于总共进行了四轮代码生成)。

  • • 使用 GPT-3.5: 我们在代码生成部分使用 GPT-3.5 替代 GPT-4,但在自动课程设计和自我验证环节,依然保持使用 GPT-4。

B.4.1 探索效果显著提升

请参阅图片说明
请参阅图片说明

图 A.1: 《Minecraft》游戏中物品的图标和相应的名称。

1 中的每个图标代表什么,图 A.1 中都有标示。

我们对每种方法进行了三轮测试。在每轮测试中,Voyager 收集到的物品包括:

  • •  第 1 轮:‘iron_ingot’(铁锭), ‘stone_shovel’(石铲), ‘iron_leggings’(铁护腿), ‘fishing_rod’(钓鱼竿), ‘pufferfish’(河豚), ‘oak_log’(橡木原木), ‘cooked_mutton’(熟羊肉), ‘green_dye’(绿色染料), ‘flint’(燧石), ‘chest’(箱子), ‘iron_sword’(铁剑), ‘string’(线), ‘ender_pearl’(末影珍珠), ‘raw_copper’(生铜), ‘crafting_table’(工作台), ‘cactus’(仙人掌), ‘lapis_lazuli’(青金石), ‘iron_pickaxe’(铁镐), ‘copper_ingot’(铜锭), ‘stone_pickaxe’(石镐), ‘wooden_hoe’(木锄), ‘scaffolding’(脚手架), ‘stick’(木棍), ‘porkchop’(猪排), ‘copper_block’(铜块), ‘gravel’(砂砾), ‘grass_block’(草方块), ‘white_bed’(白色床), ‘bone’(骨头), ‘dirt’(泥土), ‘mutton’(羊肉), ‘white_wool’(白羊毛), ‘oak_sapling’(橡树苗), ‘coal’(煤炭), ‘bamboo’(竹子), ‘wooden_pickaxe’(木镐), ‘rotten_flesh’(腐肉), ‘cooked_porkchop’(熟猪排), ‘cod’(鳕鱼), ‘iron_boots’(铁靴), ‘lightning_rod’(避雷针), ‘diorite’(闪长岩), ‘water_bucket’(水桶), ‘shears’(剪刀), ‘furnace’(熔炉), ‘andesite’(安山岩), ‘granite’(花岗岩), ‘bucket’(桶), ‘wooden_sword’(木剑), ‘sandstone’(砂岩), ‘iron_helmet’(铁头盔), ‘raw_iron’(生铁), ‘sand’(沙子), ‘acacia_log’(金合欢木原木), ‘cooked_cod’(熟鳕鱼), ‘oak_planks’(橡木板), ‘azure_bluet’(蓝色美云), ‘iron_shovel’(铁铲), ‘acacia_planks’(金合欢木板), ‘shield’(盾牌), ‘iron_axe’(铁斧), ‘iron_chestplate’(铁胸甲), ‘cobblestone’(鹅卵石);

  • • 第二轮试验包括了:铁锭‘iron_ingot’、凝灰岩‘tuff’、石铲‘stone_shovel’、铁护腿‘iron_leggings’、钓鱼竿‘fishing_rod’、熟羊肉‘cooked_mutton’、云杉木板‘spruce_planks’、火药‘gunpowder’、紫晶碎片‘amethyst_shard’、箱子‘chest’、线‘string’、熟鲑鱼‘cooked_salmon’、铁剑‘iron_sword’、生铜‘raw_copper’、工作台‘crafting_table’、火把‘torch’、青金石‘lapis_lazuli’、铁镐‘iron_pickaxe’、铜锭‘copper_ingot’、石镐‘stone_pickaxe’、木锄‘wooden_hoe’、木棍‘stick’、紫晶块‘amethyst_block’、鲑鱼‘salmon’、方解石‘calcite’、砾石‘gravel’、白色床‘white_bed’、骨头‘bone’、泥土‘dirt’、羊肉‘mutton’、白色羊毛‘white_wool’、望远镜‘spyglass’、煤炭‘coal’、木镐‘wooden_pickaxe’、鳕鱼‘cod’、铁靴‘iron_boots’、睡莲叶‘lily_pad’、深板岩‘cobbled_deepslate’、避雷针‘lightning_rod’、雪球‘snowball’、石斧‘stone_axe’、平滑玄武岩‘smooth_basalt’、闪长岩‘diorite’、水桶‘water_bucket’、熔炉‘furnace’、安山岩‘andesite’、桶‘bucket’、花岗岩‘granite’、盾牌‘shield’、铁头盔‘iron_helmet’、生铁‘raw_iron’、鹅卵石‘cobblestone’、云杉原木‘spruce_log’、熟鳕鱼‘cooked_cod’、绊线钩‘tripwire_hook’、石锄‘stone_hoe’、铁胸甲‘iron_chestplate’、石剑‘stone_sword’;

  • • 第三轮:'spruce_planks', 'dirt', 'shield', 'redstone', 'clock', 'diamond_sword', 'iron_chestplate', 'stone_pickaxe', 'leather', 'string', 'chicken', 'chest', 'diorite', 'iron_leggings', 'black_wool', 'cobblestone_wall', 'cobblestone', 'cooked_chicken', 'feather', 'stone_sword', 'raw_gold', 'gravel', 'birch_planks', 'coal', 'cobbled_deepslate', 'oak_planks', 'iron_pickaxe', 'granite', 'tuff', 'crafting_table', 'iron_helmet', 'stone_hoe', 'iron_ingot', 'stone_axe', 'birch_boat', 'stick', 'sand', 'bone', 'raw_iron', 'beef', 'rail', 'oak_sapling', 'kelp', 'gold_ingot', 'birch_log', 'wheat_seeds', 'cooked_mutton', 'furnace', 'arrow', 'stone_shovel', 'white_wool', 'andesite', 'jungle_slab', 'mutton', 'iron_sword', 'copper_ingot', 'diamond', 'torch', 'oak_log', 'cooked_beef', 'copper_block', 'flint', 'bone_meal', 'raw_copper', 'wooden_pickaxe', 'iron_boots', 'wooden_sword'。

ReAct [29]  在各个试验中搜集到的物品包括:

  • • 第一轮:'bamboo', 'dirt', 'sand', 'wheat_seeds';

  • • 第二轮:'dirt', 'rabbit', 'spruce_log', 'spruce_sapling';

  • • 第三轮:'dirt', 'pointed_dripstone';

Reflexion [30]  每轮试验中搜集到的物品为:

  • • 第一轮:'crafting_table', 'orange_tulip', 'oak_planks', 'oak_log', 'dirt';

  • • 第二轮:'spruce_log', 'dirt', 'clay_ball', 'sand', 'gravel';

  • • 第三轮:'wheat_seeds', 'oak_log', 'dirt', 'birch_log', 'sand'.

AutoGPT [28]  在各轮试验中搜集到的物品则未列出完整。

  • • 实验 1: 包含了‘羽毛’、‘橡树原木’、‘皮革’、‘木棒’、‘猪肉排’、‘鸡肉’、‘合成台’、‘小麦种子’、‘橡木板材’、‘泥巴’和‘羊排’;

  • • 实验 2: 项目有‘木制镐’、‘铁锭’、‘石块’、‘煤炭’、‘云杉木板材’、‘细绳’、‘未加工铜’、‘合成台’、‘闪长石’、‘安山岩’、‘熔炉’、‘火炬’、‘云杉幼苗’、‘花岗石’、‘铁质镐’、‘石质镐’、‘木斧’、‘生铁块’、‘木棒’、‘云杉原木’、‘泥巴’和‘鹅卵石’;

  • • 实验 3: 列表中包含‘木铲’、‘木制镐’、‘铁锭’、‘石块’、‘鳕鱼’、‘煤炭’、‘橡树原木’、‘打火石’、‘未加工铜’、‘合成台’、‘闪长石’、‘熔炉’、‘安山岩’、‘火炬’、‘花岗石’、‘青金石’、‘铁质镐’、‘石质镐’、‘生铁块’、‘木棒’、‘碎石’、‘橡木板材’、‘泥巴’、‘铁斧’和‘鹅卵石’。

B.4.2 广阔地图的深度探索

图解参考
图解参考

图 A.2: 地图探索度:展示了 Minecraft 地图上的两个俯视图。Voyager 能够比基准模型穿越更远的距离 —— 达到了基准的 2.3×2.3\times,它穿梭于多种多样的地形之中。这些轨迹是根据各个代理在与 GPT-4 交互的位置来绘制的。

为了展示地图探索范围,我们在图 A.2 中展示了代理的行动轨迹。而图 7 则是在图 A.2 的基础上,绘制了能够围绕每条轨迹的最小圆形。Voyager 在每次实验中所经过的地形类型包括:

  • • 实验 1:包括‘草甸’、‘沙漠’、‘河流’、‘热带草原’、‘森林’、‘平原’、‘竹林’、‘钟乳石洞穴’;

  • • 实验 2:有‘雪地平原’、‘冰河’、‘钟乳石洞穴’、‘雪地针叶林’、‘沙滩’;

  • •  实验 3:则是‘花森林’、‘草甸’、‘古老桦木林’、‘雪坡’、‘冻顶’、‘森林’、‘河流’、‘沙滩’、‘大海’、‘向日葵平原’、‘平原’、‘石岸’。

而 ReAct [29] 在它的实验里走过的地形类型则是:

  • • 实验 1:‘平原’、‘沙漠’、‘丛林’;

  • • 实验 2:‘雪地平原’、‘雪地针叶林’、‘雪坡’;

  • • 实验 3:‘暗森林’、‘钟乳石洞穴’、‘小林’、‘陡峰’。

Reflexion [30] 在其探索实验中遇到的地形则包括:

  • • 实验 1:‘平原’、‘花森林’;

  • • 实验 2:‘雪地针叶林’;

  • • 实验 3:‘古老桦木林’、‘河流’、‘大海’、‘沙滩’、‘平原’。

AutoGPT [28] 的实验轨迹则穿越了:

  • • 实验 1:‘平原’、‘钟乳石洞穴’、‘热带草原’、‘草甸’;

  • • 试验 2:‘snowy_taiga’;

  • • 试验 3:‘plains’、‘stony_shore’、‘forest’、‘ocean’。

B.4.3 高效适应全新任务的零样本泛化能力

参考说明
参考说明

图 A.3:面向全新任务的零样本泛化展示。我们展示了各种方法在另外两项任务中的进展过程。因为 ReAct 和 Reflexion 在这些任务上没有取得任何实质性进展,所以没有将它们包含在图中。

A.3 展示了在另外两个全新任务上的零样本泛化结果。与图 8 的情况相似,Voyager 系统能够连续不断地完成所有任务,而其他基线系统在 50 轮提示迭代后仍未能完成任何任务。我们通过终身学习建立的技能库不仅提升了 Voyager 的表现,也增强了 AutoGPT 的性能 [28]。