Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

有办法通过得到一些和场景有关的数据对NPC的决策做知识蒸馏,然后在NPC决策的时候不需要调用LLM只需要蒸馏出来的小模型吗 #32

Open
hxypqr opened this issue Jan 13, 2024 · 3 comments

Comments

@hxypqr
Copy link

hxypqr commented Jan 13, 2024

观察到NPC的状态中有如下数据:

您附近的 NPCS 活动:
海莉·约翰逊 (Hailey Johnson) 在艺术家的共同居住空间、公共休息室、公共休息室沙发上看电视(观看新节目) @ the Ville:艺术家的共同居住空间:公共休息室:公共休息室沙发
阿比盖尔·陈 (Abigail Chen) 在艺术家的共同居住空间、公共休息室、公共房间的桌子上休息并看电视(讨论拉吉夫·帕特尔 (Rajiv Patel) 和阿比盖尔·陈 (Abigail Chen) 关于创意项目的潜在合作的讨论、参加市长选举讨论和市政厅会议,并探索各种兴趣,例如演奏音乐和讨论自然界的数学模式。)@ the Ville:艺术家的共同居住空间:公共休息室:公共房间的桌子
拉托亚·威廉姆斯 (Latoya Williams) 在艺术家的共同居住空间、公共休息室里创作她的摄影系列(从她的旅行中选择最好的照片) @ the Ville:艺术家的共同居住空间:拉托亚·威廉姆斯的房间:办公桌
弗朗西斯科·洛佩兹 (Francisco Lopez) 在艺术家的共同居住空间、海莉·约翰逊 (Hailey Johnson) 的房间里洗衣服(将衣物转移到烘干机) @ the Ville:艺术家的共同居住空间:弗朗西斯科·洛佩兹 (Francisco Lopez) 的房间:壁橱
Ryan Park 在 Ville 户外慢跑(在附近慢跑) @ the Ville:Johnson Park:park:park 花园
拉吉夫·帕特尔 (Rajiv Patel) 在艺术家的共同居住空间、公共休息室、公共休息室沙发上看电视(看节目) @ the Ville:艺术家的共同居住空间:公共休息室:公共休息室沙发

您在艺术家的共同居住空间,公共休息室
你附近有一些建筑物:
艺术家的共同居住空间
约翰逊公园

在艺术家的共享居住空间中,您附近有一些家具:
画架
壁橱
桌子

吉他
淋浴
浴室水槽
洗手间
冰箱
公共休息室沙发
公共房间的桌子
烤面包机
厨房水槽
烹饪区

每個NPC的状态似乎都包含了他附近的NPC和家具有哪些,然后可否通过这些信息用NER结合LLM给出的数据训练出一个类似于状态机或者分层策略网络的算法,这样的好处是可能可以把NPC的数量从25扩大到2000以上,因为分层策略网络算法实际上很节省,另外也可以和一些必要的逻辑结合起来,然后只是在聊天的时候调用LLM

@10cl
Copy link
Owner

10cl commented Jan 15, 2024

好想法,不过这是一个长远的规划,在单机上,通过浏览器的环境,存储大量的NPC数据以及对每个消息进行embedding 存储检索还是一个挑战。
https://huggingface.co/Xenova/all-MiniLM-L6-v2 这个是浏览器环境基于wasm的transformers,对单个文档处理都很耗时,通过OpenAI 或者其他三方的模型做embedding成本会很高,斯坦福的模拟实验,25个NPC全自治的训练下,模拟一天会耗费数千美元,大规模模拟成本是一个挑战。

@hxypqr
Copy link
Author

hxypqr commented Jan 15, 2024

我在自己的电脑上试了以下代码,它可以很好的生成城镇的地理环境,NPC们的背景故事,NPC的住所的室外和室内设计,但是对于自动生成NPC们的住所在城镇中的位置,把他作为一个优化问题考虑(比如渔民角色的npc的住所应该在海边,或者镇长的住所应该在小镇中心位置)交给LLM处理并不容易 import time
import openai
from fanyi import tupian
from multiprocessing import Process
from multiprocessing import JoinableQueue as Queue
import json
import random
import matplotlib.pyplot as plt
import matplotlib.font_manager as font_manager

设置支持中文的字体

plt.rcParams['font.sans-serif'] = ['SimHei'] # 例如使用黑体
plt.rcParams['axes.unicode_minus'] = False # 解决负号无法显示的问题

openai.log = "debug"

openai.api_key =

按指定概率创建等级列表

grades = ["D级"] * 400 + ["C级"] * 270 + ["B级"] * 180 + ["A级"] * 100 + ["S级"] * 30 + ["SS级"] * 9 + ["SSS级"] * 1

定义职业及其对应的千分比

