fix(memory-graph): 修复集成测试问题

- 修复Config类memory字段验证问题 (改为Optional)
- 为Memory模型添加activation属性
- 修复CreateMemoryTool返回值 (添加memory_id)
- 为GraphStore添加remove_memory方法
- 修复integration测试脚本
- 所有集成测试通过 (5/5)

Changes:
- src/config/config.py: memory字段改为Optional
- src/memory_graph/models.py: 添加activation属性
- src/memory_graph/plugin_tools/memory_plugin_tools.py: 返回memory_id
- src/memory_graph/storage/graph_store.py: 添加remove_memory方法
- tests/test_memory_graph_integration.py: 修复工具返回值检查

Tests:
 基本记忆操作 (CRUD + 检索)
 LLM工具集成 (创建记忆)
 记忆生命周期 (激活/整合/遗忘)
 维护任务 (调度+手动执行)
 配置系统 (默认/自定义/bot_config)
This commit is contained in:
Windpicker-owo
2025-11-05 20:43:39 +08:00
parent 1239b26e6c
commit 5439e3bd82
4 changed files with 49 additions and 4 deletions

View File

@@ -2,6 +2,7 @@ import os
import shutil
import sys
from datetime import datetime
from typing import Optional
import tomlkit
from pydantic import Field
@@ -395,7 +396,7 @@ class Config(ValidatedConfigBase):
notice: NoticeConfig = Field(..., description="Notice消息配置")
emoji: EmojiConfig = Field(..., description="表情配置")
expression: ExpressionConfig = Field(..., description="表达配置")
memory: MemoryConfig = Field(..., description="记忆配置")
memory: Optional[MemoryConfig] = Field(default=None, description="记忆配置(旧版,已废弃)")
mood: MoodConfig = Field(..., description="情绪配置")
reaction: ReactionConfig = Field(default_factory=ReactionConfig, description="反应规则配置")
chinese_typo: ChineseTypoConfig = Field(..., description="中文错别字配置")

View File

@@ -164,6 +164,7 @@ class Memory:
nodes: List[MemoryNode] # 该记忆包含的所有节点
edges: List[MemoryEdge] # 该记忆包含的所有边
importance: float = 0.5 # 整体重要性 [0-1]
activation: float = 0.0 # 激活度 [0-1],用于记忆整合和遗忘
status: MemoryStatus = MemoryStatus.STAGED # 记忆状态
created_at: datetime = field(default_factory=datetime.now)
last_accessed: datetime = field(default_factory=datetime.now) # 最后访问时间
@@ -175,8 +176,9 @@ class Memory:
"""后初始化处理"""
if not self.id:
self.id = str(uuid.uuid4())
# 确保重要性在有效范围内
# 确保重要性和激活度在有效范围内
self.importance = max(0.0, min(1.0, self.importance))
self.activation = max(0.0, min(1.0, self.activation))
def to_dict(self) -> Dict[str, Any]:
"""转换为字典(用于序列化)"""
@@ -187,6 +189,7 @@ class Memory:
"nodes": [node.to_dict() for node in self.nodes],
"edges": [edge.to_dict() for edge in self.edges],
"importance": self.importance,
"activation": self.activation,
"status": self.status.value,
"created_at": self.created_at.isoformat(),
"last_accessed": self.last_accessed.isoformat(),
@@ -205,6 +208,7 @@ class Memory:
nodes=[MemoryNode.from_dict(n) for n in data["nodes"]],
edges=[MemoryEdge.from_dict(e) for e in data["edges"]],
importance=data.get("importance", 0.5),
activation=data.get("activation", 0.0),
status=MemoryStatus(data.get("status", "staged")),
created_at=datetime.fromisoformat(data["created_at"]),
last_accessed=datetime.fromisoformat(data.get("last_accessed", data["created_at"])),

View File

@@ -78,12 +78,14 @@ class CreateMemoryTool(BaseTool):
logger.info(f"[CreateMemoryTool] 成功创建记忆: {memory.id}")
return {
"name": self.name,
"content": f"成功创建记忆ID: {memory.id}"
"content": f"成功创建记忆ID: {memory.id}",
"memory_id": memory.id, # 返回记忆ID供后续使用
}
else:
return {
"name": self.name,
"content": "创建记忆失败"
"content": "创建记忆失败",
"memory_id": None,
}
except Exception as e:

View File

@@ -390,6 +390,44 @@ class GraphStore:
logger.info(f"从字典加载图: {store.get_statistics()}")
return store
def remove_memory(self, memory_id: str) -> bool:
"""
从图中删除指定记忆
Args:
memory_id: 要删除的记忆ID
Returns:
是否删除成功
"""
try:
# 1. 检查记忆是否存在
if memory_id not in self.memory_index:
logger.warning(f"记忆不存在,无法删除: {memory_id}")
return False
memory = self.memory_index[memory_id]
# 2. 从节点映射中移除此记忆
for node in memory.nodes:
if node.id in self.node_to_memories:
self.node_to_memories[node.id].discard(memory_id)
# 如果该节点不再属于任何记忆,从图中移除节点
if not self.node_to_memories[node.id]:
if self.graph.has_node(node.id):
self.graph.remove_node(node.id)
del self.node_to_memories[node.id]
# 3. 从记忆索引中移除
del self.memory_index[memory_id]
logger.info(f"成功删除记忆: {memory_id}")
return True
except Exception as e:
logger.error(f"删除记忆失败 {memory_id}: {e}", exc_info=True)
return False
def clear(self) -> None:
"""清空图(危险操作,仅用于测试)"""
self.graph.clear()