This commit is contained in:
SengokuCola
2025-05-01 22:50:29 +08:00
parent c4a7b842f6
commit 2669572b30
9 changed files with 220 additions and 188 deletions

View File

@@ -294,5 +294,5 @@ class BackgroundTaskManager:
await _run_periodic_loop(
task_name="Private Chat Activation Check",
interval=interval,
task_func=self.subheartflow_manager.sbhf_absent_private_into_focus
task_func=self.subheartflow_manager.sbhf_absent_private_into_focus,
)

View File

@@ -13,9 +13,8 @@ from src.plugins.utils.chat_message_builder import (
get_person_id_list,
)
from src.plugins.utils.prompt_builder import Prompt, global_prompt_manager
from src.plugins.chat.chat_stream import chat_manager
from typing import Optional
from src.plugins.person_info.person_info import person_info_manager
# Import the new utility function
from .utils_chat import get_chat_type_and_target_info
@@ -26,14 +25,14 @@ Prompt(
"""这是qq群聊的聊天记录请总结以下聊天记录的主题
{chat_logs}
请用一句话概括,包括人物、事件和主要信息,不要分点。""",
"chat_summary_group_prompt" # Template for group chat
"chat_summary_group_prompt", # Template for group chat
)
Prompt(
"""这是你和{chat_target}的私聊记录,请总结以下聊天记录的主题:
{chat_logs}
请用一句话概括,包括事件,时间,和主要信息,不要分点。""",
"chat_summary_private_prompt" # Template for private chat
"chat_summary_private_prompt", # Template for private chat
)
# --- End Prompt Template Definition ---
@@ -77,11 +76,12 @@ class ChattingObservation(Observation):
model=global_config.llm_observation, temperature=0.7, max_tokens=300, request_type="chat_observation"
)
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)
logger.debug(f"ChattingObservation {self.chat_id} initialized: is_group={self.is_group_chat}, target_info={self.chat_target_info}")
logger.debug(
f"ChattingObservation {self.chat_id} initialized: is_group={self.is_group_chat}, target_info={self.chat_target_info}"
)
# --- End using utility function ---
# Fetch initial messages (existing logic)
@@ -147,8 +147,7 @@ class ChattingObservation(Observation):
if self.is_group_chat:
prompt_template_name = "chat_summary_group_prompt"
prompt = await global_prompt_manager.format_prompt(
prompt_template_name,
chat_logs=oldest_messages_str
prompt_template_name, chat_logs=oldest_messages_str
)
else:
# For private chat, add chat_target to the prompt variables
@@ -157,14 +156,18 @@ class ChattingObservation(Observation):
chat_target_name = "对方" # Default fallback
if self.chat_target_info:
# Prioritize person_name, then nickname
chat_target_name = self.chat_target_info.get('person_name') or self.chat_target_info.get('user_nickname') or chat_target_name
chat_target_name = (
self.chat_target_info.get("person_name")
or self.chat_target_info.get("user_nickname")
or chat_target_name
)
# Format the private chat prompt
prompt = await global_prompt_manager.format_prompt(
prompt_template_name,
# Assuming the private prompt template uses {chat_target}
chat_target=chat_target_name,
chat_logs=oldest_messages_str
chat_logs=oldest_messages_str,
)
except Exception as e:
logger.error(f"构建总结 Prompt 失败 for chat {self.chat_id}: {e}")
@@ -183,7 +186,6 @@ class ChattingObservation(Observation):
else:
logger.warning(f"因 Prompt 构建失败,跳过 LLM 总结 for chat {self.chat_id}")
mid_memory = {
"id": str(int(datetime.now().timestamp())),
"theme": summary,

View File

@@ -13,7 +13,6 @@ from src.plugins.heartFC_chat.normal_chat import NormalChat
from src.heart_flow.mai_state_manager import MaiStateInfo
from src.heart_flow.chat_state_info import ChatState, ChatStateInfo
from src.heart_flow.sub_mind import SubMind
from src.plugins.person_info.person_info import person_info_manager
from .utils_chat import get_chat_type_and_target_info
@@ -276,8 +275,12 @@ class SubHeartflow:
# --- 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}")
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}"
)
# --- End using utility function ---
# Initialize interest system (existing logic)

