赞
踩
这里是类斯坦福小镇项目agentsim的一个调研。主要目的是寻找行为树模式的氛围npc不够智能的解决方案。下面会先简单介绍下一些关键的类,然后再讲解流程。该项目有段时间没维护了,没法直接运行,有兴趣的可以修修,主要是openai接口版本的问题。
这里我们简单阐述几个和llm相关的核心类
prompt类作用是获取提示词,提示词目录如下
actor类代表每个npc,这里涉及了所有对llm回复的处理。react函数:是一个状态机,根据不同状态选择不同的提示词ask llm。
agent类是actor的基类,负责填充提示词
tick类,消息处理的第一环,负责状态转移 。parse_react函数:解析所有的react得到的llm回复,进行状态转移
主流程其实很简单register tick到来,走初始化逻辑。后续由common tick消息驱动状态转移,每个tick会依次 处理movings,chat, using, initted中uid对应的任务逻辑。
React system比较复杂,核心函数为为**react **和 parse_react构成。后面,流程图比较难画且不方便理解。下面我们将通过描述过程的方式来帮助理解。主流程中我们可以清楚的看到,处理逻辑的核心是 movings,inited,chatted,using 这几个数组,下面留心下是什么时候添加进来的。
第一个tick往往是register tick,表示一个客户端的连接。这里初始化了一些游戏系统的资源,如npc模型,alan和ph这两个npc。然后将这两个npc的uid添加到inited数组中。
随后就是持续的common tick,开始驱动后续的状态转移。
上一个tick已经将alan和ph 这两个npc的uid注册到了inited数组中。在该tick中使用solve_init处理alan和ph。这里首先是** _qa_framework**,该函数获取提示词 qa_framework_question ,首先让llm根据用户的目标以及已有知识提出问题,再使用qa_framework_answer解决问题(目的是延迟模型的思考时间,提高模型处理问题的质量),最后获取提示词plan,让llm给出下一个plan作为result的newPlan字段存入,plan 的结构如下:
{
“building”: “…”, 需要我前往的建筑
“purpose” : “…” 目的是什么
}
接着调用parse_react,根据result进行状态转移,因为result中存在newPlan字段,则走newPlan分支。该分支下,如果当前位置和building不一样,这说明需要前往下一个building,所以这里需要通过navigate寻路,调用move将结果发送给客户端。注意无论,curlocation 和targetlocation是否相同都会添加该npc uid到movings队列中。
这里补充下,navigate寻路成功,会将path保存在对应npc model的path中。当send 客户端时,会将path清空。只要调用了move,就会将uid添加到movings数组中。
此时只有movings中有任务需要处理。slove_moving调用move,这时由于path为null,则走move的另外一个分支,即则设置result timetick-finishMoving,进行react,该状态时去llm获取行为act(使用提示词为act),npc能够进行的行为有三个 :(1)use:使用工具 (模拟工具使用)(2)chat:聊天 (3)expierence : 尝试经验中的行为action,下面是result的返回结果:
{
“action”: “use”,
“equipment” : “…”, 使用什么设备,如desk
“operation” : “…” 做什么?
}
{
“action”: “chat”,
“person” : “…”, 和谁聊天
“topic” : “…” 聊天的主题
}
{
“action”:“experience”,
“experienceID”:“…” 使用哪一次的经验
}
由于该tick是第一个行为,没有experience,所以,目前只会出现use 和chat这两种情况。和上一个tick相同
如果是use,则调用use函数使用use提示词,模拟使用物品
{“continue_time” : “…”,
“result” : “…”,使用原因
“bought_thing” : “…”,
“amount” : “…”
}
{“continue_time” : “…”,
“result” : “…”, 使用原因
“earn” : “…”
}
如果是chat,则调用chat 让llm根据提示词开始一个话题
如果是use,则使用navigate对该equipment进行寻路,如果找到path,则再次进行move,和上个tick一样,send客户端,清空model上的path。并将uid添加到using中
如果是chat,则调用_execute_chat对当前两个npc,进行5轮chat (使用提示词为chat),将结果存放在npc的chats上,并将uid添加到chatted中
所以这一轮 movings中必定会有值。
这是和之前一样先处理movings中的任务,不同的是这里还要处理chatted/using中任务
调用finish_using,设置状态为 timetick-finishUse,该状态调用_critic,进行评价(使用critic提示词),这里将根据上一轮中的act判断plan是否完成,llm返回结果如下:
{“result”: “success”/“fail”/“not_finished_yet”}
认为该plan已经有结果了,则调用_store_memory更新对people和building的impression。
{
“people”:{“person_name”:{“impression”:“xxx”,“newEpisodicMemory”:“xxx”}}
“buildings”:{“building_name”:{“impression”:“xxx”,“newEpisodicMemory”:“xxx”}}
}
如果为success,则需要将上一轮的所有act作为experience存储下来,将上一轮的acts和plan作为一个experience存储到 npc的experience map中。并将plan添加到plan_cache中表示以及完成的plans
清空所有act,然后调用_plan开始一个新的plan
则直接store_memory
最后我们看下当act 返回结果为experience的情况,如果act选择的是使用experience,我们根据id找到对应的experience中,再从experience中pop出一个act,并且设置flag为使用experience,注意这种情况和use走同一个逻辑,即走parse_react的use分支,不同的在use完毕后不会走critic流程,因为之前以及判断过了,执行完experience的所有act后,会开始一个新的plan
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。