为群总结生成增加三次重试机制
- Dify 请求失败时不再立即返回失败 - 群总结生成过程最多重试 3 次 - 增加 2 秒、4 秒递增等待,降低偶发错误影响 - 仅在三次都失败后才返回生成总结失败结果 - 补充重试次数与等待时间日志,便于排查总结异常
This commit is contained in:
@@ -233,69 +233,75 @@ class MessageSummaryPlugin(MessagePluginInterface):
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
try:
|
||||
custom_timeout = ClientTimeout(total=None, connect=10, sock_read=300)
|
||||
conn = aiohttp.TCPConnector(keepalive_timeout=60) # 保持连接活跃
|
||||
async with aiohttp.ClientSession(connector=conn, timeout=custom_timeout) as session:
|
||||
async with session.post(self._api_url, headers=headers, json=data) as response:
|
||||
response.raise_for_status() # 检查请求是否成功
|
||||
response_data = await response.json()
|
||||
max_retries = 3
|
||||
retry_delays = [2, 4]
|
||||
|
||||
self.LOG.info(f"Dify API响应状态码: {response.status}")
|
||||
self.LOG.debug(f"响应数据: {json.dumps(response_data, ensure_ascii=False, indent=2)}")
|
||||
for attempt in range(1, max_retries + 1):
|
||||
try:
|
||||
custom_timeout = ClientTimeout(total=None, connect=10, sock_read=300)
|
||||
conn = aiohttp.TCPConnector(keepalive_timeout=60) # 保持连接活跃
|
||||
async with aiohttp.ClientSession(connector=conn, timeout=custom_timeout) as session:
|
||||
async with session.post(self._api_url, headers=headers, json=data) as response:
|
||||
response.raise_for_status() # 检查请求是否成功
|
||||
response_data = await response.json()
|
||||
|
||||
# 提取回答内容
|
||||
answer = response_data.get("answer", "")
|
||||
# 去除广告内容pollinations.ai 的广告
|
||||
# answer = remove_trailing_content(answer)
|
||||
spath = ""
|
||||
# 提取token使用情况
|
||||
metadata = response_data.get("metadata", {})
|
||||
usage = metadata.get("usage", {})
|
||||
self.LOG.info(f"Dify API响应状态码: {response.status}, attempt={attempt}")
|
||||
self.LOG.debug(f"响应数据: {json.dumps(response_data, ensure_ascii=False, indent=2)}")
|
||||
|
||||
if usage:
|
||||
prompt_tokens = usage.get("prompt_tokens", 0)
|
||||
completion_tokens = usage.get("completion_tokens", 0)
|
||||
total_tokens = usage.get("total_tokens", 0)
|
||||
# 提取回答内容
|
||||
answer = response_data.get("answer", "")
|
||||
# 去除广告内容pollinations.ai 的广告
|
||||
# answer = remove_trailing_content(answer)
|
||||
spath = ""
|
||||
# 提取token使用情况
|
||||
metadata = response_data.get("metadata", {})
|
||||
usage = metadata.get("usage", {})
|
||||
|
||||
# 添加token信息
|
||||
tokens_info = f"\n\n【tokens】输入: {prompt_tokens} 生成: {completion_tokens} 总: {total_tokens}"
|
||||
answer += tokens_info
|
||||
try:
|
||||
# 使用唯一文件名并指定完整路径
|
||||
timestamp = int(time.time())
|
||||
output_path = f"summary_{timestamp}.png"
|
||||
# 构建完整的输出路径
|
||||
self.LOG.info(f"开始生成图片: {output_path}")
|
||||
spath = await convert_md_str_to_image(answer, output_path)
|
||||
self.LOG.info(f"成功生成图片: {spath}")
|
||||
except Exception as e:
|
||||
self.LOG.error(f"生成图片失败: {e}", exc_info=True)
|
||||
# 如果图片生成失败,尝试发送纯文本消息
|
||||
if usage:
|
||||
prompt_tokens = usage.get("prompt_tokens", 0)
|
||||
completion_tokens = usage.get("completion_tokens", 0)
|
||||
total_tokens = usage.get("total_tokens", 0)
|
||||
|
||||
# 添加token信息
|
||||
tokens_info = f"\n\n【tokens】输入: {prompt_tokens} 生成: {completion_tokens} 总: {total_tokens}"
|
||||
answer += tokens_info
|
||||
try:
|
||||
# 截断过长的文本,避免消息太长
|
||||
max_length = 2000
|
||||
if len(answer) > max_length:
|
||||
answer = answer[:max_length] + "\n\n... (内容过长,已截断)"
|
||||
self.LOG.info("图片生成失败,将发送文本消息作为备选方案")
|
||||
spath = None # 设置为None,让调用方知道需要发送文本
|
||||
except Exception as fallback_error:
|
||||
self.LOG.error(f"备选文本发送也失败: {fallback_error}")
|
||||
spath = None
|
||||
# 返回文本内容和图片路径
|
||||
return answer, spath
|
||||
# 使用唯一文件名并指定完整路径
|
||||
timestamp = int(time.time())
|
||||
output_path = f"summary_{timestamp}.png"
|
||||
# 构建完整的输出路径
|
||||
self.LOG.info(f"开始生成图片: {output_path}")
|
||||
spath = await convert_md_str_to_image(answer, output_path)
|
||||
self.LOG.info(f"成功生成图片: {spath}")
|
||||
except Exception as e:
|
||||
self.LOG.error(f"生成图片失败: {e}", exc_info=True)
|
||||
# 如果图片生成失败,尝试发送纯文本消息
|
||||
try:
|
||||
# 截断过长的文本,避免消息太长
|
||||
max_length = 2000
|
||||
if len(answer) > max_length:
|
||||
answer = answer[:max_length] + "\n\n... (内容过长,已截断)"
|
||||
self.LOG.info("图片生成失败,将发送文本消息作为备选方案")
|
||||
spath = None # 设置为None,让调用方知道需要发送文本
|
||||
except Exception as fallback_error:
|
||||
self.LOG.error(f"备选文本发送也失败: {fallback_error}")
|
||||
spath = None
|
||||
# 返回文本内容和图片路径
|
||||
return answer, spath
|
||||
|
||||
except aiohttp.ClientError as e:
|
||||
self.LOG.error(f"请求Dify API时出错: {e}")
|
||||
return f"生成总结时出错", None
|
||||
except aiohttp.ClientError as e:
|
||||
self.LOG.error(f"请求Dify API时出错: attempt={attempt}/{max_retries}, error={e}")
|
||||
except json.JSONDecodeError as e:
|
||||
self.LOG.error(f"解析Dify API响应时出错: attempt={attempt}/{max_retries}, error={e}")
|
||||
except Exception as e:
|
||||
self.LOG.error(f"处理总结时出现未知错误: attempt={attempt}/{max_retries}, error={e}")
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
self.LOG.error(f"解析Dify API响应时出错: {e}")
|
||||
return "解析API响应时出错", None
|
||||
if attempt < max_retries:
|
||||
delay = retry_delays[attempt - 1] if attempt - 1 < len(retry_delays) else retry_delays[-1]
|
||||
self.LOG.warning(f"群总结生成失败,准备重试: attempt={attempt}/{max_retries}, delay={delay}s")
|
||||
await asyncio.sleep(delay)
|
||||
|
||||
except Exception as e:
|
||||
self.LOG.error(f"处理总结时出现未知错误: {e}")
|
||||
return f"生成总结时出现未知错误", None
|
||||
return "生成总结时出错", None
|
||||
|
||||
async def daily_summary_job(self):
|
||||
"""定时任务:每天早上9点总结昨天的聊天信息"""
|
||||
|
||||
Reference in New Issue
Block a user