Files
cultivation-world-simulator/src/classes/ai.py
2025-08-30 22:27:18 +08:00

86 lines
3.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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