长期记忆 - 跨对话持久化的关键信息 职责: 1. 持久化用户偏好、项目事实、关键决策等 2. 支持关键词检索 3. 自动去重(基于内容相似度) 4. 定期持久化到磁盘
| 23 | * 4. 定期持久化到磁盘 |
| 24 | */ |
| 25 | public class LongTermMemory implements Memory { |
| 26 | private static final Logger log = LoggerFactory.getLogger(LongTermMemory.class); |
| 27 | private static final String STORAGE_DIR_PROPERTY = "tcode.memory.dir"; |
| 28 | private static final String STORAGE_DIR_ENV = "TCODE_MEMORY_DIR"; |
| 29 | private static final String STORAGE_FILE = "long_term_memory.json"; |
| 30 | private final Map<String, MemoryEntry> entries; |
| 31 | private final AtomicInteger tokenCounter; |
| 32 | private final ObjectMapper mapper; |
| 33 | private final File storageFile; |
| 34 | |
| 35 | public LongTermMemory() { |
| 36 | this(resolveStorageDir()); |
| 37 | } |
| 38 | |
| 39 | public LongTermMemory(File storageDir) { |
| 40 | this.entries = new ConcurrentHashMap<>(); |
| 41 | this.tokenCounter = new AtomicInteger(0); |
| 42 | this.mapper = new ObjectMapper(); |
| 43 | this.mapper.enable(SerializationFeature.INDENT_OUTPUT); |
| 44 | |
| 45 | // 确保存储目录存在 |
| 46 | File dir = storageDir; |
| 47 | if (!dir.exists()) { |
| 48 | dir.mkdirs(); |
| 49 | } |
| 50 | this.storageFile = new File(dir, STORAGE_FILE); |
| 51 | |
| 52 | // 启动时加载已有记忆 |
| 53 | loadFromDisk(); |
| 54 | } |
| 55 | |
| 56 | @Override |
| 57 | public void store(MemoryEntry entry) { |
| 58 | // 去重检查:如果已存在内容完全相同的条目,跳过 |
| 59 | boolean duplicate = entries.values().stream() |
| 60 | .anyMatch(e -> e.getContent().equals(entry.getContent())); |
| 61 | if (duplicate) { |
| 62 | return; |
| 63 | } |
| 64 | |
| 65 | entries.put(entry.getId(), entry); |
| 66 | tokenCounter.addAndGet(entry.getTokenCount()); |
| 67 | saveToDisk(); |
| 68 | } |
| 69 | |
| 70 | @Override |
| 71 | public Optional<MemoryEntry> retrieve(String id) { |
| 72 | return Optional.ofNullable(entries.get(id)); |
| 73 | } |
| 74 | |
| 75 | @Override |
| 76 | public List<MemoryEntry> search(String query, int limit) { |
| 77 | return search(query, limit, null); |
| 78 | } |
| 79 | |
| 80 | public List<MemoryEntry> search(String query, int limit, String projectKey) { |
| 81 | Set<String> queryTokens = MemoryQueryTokenizer.tokenize(query); |
| 82 |
nothing calls this directly
no outgoing calls
no test coverage detected