当前位置:AIGC资讯 > AIGC > 正文

【AIGC】AI-Agents最新成果-斯坦福AI小镇源码解读

写在前面的话:

今年年初斯坦福和谷歌的研究人员创建了一个类似于《模拟人生》的微型 RPG 虚拟世界,其中 25 个角色由 GPT 和自定义代码控制,并在arxiv上提交了论文版本,引起了对AIGC+游戏的广泛讨论;

8月,该项目在GitHub上正式开源,虽然新闻报道依旧很多,但对技术和实现方法的讨论比较少,截止目前国内网站只看到知乎上有一位大神的专栏有相关概念及生成流程的解读,本文也会引用该作者一些分析

本文将从架构和实现原理的角度探讨,项目是如何驱动 Agents 做出决策的,欢迎大家一起交流和学习

PS: 目前还有一个AI小镇Plus版本的AI-Town项目也在GitHub上开源了,其可扩展性更高,笔者有其他文章介绍该项目

文章目录

整体架构 运行方式 Web服务 Simulation Server 代码解析 Web服务 游戏引擎 AI Agent模块 LLM交互模块 收获与问题 收获 问题

整体架构

该项目的环境为 Python3.9.12,游戏逻辑由 H5 引擎 Phaser3 开发,后端用 Django 作为服务器提供 Web 服务

如下图所示,由于“游戏”本身并不存在用户操作,所以 H5 的页面仅供展示使用;

Main Loop 即驱动 Agents 做出决策的主循环,以时间为 step,通过用户命令行输入的 step 来决定进行多少步的运算(注意:这里前端并不是实时同步展示运算结果的),并将运算的结果发给 Web Server 再展示到前端;

整个架构的链路并不长,用户需要自行维护的东西也很少,将项目“跑”起来是件很容易的事情,但这也侧面说明了项目的可扩展性和自由度并不高

运行方式

Web服务

首先是一个基于 Django 的后台 Server,运行 environment/frontend_server 目录下的

python manage.py runserver

运行成功后记得设置在后台常驻,该进程为整个AI小镇的运行环境,如果关闭则无法运行后续步骤

该命令启动成功后,即可在浏览器中访问 Django 设置的网址,看到整个小镇的初始化界面

注意:打开该页面的TAB最好保持在桌面,不要最小化或者隐藏,如果检测到该TAB页不在active状态的话,会卡住后台计算的进程

Simulation Server

前面启动的Web服务实际上只提供了一个默认的沙盒环境,这个环境是静态的,当Agents的行动轨迹更新后才会进行展示,所以这一步启动的是产生并向智能体发送指令的程序,项目里将其称为 Simulation Server

运行该程序,需要在 reverie/backend_server 目录下运行

python reverie.py

该进程是阻塞式的,受用户输入驱动,一般分为两个步骤:

首先,需要选择一个预设的场景,比如官方给的例子,开启一个3个NPC的场景可以输入

base_the_ville_isabella_maria_klaus

然后需要输入一个自定义的名称,来唯一的代表本次模拟(注意该名称会作为该次模拟的所有文件的保存路径),该名称不能重名,后续也可以继承该次模拟的”记忆“

test-simulation // 可以任意取一个

其次,在选择了场景以后,就要开始生成NPC的行为,这一步也是阻塞的通过用户指令驱动,可以使用:

run 10 //模拟10步NPC行为

在运行该指令以后,注意,不会立刻让NPC动起来,程序会先调用OpenAI的接口制定一整天的行动计划,然后再分步模拟NPC的行为

经过一段时间后,NPC会根据指令进行相应的动作,这时如果想继续模拟,则继续输入上述指令进行模拟

最后,项目也提供了保存和回放模拟过程的功能,也可以继续上次进行的模拟,这里不做过多说明,下面主要来探讨整个项目是如何实现的

代码解析

Web服务

先来看看Web服务是如何基于Django搭建的,服务的启动入口文件是manage.py

目录是标准的Django目录结构,url配置位于frontend_server/settings/urls.py文件,对应的handler位于frontend_server/translator/views.py文件下

主要功能有

simulator_home(对应的handler是home) - 小镇主界面,实时更新NPC位置和动作,该函数的逻辑比较简单,分别读取storage目录下的最新NPC和环境文件并渲染到页面上
replay(对应的handler是replay)传递指定的sim_code,也就是一个用户指定的模拟id,用于回放对应的模拟过程

其余方法还包括官方的示例等等,这里不做赘述

游戏引擎

因为本质上来说,这是一款“游戏”,只是NPC的控制由 AI Agent 接管了,所以地图、NPC Controller的搭建都是基于 Phaser3 游戏引擎,其主要用于制作HTML5游戏,使用的语言为JS/TS,这里不对其用法做过多解释,如感兴趣可前往项目的 environment/frontend_server/templates 目录下查看游戏代码

游戏的地图、碰撞体都是通过配置文件预设的,不接受用户操作,因此游戏引擎也主要做展示层使用

AI Agent模块