View File

@@ -152,7 +152,7 @@ class SubMind:
# 获取观察对象
observation = self.observations[0] if self.observations else None
if not observation or not hasattr(observation, 'is_group_chat'): # Ensure it's ChattingObservation or similar
if not observation or not hasattr(observation, "is_group_chat"): # Ensure it's ChattingObservation or similar
logger.error(f"{self.log_prefix} 无法获取有效的观察对象或缺少聊天类型信息")
self.update_current_mind("(观察出错了...)")
return self.current_mind, self.past_mind
@@ -161,7 +161,9 @@ class SubMind:
chat_target_info = observation.chat_target_info
chat_target_name = "对方" # Default for private
if not is_group_chat and chat_target_info:
chat_target_name = chat_target_info.get('person_name') or chat_target_info.get('user_nickname') or chat_target_name
chat_target_name = (
chat_target_info.get("person_name") or chat_target_info.get("user_nickname") or chat_target_name
)
# --- End getting observation info ---
# 获取观察内容

View File

@@ -345,7 +345,8 @@ class SubHeartflowManager:
async with self._lock:
# 1. 筛选出所有 ABSENT 状态的 *群聊* 子心流
absent_group_subflows = [
hf for hf in self.subheartflows.values()
hf
for hf in self.subheartflows.values()
if hf.chat_state.chat_status == ChatState.ABSENT and hf.is_group_chat
]
@@ -700,7 +701,9 @@ class SubHeartflowManager:
log_reason = f"群聊随机选择 CHAT (当前 {current_chat_count}/{chat_limit})"
else:
target_state = ChatState.ABSENT # Fallback to ABSENT if CHAT limit reached
log_reason = f"群聊随机选择 CHAT 但已达上限 ({current_chat_count}/{chat_limit}),转为 ABSENT"
log_reason = (
f"群聊随机选择 CHAT 但已达上限 ({current_chat_count}/{chat_limit}),转为 ABSENT"
)
else: # 50% chance to go directly to ABSENT
target_state = ChatState.ABSENT
log_reason = "群聊随机选择 ABSENT"
@@ -732,6 +735,7 @@ class SubHeartflowManager:
logger.warning(
f"[状态转换请求] 收到对 {stream_name} 的请求,但其状态为 {current_state.value} (非 FOCUSED),不执行转换"
)
# --- 结束新增 --- #
# --- 新增:处理私聊从 ABSENT 直接到 FOCUSED 的逻辑 --- #
@@ -762,7 +766,8 @@ class SubHeartflowManager:
# --- 筛选出所有 ABSENT 状态的私聊子心流 --- #
eligible_subflows = [
hf for hf in self.subheartflows.values()
hf
for hf in self.subheartflows.values()
if hf.chat_state.chat_status == ChatState.ABSENT and not hf.is_group_chat
]
checked_count = len(eligible_subflows)
@@ -775,7 +780,9 @@ class SubHeartflowManager:
for sub_hf in eligible_subflows:
# --- 再次检查 FOCUSED 上限,因为可能有多个同时激活 --- #
if current_focused_count >= focused_limit:
logger.debug(f"{log_prefix_task} 已达专注上限 ({current_focused_count}/{focused_limit}),停止检查后续私聊。")
logger.debug(
f"{log_prefix_task} 已达专注上限 ({current_focused_count}/{focused_limit}),停止检查后续私聊。"
)
break # 已满,无需再检查其他私聊
flow_id = sub_hf.subheartflow_id
@@ -805,7 +812,9 @@ class SubHeartflowManager:
# --- 如果活跃且未达上限,则尝试转换 --- #
if is_active:
logger.info(f"{log_prefix} 检测到活跃且未达专注上限 ({current_focused_count}/{focused_limit}),尝试转换为 FOCUSED。")
logger.info(
f"{log_prefix} 检测到活跃且未达专注上限 ({current_focused_count}/{focused_limit}),尝试转换为 FOCUSED。"
)
await sub_hf.change_chat_state(ChatState.FOCUSED)
# 确认转换成功
if sub_hf.chat_state.chat_status == ChatState.FOCUSED:
@@ -813,7 +822,9 @@ class SubHeartflowManager:
current_focused_count += 1 # 更新计数器以供本轮后续检查
logger.info(f"{log_prefix} 成功进入 FOCUSED 状态。")
else:
logger.warning(f"{log_prefix} 尝试进入 FOCUSED 状态失败。当前状态: {sub_hf.chat_state.chat_status.value}")
logger.warning(
f"{log_prefix} 尝试进入 FOCUSED 状态失败。当前状态: {sub_hf.chat_state.chat_status.value}"
)
# else: # 不活跃,无需操作
# logger.debug(f"{log_prefix} 未检测到新活动,保持 ABSENT。")
@@ -822,7 +833,9 @@ class SubHeartflowManager:
# --- 循环结束后记录总结日志 --- #
if transitioned_count > 0:
logger.debug(f"{log_prefix_task} 完成,共检查 {checked_count} 个私聊,{transitioned_count} 个转换为 FOCUSED。")
logger.debug(
f"{log_prefix_task} 完成,共检查 {checked_count} 个私聊,{transitioned_count} 个转换为 FOCUSED。"
)
# --- 结束新增 --- #

