better:优化了取得聊天信息和strbuild函数,异步改为同步,优化了observation的加载

This commit is contained in:
SengokuCola
2025-06-04 13:42:19 +08:00
parent 25711e5b88
commit e483d0d94a
27 changed files with 185 additions and 248 deletions

View File

@@ -72,7 +72,7 @@ def init_prompt():
class DefaultExpressor:
def __init__(self, chat_id: str):
def __init__(self, chat_stream: ChatStream):
self.log_prefix = "expressor"
# TODO: API-Adapter修改标记
self.express_model = LLMRequest(
@@ -83,13 +83,9 @@ class DefaultExpressor:
)
self.heart_fc_sender = HeartFCSender()
self.chat_id = chat_id
self.chat_stream: Optional[ChatStream] = None
self.is_group_chat = True
self.chat_target_info = None
async def initialize(self):
self.is_group_chat, self.chat_target_info = await get_chat_type_and_target_info(self.chat_id)
self.chat_id = chat_stream.stream_id
self.chat_stream = chat_stream
self.is_group_chat, self.chat_target_info = get_chat_type_and_target_info(self.chat_id)
async def _create_thinking_message(self, anchor_message: Optional[MessageRecv], thinking_id: str):
"""创建思考消息 (尝试锚定到 anchor_message)"""
@@ -285,7 +281,7 @@ class DefaultExpressor:
timestamp=time.time(),
limit=global_config.focus_chat.observation_context_size,
)
chat_talking_prompt = await build_readable_messages(
chat_talking_prompt = build_readable_messages(
message_list_before_now,
replace_bot_name=True,
merge_messages=True,

View File

@@ -223,7 +223,7 @@ class ExpressionLearner:
return None
# 转化成str
chat_id: str = random_msg[0]["chat_id"]
# random_msg_str: str = await build_readable_messages(random_msg, timestamp_mode="normal")
# random_msg_str: str = build_readable_messages(random_msg, timestamp_mode="normal")
random_msg_str: str = await build_anonymous_messages(random_msg)
# print(f"random_msg_str:{random_msg_str}")

View File

@@ -2,6 +2,7 @@ import time
import os
from typing import Optional, Dict, Any
from src.common.logger_manager import get_logger
import json
logger = get_logger("hfc") # Logger Name Changed
@@ -111,14 +112,14 @@ class CycleDetail:
dir_name = "".join(
char for char in dir_name if char.isalnum() or char in ["_", "-", "/"] or "\u4e00" <= char <= "\u9fff"
)
print("dir_name:", dir_name)
# print("dir_name:", dir_name)
if dir_name and not os.path.exists(dir_name):
os.makedirs(dir_name, exist_ok=True)
# 写入文件
import json
file_path = os.path.join(dir_name, os.path.basename(file_path))
print("file_path:", file_path)
# print("file_path:", file_path)
with open(file_path, "a", encoding="utf-8") as f:
f.write(json.dumps(self.to_dict(), ensure_ascii=False) + "\n")

View File

@@ -21,6 +21,7 @@ from src.chat.focus_chat.info_processors.working_memory_processor import Working
# from src.chat.focus_chat.info_processors.action_processor import ActionProcessor
from src.chat.heart_flow.observation.hfcloop_observation import HFCloopObservation
from src.chat.heart_flow.observation.working_observation import WorkingMemoryObservation
from src.chat.heart_flow.observation.chatting_observation import ChattingObservation
from src.chat.heart_flow.observation.structure_observation import StructureObservation
from src.chat.heart_flow.observation.actions_observation import ActionObservation
from src.chat.focus_chat.info_processors.tool_processor import ToolProcessor
@@ -37,9 +38,15 @@ from src.config.config import global_config
install(extra_lines=3)
# 定义观察器映射:键是观察器名称,值是 (观察器类, 初始化参数)
OBSERVATION_CLASSES = {
"ChattingObservation": (ChattingObservation, "chat_id"),
"WorkingMemoryObservation": (WorkingMemoryObservation, "observe_id"),
"HFCloopObservation": (HFCloopObservation, "observe_id"),
"StructureObservation": (StructureObservation, "observe_id"),
}
# 定义处理器映射:键是处理器名称,值是 (处理器类, 可选的配置键名)
# 如果配置键名为 None则该处理器默认启用且不能通过 focus_chat_processor 配置禁用
PROCESSOR_CLASSES = {
"ChattingInfoProcessor": (ChattingInfoProcessor, None),
"MindProcessor": (MindProcessor, "mind_processor"),
@@ -81,7 +88,6 @@ class HeartFChatting:
def __init__(
self,
chat_id: str,
observations: list[Observation],
on_stop_focus_chat: Optional[Callable[[], Awaitable[None]]] = None,
):
"""
@@ -89,53 +95,44 @@ class HeartFChatting:
参数:
chat_id: 聊天流唯一标识符(如stream_id)
observations: 关联的观察列表
on_stop_focus_chat: 当收到stop_focus_chat命令时调用的回调函数
"""
# 基础属性
self.stream_id: str = chat_id # 聊天流ID
self.chat_stream: Optional[ChatStream] = None # 关联的聊天流
self.log_prefix: str = str(chat_id) # Initial default, will be updated
self.hfcloop_observation = HFCloopObservation(observe_id=self.stream_id)
self.chatting_observation = observations[0]
self.structure_observation = StructureObservation(observe_id=self.stream_id)
self.chat_stream = chat_manager.get_stream(self.stream_id)
self.log_prefix = f"[{chat_manager.get_stream_name(self.stream_id) or self.stream_id}]"
self.memory_activator = MemoryActivator()
self.working_memory = WorkingMemory(chat_id=self.stream_id)
self.working_observation = WorkingMemoryObservation(
observe_id=self.stream_id, working_memory=self.working_memory
)
# 初始化观察器
self.observations: List[Observation] = []
self._register_observations()
# 根据配置文件和默认规则确定启用的处理器
self.enabled_processor_names: List[str] = []
config_processor_settings = global_config.focus_chat_processor
self.enabled_processor_names = [
proc_name for proc_name, (_proc_class, config_key) in PROCESSOR_CLASSES.items()
if not config_key or getattr(config_processor_settings, config_key, True)
]
for proc_name, (_proc_class, config_key) in PROCESSOR_CLASSES.items():
if config_key: # 此处理器可通过配置控制
if getattr(config_processor_settings, config_key, True): # 默认启用 (如果配置中未指定该键)
self.enabled_processor_names.append(proc_name)
else: # 此处理器不在配置映射中 (config_key is None),默认启用
self.enabled_processor_names.append(proc_name)
# logger.info(f"{self.log_prefix} 将启用的处理器: {self.enabled_processor_names}")
logger.info(f"{self.log_prefix} 将启用的处理器: {self.enabled_processor_names}")
self.processors: List[BaseProcessor] = []
self._register_default_processors()
self.expressor = DefaultExpressor(chat_id=self.stream_id)
self.replyer = DefaultReplyer(chat_id=self.stream_id)
self.expressor = DefaultExpressor(chat_stream=self.chat_stream)
self.replyer = DefaultReplyer(chat_stream=self.chat_stream)
self.action_manager = ActionManager()
self.action_planner = PlannerFactory.create_planner(
log_prefix=self.log_prefix, action_manager=self.action_manager
)
self.action_modifier = ActionModifier(action_manager=self.action_manager)
self.action_observation = ActionObservation(observe_id=self.stream_id)
self.action_observation.set_action_manager(self.action_manager)
self.all_observations = observations
# 初始化状态控制
self._initialized = False
self._processing_lock = asyncio.Lock()
# 循环控制内部状态
@@ -151,41 +148,24 @@ class HeartFChatting:
# 存储回调函数
self.on_stop_focus_chat = on_stop_focus_chat
async def _initialize(self) -> bool:
"""
执行懒初始化操作
def _register_observations(self):
"""注册所有观察器"""
self.observations = [] # 清空已有的
功能:
1. 获取聊天类型(群聊/私聊)和目标信息
2. 获取聊天流对象
3. 设置日志前缀
for name, (observation_class, param_name) in OBSERVATION_CLASSES.items():
try:
# 根据参数名使用正确的参数
kwargs = {param_name: self.stream_id}
observation = observation_class(**kwargs)
self.observations.append(observation)
logger.debug(f"{self.log_prefix} 注册观察器 {name}")
except Exception as e:
logger.error(f"{self.log_prefix} 观察器 {name} 构造失败: {e}")
返回:
bool: 初始化是否成功
注意:
- 如果已经初始化过会直接返回True
- 需要获取chat_stream对象才能继续后续操作
"""
# 如果已经初始化过,直接返回成功
if self._initialized:
return True
try:
await self.expressor.initialize()
await self.replyer.initialize()
self.chat_stream = await asyncio.to_thread(chat_manager.get_stream, self.stream_id)
self.expressor.chat_stream = self.chat_stream
self.replyer.chat_stream = self.chat_stream
self.log_prefix = f"[{chat_manager.get_stream_name(self.stream_id) or self.stream_id}]"
except Exception as e:
logger.error(f"[HFC:{self.stream_id}] 初始化HFC时发生错误: {e}")
return False
# 标记初始化完成
self._initialized = True
logger.debug(f"{self.log_prefix} 初始化完成,准备开始处理消息")
return True
if self.observations:
logger.info(f"{self.log_prefix} 已注册观察器: {[o.__class__.__name__ for o in self.observations]}")
else:
logger.warning(f"{self.log_prefix} 没有注册任何观察器")
def _register_default_processors(self):
"""根据 self.enabled_processor_names 注册信息处理器"""
@@ -218,20 +198,12 @@ class HeartFChatting:
if self.processors:
logger.info(
f"{self.log_prefix}根据配置和默认规则注册处理器: {[p.__class__.__name__ for p in self.processors]}"
f"{self.log_prefix} 已注册处理器: {[p.__class__.__name__ for p in self.processors]}"
)
else:
logger.warning(f"{self.log_prefix} 没有注册任何处理器。这可能是由于配置错误或所有处理器都被禁用了。")
async def start(self):
"""
启动 HeartFChatting 的主循环。
注意:调用此方法前必须确保已经成功初始化。
"""
logger.info(f"{self.log_prefix} 开始认真聊天(HFC)...")
await self._start_loop_if_needed()
async def _start_loop_if_needed(self):
"""检查是否需要启动主循环,如果未激活则启动。"""
# 如果循环已经激活,直接返回
if self._loop_active:
@@ -313,7 +285,13 @@ class HeartFChatting:
self._current_cycle_detail.set_loop_info(loop_info)
self.hfcloop_observation.add_loop_info(self._current_cycle_detail)
# 从observations列表中获取HFCloopObservation
hfcloop_observation = next((obs for obs in self.observations if isinstance(obs, HFCloopObservation)), None)
if hfcloop_observation:
hfcloop_observation.add_loop_info(self._current_cycle_detail)
else:
logger.warning(f"{self.log_prefix} 未找到HFCloopObservation实例")
self._current_cycle_detail.timers = cycle_timers
# 防止循环过快消耗资源
@@ -457,35 +435,27 @@ class HeartFChatting:
async def _observe_process_plan_action_loop(self, cycle_timers: dict, thinking_id: str) -> dict:
try:
with Timer("观察", cycle_timers):
await self.chatting_observation.observe()
await self.working_observation.observe()
await self.hfcloop_observation.observe()
await self.structure_observation.observe()
observations: List[Observation] = []
observations.append(self.chatting_observation)
observations.append(self.working_observation)
observations.append(self.hfcloop_observation)
observations.append(self.structure_observation)
# 执行所有观察器的观察
for observation in self.observations:
await observation.observe()
loop_observation_info = {
"observations": observations,
"observations": self.observations,
}
self.all_observations = observations
with Timer("调整动作", cycle_timers):
# 处理特殊的观察
await self.action_modifier.modify_actions(observations=observations)
await self.action_modifier.modify_actions(observations=self.observations)
await self.action_observation.observe()
observations.append(self.action_observation)
self.observations.append(self.action_observation)
# 根据配置决定是否并行执行回忆和处理器阶段
# print(global_config.focus_chat.parallel_processing)
if global_config.focus_chat.parallel_processing:
# 并行执行回忆和处理器阶段
with Timer("并行回忆和处理", cycle_timers):
memory_task = asyncio.create_task(self.memory_activator.activate_memory(observations))
processor_task = asyncio.create_task(self._process_processors(observations, []))
memory_task = asyncio.create_task(self.memory_activator.activate_memory(self.observations))
processor_task = asyncio.create_task(self._process_processors(self.observations, []))
# 等待两个任务完成
running_memorys, (all_plan_info, processor_time_costs) = await asyncio.gather(
@@ -494,10 +464,10 @@ class HeartFChatting:
else:
# 串行执行
with Timer("回忆", cycle_timers):
running_memorys = await self.memory_activator.activate_memory(observations)
running_memorys = await self.memory_activator.activate_memory(self.observations)
with Timer("执行 信息处理器", cycle_timers):
all_plan_info, processor_time_costs = await self._process_processors(observations, running_memorys)
all_plan_info, processor_time_costs = await self._process_processors(self.observations, running_memorys)
loop_processor_info = {
"all_plan_info": all_plan_info,
@@ -530,7 +500,7 @@ class HeartFChatting:
logger.debug(f"{self.log_prefix} 麦麦想要:'{action_str}'")
success, reply_text, command = await self._handle_action(
action_type, reasoning, action_data, cycle_timers, thinking_id
action_type, reasoning, action_data, cycle_timers, thinking_id, self.observations
)
loop_action_info = {
@@ -566,6 +536,7 @@ class HeartFChatting:
action_data: dict,
cycle_timers: dict,
thinking_id: str,
observations: List[Observation],
) -> tuple[bool, str, str]:
"""
处理规划动作,使用动作工厂创建相应的动作处理器
@@ -589,7 +560,7 @@ class HeartFChatting:
reasoning=reasoning,
cycle_timers=cycle_timers,
thinking_id=thinking_id,
observations=self.all_observations,
observations=observations,
expressor=self.expressor,
replyer=self.replyer,
chat_stream=self.chat_stream,
@@ -613,7 +584,7 @@ class HeartFChatting:
success, reply_text = result
command = ""
logger.debug(
f"{self.log_prefix} 麦麦执行了'{action}', 原因'{reasoning}'返回结果'{success}', '{reply_text}', '{command}'"
f"{self.log_prefix} 麦麦执行了'{action}', 返回结果'{success}', '{reply_text}', '{command}'"
)
return success, reply_text, command

View File

@@ -180,8 +180,6 @@ class HeartFCMessageReceiver:
userinfo = message.message_info.user_info
messageinfo = message.message_info
# 2. 消息缓冲与流程序化
# await message_buffer.start_caching_messages(message)
chat = await chat_manager.get_or_create_stream(
platform=messageinfo.platform,
@@ -199,21 +197,8 @@ class HeartFCMessageReceiver:
):
return
# 4. 缓冲检查
# buffer_result = await message_buffer.query_buffer_result(message)
# if not buffer_result:
# msg_type = _get_message_type(message)
# type_messages = {
# "text": f"触发缓冲,消息:{message.processed_plain_text}",
# "image": "触发缓冲,表情包/图片等待中",
# "seglist": "触发缓冲,消息列表等待中",
# }
# logger.debug(type_messages.get(msg_type, "触发未知类型缓冲"))
# return
# 5. 消息存储
await self.storage.store_message(message, chat)
logger.trace(f"存储成功: {message.processed_plain_text}")
# 6. 兴趣度计算与更新
interested_rate, is_mentioned = await _calculate_interest(message)

View File

@@ -83,7 +83,7 @@ def init_prompt():
class DefaultReplyer:
def __init__(self, chat_id: str):
def __init__(self, chat_stream: ChatStream):
self.log_prefix = "replyer"
# TODO: API-Adapter修改标记
self.express_model = LLMRequest(
@@ -94,13 +94,9 @@ class DefaultReplyer:
)
self.heart_fc_sender = HeartFCSender()
self.chat_id = chat_id
self.chat_stream: Optional[ChatStream] = None
self.is_group_chat = True
self.chat_target_info = None
async def initialize(self):
self.is_group_chat, self.chat_target_info = await get_chat_type_and_target_info(self.chat_id)
self.chat_id = chat_stream.stream_id
self.chat_stream = chat_stream
self.is_group_chat, self.chat_target_info = get_chat_type_and_target_info(self.chat_id)
async def _create_thinking_message(self, anchor_message: Optional[MessageRecv], thinking_id: str):
"""创建思考消息 (尝试锚定到 anchor_message)"""
@@ -347,7 +343,7 @@ class DefaultReplyer:
timestamp=time.time(),
limit=global_config.focus_chat.observation_context_size,
)
chat_talking_prompt = await build_readable_messages(
chat_talking_prompt = build_readable_messages(
message_list_before_now,
replace_bot_name=True,
merge_messages=True,

View File

@@ -45,10 +45,7 @@ class ChattingObservation(Observation):
self.chat_id = chat_id
self.platform = "qq"
# --- Initialize attributes (defaults) ---
self.is_group_chat: bool = False
self.chat_target_info: Optional[dict] = None
# --- End Initialization ---
self.is_group_chat, self.chat_target_info = get_chat_type_and_target_info(self.chat_id)
# --- Other attributes initialized in __init__ ---
self.talking_message = []
@@ -66,6 +63,12 @@ class ChattingObservation(Observation):
self.oldest_messages_str = ""
self.compressor_prompt = ""
initial_messages = get_raw_msg_before_timestamp_with_chat(self.chat_id, self.last_observe_time, 10)
self.last_observe_time = initial_messages[-1]["time"] if initial_messages else self.last_observe_time
self.talking_message = initial_messages
self.talking_message_str = build_readable_messages(self.talking_message)
def to_dict(self) -> dict:
"""将观察对象转换为可序列化的字典"""
return {
@@ -84,16 +87,6 @@ class ChattingObservation(Observation):
"last_observe_time": self.last_observe_time,
}
async def initialize(self):
self.is_group_chat, self.chat_target_info = await get_chat_type_and_target_info(self.chat_id)
logger.debug(f"初始化observation: self.is_group_chat: {self.is_group_chat}")
logger.debug(f"初始化observation: self.chat_target_info: {self.chat_target_info}")
initial_messages = get_raw_msg_before_timestamp_with_chat(self.chat_id, self.last_observe_time, 10)
self.last_observe_time = initial_messages[-1]["time"] if initial_messages else self.last_observe_time
# logger.error(f"初始化observation: initial_messages: {initial_messages}\n\n\n\n{self.last_observe_time}")
self.talking_message = initial_messages
self.talking_message_str = await build_readable_messages(self.talking_message)
# 进行一次观察 返回观察结果observe_info
def get_observe_info(self, ids=None):
mid_memory_str = ""
@@ -226,7 +219,7 @@ class ChattingObservation(Observation):
self.talking_message = self.talking_message[messages_to_remove_count:] # 保留后半部分,即最新的
# print(f"压缩中oldest_messages: {oldest_messages}")
oldest_messages_str = await build_readable_messages(
oldest_messages_str = build_readable_messages(
messages=oldest_messages, timestamp_mode="normal_no_YMD", read_mark=0
)
@@ -270,13 +263,13 @@ class ChattingObservation(Observation):
# 构建中
# print(f"构建中self.talking_message: {self.talking_message}")
self.talking_message_str = await build_readable_messages(
self.talking_message_str = build_readable_messages(
messages=self.talking_message,
timestamp_mode="lite",
read_mark=last_obs_time_mark,
)
# print(f"构建中self.talking_message_str: {self.talking_message_str}")
self.talking_message_str_truncate = await build_readable_messages(
self.talking_message_str_truncate = build_readable_messages(
messages=self.talking_message,
timestamp_mode="normal_no_YMD",
read_mark=last_obs_time_mark,

View File

@@ -12,12 +12,12 @@ logger = get_logger("observation")
# 所有观察的基类
class WorkingMemoryObservation:
def __init__(self, observe_id, working_memory: WorkingMemory):
def __init__(self, observe_id):
self.observe_info = ""
self.observe_id = observe_id
self.last_observe_time = datetime.now().timestamp()
self.working_memory = working_memory
self.working_memory = WorkingMemory(chat_id=observe_id)
self.retrieved_working_memory = []

View File

@@ -41,11 +41,10 @@ class SubHeartflow:
self.chat_state_last_time: float = 0
self.history_chat_state: List[Tuple[ChatState, float]] = []
# --- Initialize attributes ---
self.is_group_chat: bool = False
self.chat_target_info: Optional[dict] = None
# --- End Initialization ---
self.is_group_chat, self.chat_target_info = get_chat_type_and_target_info(self.chat_id)
self.log_prefix = (
chat_manager.get_stream_name(self.subheartflow_id) or self.subheartflow_id
)
# 兴趣消息集合
self.interest_dict: Dict[str, tuple[MessageRecv, float, bool]] = {}
@@ -60,7 +59,7 @@ class SubHeartflow:
# 观察,目前只有聊天观察,可以载入多个
# 负责对处理过的消息进行观察
self.observations: List[ChattingObservation] = [] # 观察列表
# self.observations: List[ChattingObservation] = [] # 观察列表
# self.running_knowledges = [] # 运行中的知识,待完善
# 日志前缀 - Moved determination to initialize
@@ -69,16 +68,6 @@ class SubHeartflow:
async def initialize(self):
"""异步初始化方法,创建兴趣流并确定聊天类型"""
# --- Use utility function to determine chat type and fetch info ---
self.is_group_chat, self.chat_target_info = await get_chat_type_and_target_info(self.chat_id)
# Update log prefix after getting info (potential stream name)
self.log_prefix = (
chat_manager.get_stream_name(self.subheartflow_id) or self.subheartflow_id
) # Keep this line or adjust if utils provides name
# logger.debug(
# f"SubHeartflow {self.chat_id} initialized: is_group={self.is_group_chat}, target_info={self.chat_target_info}"
# )
# 根据配置决定初始状态
if global_config.chat.chat_mode == "focus":
logger.debug(f"{self.log_prefix} 配置为 focus 模式,将直接尝试进入 FOCUSED 状态。")
@@ -214,23 +203,17 @@ class SubHeartflow:
# 如果实例不存在,则创建并启动
logger.info(f"{log_prefix} 麦麦准备开始专注聊天...")
try:
# 创建 HeartFChatting 实例,并传递 从构造函数传入的 回调函数
self.heart_fc_instance = HeartFChatting(
chat_id=self.subheartflow_id,
observations=self.observations,
# observations=self.observations,
on_stop_focus_chat=self._handle_stop_focus_chat_request,
)
# 初始化并启动 HeartFChatting
if await self.heart_fc_instance._initialize():
await self.heart_fc_instance.start()
logger.debug(f"{log_prefix} 麦麦已成功进入专注聊天模式 (新实例已启动)。")
return True
else:
logger.error(f"{log_prefix} HeartFChatting 初始化失败,无法进入专注模式。")
self.heart_fc_instance = None # 初始化失败,清理实例
return False
await self.heart_fc_instance.start()
logger.debug(f"{log_prefix} 麦麦已成功进入专注聊天模式 (新实例已启动)。")
return True
except Exception as e:
logger.error(f"{log_prefix} 创建或启动 HeartFChatting 实例时出错: {e}")
logger.error(traceback.format_exc())

View File

@@ -98,9 +98,9 @@ class SubHeartflowManager:
)
# 首先创建并添加聊天观察者
observation = ChattingObservation(chat_id=subheartflow_id)
await observation.initialize()
new_subflow.add_observation(observation)
# observation = ChattingObservation(chat_id=subheartflow_id)
# await observation.initialize()
# new_subflow.add_observation(observation)
# 然后再进行异步初始化,此时 SubHeartflow 内部若需启动 HeartFChatting就能拿到 observation
await new_subflow.initialize()

View File

@@ -7,7 +7,7 @@ from src.person_info.person_info import person_info_manager
logger = get_logger("heartflow_utils")
async def get_chat_type_and_target_info(chat_id: str) -> Tuple[bool, Optional[Dict]]:
def get_chat_type_and_target_info(chat_id: str) -> Tuple[bool, Optional[Dict]]:
"""
获取聊天类型(是否群聊)和私聊对象信息。
@@ -24,8 +24,7 @@ async def get_chat_type_and_target_info(chat_id: str) -> Tuple[bool, Optional[Di
chat_target_info = None
try:
chat_stream = await asyncio.to_thread(chat_manager.get_stream, chat_id) # Use to_thread if get_stream is sync
# If get_stream is already async, just use: chat_stream = await chat_manager.get_stream(chat_id)
chat_stream = chat_manager.get_stream(chat_id)
if chat_stream:
if chat_stream.group_info:
@@ -49,11 +48,11 @@ async def get_chat_type_and_target_info(chat_id: str) -> Tuple[bool, Optional[Di
# Try to fetch person info
try:
# Assume get_person_id is sync (as per original code), keep using to_thread
person_id = await asyncio.to_thread(person_info_manager.get_person_id, platform, user_id)
person_id = person_info_manager.get_person_id(platform, user_id)
person_name = None
if person_id:
# get_value is async, so await it directly
person_name = await person_info_manager.get_value(person_id, "person_name")
person_name = person_info_manager.get_value_sync(person_id, "person_name")
target_info["person_id"] = person_id
target_info["person_name"] = person_name

View File

@@ -1422,7 +1422,7 @@ class ParahippocampalGyrus:
# 1. 使用 build_readable_messages 生成格式化文本
# build_readable_messages 只返回一个字符串,不需要解包
input_text = await build_readable_messages(
input_text = build_readable_messages(
messages,
merge_messages=True, # 合并连续消息
timestamp_mode="normal_no_YMD", # 使用 'YYYY-MM-DD HH:MM:SS' 格式

View File

@@ -40,8 +40,7 @@ class NormalChat:
# Interest dict
self.interest_dict = interest_dict
self.is_group_chat: bool = False
self.chat_target_info: Optional[dict] = None
self.is_group_chat, self.chat_target_info = get_chat_type_and_target_info(self.stream_id)
self.willing_amplifier = 1
self.start_time = time.time()
@@ -73,7 +72,6 @@ class NormalChat:
if self._initialized:
return
self.is_group_chat, self.chat_target_info = await get_chat_type_and_target_info(self.stream_id)
self.stream_name = chat_manager.get_stream_name(self.stream_id) or self.stream_id
# 初始化Normal Chat专用表达器

View File

@@ -21,6 +21,7 @@ def init_prompt():
"""
你的自我认知是:
{self_info_block}
请记住你的性格,身份和特点。
注意除了下面动作选项之外你在聊天中不能做其他任何事情这是你能力的边界现在请你选择合适的action:
@@ -32,37 +33,30 @@ def init_prompt():
- 其他action表示在普通回复的基础上执行相应的额外动作
你必须从上面列出的可用action中选择一个并说明原因。
你的决策必须以严格的 JSON 格式输出,且仅包含 JSON 内容,不要有任何其他文字或解释。
{moderation_prompt}
当前聊天上下文
你是群内的一员,你现在正在参与群内的闲聊,以下是群内的聊天内容
{chat_context}
基于以上聊天上下文和用户的最新消息选择最合适的action。
你以下面格式输出你选择的action
{{
"action": "action_name",
"reasoning": "说明你做出该action的原因",
"参数1": "参数1的值",
"参数2": "参数2的值",
"参数3": "参数3的值",
...
}}
请输出你的决策 JSON""",
以动作的输出要求,以严格的 JSON 格式输出,且仅包含 JSON 内容。
请输出你提取的JSON不要有任何其他文字或解释
""",
"normal_chat_planner_prompt",
)
Prompt(
"""
action_name: {action_name}
描述:{action_description}
参数
{action_parameters}
动作要求:
{action_require}""",
动作:{action_name}
该动作的描述:{action_description}
使用该动作的场景
{action_require}
输出要求:
{{
"action": "{action_name}",{action_parameters}
}}
""",
"normal_chat_action_prompt",
)
@@ -230,22 +224,26 @@ class NormalChatPlanner:
action_parameters = action_info.get("parameters", {})
action_require = action_info.get("require", [])
# 格式化参数
parameters_text = ""
for param_name, param_desc in action_parameters.items():
parameters_text += f" - {param_name}: {param_desc}\n"
if action_parameters:
param_text = "\n"
for param_name, param_description in action_parameters:
param_text += f' "{param_name}":"{param_description}"\n'
param_text = param_text.rstrip('\n')
else:
param_text = ""
# 格式化要求
require_text = ""
for req in action_require:
require_text += f" - {req}\n"
for require_item in action_require:
require_text += f"- {require_item}\n"
require_text = require_text.rstrip('\n')
# 构建单个动作的提示
action_prompt = await global_prompt_manager.format_prompt(
"normal_chat_action_prompt",
action_name=action_name,
action_description=action_description,
action_parameters=parameters_text,
action_parameters=param_text,
action_require=require_text,
)
action_options_text += action_prompt + "\n\n"

View File

@@ -190,7 +190,7 @@ class PromptBuilder:
timestamp=time.time(),
limit=global_config.focus_chat.observation_context_size,
)
chat_talking_prompt = await build_readable_messages(
chat_talking_prompt = build_readable_messages(
message_list_before_now,
replace_bot_name=True,
merge_messages=False,

View File

@@ -150,7 +150,7 @@ def num_new_messages_since_with_users(
return count_messages(message_filter=filter_query)
async def _build_readable_messages_internal(
def _build_readable_messages_internal(
messages: List[Dict[str, Any]],
replace_bot_name: bool = True,
merge_messages: bool = False,
@@ -214,7 +214,7 @@ async def _build_readable_messages_internal(
if replace_bot_name and user_id == global_config.bot.qq_account:
person_name = f"{global_config.bot.nickname}(你)"
else:
person_name = await person_info_manager.get_value(person_id, "person_name")
person_name = person_info_manager.get_value_sync(person_id, "person_name")
# 如果 person_name 未设置,则使用消息中的 nickname 或默认名称
if not person_name:
@@ -232,7 +232,7 @@ async def _build_readable_messages_internal(
aaa = match.group(1)
bbb = match.group(2)
reply_person_id = person_info_manager.get_person_id(platform, bbb)
reply_person_name = await person_info_manager.get_value(reply_person_id, "person_name")
reply_person_name = person_info_manager.get_value_sync(reply_person_id, "person_name")
if not reply_person_name:
reply_person_name = aaa
# 在内容前加上回复信息
@@ -249,7 +249,7 @@ async def _build_readable_messages_internal(
aaa = m.group(1)
bbb = m.group(2)
at_person_id = person_info_manager.get_person_id(platform, bbb)
at_person_name = await person_info_manager.get_value(at_person_id, "person_name")
at_person_name = person_info_manager.get_value_sync(at_person_id, "person_name")
if not at_person_name:
at_person_name = aaa
new_content += f"@{at_person_name}"
@@ -377,13 +377,13 @@ async def build_readable_messages_with_list(
将消息列表转换为可读的文本格式,并返回原始(时间戳, 昵称, 内容)列表。
允许通过参数控制格式化行为。
"""
formatted_string, details_list = await _build_readable_messages_internal(
formatted_string, details_list = _build_readable_messages_internal(
messages, replace_bot_name, merge_messages, timestamp_mode, truncate
)
return formatted_string, details_list
async def build_readable_messages(
def build_readable_messages(
messages: List[Dict[str, Any]],
replace_bot_name: bool = True,
merge_messages: bool = False,
@@ -398,7 +398,7 @@ async def build_readable_messages(
"""
if read_mark <= 0:
# 没有有效的 read_mark直接格式化所有消息
formatted_string, _ = await _build_readable_messages_internal(
formatted_string, _ = _build_readable_messages_internal(
messages, replace_bot_name, merge_messages, timestamp_mode, truncate
)
return formatted_string
@@ -410,10 +410,10 @@ async def build_readable_messages(
# 分别格式化
# 注意:这里决定对已读和未读部分都应用相同的 truncate 设置
# 如果需要不同的行为(例如只截断已读部分),需要调整这里的调用
formatted_before, _ = await _build_readable_messages_internal(
formatted_before, _ = _build_readable_messages_internal(
messages_before_mark, replace_bot_name, merge_messages, timestamp_mode, truncate
)
formatted_after, _ = await _build_readable_messages_internal(
formatted_after, _ = _build_readable_messages_internal(
messages_after_mark,
replace_bot_name,
merge_messages,

View File

@@ -273,7 +273,7 @@ class ActionPlanner:
if hasattr(observation_info, "new_messages_count") and observation_info.new_messages_count > 0:
if hasattr(observation_info, "unprocessed_messages") and observation_info.unprocessed_messages:
new_messages_list = observation_info.unprocessed_messages
new_messages_str = await build_readable_messages(
new_messages_str = build_readable_messages(
new_messages_list,
replace_bot_name=True,
merge_messages=False,

View File

@@ -89,7 +89,7 @@ class Conversation:
timestamp=time.time(),
limit=30, # 加载最近30条作为初始上下文可以调整
)
chat_talking_prompt = await build_readable_messages(
chat_talking_prompt = build_readable_messages(
initial_messages,
replace_bot_name=True,
merge_messages=False,

View File

@@ -366,7 +366,7 @@ class ObservationInfo:
# 更新历史记录字符串 (只使用最近一部分生成例如20条)
history_slice_for_str = self.chat_history[-20:]
try:
self.chat_history_str = await build_readable_messages(
self.chat_history_str = build_readable_messages(
history_slice_for_str,
replace_bot_name=True,
merge_messages=False,

View File

@@ -91,7 +91,7 @@ class GoalAnalyzer:
if observation_info.new_messages_count > 0:
new_messages_list = observation_info.unprocessed_messages
new_messages_str = await build_readable_messages(
new_messages_str = build_readable_messages(
new_messages_list,
replace_bot_name=True,
merge_messages=False,
@@ -224,7 +224,7 @@ class GoalAnalyzer:
async def analyze_conversation(self, goal, reasoning):
messages = self.chat_observer.get_cached_messages()
chat_history_text = await build_readable_messages(
chat_history_text = build_readable_messages(
messages,
replace_bot_name=True,
merge_messages=False,

View File

@@ -53,7 +53,7 @@ class KnowledgeFetcher:
Tuple[str, str]: (获取的知识, 知识来源)
"""
# 构建查询上下文
chat_history_text = await build_readable_messages(
chat_history_text = build_readable_messages(
chat_history,
replace_bot_name=True,
merge_messages=False,

View File

@@ -173,7 +173,7 @@ class ReplyGenerator:
chat_history_text = observation_info.chat_history_str
if observation_info.new_messages_count > 0 and observation_info.unprocessed_messages:
new_messages_list = observation_info.unprocessed_messages
new_messages_str = await build_readable_messages(
new_messages_str = build_readable_messages(
new_messages_list,
replace_bot_name=True,
merge_messages=False,

View File

@@ -361,6 +361,30 @@ class PersonInfoManager:
logger.trace(f"获取{person_id}{field_name}失败或值为None已返回默认值{default_value} (Peewee)")
return default_value
@staticmethod
def get_value_sync(person_id: str, field_name: str):
"""同步版本获取指定person_id文档的字段值若不存在该字段则返回该字段的全局默认值"""
if not person_id:
logger.debug("get_value_sync获取失败person_id不能为空")
return person_info_default.get(field_name)
if field_name not in PersonInfo._meta.fields:
if field_name in person_info_default:
logger.trace(f"字段'{field_name}'不在Peewee模型中但存在于默认配置中。返回配置默认值。")
return copy.deepcopy(person_info_default[field_name])
logger.debug(f"get_value_sync获取失败字段'{field_name}'未在Peewee模型和默认配置中定义。")
return None
record = PersonInfo.get_or_none(PersonInfo.person_id == person_id)
if record:
value = getattr(record, field_name)
if value is not None:
return value
default_value = copy.deepcopy(person_info_default.get(field_name))
logger.trace(f"获取{person_id}{field_name}失败或值为None已返回默认值{default_value} (Peewee)")
return default_value
@staticmethod
async def get_values(person_id: str, field_names: list) -> dict:
"""获取指定person_id文档的多个字段值若不存在该字段则返回该字段的全局默认值"""

View File

@@ -345,7 +345,7 @@ class RelationshipManager:
if user_messages:
readable_messages = await build_readable_messages(
readable_messages = build_readable_messages(
messages=user_messages,
replace_bot_name=True,
timestamp_mode="relative",

View File

@@ -36,9 +36,9 @@ def generate_config():
print("请记得编辑该文件填入您的火山引擎API 密钥。")
except IOError as e:
print(f"错误:无法写入配置文件 {config_file_path}。原因: {e}")
else:
print(f"配置文件已存在: {config_file_path}")
print("未进行任何更改。如果您想重新生成,请先删除或重命名现有文件。")
# else:
# print(f"配置文件已存在: {config_file_path}")
# print("未进行任何更改。如果您想重新生成,请先删除或重命名现有文件。")
if __name__ == "__main__":

View File

@@ -1,5 +1,5 @@
[inner]
version = "2.12.1"
version = "2.12.2"
#----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读----
#如果你想要修改配置文件请在修改后将version的值进行变更
@@ -95,16 +95,11 @@ talk_frequency_down_groups = [] #降低回复频率的群号码
[focus_chat] #专注聊天
think_interval = 3 # 思考间隔 单位秒,可以有效减少消耗
consecutive_replies = 1 # 连续回复能力,值越高,麦麦连续回复的概率越高
processor_max_time = 20 # 处理器最大时间,单位秒,如果超过这个时间,处理器会自动停止
observation_context_size = 20 # 观察到的最长上下文大小
compressed_length = 8 # 不能大于observation_context_size,心流上下文压缩的最短压缩长度超过心流观察到的上下文长度会压缩最短压缩长度为5
compress_length_limit = 4 #最多压缩份数,超过该数值的压缩上下文会被删除
# 不建议更改
planner_type = "simple" # 规划器类型可选值complex复杂规划器, simple简单规划器
[focus_chat_processor] # 专注聊天处理器打开可以实现更多功能但是会增加token消耗
self_identify_processor = true # 是否启用自我识别处理器
relation_processor = true # 是否启用关系识别处理器

View File

@@ -134,10 +134,8 @@ class TestBuildReadableMessages(unittest.TestCase):
simple_msgs = [test_msg]
# 运行内部函数
result_text, result_details = asyncio.run(
_build_readable_messages_internal(
simple_msgs, replace_bot_name=True, merge_messages=False, timestamp_mode="absolute", truncate=False
)
result_text, result_details = _build_readable_messages_internal(
simple_msgs, replace_bot_name=True, merge_messages=False, timestamp_mode="absolute", truncate=False
)
logger.info(f"内部函数返回结果: {result_text[:200] if result_text else ''}")