最核心的 AI Agent 模块从主函数入手开始看起,入口文件在 reverie/reverie.py,其创建一个 ReverieServer 对象,并进行初始化操作,包括但不限于初始化Agent状态、地图、以及系统配置等然后调用 ReverieServer 的 open_server 方法,开启主循环

整个流程如图所示:

该模块的运行是由用户命令行驱动的,不会一次性进行一整个周期的模拟,而是根据用户输入的step数量决定跑多少时间(游戏时间)。这里有个很友好的点是,因为正常模式跑起来以后整个模块处于黑盒状态,并不能被干预,但其实看源码可以发现提供了各类 debug 模式供用户选择,可以看到中间状态及推演过程,具体命令可阅读 reverie.py 文件

主循环最重要的部分就是调用 agent.move() 函数以后,是如何让每个 Agent 做出相应的决策的,这里需要通过几个核心类(数据结构)来进行介绍:

Maze类(地图类)
Maze类使用一种特定的数据结构来表示整个沙箱世界的地图,恰好看到之前写分析此项目的博主更新了一篇讲地图结构的文章,概念介绍可以直接参考这篇文章

其实关键点就是为了便于让Agent具象化的理解沙箱世界的空间结构,整个2D地图被映射成了如下的树状结构,地图中的每一个tile(或者说每一个坐标),都会包含这些信息

世界 World -> 区域 Sector -> 场所 Arenas -> 物品 Game Objects

而Maze类除了创建这样的一个数据结构之外,还提供部分常用方法,比如说获取某个坐标点的上述结构信息、获取某一个区域范围内(比如角色视野范围内)的所有坐标、到达某个坐标点、设置某个坐标点上的物品状态等等,相当于提供了一个地图以及与地图交互的接口

Scratch、AssociativeMemory、MemoryTree 类(记忆流)
这三个类都与记忆流的构建有关,Scratch 类代表短期记忆,AssociativeMemory 代表联想记忆,MemoryTree 代表空间记忆,它们共同作用于Agent的决策

Scratch 类是Agent的短期记忆,包含个人以及世界相关的参数、个人的计划(长期、当天、小时计划)、当前进行的动作、已经规划好的行动路径等,并提供所有参数的修改接口;

AssociativeMemory 类是Agent的联想记忆,其主要功能是维护一个 ConceptNode对象的列表,ConceptNode节点可以是事件、想法或者对话;下面是一个ConceptNode的示例:

MemoryTree 类是Agent对于世界空间结构的有限记忆(其不一定了解整个世界的结构),其维护的空间记忆结构和世界结构类似,也是树状结构

Persona类(Agent类)
最重要的一个类,其定义了Agent是如何进行动作的,其中主循环会调用每个Persona类的move方法,一张图简单表示其输入输出

可以看到move方法通过当前的地图状态、agent对象列表、以及当前位置和时间,决定Agent的行动

该方法的流程具体来说可以分为:

根据时间判断当前是否为新的一天(或更特别的,为第一天),当为新的一天时需要进行长期/当日计划,这一点与人类的习惯相符

调用perceive方法(感知),此方法用于感知Agent当前周围正在发生的事情,并根据发生的范围对其进行排序,返回一个ConceptNode列表(上面有介绍)

调用retrieve方法,根据perceive方法得到用户感知到的想法,即ConceptNode列表作为输入,输出一组经过排序的相关的事件和想法

调用plan方法(计划),此方法即模仿人类的思考过程,以retrieve方法的输出作为输入,的整体上分为几个阶段:如果是新的一天,进行长期规划⇒如果当前动作停止,决定下一个进行的动作⇒根据目前感知到的事件,决定当前专注于其中哪一件⇒对于目前专注的事件,决定进行什么动作(有三种动作:与某人交谈、对事件进行反应、不对事件进行反应)

调用reflect方法,这里翻译成中文应该叫“反思”,同样参考这篇文章对反思这个概念的解释

通俗的来说,上面的思考流程都是浅层次的对事件的感知和反应,但人类有更高层次的总结、提炼、发掘潜在意图等思考方式,“反思”即模拟这种思考方式,对近段时间发生的事件进行更高层次的思考,并将思考结果作为记忆流的一部分存储起来

调用execute方法,到这一步Agent已经决定了自己要做的事情,在execute方法中会根据目的地选择最优路径,或者是原地等待等

LLM交互模块

接下来将主要探索一下,项目里是如何构建prompt并且和GPT进行交互的,这部分将偏重代码讲解

由代码调用路径可知,使用了GPT相关接口的文件主要有 converse.py(对话生成),plan.py(计划生成),reflect.py(反思),代码非常冗长,下面以每个文件里的一个典型调用用例进行讲解:

plan.py 包含进行每日计划、小时计划、具体动作等问答模板的生成,以每日计划为例:

这是一个指定格式的prompt,在一天开始时,会传入对应的参数并让GPT进行续写,这里比较重要的是 CommonsetLifestyle

其中 Commonset是一个对角色的最小化描述集,包括以下的部分信息

Lifestyle则更简洁,一般是角色的生活习惯的描述

在plan阶段,主要是通过预设的Agent的生活习惯和人物背景构造prompt,由GPT进行当天计划的续写(笔者注:这里计划的自由度其实挺低的,GPT了解不到除了设定中给到的地点/人物以外的其他信息)

