Fix/develop memory deail (#69)
* 新增记忆空间详情 * 新增记忆空间详情 * 新增记忆关联的数量 * 修改记忆时间线 * 修改记忆时间线 * 修改记忆时间线 * Parameterize elementId in Cypher query * 关系演化,互动频率优化 * 关系演化,互动频率优化 * 关系演化,互动频率优化 * 关系演化,互动频率优化 * 关系演化,互动频率优化 * 关系演化,互动频率优化 --------- Co-authored-by: Ke Sun <33739460+keeees@users.noreply.github.com>
This commit is contained in:
@@ -727,7 +727,6 @@ SET m += {
|
|||||||
dialog_id: summary.dialog_id,
|
dialog_id: summary.dialog_id,
|
||||||
chunk_ids: summary.chunk_ids,
|
chunk_ids: summary.chunk_ids,
|
||||||
content: summary.content,
|
content: summary.content,
|
||||||
memory_type: summary.memory_type,
|
|
||||||
summary_embedding: summary.summary_embedding,
|
summary_embedding: summary.summary_embedding,
|
||||||
config_id: summary.config_id,
|
config_id: summary.config_id,
|
||||||
importance_score: CASE WHEN summary.importance_score IS NOT NULL THEN summary.importance_score ELSE coalesce(m.importance_score, 0.5) END,
|
importance_score: CASE WHEN summary.importance_score IS NOT NULL THEN summary.importance_score ELSE coalesce(m.importance_score, 0.5) END,
|
||||||
@@ -942,7 +941,7 @@ RETURN
|
|||||||
"""
|
"""
|
||||||
Memory_Timeline_Statement="""
|
Memory_Timeline_Statement="""
|
||||||
MATCH (n)
|
MATCH (n)
|
||||||
WHERE elementId(n) = "4:f6039a9b-d553-4ba2-9b1c-d9a18917801f:77003"
|
WHERE elementId(n) = $id
|
||||||
|
|
||||||
CALL {
|
CALL {
|
||||||
WITH n
|
WITH n
|
||||||
@@ -995,7 +994,7 @@ RETURN
|
|||||||
"""
|
"""
|
||||||
Memory_Space_Emotion_MemorySummary="""
|
Memory_Space_Emotion_MemorySummary="""
|
||||||
MATCH (n)-[]-(e)
|
MATCH (n)-[]-(e)
|
||||||
WHERE elementId(n) = "4:f6039a9b-d553-4ba2-9b1c-d9a18917801f:77019"
|
WHERE elementId(n) = $id
|
||||||
AND EXISTS {
|
AND EXISTS {
|
||||||
MATCH (e)-[]-(ms)
|
MATCH (e)-[]-(ms)
|
||||||
WHERE ms:MemorySummary OR ms:ExtractedEntity
|
WHERE ms:MemorySummary OR ms:ExtractedEntity
|
||||||
@@ -1020,36 +1019,23 @@ RETURN DISTINCT
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
'''获取实体'''
|
'''获取实体'''
|
||||||
Memory_Space_Interaction_Statement="""
|
|
||||||
|
Memory_Space_User="""
|
||||||
|
MATCH (n)-[r]->(m)
|
||||||
|
WHERE n.group_id = $group_id AND m.name="用户"
|
||||||
|
return DISTINCT elementId(m) as id
|
||||||
|
"""
|
||||||
|
Memory_Space_Entity="""
|
||||||
MATCH (n)-[]-(m)
|
MATCH (n)-[]-(m)
|
||||||
WHERE elementId(n) = $id
|
WHERE elementId(m) = $id AND m.entity_type = "Person"
|
||||||
AND m.entity_type = "Person"
|
|
||||||
RETURN
|
RETURN
|
||||||
m.name AS name,
|
DISTINCT m.name as name,m.group_id as group_id
|
||||||
m.importance_score AS importance_score;
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
Memory_Space_Associative="""
|
||||||
Memory_Space_Interaction_ExtractedEntity="""
|
MATCH (u)-[]-(x)-[]-(h)
|
||||||
MATCH (n)-[]-(e)
|
WHERE elementId(u) = $user_id
|
||||||
WHERE elementId(n) = $id
|
AND elementId(h) = $id
|
||||||
AND EXISTS {
|
|
||||||
MATCH (e)-[]-(ms:ExtractedEntity)
|
|
||||||
}
|
|
||||||
RETURN DISTINCT
|
RETURN DISTINCT
|
||||||
e.name AS name,
|
x.statement as statement,x.created_at as created_at
|
||||||
e.importance_score AS importance_score;
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Memory_Space_Interaction_Summary="""
|
|
||||||
MATCH (n)-[]-(e)
|
|
||||||
WHERE elementId(n) = $id
|
|
||||||
AND EXISTS {
|
|
||||||
MATCH (e)-[]-(ms:ExtractedEntity)
|
|
||||||
}
|
|
||||||
RETURN DISTINCT
|
|
||||||
e.name AS name,
|
|
||||||
e.importance_score AS importance_score;
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ Memory_Timeline_Statement,
|
|||||||
Memory_Space_Emotion_Statement,
|
Memory_Space_Emotion_Statement,
|
||||||
Memory_Space_Emotion_MemorySummary,
|
Memory_Space_Emotion_MemorySummary,
|
||||||
Memory_Space_Emotion_ExtractedEntity,
|
Memory_Space_Emotion_ExtractedEntity,
|
||||||
Memory_Space_Interaction_Statement,
|
Memory_Space_Associative,Memory_Space_User,Memory_Space_Entity
|
||||||
Memory_Space_Interaction_ExtractedEntity,
|
|
||||||
Memory_Space_Interaction_Summary
|
|
||||||
)
|
)
|
||||||
from app.repositories.neo4j.neo4j_connector import Neo4jConnector
|
from app.repositories.neo4j.neo4j_connector import Neo4jConnector
|
||||||
from typing import Dict, List, Any, Optional
|
from typing import Dict, List, Any, Optional
|
||||||
@@ -578,6 +576,7 @@ class MemoryInteraction:
|
|||||||
# 如果解析失败,返回原始字符串
|
# 如果解析失败,返回原始字符串
|
||||||
return iso_string
|
return iso_string
|
||||||
|
|
||||||
|
|
||||||
async def get_interaction_frequency(self) -> Dict[str, Any]:
|
async def get_interaction_frequency(self) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
获取交互频率数据
|
获取交互频率数据
|
||||||
@@ -588,26 +587,36 @@ class MemoryInteraction:
|
|||||||
try:
|
try:
|
||||||
logger.info(f"获取交互数据 - ID: {self.id}, Table: {self.table}")
|
logger.info(f"获取交互数据 - ID: {self.id}, Table: {self.table}")
|
||||||
|
|
||||||
if self.table == 'Statement':
|
ori_data= await self.connector.execute_query(Memory_Space_Entity, id=self.id)
|
||||||
results = await self.connector.execute_query(Memory_Space_Interaction_Statement, id=self.id)
|
if ori_data!=[]:
|
||||||
elif self.table == 'ExtractedEntity':
|
# name = ori_data[0]['name']
|
||||||
results = await self.connector.execute_query(Memory_Space_Interaction_ExtractedEntity, id=self.id)
|
group_id = ori_data[0]['group_id']
|
||||||
else:
|
Space_User = await self.connector.execute_query(Memory_Space_User, group_id=group_id)
|
||||||
# MemorySummary/Chunk类型查询
|
if not Space_User:
|
||||||
results = await self.connector.execute_query(Memory_Space_Interaction_Summary, id=self.id)
|
return "不存在用户"
|
||||||
|
user_id=Space_User[0]['id']
|
||||||
|
|
||||||
# 处理查询结果
|
results = await self.connector.execute_query(Memory_Space_Associative, id=self.id,user_id=user_id)
|
||||||
interaction_data = self._process_interaction_results(results)
|
|
||||||
|
|
||||||
# 转换Neo4j类型
|
|
||||||
final_data = self._convert_neo4j_types(interaction_data)
|
# 处理查询结果
|
||||||
|
interaction_data = self._process_interaction_results(results)
|
||||||
logger.info(f"成功获取 {len(final_data)} 条交互数据")
|
|
||||||
|
# 转换Neo4j类型
|
||||||
|
final_data = self._convert_neo4j_types(interaction_data)
|
||||||
|
|
||||||
|
logger.info(f"成功获取 {len(final_data)} 条交互数据")
|
||||||
|
|
||||||
|
return {
|
||||||
|
'success': True,
|
||||||
|
'data': final_data,
|
||||||
|
'total': len(final_data)
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
'success': True,
|
'success': False,
|
||||||
'data': final_data,
|
'data': [],
|
||||||
'total': len(final_data)
|
'total': 0
|
||||||
}
|
}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -621,36 +630,57 @@ class MemoryInteraction:
|
|||||||
|
|
||||||
def _process_interaction_results(self, results: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
def _process_interaction_results(self, results: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
处理交互查询结果
|
处理交互查询结果,按季度统计交互频率
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
results: Neo4j查询结果
|
results: Neo4j查询结果
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
处理后的交互数据列表
|
按季度统计的交互数据列表,格式: [{"created_at": "2026Q1", "count": 3}]
|
||||||
"""
|
"""
|
||||||
interaction_data = []
|
from collections import defaultdict
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
# 检查results是否为空或不是列表
|
# 用于按季度分组计数
|
||||||
if not results or not isinstance(results, list):
|
quarterly_counts = defaultdict(int)
|
||||||
logger.warning(f"交互查询结果为空或格式不正确: {type(results)}")
|
|
||||||
return interaction_data
|
|
||||||
|
|
||||||
for record in results:
|
for record in results:
|
||||||
# 检查record是否为字典类型
|
# 过滤掉statement为None的记录
|
||||||
if not isinstance(record, dict):
|
if not isinstance(record, dict) or record.get('statement') is None:
|
||||||
logger.warning(f"跳过非字典类型的记录: {type(record)} - {record}")
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 只保留交互相关的字段
|
created_at = record.get('created_at')
|
||||||
name = record.get('name')
|
if not created_at:
|
||||||
if name is not None:
|
continue
|
||||||
interaction_record = {
|
|
||||||
'name': name,
|
try:
|
||||||
'importance_score': record.get('importance_score', 0.0),
|
# 处理不同类型的时间格式
|
||||||
'interaction_count': record.get('interaction_count', 1) # 默认交互次数为1
|
if isinstance(created_at, str):
|
||||||
}
|
# 解析ISO格式时间字符串
|
||||||
interaction_data.append(interaction_record)
|
dt = datetime.fromisoformat(created_at.replace('Z', '+00:00'))
|
||||||
|
elif hasattr(created_at, 'year') and hasattr(created_at, 'month'):
|
||||||
|
# 处理Neo4j DateTime对象
|
||||||
|
dt = datetime(created_at.year, created_at.month, created_at.day)
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
# 计算季度
|
||||||
|
quarter = (dt.month - 1) // 3 + 1
|
||||||
|
quarter_key = f"{dt.year}.Q{quarter}"
|
||||||
|
# 增加该季度的计数
|
||||||
|
quarterly_counts[quarter_key] += 1
|
||||||
|
|
||||||
|
except (ValueError, AttributeError) as e:
|
||||||
|
logger.warning(f"解析时间失败: {e}, 原始值: {created_at}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 转换为所需格式并按时间排序
|
||||||
|
interaction_data = [
|
||||||
|
{"created_at": quarter, "count": count}
|
||||||
|
for quarter, count in quarterly_counts.items()
|
||||||
|
]
|
||||||
|
|
||||||
|
# 按季度排序(最新的在前)
|
||||||
|
interaction_data.sort(key=lambda x: x["created_at"], reverse=True)
|
||||||
|
|
||||||
return interaction_data
|
return interaction_data
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user