Voyager:自动课程、技能库与可验证的开放式学习
原始来源
Guanzhi Wang 等,2023:Voyager: An Open-Ended Embodied Agent with Large Language Models,本页按 arXiv v2 阅读。
本页是原创学习导读,不含原文复刻;论文版权与完整内容以原始来源为准。
学完后你应该能做到什么
读完这一篇,你不只是能说出 Voyager 有“自动课程、技能库、迭代提示”三个模块,还应该能:
- 给定 Agent 的当前状态、成功史和失败史,解释自动课程为什么会提出某个下一任务。
- 从任务提出开始,完整追踪“检索技能—生成代码—执行—反馈—验证—入库”的状态变化。
- 区分环境反馈、程序执行错误和自我验证三类信号各自解决的问题。
- 说明技能库为什么不等于向量数据库,也不等于把全部对话历史保存下来。
- 说明论文证据只支持怎样的 Minecraft 高层控制场景,以及把它迁移到现实系统时还缺什么。
先把问题说清楚:没有终点的环境怎样学习
普通任务通常先给出一个清晰目标,例如“修复这个测试”或“订一张机票”。Minecraft 不一样:世界没有唯一的最终任务,Agent 可以采集、制作、探索或战斗。如果只给一句“尽可能多地发现东西”,模型很容易遇到三种问题:
- 目标过大:它不知道先砍树还是先找钻石,直接尝试高难任务会不断失败。
- 经验不累积:即使某一次成功制作木镐,下一次仍从头生成同一段行为。
- 失败不可诊断:代码没有语法错误,不代表任务完成;拿错材料、走不到目标或误判完成都会让循环失效。
Voyager 的核心不是“让 GPT-4 玩游戏”,而是给开放式学习补上三个工程闭环:谁决定下一步学什么、成功经验怎样变成可执行资产、环境如何证明这一步真的学会了。
先认识几个术语:
- 自动课程(automatic curriculum):根据能力边界动态提出下一项任务,不是预先写死的课程表。
- 技能(skill):论文中主要指一段经过验证的可执行 JavaScript 程序,能够完成跨多个时间步的行为。
- 技能库(skill library):保存技能描述、向量索引和程序本体的长期资产库。
- 自我验证(self-verification):由另一个 GPT-4 角色根据任务和当前状态判断是否成功,并在失败时给出批评。
- 开放式学习(open-ended learning):没有固定终点,系统持续发现新目标并扩展能力边界。
这里的“学习”不等于更新模型权重
Voyager 通过黑盒 API 调用 GPT-4,在提示上下文和外部技能库中积累能力。论文没有对 GPT-4 做梯度训练或参数微调,因此更准确的说法是“上下文中的终身学习”和“外部可执行记忆”。
机制契约:输入、输出和状态分别是什么
| 部分 | Voyager 中的内容 | 初学者要抓住的意义 |
|---|---|---|
| 输入 | 当前背包、装备、附近方块与实体、生物群系、时间、生命值、饥饿值、位置 | 课程和代码生成必须知道“现在有什么、身处哪里” |
| 历史 | 已完成任务、失败任务、探索进度 | 下一目标不能无视已经会什么、刚刚为什么失败 |
| 长期状态 | 经过验证的技能程序及其描述向量 | 成功经验跨任务保留,可以检索和组合 |
| 临时状态 | 当前目标、候选代码、环境反馈、解释器错误、验证批评、尝试轮次 | 一次任务内部不断更新的工作记忆 |
| 输出 | 新完成的目标、已验证程序、新的技能描述与索引 | 这轮学习产生可复用能力,而不只产生一段聊天 |
| 停止条件 | 验证成功,或最多四轮代码生成后仍失败 | 成功则入库并换目标;失败则暂时放弃,由课程以后再提 |
完整控制流如下:
flowchart TD S["读取世界状态、完成史与失败史"] --> C["Automatic Curriculum:提出下一目标"] C --> Q["根据目标、计划和环境反馈检索 Top-5 技能"] Q --> G["GPT-4 生成或修改可执行程序"] G --> X["通过 Mineflayer 高层 API 执行代码"] X --> F["收集环境反馈与解释器错误"] F --> V["Self-Verification:判断是否完成并给出批评"] V -->|"成功"| K["生成技能描述,程序写入技能库"] K --> S V -->|"失败且不足四轮"| G V -->|"四轮仍失败"| B["记录失败并暂时换任务"] B --> S
这张图有两个容易忽略的门:候选程序只有验证成功后才能入库;连续失败不会无限重试,而是把控制权交还给课程模块。
核心机制一:自动课程寻找“能力前沿”
自动课程不是随机任务生成器。论文把几个部分放入给 GPT-4 的课程提示:
- 总方向与约束,例如尽可能发现多样事物,同时不要提出当前明显做不到的任务。
- 当前世界状态,包括背包、装备、附近环境和生存状态。
- 已完成和失败任务,用来估计探索进度与能力边界。
- 由 GPT-3.5 围绕当前状态自问自答得到的补充背景;作者用较便宜的模型完成这类标准语言任务。
课程的目标可以理解成:选择一个有新颖性、比现有能力稍难、但由当前资源和已有技能可以够到的任务。太简单只会重复;太难会耗尽调用预算;完全随机则可能要求一个刚出生的角色直接取得钻石。
失败记录也不是永久黑名单。一个任务现在失败,可能只是缺少前置资源或技能。Voyager 在一项任务最多进行四轮代码生成;仍未成功时先换任务,自动课程可以在能力增长后重新提出它。这就是“失败—绕行—升级—再挑战”的开放式进展。
核心机制二:技能库保存的是可执行行为
每当程序通过自我验证,系统让 GPT-3.5 生成技能描述,以描述的 embedding 作为检索键,以程序本体作为值。新任务到来时,系统先生成一般性解决建议,再把任务计划和环境反馈组成查询,检索最相关的五个技能放入代码生成提示。
因此技能库有三层:
| 层 | 作用 | 不能替代什么 |
|---|---|---|
| 描述 | 说明技能做什么,帮助生成检索查询 | 不证明程序可运行 |
| 向量索引 | 从大量技能中找到语义相关候选 | 不负责执行,也不保证候选适用 |
| 程序本体 | 真正完成行为,可以被新程序调用 | 仍依赖当前环境、控制 API 和验证 |
论文选择代码作为动作空间,而不是让模型逐个输出“向前一步、点击一次”。一段程序可以包含循环、条件和函数调用,所以能表达跨时间的行为;更复杂的技能也可以调用较简单的技能。例如“制作铁镐”可以组合采木、制作工作台、制作石镐、采矿和冶炼等已有程序。
不过,所谓“可组合”不是魔法。组合后的程序仍需在当前世界执行并重新验证;旧技能可能因为资源、地形、API 或前置条件不同而失败。向量搜索找到的是候选,不是正确性证明。
核心机制三:三类反馈把生成变成可修正循环
Voyager 不要求模型一次写对代码。每一轮执行后,它收集三类性质不同的反馈:
- 环境反馈:程序执行到了哪里、还缺什么。例如“无法制作铁胸甲,因为还缺 7 个铁锭”。它回答的是“世界状态为什么没有达到目标”。
- 执行错误:解释器或 API 抛出的语法错误、无效操作和不存在的函数。它回答的是“程序为什么没有正确运行”。
- 自我验证与批评:另一个 GPT-4 根据任务和当前状态判断是否成功;若失败,再建议下一轮如何修改。它回答的是“即使程序跑完,任务是否真的完成”。
只看异常会漏掉“程序正常退出但什么也没完成”;只看最终背包又可能不知道代码在哪一步出错。三类信号共同构成了从症状到原因再到验收的链路。
自我验证不是客观真相
论文也记录了验证器漏判的例子,例如未把蜘蛛丝识别为击败蜘蛛的成功信号。生产系统应优先使用可执行断言、测试、结构化状态查询和必要的人工审批;模型判断只应覆盖难以程序化的部分。
走完一次:从“已有木镐”到掌握“制作石镐”
下面是为了教学简化的轨迹,不是论文中的逐字运行日志,但每一步都对应论文机制。
| 轮次 | 系统看到的状态 | 发生的动作 | 状态怎样变化 |
|---|---|---|---|
| 0 | 背包有木板和木镐;“采木”已完成;“直接找钻石”失败过 | 自动课程提出“制作一把石镐” | 建立当前目标,避免再次跳到钻石 |
| 1 | 技能库有 mineWoodLog、craftCraftingTable | 检索相关技能,模型生成采石并制作石镐的程序 | 得到候选程序,但尚未入库 |
| 1 执行 | 附近没有可直接开采的石头 | 程序运行,环境反馈显示没有取得足够圆石 | 无语法异常,但任务状态未完成 |
| 1 验证 | 背包里没有石镐 | 验证器判定失败,建议先寻找暴露的石块 | 形成下一轮批评 |
| 2 | 上一轮代码、环境反馈和批评都在提示中 | 程序加入探索和路径处理,再调用已有采集技能 | 取得圆石并完成制作 |
| 2 验证 | 背包出现石镐 | 验证器判定成功 | 生成通用描述并把程序写入技能库 |
| 下一轮 | 完成史增加“制作石镐”,库中多一个技能 | 自动课程可以提出“采集铁矿”等更高一级目标 | 能力前沿向外移动 |
关键不是第二轮代码写得多漂亮,而是失败信息被保留、验收发生在执行之后、成功程序进入长期记忆、课程再据此升级目标。
用伪代码把控制流钉死
completed, failed, skill_library = [], [], []
while exploration_budget_remaining():
state = observe_world()
task = curriculum.propose(state, completed, failed)
feedback = errors = critique = None
for attempt in range(4):
plan = make_general_plan(task, state, feedback)
skills = skill_library.retrieve(plan, feedback, top_k=5)
program = generate_program(
task=task,
state=state,
retrieved_skills=skills,
previous_feedback=feedback,
previous_errors=errors,
previous_critique=critique,
)
feedback, errors = execute_in_minecraft(program)
state = observe_world()
success, critique = verifier.check(task, state)
if success:
skill_library.add(describe(program), program)
completed.append(task)
break
else:
failed.append(task)这段伪代码刻意表现了两个状态层次:feedback/errors/critique 服务于当前任务的短循环;completed/failed/skill_library 跨任务保留,服务于长期探索。
证据该怎么看:支持机制,不等于证明通用自主性
论文在基于 MineDojo 的 Minecraft 环境中,用 Mineflayer JavaScript API 做高层控制,并将 ReAct、Reflexion 和 AutoGPT 改造到相同设置。作者报告:在最多 160 次提示迭代的探索实验中,Voyager 发现 63 种独特物品,是基线的 3.3 倍;行进距离是基线的 2.3 倍;若以提示迭代数衡量,若干技术树里程碑最快达到 15.3 倍加速。新世界、清空背包后的未见任务实验也显示技能库能够被复用。
消融结果更直接地支持机制:随机课程替代自动课程后,发现物品数下降 93%;移除自我验证后下降 73%;没有技能库的版本在后期更容易停滞。这里应读成“三个模块在作者设置中都贡献了长期进展”,不能读成某个百分比会在其他领域复现。
证据边界同样重要:
- 基线是为 Minecraft 重新解释和适配的文本 Agent,并非原封不动的通用实现。
- 实验通过高层 API 控制角色,不比较像素感知、3D 视觉或低层运动能力。
- 论文中的关键技术树结果只有少量重复试验,不能把单一游戏结果直接外推到机器人或企业系统。
- API 成本、模型版本和 Minecraft 环境都属于当时的实验条件,不是机制永久不变的性能承诺。
失败模式与调试顺序
| 失败现象 | 可能根因 | 应先检查什么 |
|---|---|---|
| 课程提出不存在的物品 | 模型幻觉、世界规则没有被充分约束 | 用能力目录或规则校验候选任务 |
| 程序反复调用不存在的函数 | 控制 API 描述不清或代码生成幻觉 | 暴露真实 schema,并把解释器错误原样回传 |
| 程序正常结束但目标未完成 | 只检查进程退出,没有检查世界状态 | 为任务建立明确成功断言 |
| 错误技能进入库后持续污染 | 验证器误判、入库门槛太低 | 重新验证、增加版本和回归样例、支持失效处理 |
| 总在简单任务中循环 | 课程没有新颖性或能力边界信息 | 检查完成史、失败史及难度策略 |
| 复杂技能调用旧技能后失败 | 前置条件变化、环境或 API 版本漂移 | 给技能记录适用条件、依赖和失败语义 |
历史贡献与今天的工程解释
论文在 2023 年直接提出并验证的贡献,是把自动课程、可执行技能库和带三类反馈的迭代提示组合成 Minecraft 中的开放式终身学习 Agent,而且无需修改黑盒 LLM 权重。
今天可以迁移的工程解释,是把长期 Agent 设计成两层循环:内层用真实执行反馈把一个任务做对,外层用能力状态选择下一项可达任务;只有通过验收的产物才能成为可复用资产。这可以映射到数据分析、代码维护或运维自动化,但映射不等于论文已经验证了这些领域。
现实系统还必须补上论文游戏环境弱化的部分:权限、不可逆操作审批、成本预算、并发状态、技能版本、依赖锁定、数据隐私和人工接管。尤其不能把“另一个模型说成功了”当成支付、删除或生产部署的唯一门禁。
自测:确认你真的掌握了机制
- 为什么自动课程必须同时看到当前状态、完成史和失败史?少任何一项分别会怎样?
- 一个技能的向量检索结果很相关,为什么仍不能直接执行并判定成功?
- 环境反馈、执行错误和自我验证分别能发现哪一类失败?请各举一例。
- Voyager 连续四轮没有完成当前任务后会怎样?为什么这比无限重试更适合开放式探索?
- 如果把 Voyager 思路用于代码 Agent,哪些部分可以直接映射,哪些安全条件必须新增?
参考答案与判断要点
- 当前状态决定可用资源,完成史避免重复并标示能力,失败史揭示当前边界;缺失它们会分别导致不可执行目标、重复学习或反复撞上同一失败。
- 相似只代表候选技能语义接近;当前环境、前置条件、依赖和版本仍可能不同,组合后必须重新执行与验证。
- 环境反馈发现“还缺材料”等世界进度问题;执行错误发现语法、API 或无效操作;自我验证发现“程序跑完但任务没完成”。
- 它记录失败并交回课程模块换任务,以后能力增长后可以再挑战,从而控制成本并避免局部死循环。
- 课程可映射为下一项可达 Issue,技能库可映射为经测试的脚本或修复模式,反馈可映射为测试与日志;还要新增仓库版本、沙箱、权限、审批、回滚和供应链约束。