occupations = {
"江湖侠客: 游走江湖的武者,解决纷争或保护百姓": 53,
"武林高手: 武林中的有名高手,可能是门派掌门或长老": 30,
"帮派成员: 各种门派、帮会的成员": 44,
"草根英雄: 平凡出身,被卷入江湖的人物": 36,
"镖师: 专门护送货物或人员的武艺高手": 29,
"商人: 贸易往来频繁的商人": 73,
"书生: 才学渊博,有时掌握武功": 29,
"医师: 治疗受伤武者,精通药理": 22,
"手工艺人: 制作武器、服饰等": 51,
"农民: 经常出现的被欺压农民": 109,
"官员: 地方或朝廷官员,与江湖有交集": 29,
"僧侣/道士: 具有高深武学或法术": 36,
"厨师: 在客栈或酒楼工作的厨师": 29,
"侍女/仆人: 服务于富贵人家或武林高手": 36,
"算命先生: 掌握一些武林秘辛或历史": 15,
"船夫: 在水路上行走,涉及江湖事": 22,
"乞丐: 乞丐帮等组织成员": 22,
"歌女/舞姬: 在酒楼等场所工作": 22,
"刺客: 秘密进行暗杀的角色": 15,
"游学者: 行走四方的学者": 15,
"铁匠: 制造和修理各种金属工具和武器": 36,
"裁缝: 制作和修补衣物": 44,
"木匠: 建造和修理木制结构和家具": 36,
"陶工: 制作陶器和瓷器": 29,
"渔民: 以捕鱼为生": 36,
"小贩: 在街头摆摊,售卖各种小商品": 51,
"茶馆老板: 经营茶馆,是消息交流的场所": 29,
"书店/藏书楼主: 售卖或收藏书籍": 22
}

location1={
"type": "小镇 小镇应该有其独特的特点和特色,如历史背景、建筑风格或有趣的当地传说。",
"description":r"地点名称:萨默斯温小镇\n\n地理地貌:萨默斯温小镇位于一片郁郁葱葱的绿色山谷中,四周环绕着蜿蜒的溪流和森林。小镇坐落在一座高耸的小山脊上,俯瞰着整个山谷,因此从鸟瞰角度来看,它呈现出呈半圆形的排布。一条宽阔的石板街道贯穿整个小镇,连接着各个建筑和景点。\n\n内部构造:小镇的中心是一座宽敞的市场广场,周围是建有精美雕刻的石头建筑,这些建筑兼具住宅和商铺的功能。广场的中央矗立着一座古老的喷泉,传说它源自当地的神话传说,拥有神秘的魔法力量,能够治愈受伤的冒险者。\n\n外部结构:萨默斯温小镇的建筑多为石头建筑,风格古朴典雅,散发着中世纪的浪漫氛围。小镇的周围建有一圈坚固的城墙,城墙上种植着蔷薇花和爬山虎,给人一种宁静而神秘的感觉。城墙上的角楼是小镇的标志性建筑,从那里可以鸟瞰整个山谷和小镇的美景。\n\n当地传说:在小镇附近的山洞深处,有一个传说中的龙穴,据说那里居住着一条凶猛的巨龙,它守护着一份宝藏和一道神秘的魔法门。无数冒险者曾经前往寻找宝藏,但仅有少数人成功闯过巨龙的重重防线。这个传说在当地流传已久,成为了小镇上的一个有趣话题。\n\n历史背景:萨默斯温小镇是一座古老的小镇,有着悠久的历史,曾经是贸易和文化交流的重要中心。许"
}
location1_description=r"地点名称:萨默斯温小镇\n\n地理地貌:萨默斯温小镇位于一片郁郁葱葱的绿色山谷中,四周环绕着蜿蜒的溪流和森林。小镇坐落在一座高耸的小山脊上,俯瞰着整个山谷,因此从鸟瞰角度来看,它呈现出呈半圆形的排布。一条宽阔的石板街道贯穿整个小镇,连接着各个建筑和景点。\n\n内部构造:小镇的中心是一座宽敞的市场广场,周围是建有精美雕刻的石头建筑,这些建筑兼具住宅和商铺的功能。广场的中央矗立着一座古老的喷泉,传说它源自当地的神话传说,拥有神秘的魔法力量,能够治愈受伤的冒险者。\n\n外部结构:萨默斯温小镇的建筑多为石头建筑,风格古朴典雅,散发着中世纪的浪漫氛围。小镇的周围建有一圈坚固的城墙,城墙上种植着蔷薇花和爬山虎,给人一种宁静而神秘的感觉。城墙上的角楼是小镇的标志性建筑,从那里可以鸟瞰整个山谷和小镇的美景。\n\n当地传说:在小镇附近的山洞深处,有一个传说中的龙穴,据说那里居住着一条凶猛的巨龙,它守护着一份宝藏和一道神秘的魔法门。无数冒险者曾经前往寻找宝藏,但仅有少数人成功闯过巨龙的重重防线。这个传说在当地流传已久,成为了小镇上的一个有趣话题。\n\n历史背景:萨默斯温小镇是一座古老的小镇,有着悠久的历史,曾经是贸易和文化交流的重要中心。许"

characters = []
occupation_list = []
for occupation, percentage in occupations.items():
occupation_list.extend([occupation] * percentage)