View File

@@ -6,6 +6,7 @@ from src.plugins.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]]:
"""
获取聊天类型(是否群聊)和私聊对象信息。
@@ -38,11 +39,11 @@ async def get_chat_type_and_target_info(chat_id: str) -> Tuple[bool, Optional[Di
# Initialize target_info with basic info
target_info = {
'platform': platform,
'user_id': user_id,
'user_nickname': user_info.user_nickname,
'person_id': None,
'person_name': None
"platform": platform,
"user_id": user_id,
"user_nickname": user_info.user_nickname,
"person_id": None,
"person_name": None,
}
# Try to fetch person info
@@ -54,10 +55,12 @@ async def get_chat_type_and_target_info(chat_id: str) -> Tuple[bool, Optional[Di
# get_value is async, so await it directly
person_name = await person_info_manager.get_value(person_id, "person_name")
target_info['person_id'] = person_id
target_info['person_name'] = person_name
target_info["person_id"] = person_id
target_info["person_name"] = person_name
except Exception as person_e:
logger.warning(f"获取 person_id 或 person_name 时出错 for {platform}:{user_id} in utils: {person_e}")
logger.warning(
f"获取 person_id 或 person_name 时出错 for {platform}:{user_id} in utils: {person_e}"
)
chat_target_info = target_info
else:

View File

@@ -26,7 +26,6 @@ from .heartFC_sender import HeartFCSender
from src.plugins.chat.utils import process_llm_response
from src.plugins.respon_info_catcher.info_catcher import info_catcher_manager
from src.plugins.moods.moods import MoodManager
from src.individuality.individuality import Individuality
from src.heart_flow.utils_chat import get_chat_type_and_target_info
@@ -255,7 +254,9 @@ class HeartFChatting:
try:
self.chat_stream = await asyncio.to_thread(chat_manager.get_stream, self.stream_id)
if not self.chat_stream:
logger.error(f"[HFC:{self.stream_id}] 获取ChatStream失败 during _initialize, though util func might have succeeded earlier.")
logger.error(
f"[HFC:{self.stream_id}] 获取ChatStream失败 during _initialize, though util func might have succeeded earlier."
)
return False # Cannot proceed without chat_stream object
# Update log prefix using the fetched stream object
self.log_prefix = f"[{chat_manager.get_stream_name(self.stream_id) or self.stream_id}]"
@@ -263,7 +264,9 @@ class HeartFChatting:
logger.error(f"[HFC:{self.stream_id}] 获取ChatStream时出错 in _initialize: {e}")
return False
logger.debug(f"{self.log_prefix} HeartFChatting initialized: is_group={self.is_group_chat}, target_info={self.chat_target_info}")
logger.debug(
f"{self.log_prefix} HeartFChatting initialized: is_group={self.is_group_chat}, target_info={self.chat_target_info}"
)
# --- End using utility function ---
self._initialized = True
@@ -859,7 +862,7 @@ class HeartFChatting:
observed_messages_str=observed_messages_str, # <-- Pass local variable
current_mind=current_mind, # <-- Pass argument
structured_info=self.sub_mind.structured_info, # <-- Pass SubMind info
current_available_actions=current_available_actions # <-- Pass determined actions
current_available_actions=current_available_actions, # <-- Pass determined actions
)
# --- 调用 LLM (普通文本生成) ---
@@ -1283,7 +1286,11 @@ class HeartFChatting:
sender_name_for_prompt = "某人" # Default for group or if info unavailable
if not self.is_group_chat and self.chat_target_info:
# Prioritize person_name, then nickname
sender_name_for_prompt = self.chat_target_info.get('person_name') or self.chat_target_info.get('user_nickname') or sender_name_for_prompt
sender_name_for_prompt = (
self.chat_target_info.get("person_name")
or self.chat_target_info.get("user_nickname")
or sender_name_for_prompt
)
# --- End determining sender_name ---
# 3. 构建 Prompt

View File

@@ -302,10 +302,8 @@ class PromptBuilder:
current_mind_info=None,
structured_info=None,
message_txt=None,
sender_name = "某人",
sender_name="某人",
) -> Optional[str]:
is_group_chat = bool(chat_stream.group_info)
if build_mode == "normal":
return await self._build_prompt_normal(chat_stream, message_txt, sender_name)
@@ -332,7 +330,9 @@ class PromptBuilder:
limit=global_config.observation_context_size,
)
elif chat_stream.user_info:
who_chat_in_group.append((chat_stream.user_info.platform, chat_stream.user_info.user_id, chat_stream.user_info.user_nickname))
who_chat_in_group.append(
(chat_stream.user_info.platform, chat_stream.user_info.user_id, chat_stream.user_info.user_nickname)
)
relation_prompt = ""
for person in who_chat_in_group:
@@ -425,8 +425,6 @@ class PromptBuilder:
end_time = time.time()
logger.debug(f"知识检索耗时: {(end_time - start_time):.3f}")
if global_config.ENABLE_SCHEDULE_GEN:
schedule_prompt = await global_prompt_manager.format_prompt(
"schedule_prompt", schedule_info=bot_schedule.get_current_num_task(num=1, time_info=False)
@@ -764,7 +762,9 @@ class PromptBuilder:
chat_context_description = "你现在正在一个群聊中"
chat_target_name = None # Only relevant for private
if not is_group_chat and chat_target_info:
chat_target_name = chat_target_info.get('person_name') or chat_target_info.get('user_nickname') or "对方"
chat_target_name = (
chat_target_info.get("person_name") or chat_target_info.get("user_nickname") or "对方"
)
chat_context_description = f"你正在和 {chat_target_name} 私聊"
# --- End determining chat context ---

View File

@@ -63,7 +63,9 @@ class NormalChat:
self.is_group_chat, self.chat_target_info = await get_chat_type_and_target_info(self.stream_id)
# Update stream_name again after potential async call in util func
self.stream_name = chat_manager.get_stream_name(self.stream_id) or self.stream_id
logger.debug(f"[{self.stream_name}] NormalChat initialized: is_group={self.is_group_chat}, target_info={self.chat_target_info}")
logger.debug(
f"[{self.stream_name}] NormalChat initialized: is_group={self.is_group_chat}, target_info={self.chat_target_info}"
)
# --- End using utility function ---
self._initialized = True
logger.info(f"[{self.stream_name}] NormalChat 实例 initialize 完成 (异步部分)。")