聊天记录互通完全回归😋

This commit is contained in:
tt-P607
2025-10-20 10:57:02 +08:00
committed by Windpicker-owo
parent 1ff266237d
commit 237e53c847

View File

@@ -125,33 +125,55 @@ async def build_cross_context_s4u(
) -> str: ) -> str:
""" """
构建跨群聊/私聊上下文 (S4U模式)。 构建跨群聊/私聊上下文 (S4U模式)。
优先展示目标用户的私聊记录(双向),其次按时间顺序展示其他群聊记录。
基于全局S4U配置检索目标用户在其他聊天中的发言。
""" """
logger.info("[S4U] Starting S4U context build.") logger.debug("[S4U] Starting S4U context build.")
cross_context_config = global_config.cross_context cross_context_config = global_config.cross_context
if not target_user_info or not (user_id := target_user_info.get("user_id")): if not target_user_info or not (user_id := target_user_info.get("user_id")):
logger.warning(f"[S4U] Failed: target_user_info ({target_user_info}) or user_id is missing.") logger.warning(f"[S4U] Failed: target_user_info ({target_user_info}) or user_id is missing.")
return "" return ""
logger.info(f"[S4U] Target user ID: {user_id}") logger.debug(f"[S4U] Target user ID: {user_id}")
chat_manager = get_chat_manager() chat_manager = get_chat_manager()
all_user_messages = [] private_context_block = ""
group_context_blocks = []
# 1. 根据全局S4U配置确定要扫描的聊天范围 # --- 1. 优先处理私聊上下文 ---
private_stream_id = chat_manager.get_stream_id(chat_stream.platform, user_id, is_group=False)
if private_stream_id and private_stream_id != chat_stream.stream_id:
logger.debug(f"[S4U] Found private chat with target user: {private_stream_id}")
try:
user_ids_to_fetch = [str(user_id), str(global_config.bot.qq_account)]
messages_by_stream = await get_user_messages_from_streams(
user_ids=user_ids_to_fetch,
stream_ids=[private_stream_id],
timestamp_after=time.time() - (3 * 24 * 60 * 60), # 3天
limit_per_stream=cross_context_config.s4u_limit,
)
if private_messages := messages_by_stream.get(private_stream_id):
chat_name = await chat_manager.get_stream_name(private_stream_id) or "私聊"
title = f'[以下是您与"{chat_name}"的近期私聊记录]\n'
formatted, _ = await build_readable_messages_with_id(private_messages, timestamp_mode="relative")
private_context_block = f"{title}{formatted}"
logger.debug(f"[S4U] Generated private context block of length {len(private_context_block)}.")
except Exception as e:
logger.error(f"S4U模式下处理私聊记录失败: {e}")
# --- 2. 处理其他群聊上下文 ---
streams_to_scan = [] streams_to_scan = []
# 根据全局S4U配置确定要扫描的聊天范围
if cross_context_config.s4u_mode == "whitelist": if cross_context_config.s4u_mode == "whitelist":
for chat_str in cross_context_config.s4u_whitelist_chats: for chat_str in cross_context_config.s4u_whitelist_chats:
try: try:
platform, chat_type, chat_raw_id = chat_str.split(":") platform, chat_type, chat_raw_id = chat_str.split(":")
is_group = chat_type == "group" is_group = chat_type == "group"
stream_id = chat_manager.get_stream_id(platform, chat_raw_id, is_group=is_group) stream_id = chat_manager.get_stream_id(platform, chat_raw_id, is_group=is_group)
if stream_id and stream_id != chat_stream.stream_id: if stream_id and stream_id != chat_stream.stream_id and stream_id != private_stream_id:
streams_to_scan.append(stream_id) streams_to_scan.append(stream_id)
except ValueError: except ValueError:
logger.warning(f"无效的S4U白名单格式: {chat_str}") logger.warning(f"无效的S4U白名单格式: {chat_str}")
else: # blacklist mode else: # blacklist mode
blacklisted_streams = set() blacklisted_streams = {private_stream_id}
for chat_str in cross_context_config.s4u_blacklist_chats: for chat_str in cross_context_config.s4u_blacklist_chats:
try: try:
platform, chat_type, chat_raw_id = chat_str.split(":") platform, chat_type, chat_raw_id = chat_str.split(":")
@@ -161,71 +183,58 @@ async def build_cross_context_s4u(
blacklisted_streams.add(stream_id) blacklisted_streams.add(stream_id)
except ValueError: except ValueError:
logger.warning(f"无效的S4U黑名单格式: {chat_str}") logger.warning(f"无效的S4U黑名单格式: {chat_str}")
for stream_id in chat_manager.streams: for stream_id in chat_manager.streams:
if stream_id != chat_stream.stream_id and stream_id not in blacklisted_streams: if stream_id != chat_stream.stream_id and stream_id not in blacklisted_streams:
streams_to_scan.append(stream_id) streams_to_scan.append(stream_id)
logger.info(f"[S4U] Scan mode: {cross_context_config.s4u_mode}.") logger.debug(f"[S4U] Found {len(streams_to_scan)} group streams to scan.")
logger.info(f"[S4U] Whitelist: {cross_context_config.s4u_whitelist_chats}, Blacklist: {cross_context_config.s4u_blacklist_chats}.")
logger.info(f"[S4U] Found {len(streams_to_scan)} streams to scan: {streams_to_scan}")
# 2. 从筛选出的聊天流中获取目标用户的消息 if streams_to_scan:
# 2. 从筛选出的聊天流中获取目标用户的消息 (优化后) messages_by_stream = await get_user_messages_from_streams(
limit = cross_context_config.s4u_limit user_ids=[str(user_id)],
# 约 3 天内的消息 stream_ids=streams_to_scan,
timestamp_after = time.time() - (3 * 24 * 60 * 60) timestamp_after=time.time() - (3 * 24 * 60 * 60),
limit_per_stream=cross_context_config.s4u_limit,
)
# 如果是私聊则同时获取bot自身的消息 all_group_messages = []
user_ids_to_fetch = [str(user_id)] for stream_id, user_messages in messages_by_stream.items():
if chat_stream.group_info is None: if user_messages:
user_ids_to_fetch.append(str(global_config.bot.qq_account)) latest_timestamp = max(msg.get("time", 0) for msg in user_messages)
all_group_messages.append(
{"stream_id": stream_id, "messages": user_messages, "latest_timestamp": latest_timestamp}
)
# 一次性批量查询 all_group_messages.sort(key=lambda x: x["latest_timestamp"], reverse=True)
messages_by_stream = await get_user_messages_from_streams(
user_ids=user_ids_to_fetch,
stream_ids=streams_to_scan,
timestamp_after=timestamp_after,
limit_per_stream=limit,
)
for stream_id, user_messages in messages_by_stream.items(): # 计算群聊的额度
if user_messages: remaining_limit = cross_context_config.s4u_stream_limit - (1 if private_context_block else 0)
logger.info(f"[S4U] Found {len(user_messages)} messages for user {user_id} in stream {stream_id}.") limited_group_messages = all_group_messages[:remaining_limit]
latest_timestamp = max(msg.get("time", 0) for msg in user_messages)
all_user_messages.append(
{"stream_id": stream_id, "messages": user_messages, "latest_timestamp": latest_timestamp}
)
# 3. 按最新消息时间排序,并根据 stream_limit 截断 for item in limited_group_messages:
all_user_messages.sort(key=lambda x: x["latest_timestamp"], reverse=True) try:
limited_messages = all_user_messages[: cross_context_config.s4u_stream_limit] chat_name = await chat_manager.get_stream_name(item["stream_id"]) or "未知群聊"
user_name = target_user_info.get("person_name") or target_user_info.get("user_nickname") or user_id
title = f'[以下是"{user_name}""{chat_name}"的近期发言]\n'
formatted, _ = await build_readable_messages_with_id(item["messages"], timestamp_mode="relative")
group_context_blocks.append(f"{title}{formatted}")
except Exception as e:
logger.error(f"S4U模式下格式化群聊消息失败 (stream: {item['stream_id']}): {e}")
# 4. 格式化最终的消息文本 # --- 3. 组合最终上下文 ---
cross_context_blocks = [] if not private_context_block and not group_context_blocks:
for item in limited_messages: logger.debug("[S4U] No context blocks were generated. Returning empty string.")
stream_id = item["stream_id"]
messages = item["messages"]
try:
chat_name = await chat_manager.get_stream_name(stream_id) or "未知聊天"
user_name = target_user_info.get("person_name") or target_user_info.get("user_nickname") or user_id
# 如果是私聊,标题不显示用户名
stream = await chat_manager.get_stream(stream_id)
is_private = stream.group_info is None if stream else False
title = f'[以下是您在"{chat_name}"的近期发言]\n' if is_private else f'[以下是"{user_name}""{chat_name}"的近期发言]\n'
formatted, _ = await build_readable_messages_with_id(messages, timestamp_mode="relative")
cross_context_blocks.append(f"{title}{formatted}")
except Exception as e:
logger.error(f"S4U模式下格式化消息失败 (stream: {stream_id}): {e}")
if not cross_context_blocks:
logger.info("[S4U] No context blocks were generated. Returning empty string.")
return "" return ""
final_context = "### 其他群聊中的聊天记录\n" + "\n\n".join(cross_context_blocks) + "\n" final_context_parts = []
logger.info(f"[S4U] Successfully generated {len(cross_context_blocks)} context blocks. Total length: {len(final_context)}.") if private_context_block:
final_context_parts.append(private_context_block)
if group_context_blocks:
group_context_str = "\n\n".join(group_context_blocks)
final_context_parts.append(f"### 其他群聊中的聊天记录\n{group_context_str}")
final_context = "\n\n".join(final_context_parts) + "\n"
logger.debug(f"[S4U] Successfully generated S4U context. Total length: {len(final_context)}.")
return final_context return final_context
@@ -337,3 +346,4 @@ async def get_intercom_group_context(group_name: str, limit_per_chat: int = 20,
return None return None
return "# 跨上下文参考\n" + "\n\n".join(cross_context_messages) + "\n" return "# 跨上下文参考\n" + "\n\n".join(cross_context_messages) + "\n"