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 3ea6d1dcbf
commit 23b011e6ab
4 changed files with 49 additions and 4 deletions

View File

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

View File

@@ -164,6 +164,7 @@ class Memory:
nodes: List[MemoryNode] # 该记忆包含的所有节点 nodes: List[MemoryNode] # 该记忆包含的所有节点
edges: List[MemoryEdge] # 该记忆包含的所有边 edges: List[MemoryEdge] # 该记忆包含的所有边
importance: float = 0.5 # 整体重要性 [0-1] importance: float = 0.5 # 整体重要性 [0-1]
activation: float = 0.0 # 激活度 [0-1],用于记忆整合和遗忘
status: MemoryStatus = MemoryStatus.STAGED # 记忆状态 status: MemoryStatus = MemoryStatus.STAGED # 记忆状态
created_at: datetime = field(default_factory=datetime.now) created_at: datetime = field(default_factory=datetime.now)
last_accessed: datetime = field(default_factory=datetime.now) # 最后访问时间 last_accessed: datetime = field(default_factory=datetime.now) # 最后访问时间
@@ -175,8 +176,9 @@ class Memory:
"""后初始化处理""" """后初始化处理"""
if not self.id: if not self.id:
self.id = str(uuid.uuid4()) self.id = str(uuid.uuid4())
# 确保重要性在有效范围内 # 确保重要性和激活度在有效范围内
self.importance = max(0.0, min(1.0, self.importance)) 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]: def to_dict(self) -> Dict[str, Any]:
"""转换为字典(用于序列化)""" """转换为字典(用于序列化)"""
@@ -187,6 +189,7 @@ class Memory:
"nodes": [node.to_dict() for node in self.nodes], "nodes": [node.to_dict() for node in self.nodes],
"edges": [edge.to_dict() for edge in self.edges], "edges": [edge.to_dict() for edge in self.edges],
"importance": self.importance, "importance": self.importance,
"activation": self.activation,
"status": self.status.value, "status": self.status.value,
"created_at": self.created_at.isoformat(), "created_at": self.created_at.isoformat(),
"last_accessed": self.last_accessed.isoformat(), "last_accessed": self.last_accessed.isoformat(),
@@ -205,6 +208,7 @@ class Memory:
nodes=[MemoryNode.from_dict(n) for n in data["nodes"]], nodes=[MemoryNode.from_dict(n) for n in data["nodes"]],
edges=[MemoryEdge.from_dict(e) for e in data["edges"]], edges=[MemoryEdge.from_dict(e) for e in data["edges"]],
importance=data.get("importance", 0.5), importance=data.get("importance", 0.5),
activation=data.get("activation", 0.0),
status=MemoryStatus(data.get("status", "staged")), status=MemoryStatus(data.get("status", "staged")),
created_at=datetime.fromisoformat(data["created_at"]), created_at=datetime.fromisoformat(data["created_at"]),
last_accessed=datetime.fromisoformat(data.get("last_accessed", 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}") logger.info(f"[CreateMemoryTool] 成功创建记忆: {memory.id}")
return { return {
"name": self.name, "name": self.name,
"content": f"成功创建记忆ID: {memory.id}" "content": f"成功创建记忆ID: {memory.id}",
"memory_id": memory.id, # 返回记忆ID供后续使用
} }
else: else:
return { return {
"name": self.name, "name": self.name,
"content": "创建记忆失败" "content": "创建记忆失败",
"memory_id": None,
} }
except Exception as e: except Exception as e:

View File

@@ -390,6 +390,44 @@ class GraphStore:
logger.info(f"从字典加载图: {store.get_statistics()}") logger.info(f"从字典加载图: {store.get_statistics()}")
return store 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: def clear(self) -> None:
"""清空图(危险操作,仅用于测试)""" """清空图(危险操作,仅用于测试)"""
self.graph.clear() self.graph.clear()