Merge pull request #706 from SuanmoSuanyangTechnology/fix/activity

活动统计
This commit is contained in:
Ke Sun
2026-03-26 20:50:06 +08:00
committed by GitHub
2 changed files with 124 additions and 124 deletions

View File

@@ -1,124 +1,124 @@
""" # """
Recent Activity Stats Cache # Recent Activity Stats Cache
记忆提取活动统计缓存模块 # 记忆提取活动统计缓存模块
用于缓存每次记忆提取流程的统计数据,按 workspace_id 存储24小时后释放 # 用于缓存每次记忆提取流程的统计数据,按 workspace_id 存储24小时后释放
查询命令cache:memory:activity_stats:by_workspace:7de31a97-40a6-4fc0-b8d3-15c89f523843 # 查询命令cache:memory:activity_stats:by_workspace:7de31a97-40a6-4fc0-b8d3-15c89f523843
""" # """
import json # import json
import logging # import logging
from typing import Optional, Dict, Any # from typing import Optional, Dict, Any
from datetime import datetime # from datetime import datetime
from app.aioRedis import aio_redis # from app.aioRedis import aio_redis
logger = logging.getLogger(__name__) # logger = logging.getLogger(__name__)
# 缓存过期时间24小时 # # 缓存过期时间24小时
ACTIVITY_STATS_CACHE_EXPIRE = 86400 # ACTIVITY_STATS_CACHE_EXPIRE = 86400
class ActivityStatsCache: # class ActivityStatsCache:
"""记忆提取活动统计缓存类""" # """记忆提取活动统计缓存类"""
PREFIX = "cache:memory:activity_stats" # PREFIX = "cache:memory:activity_stats"
@classmethod # @classmethod
def _get_key(cls, workspace_id: str) -> str: # def _get_key(cls, workspace_id: str) -> str:
"""生成 Redis key # """生成 Redis key
Args: # Args:
workspace_id: 工作空间ID # workspace_id: 工作空间ID
Returns: # Returns:
完整的 Redis key # 完整的 Redis key
""" # """
return f"{cls.PREFIX}:by_workspace:{workspace_id}" # return f"{cls.PREFIX}:by_workspace:{workspace_id}"
@classmethod # @classmethod
async def set_activity_stats( # async def set_activity_stats(
cls, # cls,
workspace_id: str, # workspace_id: str,
stats: Dict[str, Any], # stats: Dict[str, Any],
expire: int = ACTIVITY_STATS_CACHE_EXPIRE, # expire: int = ACTIVITY_STATS_CACHE_EXPIRE,
) -> bool: # ) -> bool:
"""设置记忆提取活动统计缓存 # """设置记忆提取活动统计缓存
Args: # Args:
workspace_id: 工作空间ID # workspace_id: 工作空间ID
stats: 统计数据,格式: # stats: 统计数据,格式:
{ # {
"chunk_count": int, # "chunk_count": int,
"statements_count": int, # "statements_count": int,
"triplet_entities_count": int, # "triplet_entities_count": int,
"triplet_relations_count": int, # "triplet_relations_count": int,
"temporal_count": int, # "temporal_count": int,
} # }
expire: 过期时间默认24小时 # expire: 过期时间默认24小时
Returns: # Returns:
是否设置成功 # 是否设置成功
""" # """
try: # try:
key = cls._get_key(workspace_id) # key = cls._get_key(workspace_id)
payload = { # payload = {
"stats": stats, # "stats": stats,
"generated_at": datetime.now().isoformat(), # "generated_at": datetime.now().isoformat(),
"workspace_id": workspace_id, # "workspace_id": workspace_id,
"cached": True, # "cached": True,
} # }
value = json.dumps(payload, ensure_ascii=False) # value = json.dumps(payload, ensure_ascii=False)
await aio_redis.set(key, value, ex=expire) # await aio_redis.set(key, value, ex=expire)
logger.info(f"设置活动统计缓存成功: {key}, 过期时间: {expire}") # logger.info(f"设置活动统计缓存成功: {key}, 过期时间: {expire}秒")
return True # return True
except Exception as e: # except Exception as e:
logger.error(f"设置活动统计缓存失败: {e}", exc_info=True) # logger.error(f"设置活动统计缓存失败: {e}", exc_info=True)
return False # return False
@classmethod # @classmethod
async def get_activity_stats( # async def get_activity_stats(
cls, # cls,
workspace_id: str, # workspace_id: str,
) -> Optional[Dict[str, Any]]: # ) -> Optional[Dict[str, Any]]:
"""获取记忆提取活动统计缓存 # """获取记忆提取活动统计缓存
Args: # Args:
workspace_id: 工作空间ID # workspace_id: 工作空间ID
Returns: # Returns:
统计数据字典,缓存不存在或已过期返回 None # 统计数据字典,缓存不存在或已过期返回 None
""" # """
try: # try:
key = cls._get_key(workspace_id) # key = cls._get_key(workspace_id)
value = await aio_redis.get(key) # value = await aio_redis.get(key)
if value: # if value:
payload = json.loads(value) # payload = json.loads(value)
logger.info(f"命中活动统计缓存: {key}") # logger.info(f"命中活动统计缓存: {key}")
return payload # return payload
logger.info(f"活动统计缓存不存在或已过期: {key}") # logger.info(f"活动统计缓存不存在或已过期: {key}")
return None # return None
except Exception as e: # except Exception as e:
logger.error(f"获取活动统计缓存失败: {e}", exc_info=True) # logger.error(f"获取活动统计缓存失败: {e}", exc_info=True)
return None # return None
@classmethod # @classmethod
async def delete_activity_stats( # async def delete_activity_stats(
cls, # cls,
workspace_id: str, # workspace_id: str,
) -> bool: # ) -> bool:
"""删除记忆提取活动统计缓存 # """删除记忆提取活动统计缓存
Args: # Args:
workspace_id: 工作空间ID # workspace_id: 工作空间ID
Returns: # Returns:
是否删除成功 # 是否删除成功
""" # """
try: # try:
key = cls._get_key(workspace_id) # key = cls._get_key(workspace_id)
result = await aio_redis.delete(key) # result = await aio_redis.delete(key)
logger.info(f"删除活动统计缓存: {key}, 结果: {result}") # logger.info(f"删除活动统计缓存: {key}, 结果: {result}")
return result > 0 # return result > 0
except Exception as e: # except Exception as e:
logger.error(f"删除活动统计缓存失败: {e}", exc_info=True) # logger.error(f"删除活动统计缓存失败: {e}", exc_info=True)
return False # return False

