mirror of
https://github.com/Nezumi-2711/astrbot_plugin_qq_group_daily_analysis.git
synced 2026-09-22 13:38:43 +00:00
fix(analysis): 加强提示构建日志与空检查
- 增加prompt构建和消息提取的调试日志 - 在topic分析链路中跳过空消息和空prompt - 防止LLM调用传入空prompt导致的错误
This commit is contained in:
@@ -102,15 +102,25 @@ class BaseAnalyzer(ABC):
|
||||
"""
|
||||
try:
|
||||
# 1. 构建提示词
|
||||
logger.debug(f"{self.get_data_type()}分析开始构建prompt,输入数据类型: {type(data)}")
|
||||
logger.debug(f"{self.get_data_type()}分析输入数据长度: {len(data) if hasattr(data, '__len__') else 'N/A'}")
|
||||
|
||||
prompt = self.build_prompt(data)
|
||||
logger.info(f"开始{self.get_data_type()}分析,构建提示词完成")
|
||||
logger.debug(f"{self.get_data_type()}分析prompt长度: {len(prompt) if prompt else 0}")
|
||||
logger.debug(f"{self.get_data_type()}分析prompt前100字符: {prompt[:100] if prompt else 'None'}...")
|
||||
|
||||
# 检查 prompt 是否为空
|
||||
if not prompt or not prompt.strip():
|
||||
logger.warning(f"{self.get_data_type()}分析: prompt 为空或只包含空白字符,跳过LLM调用")
|
||||
return [], TokenUsage()
|
||||
|
||||
# 2. 调用LLM
|
||||
max_tokens = self.get_max_tokens()
|
||||
temperature = self.get_temperature()
|
||||
|
||||
response = await call_provider_with_retry(
|
||||
self.context, self.config_manager, prompt,
|
||||
self.context, self.config_manager, prompt,
|
||||
max_tokens, temperature, umo
|
||||
)
|
||||
|
||||
|
||||
@@ -52,6 +52,13 @@ class TopicAnalyzer(BaseAnalyzer):
|
||||
logger.error(f"build_prompt 期望列表,但收到: {type(messages)}")
|
||||
return ""
|
||||
|
||||
# 检查消息列表是否为空
|
||||
if not messages:
|
||||
logger.warning("build_prompt 收到空消息列表")
|
||||
return ""
|
||||
|
||||
logger.debug(f"build_prompt 第一条消息内容: {messages[0] if messages else '无'}")
|
||||
|
||||
# 提取文本消息
|
||||
text_messages = []
|
||||
for i, msg in enumerate(messages):
|
||||
@@ -72,21 +79,61 @@ class TopicAnalyzer(BaseAnalyzer):
|
||||
nickname = sender.get("nickname", "") or sender.get("card", "")
|
||||
msg_time = datetime.fromtimestamp(msg.get("time", 0)).strftime("%H:%M")
|
||||
|
||||
for content in msg.get("message", []):
|
||||
if content.get("type") == "text":
|
||||
message_list = msg.get("message", [])
|
||||
logger.debug(f"build_prompt 消息 {i+1} 的 message 字段类型: {type(message_list)}, 长度: {len(message_list) if hasattr(message_list, '__len__') else 'N/A'}")
|
||||
|
||||
# 提取文本内容,可能分布在多个 content 中
|
||||
text_parts = []
|
||||
for j, content in enumerate(message_list):
|
||||
logger.debug(f"build_prompt 处理消息 {i+1} 的内容 {j+1}, 类型: {type(content)}")
|
||||
if not isinstance(content, dict):
|
||||
logger.warning(f"build_prompt 跳过非字典类型的内容: {type(content)} - {content}")
|
||||
continue
|
||||
|
||||
content_type = content.get("type", "")
|
||||
logger.debug(f"build_prompt 内容类型: {content_type}")
|
||||
|
||||
if content_type == "text":
|
||||
text = content.get("data", {}).get("text", "").strip()
|
||||
if text and len(text) > 2 and not text.startswith("/"):
|
||||
# 清理消息内容
|
||||
text = text.replace('“', '"').replace('”', '"')
|
||||
text = text.replace('‘', "'").replace('’', "'")
|
||||
text = text.replace('\n', ' ').replace('\r', ' ')
|
||||
text = text.replace('\t', ' ')
|
||||
text = re.sub(r'[\x00-\x1f\x7f-\x9f]', '', text)
|
||||
text_messages.append({
|
||||
"sender": nickname,
|
||||
"time": msg_time,
|
||||
"content": text.strip()
|
||||
})
|
||||
logger.debug(f"build_prompt 提取到的文本: '{text}' (长度: {len(text)})")
|
||||
if text:
|
||||
text_parts.append(text)
|
||||
elif content_type == "at":
|
||||
# 处理 @ 消息,转换为文本
|
||||
at_qq = content.get("data", {}).get("qq", "")
|
||||
if at_qq:
|
||||
at_text = f"@{at_qq}"
|
||||
text_parts.append(at_text)
|
||||
logger.debug(f"build_prompt 提取到@消息: {at_text}")
|
||||
elif content_type == "reply":
|
||||
# 处理回复消息,添加标记
|
||||
reply_id = content.get("data", {}).get("id", "")
|
||||
if reply_id:
|
||||
reply_text = f"[回复:{reply_id}]"
|
||||
text_parts.append(reply_text)
|
||||
logger.debug(f"build_prompt 提取到回复消息: {reply_text}")
|
||||
|
||||
# 合并所有文本部分
|
||||
combined_text = "".join(text_parts).strip()
|
||||
logger.debug(f"build_prompt 合并后的文本: '{combined_text}' (长度: {len(combined_text)})")
|
||||
|
||||
if combined_text and len(combined_text) > 2 and not combined_text.startswith("/"):
|
||||
# 清理消息内容
|
||||
cleaned_text = combined_text.replace('“', '"').replace('”', '"')
|
||||
cleaned_text = cleaned_text.replace('‘', "'").replace('’', "'")
|
||||
cleaned_text = cleaned_text.replace('\n', ' ').replace('\r', ' ')
|
||||
cleaned_text = cleaned_text.replace('\t', ' ')
|
||||
cleaned_text = re.sub(r'[\x00-\x1f\x7f-\x9f]', '', cleaned_text)
|
||||
|
||||
logger.debug(f"build_prompt 清理后的文本: '{cleaned_text}'")
|
||||
|
||||
text_messages.append({
|
||||
"sender": nickname,
|
||||
"time": msg_time,
|
||||
"content": cleaned_text
|
||||
})
|
||||
else:
|
||||
logger.debug(f"build_prompt 跳过文本: '{combined_text}' (长度不足或以/开头)")
|
||||
except Exception as e:
|
||||
logger.error(f"build_prompt 处理第 {i+1} 条消息时出错: {e}", exc_info=True)
|
||||
continue
|
||||
@@ -94,8 +141,11 @@ class TopicAnalyzer(BaseAnalyzer):
|
||||
logger.debug(f"build_prompt 提取到 {len(text_messages)} 条文本消息")
|
||||
|
||||
if not text_messages:
|
||||
logger.warning("build_prompt 没有提取到有效的文本消息,返回空prompt")
|
||||
return ""
|
||||
|
||||
logger.debug(f"build_prompt 第一条文本消息: {text_messages[0] if text_messages else '无'}")
|
||||
|
||||
# 构建消息文本
|
||||
messages_text = "\n".join([
|
||||
f"[{msg['time']}] {msg['sender']}: {msg['content']}"
|
||||
@@ -103,6 +153,10 @@ class TopicAnalyzer(BaseAnalyzer):
|
||||
])
|
||||
|
||||
max_topics = self.get_max_count()
|
||||
|
||||
logger.debug(f"build_prompt 准备构建prompt,max_topics={max_topics}")
|
||||
logger.debug(f"build_prompt messages_text 长度: {len(messages_text)}")
|
||||
|
||||
prompt = f"""
|
||||
你是一个帮我进行群聊信息总结的助手,生成总结内容时,你需要严格遵守下面的几个准则:
|
||||
请分析接下来提供的群聊记录,提取出最多{max_topics}个主要话题。
|
||||
@@ -144,6 +198,8 @@ class TopicAnalyzer(BaseAnalyzer):
|
||||
|
||||
注意:返回的内容必须是纯JSON,不要包含markdown代码块标记或其他格式
|
||||
"""
|
||||
logger.debug(f"build_prompt 构建的prompt长度: {len(prompt)}")
|
||||
logger.debug(f"build_prompt prompt前100字符: {prompt[:100]}...")
|
||||
return prompt
|
||||
|
||||
def extract_with_regex(self, result_text: str, max_topics: int) -> List[Dict]:
|
||||
@@ -233,6 +289,12 @@ class TopicAnalyzer(BaseAnalyzer):
|
||||
提取的文本消息列表
|
||||
"""
|
||||
logger.debug(f"extract_text_messages 开始处理,输入消息数量: {len(messages) if messages else 0}")
|
||||
logger.debug(f"extract_text_messages 输入消息类型: {type(messages)}")
|
||||
|
||||
if not messages:
|
||||
logger.warning("extract_text_messages 收到空消息列表")
|
||||
return []
|
||||
|
||||
text_messages = []
|
||||
|
||||
for i, msg in enumerate(messages):
|
||||
@@ -272,6 +334,8 @@ class TopicAnalyzer(BaseAnalyzer):
|
||||
continue
|
||||
|
||||
logger.debug(f"extract_text_messages 完成,提取到 {len(text_messages)} 条文本消息")
|
||||
if text_messages:
|
||||
logger.debug(f"extract_text_messages 第一条文本消息: {text_messages[0]}")
|
||||
return text_messages
|
||||
|
||||
async def analyze_topics(self, messages: List[Dict], umo: str = None) -> Tuple[List[SummaryTopic], TokenUsage]:
|
||||
@@ -292,7 +356,7 @@ class TopicAnalyzer(BaseAnalyzer):
|
||||
logger.debug(f"第一条消息类型: {type(messages[0]) if messages else '无'}")
|
||||
logger.debug(f"第一条消息内容: {messages[0] if messages else '无'}")
|
||||
|
||||
# 提取文本消息
|
||||
# 检查是否有有效的文本消息
|
||||
text_messages = self.extract_text_messages(messages)
|
||||
logger.debug(f"提取到 {len(text_messages)} 条文本消息")
|
||||
|
||||
@@ -306,7 +370,8 @@ class TopicAnalyzer(BaseAnalyzer):
|
||||
logger.debug(f"第一条文本消息类型: {type(text_messages[0])}")
|
||||
logger.debug(f"第一条文本消息内容: {text_messages[0]}")
|
||||
|
||||
return await self.analyze(text_messages, umo)
|
||||
# 直接传入原始消息,让 build_prompt 方法处理
|
||||
return await self.analyze(messages, umo)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"话题分析失败: {e}", exc_info=True)
|
||||
|
||||
@@ -39,6 +39,14 @@ async def call_provider_with_retry(context, config_manager, prompt: str, max_tok
|
||||
try:
|
||||
if custom_api_key and custom_api_base and custom_model:
|
||||
logger.info(f"使用自定义LLM提供商: {custom_api_base} model={custom_model}")
|
||||
logger.debug(f"自定义LLM提供商 prompt 长度: {len(prompt) if prompt else 0}")
|
||||
logger.debug(f"自定义LLM提供商 prompt 前100字符: {prompt[:100] if prompt else 'None'}...")
|
||||
|
||||
# 检查 prompt 是否为空
|
||||
if not prompt or not prompt.strip():
|
||||
logger.error("自定义LLM提供商: prompt 为空或只包含空白字符,无法发送请求")
|
||||
return None
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
headers = {
|
||||
"Authorization": f"Bearer {custom_api_key}",
|
||||
@@ -98,6 +106,15 @@ async def call_provider_with_retry(context, config_manager, prompt: str, max_tok
|
||||
if not provider:
|
||||
logger.error("provider 为空,无法调用 text_chat,直接返回 None")
|
||||
return None
|
||||
|
||||
logger.debug(f"LLM provider prompt 长度: {len(prompt) if prompt else 0}")
|
||||
logger.debug(f"LLM provider prompt 前100字符: {prompt[:100] if prompt else 'None'}...")
|
||||
|
||||
# 检查 prompt 是否为空
|
||||
if not prompt or not prompt.strip():
|
||||
logger.error("LLM provider: prompt 为空或只包含空白字符,无法调用 text_chat")
|
||||
return None
|
||||
|
||||
coro = provider.text_chat(prompt=prompt, max_tokens=max_tokens, temperature=temperature)
|
||||
return await asyncio.wait_for(coro, timeout=timeout)
|
||||
except asyncio.TimeoutError as e:
|
||||
|
||||
Reference in New Issue
Block a user