聊天记录互通完全回归😋

This commit is contained in:
tt-P607
2025-10-20 10:57:02 +08:00
parent c1fa237ba7
commit 08b990eb70

View File

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