View File

@@ -260,24 +260,24 @@ async def write(
with open(log_file, "a", encoding="utf-8") as f: with open(log_file, "a", encoding="utf-8") as f:
f.write(f"=== Pipeline Run Completed: {timestamp} ===\n\n") f.write(f"=== Pipeline Run Completed: {timestamp} ===\n\n")
# 将提取统计写入 Redis按 workspace_id 存储 # # 将提取统计写入 Redis按 workspace_id 存储
try: # try:
from app.cache.memory.activity_stats_cache import ActivityStatsCache # from app.cache.memory.activity_stats_cache import ActivityStatsCache
stats_to_cache = { # stats_to_cache = {
"chunk_count": len(all_chunk_nodes) if all_chunk_nodes else 0, # "chunk_count": len(all_chunk_nodes) if all_chunk_nodes else 0,
"statements_count": len(all_statement_nodes) if all_statement_nodes else 0, # "statements_count": len(all_statement_nodes) if all_statement_nodes else 0,
"triplet_entities_count": len(all_entity_nodes) if all_entity_nodes else 0, # "triplet_entities_count": len(all_entity_nodes) if all_entity_nodes else 0,
"triplet_relations_count": len(all_entity_entity_edges) if all_entity_entity_edges else 0, # "triplet_relations_count": len(all_entity_entity_edges) if all_entity_entity_edges else 0,
"temporal_count": 0, # "temporal_count": 0,
} # }
await ActivityStatsCache.set_activity_stats( # await ActivityStatsCache.set_activity_stats(
workspace_id=str(memory_config.workspace_id), # workspace_id=str(memory_config.workspace_id),
stats=stats_to_cache, # stats=stats_to_cache,
) # )
logger.info(f"[WRITE] 活动统计已写入 Redis: workspace_id={memory_config.workspace_id}") # logger.info(f"[WRITE] 活动统计已写入 Redis: workspace_id={memory_config.workspace_id}")
except Exception as cache_err: # except Exception as cache_err:
logger.warning(f"[WRITE] 写入活动统计缓存失败(不影响主流程): {cache_err}", exc_info=True) # logger.warning(f"[WRITE] 写入活动统计缓存失败(不影响主流程): {cache_err}", exc_info=True)
logger.info("=== Pipeline Complete ===") # logger.info("=== Pipeline Complete ===")
logger.info(f"Total execution time: {total_time:.2f} seconds") # logger.info(f"Total execution time: {total_time:.2f} seconds")