refactor llm
This commit is contained in:
@@ -168,7 +168,7 @@ class ActualActionMixin():
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def finish(self, **params) -> list[Event]:
|
||||
async def finish(self, **params) -> list[Event]:
|
||||
return []
|
||||
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ class Battle(InstantAction):
|
||||
|
||||
# InstantAction 已实现 step 完成
|
||||
|
||||
def finish(self, avatar_name: str) -> list[Event]:
|
||||
async def finish(self, avatar_name: str) -> list[Event]:
|
||||
res = self._last_result
|
||||
if not (isinstance(res, tuple) and len(res) == 4):
|
||||
return []
|
||||
@@ -87,10 +87,10 @@ class Battle(InstantAction):
|
||||
pass
|
||||
result_event = Event(self.world.month_stamp, result_text, related_avatars=rel_ids, is_major=True)
|
||||
|
||||
# 生成战斗小故事(同步调用,与其他动作保持一致)
|
||||
# 生成战斗小故事
|
||||
target = self._get_target(avatar_name)
|
||||
start_text = self._start_event_content if hasattr(self, '_start_event_content') else result_event.content
|
||||
story = StoryTeller.tell_story(start_text, result_event.content, self.avatar, target, prompt=self.STORY_PROMPT)
|
||||
story = await StoryTeller.tell_story(start_text, result_event.content, self.avatar, target, prompt=self.STORY_PROMPT)
|
||||
story_event = Event(self.world.month_stamp, story, related_avatars=rel_ids, is_story=True)
|
||||
|
||||
return [result_event, story_event]
|
||||
|
||||
@@ -118,7 +118,7 @@ class Breakthrough(TimedAction):
|
||||
|
||||
# TimedAction 已统一 step 逻辑
|
||||
|
||||
def finish(self) -> list[Event]:
|
||||
async def finish(self) -> list[Event]:
|
||||
if not self._last_result:
|
||||
return []
|
||||
result_ok = self._last_result[0] == "success"
|
||||
@@ -139,7 +139,7 @@ class Breakthrough(TimedAction):
|
||||
|
||||
# 故事参与者:本体 +(可选)相关角色
|
||||
prompt = TribulationSelector.get_story_prompt(str(calamity))
|
||||
story = StoryTeller.tell_story(core_text, ("突破成功" if result_ok else "突破失败"), self.avatar, self._calamity_other, prompt=prompt)
|
||||
story = await StoryTeller.tell_story(core_text, ("突破成功" if result_ok else "突破失败"), self.avatar, self._calamity_other, prompt=prompt)
|
||||
events.append(Event(self.world.month_stamp, story, related_avatars=rel_ids, is_story=True))
|
||||
return events
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ class Catch(TimedAction):
|
||||
region = self.avatar.tile.region
|
||||
return Event(self.world.month_stamp, f"{self.avatar.name} 在 {region.name} 尝试御兽", related_avatars=[self.avatar.id])
|
||||
|
||||
def finish(self) -> list[Event]:
|
||||
async def finish(self) -> list[Event]:
|
||||
res = self._caught_result
|
||||
if not (isinstance(res, tuple) and len(res) == 3):
|
||||
return []
|
||||
|
||||
@@ -71,7 +71,7 @@ class Cultivate(TimedAction):
|
||||
|
||||
# TimedAction 已统一 step 逻辑
|
||||
|
||||
def finish(self) -> list[Event]:
|
||||
async def finish(self) -> list[Event]:
|
||||
return []
|
||||
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ class DevourMortals(TimedAction):
|
||||
def start(self) -> Event:
|
||||
return Event(self.world.month_stamp, f"{self.avatar.name} 在城镇开始吞噬凡人", related_avatars=[self.avatar.id])
|
||||
|
||||
def finish(self) -> list[Event]:
|
||||
async def finish(self) -> list[Event]:
|
||||
return []
|
||||
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ class Escape(InstantAction):
|
||||
|
||||
# InstantAction 已实现 step 完成
|
||||
|
||||
def finish(self, avatar_name: str) -> list[Event]:
|
||||
async def finish(self, avatar_name: str) -> list[Event]:
|
||||
return []
|
||||
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ class Harvest(TimedAction):
|
||||
|
||||
# TimedAction 已统一 step 逻辑
|
||||
|
||||
def finish(self) -> list[Event]:
|
||||
async def finish(self) -> list[Event]:
|
||||
return []
|
||||
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ class HelpMortals(TimedAction):
|
||||
|
||||
# TimedAction 已统一 step 逻辑
|
||||
|
||||
def finish(self) -> list[Event]:
|
||||
async def finish(self) -> list[Event]:
|
||||
return []
|
||||
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ class Hunt(TimedAction):
|
||||
|
||||
# TimedAction 已统一 step 逻辑
|
||||
|
||||
def finish(self) -> list[Event]:
|
||||
async def finish(self) -> list[Event]:
|
||||
return []
|
||||
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ class MoveAwayFromAvatar(TimedAction):
|
||||
|
||||
# TimedAction 已统一 step 逻辑
|
||||
|
||||
def finish(self, avatar_name: str) -> list[Event]:
|
||||
async def finish(self, avatar_name: str) -> list[Event]:
|
||||
return []
|
||||
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ class MoveAwayFromRegion(InstantAction):
|
||||
|
||||
# InstantAction 已实现 step 完成
|
||||
|
||||
def finish(self, region: str) -> list[Event]:
|
||||
async def finish(self, region: str) -> list[Event]:
|
||||
return []
|
||||
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ class MoveToAvatar(DefineAction, ActualActionMixin):
|
||||
done = self.avatar.tile == target.tile
|
||||
return ActionResult(status=(ActionStatus.COMPLETED if done else ActionStatus.RUNNING), events=[])
|
||||
|
||||
def finish(self, avatar_name: str) -> list[Event]:
|
||||
async def finish(self, avatar_name: str) -> list[Event]:
|
||||
return []
|
||||
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ class MoveToRegion(DefineAction, ActualActionMixin):
|
||||
done = self.avatar.is_in_region(r) or ((self.avatar.pos_x, self.avatar.pos_y) in getattr(r, "cors", ()))
|
||||
return ActionResult(status=(ActionStatus.COMPLETED if done else ActionStatus.RUNNING), events=[])
|
||||
|
||||
def finish(self, region: Region | str) -> list[Event]:
|
||||
async def finish(self, region: Region | str) -> list[Event]:
|
||||
return []
|
||||
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ class NurtureWeapon(TimedAction):
|
||||
related_avatars=[self.avatar.id]
|
||||
)
|
||||
|
||||
def finish(self) -> list[Event]:
|
||||
async def finish(self) -> list[Event]:
|
||||
weapon_name = self.avatar.weapon.name if self.avatar.weapon else "兵器"
|
||||
proficiency = self.avatar.weapon_proficiency
|
||||
# 注意:升华事件已经在_execute中添加,这里只添加完成事件
|
||||
|
||||
@@ -31,7 +31,7 @@ class Play(TimedAction):
|
||||
|
||||
# TimedAction 已统一 step 逻辑
|
||||
|
||||
def finish(self) -> list[Event]:
|
||||
async def finish(self) -> list[Event]:
|
||||
return []
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ class PlunderMortals(TimedAction):
|
||||
|
||||
# TimedAction 已统一 step 逻辑
|
||||
|
||||
def finish(self) -> list[Event]:
|
||||
async def finish(self) -> list[Event]:
|
||||
return []
|
||||
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ class SelfHeal(TimedAction):
|
||||
|
||||
# TimedAction 已统一 step 逻辑
|
||||
|
||||
def finish(self) -> list[Event]:
|
||||
async def finish(self) -> list[Event]:
|
||||
healed_total = int(getattr(self, "_healed_total", 0))
|
||||
# 统一用一次事件简要反馈
|
||||
return [Event(self.world.month_stamp, f"{self.avatar.name} 疗伤完成,HP已回满(本次恢复{healed_total}点,当前HP {self.avatar.hp})", related_avatars=[self.avatar.id])]
|
||||
|
||||
@@ -79,7 +79,7 @@ class SellItems(InstantAction):
|
||||
|
||||
# InstantAction 已实现 step 完成
|
||||
|
||||
def finish(self, item_name: str) -> list[Event]:
|
||||
async def finish(self, item_name: str) -> list[Event]:
|
||||
return []
|
||||
|
||||
|
||||
|
||||
@@ -75,6 +75,6 @@ class SwitchWeapon(InstantAction):
|
||||
related_avatars=[self.avatar.id]
|
||||
)
|
||||
|
||||
def finish(self, weapon_type_name: str) -> list[Event]:
|
||||
async def finish(self, weapon_type_name: str) -> list[Event]:
|
||||
return []
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ from typing import TYPE_CHECKING
|
||||
|
||||
from src.classes.world import World
|
||||
from src.classes.event import Event, NULL_EVENT
|
||||
from src.utils.llm import get_ai_prompt_and_call_llm_async
|
||||
from src.utils.llm import call_ai_action
|
||||
from src.classes.typings import ACTION_NAME_PARAMS_PAIRS
|
||||
from src.utils.config import CONFIG
|
||||
from src.classes.actions import ACTION_INFOS_STR
|
||||
@@ -70,7 +70,7 @@ class LLMAI(AI):
|
||||
"global_info": global_info,
|
||||
"general_action_infos": general_action_infos,
|
||||
}
|
||||
res = await get_ai_prompt_and_call_llm_async(info)
|
||||
res = await call_ai_action(info)
|
||||
results: dict[Avatar, tuple[ACTION_NAME_PARAMS_PAIRS, str, str]] = {}
|
||||
for avatar in avatars_to_decide:
|
||||
r = res[avatar.name]
|
||||
|
||||
@@ -365,7 +365,7 @@ class Avatar(AvatarSaveMixin, AvatarLoadMixin):
|
||||
result: ActionResult = action.step(**params_for_step)
|
||||
if result.status == ActionStatus.COMPLETED:
|
||||
params_for_finish = filter_kwargs_for_callable(action.finish, params)
|
||||
finish_events = action.finish(**params_for_finish)
|
||||
finish_events = await action.finish(**params_for_finish)
|
||||
# 仅当当前动作仍然是刚才执行的那个实例时才清空
|
||||
# 若在 step() 内部通过"抢占"机制切换了动作(如 Escape 失败立即切到 Battle),不要清空新动作
|
||||
if self.current_action is action_instance_before:
|
||||
|
||||
@@ -478,7 +478,7 @@ async def try_trigger_fortune(avatar: Avatar) -> list[Event]:
|
||||
base_event = Event(month_at_finish, event_text, related_avatars=related_avatars, is_major=True)
|
||||
|
||||
# 生成故事事件
|
||||
story = await StoryTeller.tell_story_async(event_text, res_text, *actors_for_story, prompt=story_prompt)
|
||||
story = await StoryTeller.tell_story(event_text, res_text, *actors_for_story, prompt=story_prompt)
|
||||
story_event = Event(month_at_finish, story, related_avatars=related_avatars, is_story=True)
|
||||
|
||||
# 返回基础事件和故事事件
|
||||
|
||||
@@ -12,7 +12,7 @@ if TYPE_CHECKING:
|
||||
|
||||
from src.classes.event import Event
|
||||
from src.utils.config import CONFIG
|
||||
from src.utils.llm import get_prompt_and_call_llm_async
|
||||
from src.utils.llm import call_llm_with_template, LLMMode
|
||||
from src.run.log import get_logger
|
||||
|
||||
logger = get_logger().logger
|
||||
@@ -91,7 +91,7 @@ async def generate_long_term_objective(avatar: "Avatar") -> Optional[LongTermObj
|
||||
}
|
||||
|
||||
# 调用LLM并自动解析JSON(使用fast模型)
|
||||
response_data = await get_prompt_and_call_llm_async(template_path, infos, mode="fast")
|
||||
response_data = await call_llm_with_template(template_path, infos, LLMMode.FAST)
|
||||
|
||||
content = response_data.get("long_term_objective", "").strip()
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ class DualCultivation(MutualAction):
|
||||
initiator.cultivation_progress.add_exp(exp_gain)
|
||||
self._dual_exp_gain = exp_gain
|
||||
|
||||
def finish(self, target_avatar: "Avatar|str") -> list[Event]:
|
||||
async def finish(self, target_avatar: "Avatar|str") -> list[Event]:
|
||||
target = self._get_target_avatar(target_avatar)
|
||||
events: list[Event] = []
|
||||
success = self._dual_cultivation_success
|
||||
@@ -111,7 +111,7 @@ class DualCultivation(MutualAction):
|
||||
|
||||
# 生成恋爱/双修小故事
|
||||
start_text = self._start_event_content or result_event.content
|
||||
story = StoryTeller.tell_story(start_text, result_event.content, self.avatar, target, prompt=self.STORY_PROMPT)
|
||||
story = await StoryTeller.tell_story(start_text, result_event.content, self.avatar, target, prompt=self.STORY_PROMPT)
|
||||
story_event = Event(self.world.month_stamp, story, related_avatars=[self.avatar.id, target.id], is_story=True)
|
||||
events.append(story_event)
|
||||
else:
|
||||
|
||||
@@ -79,7 +79,7 @@ class GiftSpiritStone(MutualAction):
|
||||
# 目标获得灵石
|
||||
target.magic_stone += self.GIFT_AMOUNT
|
||||
|
||||
def finish(self, target_avatar: "Avatar|str") -> list[Event]:
|
||||
async def finish(self, target_avatar: "Avatar|str") -> list[Event]:
|
||||
target = self._get_target_avatar(target_avatar)
|
||||
events: list[Event] = []
|
||||
success = self._gift_success
|
||||
@@ -98,7 +98,7 @@ class GiftSpiritStone(MutualAction):
|
||||
# 生成赠送小故事
|
||||
from src.classes.story_teller import StoryTeller
|
||||
start_text = self._start_event_content or result_event.content
|
||||
story = StoryTeller.tell_story(
|
||||
story = await StoryTeller.tell_story(
|
||||
start_text,
|
||||
result_text,
|
||||
self.avatar,
|
||||
|
||||
@@ -90,7 +90,7 @@ class Impart(MutualAction):
|
||||
target.cultivation_progress.add_exp(exp_gain)
|
||||
self._impart_exp_gain = exp_gain
|
||||
|
||||
def finish(self, target_avatar: "Avatar|str") -> list[Event]:
|
||||
async def finish(self, target_avatar: "Avatar|str") -> list[Event]:
|
||||
target = self._get_target_avatar(target_avatar)
|
||||
events: list[Event] = []
|
||||
success = self._impart_success
|
||||
@@ -110,7 +110,7 @@ class Impart(MutualAction):
|
||||
# 生成师徒传道小故事
|
||||
from src.classes.story_teller import StoryTeller
|
||||
start_text = self._start_event_content or result_event.content
|
||||
story = StoryTeller.tell_story(
|
||||
story = await StoryTeller.tell_story(
|
||||
start_text,
|
||||
result_text,
|
||||
self.avatar,
|
||||
|
||||
@@ -7,7 +7,7 @@ import asyncio
|
||||
from src.classes.action.action import DefineAction, ActualActionMixin, LLMAction
|
||||
from src.classes.tile import get_avatar_distance
|
||||
from src.classes.event import Event
|
||||
from src.utils.llm import get_prompt_and_call_llm, get_prompt_and_call_llm_async
|
||||
from src.utils.llm import call_llm_with_template, LLMMode
|
||||
from src.utils.config import CONFIG
|
||||
from src.classes.relation import relation_display_names, Relation
|
||||
from src.classes.relations import get_possible_new_relations
|
||||
@@ -81,17 +81,10 @@ class MutualAction(DefineAction, LLMAction, TargetingMixin):
|
||||
"feedback_actions": feedback_actions,
|
||||
}
|
||||
|
||||
def _call_llm_feedback(self, infos: dict) -> dict:
|
||||
"""
|
||||
兼容保留:同步调用(不在事件循环内使用)。
|
||||
"""
|
||||
async def _call_llm_feedback(self, infos: dict) -> dict:
|
||||
"""异步调用 LLM 获取反馈"""
|
||||
template_path = self._get_template_path()
|
||||
res = get_prompt_and_call_llm(template_path, infos, mode="fast")
|
||||
return res
|
||||
|
||||
async def _call_llm_feedback_async(self, infos: dict) -> dict:
|
||||
template_path = self._get_template_path()
|
||||
return await get_prompt_and_call_llm_async(template_path, infos, mode="fast")
|
||||
return await call_llm_with_template(template_path, infos, LLMMode.FAST)
|
||||
|
||||
def _set_target_immediate_action(self, target_avatar: "Avatar", action_name: str, action_params: dict) -> None:
|
||||
"""
|
||||
@@ -132,16 +125,14 @@ class MutualAction(DefineAction, LLMAction, TargetingMixin):
|
||||
return self.find_avatar_by_name(target_avatar)
|
||||
return target_avatar
|
||||
|
||||
def _execute(self, target_avatar: "Avatar|str") -> None:
|
||||
"""
|
||||
保留同步实现(不在事件循环内使用)。
|
||||
"""
|
||||
async def _execute(self, target_avatar: "Avatar|str") -> None:
|
||||
"""异步执行互动动作"""
|
||||
target_avatar = self._get_target_avatar(target_avatar)
|
||||
if target_avatar is None:
|
||||
return
|
||||
|
||||
infos = self._build_prompt_infos(target_avatar)
|
||||
res = self._call_llm_feedback(infos)
|
||||
res = await self._call_llm_feedback(infos)
|
||||
r = res.get(infos["avatar_name_2"], {})
|
||||
thinking = r.get("thinking", "")
|
||||
feedback = r.get("feedback", "")
|
||||
@@ -209,12 +200,8 @@ class MutualAction(DefineAction, LLMAction, TargetingMixin):
|
||||
# 若无任务,创建异步任务
|
||||
if self._feedback_task is None and self._feedback_cached is None:
|
||||
infos = self._build_prompt_infos(target)
|
||||
try:
|
||||
loop = asyncio.get_running_loop()
|
||||
self._feedback_task = loop.create_task(self._call_llm_feedback_async(infos))
|
||||
except RuntimeError:
|
||||
# 无运行中的事件循环时,退化为同步调用(如离线批处理)
|
||||
self._feedback_cached = self._call_llm_feedback(infos)
|
||||
loop = asyncio.get_running_loop()
|
||||
self._feedback_task = loop.create_task(self._call_llm_feedback(infos))
|
||||
|
||||
# 若任务已完成,消费结果
|
||||
if self._feedback_task is not None and self._feedback_task.done():
|
||||
@@ -238,7 +225,7 @@ class MutualAction(DefineAction, LLMAction, TargetingMixin):
|
||||
|
||||
return ActionResult(status=ActionStatus.RUNNING, events=[])
|
||||
|
||||
def finish(self, target_avatar: "Avatar|str") -> list[Event]:
|
||||
async def finish(self, target_avatar: "Avatar|str") -> list[Event]:
|
||||
"""
|
||||
完成互动动作,事件已在 step 中处理,无需额外事件
|
||||
"""
|
||||
|
||||
@@ -10,7 +10,7 @@ if TYPE_CHECKING:
|
||||
|
||||
from src.classes.event import Event
|
||||
from src.utils.config import CONFIG
|
||||
from src.utils.llm import get_prompt_and_call_llm_async
|
||||
from src.utils.llm import call_llm_with_template, LLMMode
|
||||
from src.run.log import get_logger
|
||||
|
||||
logger = get_logger().logger
|
||||
@@ -73,7 +73,7 @@ async def generate_nickname(avatar: "Avatar") -> Optional[str]:
|
||||
}
|
||||
|
||||
# 调用LLM并自动解析JSON
|
||||
response_data = await get_prompt_and_call_llm_async(template_path, infos, mode="fast")
|
||||
response_data = await call_llm_with_template(template_path, infos, LLMMode.FAST)
|
||||
|
||||
nickname = response_data.get("nickname", "").strip()
|
||||
thinking = response_data.get("thinking", "")
|
||||
|
||||
@@ -3,8 +3,11 @@ from __future__ import annotations
|
||||
from typing import Dict, TYPE_CHECKING
|
||||
import random
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from src.classes.avatar import Avatar
|
||||
|
||||
from src.utils.config import CONFIG
|
||||
from src.utils.llm import get_prompt_and_call_llm, get_prompt_and_call_llm_async
|
||||
from src.utils.llm import call_llm_with_template, LLMMode
|
||||
|
||||
story_styles = [
|
||||
"平淡叙述:语句克制、少修饰、像旁观者记录。",
|
||||
@@ -67,32 +70,7 @@ class StoryTeller:
|
||||
return f"{event}。{res}。{style}"
|
||||
|
||||
@staticmethod
|
||||
def tell_story(event: str, res: str, *actors: "Avatar", prompt: str = "") -> str:
|
||||
"""
|
||||
生成小故事(同步版本)。
|
||||
基于 `static/templates/story.txt` 模板,失败时返回降级文案。
|
||||
|
||||
Args:
|
||||
event: 事件描述
|
||||
res: 结果描述
|
||||
*actors: 参与的角色(1-2个)
|
||||
prompt: 可选的故事提示词
|
||||
"""
|
||||
avatar_infos = StoryTeller._build_avatar_infos(*actors)
|
||||
infos = StoryTeller._build_template_data(event, res, avatar_infos, prompt)
|
||||
|
||||
try:
|
||||
data = get_prompt_and_call_llm(StoryTeller.TEMPLATE_PATH, infos, mode="fast")
|
||||
story = data.get("story", "").strip()
|
||||
if story:
|
||||
return story
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return StoryTeller._make_fallback_story(event, res, infos["style"])
|
||||
|
||||
@staticmethod
|
||||
async def tell_story_async(event: str, res: str, *actors: "Avatar", prompt: str = "") -> str:
|
||||
async def tell_story(event: str, res: str, *actors: "Avatar", prompt: str = "") -> str:
|
||||
"""
|
||||
生成小故事(异步版本)。
|
||||
基于 `static/templates/story.txt` 模板,失败时返回降级文案。
|
||||
@@ -107,8 +85,8 @@ class StoryTeller:
|
||||
infos = StoryTeller._build_template_data(event, res, avatar_infos, prompt)
|
||||
|
||||
try:
|
||||
data = await get_prompt_and_call_llm_async(StoryTeller.TEMPLATE_PATH, infos, mode="fast")
|
||||
story = str(data.get("story", "")).strip()
|
||||
data = await call_llm_with_template(StoryTeller.TEMPLATE_PATH, infos, LLMMode.FAST)
|
||||
story = data.get("story", "").strip()
|
||||
if story:
|
||||
return story
|
||||
except Exception:
|
||||
@@ -116,4 +94,5 @@ class StoryTeller:
|
||||
|
||||
return StoryTeller._make_fallback_story(event, res, infos["style"])
|
||||
|
||||
|
||||
__all__ = ["StoryTeller"]
|
||||
Reference in New Issue
Block a user