上一篇文章对项目的核心文件的功能做了单独介绍。接下来我们就具体看看各个模块的功能实现。这一篇先看前端环境模拟部分。
之前介绍编译运行的文章中提到,启动和常用功能大概有下面几项:
- 🚀 启动 environment 服务启动 simulation 服务运行和保存 Simulation重放之前保存的 simulation演示 simulation
以上 5 点可以分为两类。一部分是真正调用 LLM 大模型生成对话和记忆,然后检索规划,最终保存整个过程。另一部分是把前面运行的过程保存下来的场景,进行重放和演示。
功能介绍
今天重点学习重放 replay,启动 environment 前端服务。前端环境是一个基于 Django 框架的应用。
cd environment/frontend_serverpython manage.py runserver
打开 http://localhost:8000/
浏览器显示"Your environment server is up and running,"说明运行成功了。
打开 http://localhost:8000/replay/July1_the_ville_isabella_maria_klaus-step-3-20/1/
开始 replay。
整个页面分为三部分:
- 地图及角色动画展示,基于 Phaser 3.0 实现。这部分后面有时间单独介绍。时间和回放控制👯 角色列表和角色详情,核心信息展示。
代码
# http://localhost:8000/replay/July1_the_ville_isabella_maria_klaus-step-3-20/1/# 拆解http://localhost:8000/# base urlreplay/# commandJuly1_the_ville_isabella_maria_klaus-step-3-20/# simulation 文件1/# 步骤,可以手动指定第几步
启动 django 服务,指定默认配置文件目录为 frontend_server.settings
def main(): os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'frontend_server.settings') try: from django.core.management import execute_from_command_line execute_from_command_line(sys.argv)if __name__ == '__main__': main()
环境配置文件
# 需要跟随 django 启动的模块INSTALLED_APPS = [ # ... 'translator', 'corsheaders', 'storages',]# 项目的 URL 路由入口模块ROOT_URLCONF = 'frontend_server.urls'# 静态资源与媒体文件STATIC_URL = '/static/'STATIC_ROOT = os.path.join(os.path.dirname(BASE_DIR), "static_root")STATICFILES_DIRS = ( os.path.join(BASE_DIR, "static_dirs"),)MEDIA_URL = '/media/'MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), "media_root")
解析刚才的 url,匹配到 replay/
,转到 translator.replay
方法
from translator import views as translator_viewsurlpatterns = [ url(r'^$', translator_views.landing, name='landing'), url(r'^simulator_home$', translator_views.home, name='home'), url(r'^demo/(?P<sim_code>[\w-]+)/(?P<step>[\w-]+)/(?P<play_speed>[\w-]+)/$', translator_views.demo, name='demo'), url(r'^replay/(?P<sim_code>[\w-]+)/(?P<step>[\w-]+)/$', translator_views.replay, name='replay'), url(r'^replay_persona_state/(?P<sim_code>[\w-]+)/(?P<step>[\w-]+)/(?P<persona_name>[\w-]+)/$', translator_views.replay_persona_state, name='replay_persona_state'), url(r'^process_environment/$', translator_views.process_environment, name='process_environment'), url(r'^update_environment/$', translator_views.update_environment, name='update_environment'), url(r'^path_tester/$', translator_views.path_tester, name='path_tester'), url(r'^path_tester_update/$', translator_views.path_tester_update, name='path_tester_update'), path('admin/', admin.site.urls),]
解析
def replay(request, sim_code, step): persona_names_set = set()# 人物角色列表 for i in find_filenames(f"storage/{sim_code}/personas", ""): # 遍历存储的 replay 文件,找到人物角色 persona_names_set.add(x) for i in find_filenames(f"storage/{sim_code}/environment", ".json"): file_count += [int(x.split(".")[0])] curr_json = f'storage/{sim_code}/environment/{str(max(file_count))}.json' with open(curr_json) as json_file: persona_init_pos_dict = json.load(json_file) for key, val in persona_init_pos_dict.items(): if key in persona_names_set: persona_init_pos += [[key, val["x"], val["y"]]] context = {"sim_code": sim_code,# 模拟文件名:July1_the_ville_isabella_maria_klaus-step-3-20 "step": step,# 对应 environment/ 下面的 *.json 文件,游戏里面的 10 秒代表一步,也就是 10s 一个 json 文件 "persona_names": persona_names,# 对应 personas 目录下的名字 "persona_init_pos": persona_init_pos, # 角色 初始的 位置 "mode": "replay"}# 控制是否显示 play/pause 按钮 template = "home/home.html" return render(request, template, context)
replay
函数读取的 json 文件:
July1_the_ville_isabella_maria_klaus-step-3-20├── environment│ ├── 0.json# 角色坐标文件│ ├── 1.json│ ├── 10.json│ ├── 100.json│ ├── 1000.json│ ├── 1001.json│ ├── ...├── personas│ ├── Isabella\ Rodriguez# 角色文件│ │ └── bootstrap_memory│ │ ├── associative_memory# 关联记忆│ │ │ ├── embeddings.json# embedding 矢量存储│ │ │ ├── kw_strength.json#?│ │ │ └── nodes.json# 记忆节点│ │ ├── scratch.json# 角色的基础信息│ │ └── spatial_memory.json# 空间环境记忆│ ├── Klaus\ Mueller│ │ └── bootstrap_memory│ │ ├── associative_memory│ │ │ ├── embeddings.json│ │ │ ├── kw_strength.json│ │ │ └── nodes.json│ │ ├── scratch.json│ │ └── spatial_memory.json│ └── Maria\ Lopez│ └── bootstrap_memory│ ├── associative_memory│ │ ├── embeddings.json│ │ ├── kw_strength.json│ │ └── nodes.json│ ├── scratch.json│ └── spatial_memory.json└── reverie └── meta.json# simulator 基本信息
接下来 render 开始渲染界面 home/home.html
,并且引入了 phaser 和 main_script.html
脚本。
<script src='https://cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js'></script>{% include 'home/main_script.html' %}
main_script.html
主要负责游戏界面处理,同时负责和 django 服务通信。
function preload() {}function create() {}function update(time, delta) { if (phase == "process") { var retrieve_xobj = new XMLHttpRequest(); retrieve_xobj.overrideMimeType("application/json"); retrieve_xobj.open('POST', "{% url 'process_environment' %}", true); retrieve_xobj.send(json); // Finally, we update the phase variable to start the "udpate" process. // Now that we sent all persona locations to the backend server, we need // to wait until the backend determines what the personas will do next. phase = "update"; } else if (phase == "update") { var update_xobj = new XMLHttpRequest(); update_xobj.overrideMimeType("application/json"); update_xobj.open('POST', "{% url 'update_environment' %}", true); update_xobj.send(JSON.stringify({"step": step, "sim_code": sim_code })); phase = "execute"; } else { phase = "process"; }}
调用 process_environment
和update_environment
接口处理数据
def process_environment(request): """ <FRONTEND to BACKEND> This sends the frontend visual world information to the backend server. It does this by writing the current environment representation to "storage/environment.json" file. """ data = json.loads(request.body) step = data["step"] sim_code = data["sim_code"] environment = data["environment"] with open(f"storage/{sim_code}/environment/{step}.json", "w") as outfile: outfile.write(json.dumps(environment, indent=2)) return HttpResponse("received")def update_environment(request): """ <BACKEND to FRONTEND> This sends the backend computation of the persona behavior to the frontend visual server. It does this by reading the new movement information from "storage/movement.json" file. """ data = json.loads(request.body) step = data["step"] sim_code = data["sim_code"] response_data = {"<step>": -1} if (check_if_file_exists(f"storage/{sim_code}/movement/{step}.json")): with open(f"storage/{sim_code}/movement/{step}.json") as json_file: response_data = json.load(json_file) response_data["<step>"] = step return JsonResponse(response_data)
Agent 数据结构
关于一个 Agent 的描述