Spaces:
Sleeping
Sleeping
| """ | |
| environments/trace_env/agents/memory.py | |
| MemoryAgent — episodic + semantic memory store for the Trace agent. | |
| Stores findings across steps without centralizing raw data. | |
| Provides a compressed summary for the observation prompt. | |
| """ | |
| from __future__ import annotations | |
| from typing import Any | |
| from collections import deque | |
| class MemoryAgent: | |
| MAX_ENTRIES = 20 # hard cap to prevent memory stuffing | |
| def __init__(self, config: dict): | |
| self.config = config | |
| self._episodic: deque = deque(maxlen=self.MAX_ENTRIES) | |
| self._semantic: dict[str, Any] = {} | |
| def reset(self): | |
| self._episodic.clear() | |
| self._semantic.clear() | |
| def store(self, content: str, metadata: dict = None): | |
| """Store a finding into episodic memory.""" | |
| entry = {"content": content, "metadata": metadata or {}} | |
| self._episodic.append(entry) | |
| # Build semantic index (simple keyword extraction) | |
| for word in content.lower().split(): | |
| if len(word) > 4: | |
| self._semantic.setdefault(word, []).append(len(self._episodic) - 1) | |
| def recall(self, query: str, top_k: int = 3) -> list[str]: | |
| """Retrieve relevant memories by keyword match.""" | |
| scores = {} | |
| for word in query.lower().split(): | |
| if word in self._semantic: | |
| for idx in self._semantic[word]: | |
| scores[idx] = scores.get(idx, 0) + 1 | |
| top = sorted(scores, key=scores.get, reverse=True)[:top_k] | |
| return [self._episodic[i]["content"] for i in top if i < len(self._episodic)] | |
| def summarize(self) -> str: | |
| """Return a compressed summary for the observation prompt.""" | |
| if not self._episodic: | |
| return "(empty memory)" | |
| entries = list(self._episodic)[-5:] # last 5 entries | |
| lines = [f"- {e['content'][:80]}" for e in entries] | |
| return "\n".join(lines) | |
| def size(self) -> int: | |
| return len(self._episodic) | |