Coverage for memory/long_term.py: 22%

1"""

2中期记忆模块

3

4使用 Markdown 文件存储重要信息，Agent 可以主动写入和读取。

5特点：

6- 持久化存储在本地文件

7- Agent 可以主动管理记忆

8- 启动时自动加载并注入到 system prompt

9"""

10

11 from pathlib import Path

12 from datetime import datetime

13 from qrclaw.logger import get_logger

14

15 logger = get_logger("qrclaw.memory.long_term")

16

17

18 class LongTermMemory:

19 """长期记忆管理器"""

20

21 def __init__(self, memory_file: Path):

22 """

23 初始化长期记忆。

24

25 Args:

26 memory_file: 记忆文件路径（由 Workspace 提供）

27 """

28 self.memory_file = memory_file

29 self._ensure_file()

30 logger.info(f"中期记忆初始化: {self.memory_file}")

31

32 def _ensure_file(self):

33 """确保记忆文件存在"""

34 self.memory_file.parent.mkdir(parents=True, exist_ok=True)

35 if not self.memory_file.exists():

36 self.memory_file.write_text("# QRClaw 中期记忆\n\n", encoding="utf-8")

37 logger.debug("创建中期记忆文件")

38

39 def load(self) -> str:

40 """

41 加载记忆内容

42

43 Returns:

44 str: 记忆内容（Markdown 格式）

45 """

46 try:

47 content = self.memory_file.read_text(encoding="utf-8")

48 logger.info(f"加载中期记忆: {len(content)} 字符")

49 return content

50 except Exception as e:

51 logger.error(f"加载中期记忆失败: {e}", exc_info=True)

52 return ""

53

54 def save(self, content: str) -> bool:

55 """

56 保存记忆内容（覆盖）

57

58 Args:

59 content: 要保存的内容

60

61 Returns:

62 bool: 是否成功

63 """

64 try:

65 self.memory_file.write_text(content, encoding="utf-8")

66 logger.info(f"保存中期记忆: {len(content)} 字符")

67 return True

68 except Exception as e:

69 logger.error(f"保存中期记忆失败: {e}", exc_info=True)

70 return False

71

72 def append(self, content: str, title: str = None) -> bool:

73 """

74 追加记忆内容

75

76 Args:

77 content: 要追加的内容

78 title: 可选的标题

79

80 Returns:

81 bool: 是否成功

82 """

83 try:

84 timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

85

86 # 构建追加内容

87 append_text = f"\n---\n\n"

88 if title:

89 append_text += f"## {title}\n\n"

90 append_text += f"**时间**: {timestamp}\n\n"

91 append_text += f"{content}\n"

92

93 # 追加到文件

94 with open(self.memory_file, "a", encoding="utf-8") as f:

95 f.write(append_text)

96

97 logger.info(f"追加中期记忆: {title or '无标题'}, {len(content)} 字符")

98 return True

99 except Exception as e:

100 logger.error(f"追加中期记忆失败: {e}", exc_info=True)

101 return False

102

103 def clear(self) -> bool:

104 """

105 清空记忆（保留标题）

106

107 Returns:

108 bool: 是否成功

109 """

110 try:

111 self.memory_file.write_text("# QRClaw 中期记忆\n\n", encoding="utf-8")

112 logger.info("清空中期记忆")

113 return True

114 except Exception as e:

115 logger.error(f"清空中期记忆失败: {e}", exc_info=True)

116 return False

117

118 def exists(self) -> bool:

119 """检查记忆文件是否存在"""

120 return self.memory_file.exists()

121

122 def size(self) -> int:

123 """获取记忆文件大小（字符数）"""

124 if self.exists():

125 return len(self.load())

126 return 0