代码作为技能的载体,技能库作为跨任务记忆——Voyager 在游戏里验证了这套思路

为什么选 Minecraft?

2023 年的 Voyager 论文(Wang et al., NeurIPS 2023)选择 Minecraft 作为实验场。Minecraft 有几个对 Agent 研究有价值的性质:

  • 开放世界:没有固定终点,Agent 需要自主设定目标
  • 层级任务:砍木头 → 做木板 → 做工作台 → 做石镐,技能之间有清晰的依赖链
  • 可执行反馈:每一步操作都有明确的成功或失败信号
  • API 可控:Mineflayer 提供 JavaScript API,Agent 的每个动作都是可执行的代码

这几个特性使得 Minecraft 成为研究终身学习 Agent 较合适的实验场。


系统架构:三个核心组件

Voyager 由三个模块协同工作:

┌─────────────────────────────────────────────────────┐
│                    Voyager                          │
│                                                     │
│  ┌─────────────┐  ┌──────────────┐  ┌───────────┐  │
│  │  自动课程   │  │  代码生成器  │  │  技能库   │  │
│  │  Curriculum │→ │  Code Gen    │→ │  Skill    │  │
│  │             │  │              │  │  Library  │  │
│  └─────────────┘  └──────────────┘  └───────────┘  │
│         ↑                │                 │        │
│         └────────────────┴─────────────────┘        │
│                    执行反馈                          │
└─────────────────────────────────────────────────────┘

1. 自动课程(Automatic Curriculum)

Agent 不等待人类给任务,它自己决定下一步要做什么。课程模块维护一个"任务状态图":

  • 已解锁的资源(背包、周围环境)
  • 已掌握的技能(技能库中的函数)
  • 当前游戏阶段

基于这些状态,它向 GPT-4 提问:"我现在有这些资源和技能,接下来应该探索什么目标?"GPT-4 给出建议,课程模块评估可行性后发起任务,形成一个自主探索循环,而不是被动执行预定脚本。

2. 代码生成器(Code-as-Action)

Voyager 的每个动作都是一段 JavaScript 函数,由 GPT-4 生成,通过 Mineflayer API 在游戏中执行:

// 由 GPT-4 生成的技能:挖取石头
async function mineStone(bot) {
  const stone = bot.findBlock({
    matching: mcData.blocksByName.stone.id,
    maxDistance: 32,
  });
  if (!stone) {
    throw new Error("找不到石头,需要先探索");
  }
  await bot.tool.equipForBlock(stone);
  const pos = stone.position;
  await bot.dig(stone);
  await bot.waitForTicks(5);
  console.log(`成功挖取石头,位置:${pos}`);
}

代码的优势在于:

  • 可组合craftWoodenPickaxe() 可以复用 collectWood()makeWorkbench()
  • 可验证:执行报错立刻反馈,GPT-4 看到错误信息可以修正
  • 可存储:函数是可持久化的技能单元

3. 技能库(Skill Library)

每次成功执行的代码函数,连同它的自然语言描述,都被存入技能库。技能库本质上是一个向量数据库:

# 技能存储结构
{
  "name": "mineStone",
  "description": "挖取石头,自动寻找最近的石块并使用合适的工具",
  "code": "async function mineStone(bot) { ... }",
  "embedding": [...],   # 用于语义检索
  "dependencies": ["equipTool", "findBlock"],
}

当 Agent 接到新任务时,它先检索技能库,把语义相关的历史技能放入 prompt 作为参考,然后生成新技能。新技能往往可以直接调用旧函数,技能随时间持续积累。


迭代调试:执行失败后重新生成

Voyager 的代码生成不是 one-shot 的。每次执行失败,Agent 收集错误信息,重新生成修正版本:

尝试 1:生成 mineIronOre()
  → 执行报错:"没有合适的镐子"

尝试 2:修正 → 先调用 craftStonePick(),再挖铁矿
  → 执行报错:"附近没有铁矿石"

尝试 3:修正 → 添加探索逻辑,扩大搜索范围后再挖矿
  → 执行成功,技能入库

这个模式和程序员写代码的过程类似:写→跑→看报错→改→再跑。每一轮的错误信息都是有效的监督信号,不需要额外的人工标注。


实验结果

论文在 Voyager 与 AutoGPT 等基线之间进行了对比:

指标VoyagerAutoGPT(基准)
解锁科技树进度3.3x1x
获得唯一物品数3.1x1x
地图探索距离2.1x1x

Voyager 积累的技能库有迁移能力:将技能库迁移到一个全新的世界,Agent 能更快达成目标。技能不随环境重置而清空,这是终身学习的核心价值所在。


Voyager 的几个设计原则

代码作为技能表示:自然语言描述的技能难以精确复用,而函数可以被调用、被组合、被验证,是 Agent 长期记忆较合适的载体。

执行错误作为训练信号:不需要人类标注"哪步错了",运行时的 traceback 本身就是精确的反馈。这是代码执行比纯文本推理的一个优势。

课程而非固定任务列表:固定任务列表会让 Agent 遇到分布外问题。自主生成课程才能支持开放域探索。

技能库作为长期记忆:上下文窗口是短期记忆,技能库才是跨会话、跨任务的积累,是 Agent 区别于单次 LLM 调用的地方之一。


与后续工作的关系

Voyager 之后,CodeAct(第 05 篇)把"代码作为动作空间"的思路推广到了通用 Agent;OpenDevin 和 SWE-agent 则把类似架构应用于真实软件工程任务。Voyager 的技能库设计,也影响了 Skill-in-Context、ExpeL 等记忆增强 Agent 的工作。

版权声明: 如无特别声明,本文版权归 sshipanoo 所有,转载请注明本文链接。

(采用 CC BY-NC-SA 4.0 许可协议进行授权)

本文标题:04. Voyager:用代码探索开放世界的终身学习 Agent

本文链接:https://www.sshipanoo.com/blog/ai/code-agent-harness/04-Voyager/

本文最后一次更新为 天前,文章中的某些内容可能已过时!