converse.py中提供了让GPT生成可能产生的人物对话的接口,同时还包括一些总结人物对话要点、通过对话总结人物关系之类的接口,下面以生成人物对话的接口为例

这里的 <INPUT 2> 指的是在此之前是否有已经发生过的对话,<INPUT 3>的Context则是指该对话发生的上下文(时间、地点、人物等),实际决定这段对话的讨论主题的其实是 <INPUT 6>和<INPUT 10>,而这两个参数来自于另一次GPT调用的结果

reflect.py的对GPT的调用过程同样可以参考这篇文章

引用文章中对于reflect的流程描述,其中的步骤2和4都存在对GPT的调用

整体来说,项目中对于GPT的调用都是基于精确设定并且格式化的prompt,对于输出的格式要求也很严格,而且并不是一次性将所有的信息都告诉大模型,每次的提问都只解决一个比较小的问题:比如这个小时做什么、接下来的对话主题是什么,如何搜集、整理大模型所需要的上下文,实际还是hard code的,这一点可能并不那么理想

收获与问题

收获

这个项目展示了构建AI-Agent的范例,有很多对于现实世界和人类思维的抽象是可以借鉴的,具体地:

构建记忆流的方式,因为Agent所能接受的Context是有限的,目前的大语言模型也无法突破这个限制。通过长期记忆/联想记忆/空间记忆,以及“反思”的方式总结更高层次的记忆,为Agent做决策时提供准确并精简的Context,这一点是值得学习的 将现实世界抽象为树状的数据结构,有利于Agent快速理解周边环境 计划和反应,为了让Agent能生成可信的行为模式,自顶而下的为每个Agent生成长期/短期的计划,而当Agent遇到一些突发事件时,能结合计划与突发事件共同做出行为的决策(这一点也符合人类习惯
问题

尽管项目代码注释十分详尽,但确实存在一些卡主流程的bug

LLM体现出的智慧受限于给到的Context,与其说LLM生成行为和对话,不如说其是在归纳和总结Context中给到的信息,并做出选择,或者这样说,Agent 的智慧更加取决于项目的编码方式而非 LLM 的能力。可能我们对于大模型有更多的期待,比如说只给定一个世界观,减少一些约束条件,让 Agent 能有更多自由发挥的空间? Agent的“性格”,具有不同性格的 Agent 是否某一件事情做出差异化的反应,这一点似乎在项目中还没有看到,目前 Agent 更加注重于符合“人”的行为逻辑

总结

### 文章总结
本文详细介绍了斯坦福和谷歌研究人员创建的模拟《模拟人生》的 RPG 虚拟世界的项目及其在GitHub上的开源实现。该项目利用GPT和自定义代码控制25个角色,展示了AIGC(AI生成内容)技术在游戏领域的应用潜力。以下是文章的主要内容概括:
**项目背景**:
- 年初,斯坦福和谷歌的研究团队创建了一个微型RPG虚拟世界,并在arxiv发表论文。
- 项目在8月正式在GitHub上开源,引发热烈讨论,但技术和实现方法的讨论相对较少。
**整体架构**:
- 使用Python3.9.12开发,游戏逻辑由Phaser3 H5引擎构建,后端用Django服务器提供Web服务。
- 项目通过Main Loop驱动Agents做出决策,用户输入步骤数决定运算时间,运算结果通过Web Server展示在前端。
**运行方式**:
1. **Web服务**:基于Django的后台Server,需保持活跃以维持运行环境。
2. **Simulation Server**:负责生成并向智能体发送指令,可预设场景,通过用户指令逐步模拟NPC行为。
**代码解析**:
1. **Web服务**:使用Django框架搭建,主要提供小镇的主界面和回放功能,页面实时更新NPC的位置和动作。
2. **游戏引擎**:使用Phaser3构建地图和NPC控制器,主要用于展示层,不接受用户操作。
3. **AI Agent模块**:核心模块,由用户命令行驱动,通过主循环调用`agent.move()`决定每个Agent的行动。关键技术包括Maze类(地图表示)、记忆流(Scratch、AssociativeMemory、MemoryTree类)和Persona类(Agent逻辑)。
4. **LLM交互模块**:通过定义精确的prompt与GPT进行交互,为Agent生成计划、对话等。
**收获与问题**:
**收获**:
- 构建了构建记忆流的方法,通过长期记忆、联想记忆、空间记忆和反思提高Agent决策的精确度。
- 将现实世界抽象为树状数据结构,方便Agent理解环境。
- 充分利用计划和反应机制,使Agent行为更加真实可信。
**问题**:
- 存在一些卡住流程的bug,影响项目实际运行。
- LLM的智慧受限于给定的Context,Agent的智能更依赖于编码方式而非LLM能力。
- Agent性格差异化未充分体现,缺乏根据你不同性格对同一事件做出差异化反应的功能。
**总结**:该项目为AGI(通用人工智能)在游戏领域的应用提供了重要参考,尽管存在一些问题和局限性,但在架构设计、AI Agent行为模拟等方面具有借鉴意义。

更新时间 2024-07-25