86 lines
3.0 KiB
Python
86 lines
3.0 KiB
Python
"""
|
||
NPC AI的类。
|
||
这里指的不是LLM或者Machine Learning,而是NPC的决策机制
|
||
分为两类:规则AI和LLM AI
|
||
"""
|
||
from abc import ABC, abstractmethod
|
||
|
||
from src.classes.world import World
|
||
from src.classes.tile import Region
|
||
from src.classes.root import corres_essence_type
|
||
from src.classes.action import ACTION_SPACE_STR
|
||
from src.utils.llm import get_ai_prompt_and_call_llm
|
||
|
||
class AI(ABC):
|
||
"""
|
||
AI的基类
|
||
"""
|
||
def __init__(self, avatar: 'Avatar'):
|
||
self.avatar = avatar
|
||
|
||
@abstractmethod
|
||
def decide(self, world: World) -> tuple[str, dict]:
|
||
"""
|
||
决定做什么
|
||
"""
|
||
pass
|
||
|
||
class RuleAI(AI):
|
||
"""
|
||
规则AI
|
||
"""
|
||
def decide(self, world: World) -> tuple[str, dict]:
|
||
"""
|
||
决定做什么
|
||
先做一个简单的:
|
||
1. 找到自己灵根对应的最好的区域
|
||
2. 检测自己是否在最好的区域
|
||
3. 如果不在,则移动到最好的区域
|
||
4. 如果已经到达最好的区域,则进行修炼
|
||
5. 如果需要突破境界了,则突破境界
|
||
"""
|
||
best_region = self.get_best_region(list(world.map.regions.values()))
|
||
if self.avatar.is_in_region(best_region):
|
||
if self.avatar.cultivation_progress.can_break_through():
|
||
return "Breakthrough", {}
|
||
else:
|
||
return "Cultivate", {}
|
||
else:
|
||
return "MoveToRegion", {"region": best_region}
|
||
|
||
def get_best_region(self, regions: list[Region]) -> Region:
|
||
"""
|
||
根据avatar的灵根找到最适合的区域
|
||
"""
|
||
root = self.avatar.root
|
||
essence_type = corres_essence_type[root]
|
||
region_with_best_essence = max(regions, key=lambda region: region.essence.get_density(essence_type))
|
||
return region_with_best_essence
|
||
|
||
class LLMAI(AI):
|
||
"""
|
||
LLM AI
|
||
"""
|
||
# TODO:动作链
|
||
"""
|
||
AI动作应该分两类:
|
||
1. 动作链,一定时间内的长期规划,动作按照这个动作链来执行(以及何时终止并执行下一个动作)
|
||
2. 突发情况,比如突然有人要攻击NPC,这个时候的反应
|
||
不能每个单步step都调用一次LLM来决定下一步做什么。这样子一方面动作一直乱变,另一方面也太费token了。
|
||
decide的作用是,拉取既有的动作链(如果没有了就call_llm),再根据动作链决定动作,以及动作之间的衔接。
|
||
"""
|
||
def decide(self, world: World) -> tuple[str, dict]:
|
||
"""
|
||
决定做什么
|
||
"""
|
||
action_space_str = ACTION_SPACE_STR
|
||
avatar_infos_str = str(self.avatar)
|
||
regions_str = "\n".join([str(region) for region in world.map.regions.values()])
|
||
dict_info = {
|
||
"action_space": action_space_str,
|
||
"avatar_infos": avatar_infos_str,
|
||
"regions": regions_str
|
||
}
|
||
res = get_ai_prompt_and_call_llm(dict_info)
|
||
action_name, action_params = res["action_name"], res["action_params"]
|
||
return action_name, action_params |