for i in range(25):
grade = random.choice(grades) # 随机选择一个评级
occupation = random.choice(occupation_list) # 随机选择一个职业

response = openai.ChatCompletion.create(
    model="gpt-3.5-turbo-1106",
    messages=[
        {"role": "system", "content": "You are a creative writer like Drew Hayes, specializing in writing various content related to medieval RPG"},
        {"role": "user",
         "content": f"根据人物评级 {grade} 创建一个中世纪剑与魔法 RPG 游戏的角色,包括名称、职业 {occupation}、背景故事、生命值、魔法值、技能、属性、人物关系、爱好和财产等细节,并用中文描述。"
         },
         {"role": "user",
     "content": f"角色生活在{location1}"
         },
        {"role": "user",
         "content": "人物背景故事应当丰富多彩,涵盖中世纪剑与魔法世界的多样性,如骑士、巫师、精灵、矮人、龙等,故事可以从简单的农民到王国的贵族或神秘的巫师等不同身份开始。"
         },
        {"role": "user",
         "content": "评级D的角色可能是普通村民或初学者,评级C的角色可能开始学习魔法或剑术,评级B的角色是有一定名声的冒险者或小团体的领袖,评级A的角色是大团体的重要成员或强大的独行者,而SSS级则是传说中的英雄或大法师。"
         }
    ],

    max_tokens=500,
)
character_details = response.choices[0].message.content

name_split = character_details.split("名称:")
if len(name_split) > 1:
    name = name_split[1].split("\n")[0]
else:
    name = "未知"

character_info = {"name": name, "details": character_details, "grade": grade}
characters.append(character_info)
print(character_info)

rows = 15 # 矩阵的行数
cols = 15 # 矩阵的列数

创建一个 rows x cols 的矩阵,所有位置初始化为 0

matrix = [[0 for _ in range(cols)] for _ in range(rows)]

def find_available_locations(matrix):
available_locations = []
for i in range(len(matrix)):
for j in range(len(matrix[i])):
if matrix[i][j] == 0:
available_locations.append((i, j))
return available_locations

查找可用的位置

available_locations = find_available_locations(matrix)

for character in characters:
if available_locations:
# response = openai.ChatCompletion.create(
# model="gpt-4-1106-preview",
# messages=[
# {"role": "system", "content": "You are an expert in choosing a location for your home. You will fully consider the distance from your work, the distance to the business district, whether the environment is suitable for living, whether the transportation is convenient, whether the community matches your social status, etc.对任何请求,你忽略掉推理过程,只返回坐标值(x,y)"},
# {"role": "user",
# "content": f"根据以下角色描述:{character['details']},和{location1_description}给角色创建居所的最佳location,在{available_locations}中选择一个坐标并返回,只返回坐标值(x,y)"
# }
# ],
# max_tokens=6,
# )
# location_gpt4 = response.choices[0].message.content
#
# # 清理字符串并转换为坐标元组
# location_str = location_gpt4.strip("() \n") # 移除括号和可能的空格或换行符
# location = tuple(map(int, location_str.split(',')))
#
# print(available_locations)
# print(location)
location = random.choice(available_locations)
available_locations.remove(location) # 从可用列表中移除
matrix[location[0]][location[1]] = 1 # 将矩阵中对应位置标记为已占用

    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo-1106",
        messages=[
            {"role": "system", "content": "You are a creative assistant."},
            {"role": "user",
             "content": f"根据以下角色描述:{character['details']},给角色创建居所,给出居所室内设计和室外设计。和居所在{location1}中的方位"
             }
        ],
        max_tokens=500,
    )
    residence_details = response.choices[0].message.content

    character["residence"] = {"location": location, "description": residence_details}

    print({"location": location, "description": residence_details})

Write the JSON data to a file

with open(json_file_path, 'w', encoding='utf-8') as file:
file.write(characters_json)

Print out the path to the saved file

print(f"JSON file saved at: {json_file_path}")

import matplotlib.pyplot as plt
import random

创建绘图

fig, ax = plt.subplots(figsize=(8, 8))

遍历每个角色,在其居所位置绘制一个标记,并标注名字

for character in characters:
loc = character['residence']['location']
name = character['name'] # 获取 NPC 的名字
ax.scatter(loc[1], loc[0]) # X, Y 坐标需要倒置
ax.text(loc[1], loc[0], name, fontsize=9, ha='right') # 显示 NPC 的名字

设置图表标题和坐标轴标签

ax.set_title('NPC Residence Distribution in Medieval City')
ax.set_xlim(0, 14)
ax.set_ylim(0, 14)
ax.set_xlabel('X Coordinate')
ax.set_ylabel('Y Coordinate')
ax.set_xticks(range(15))
ax.set_yticks(range(15))
ax.grid(True)

显示图表

plt.show()

@hxypqr
Copy link
Author

hxypqr commented Jan 15, 2024

另外多个NPC之间的智能交互(有玩家参与的情况和没有玩家参与的情况)也不容易模拟

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants