3.4: Add per-agent memory scoping
MemorySystem now accepts optional scope parameter. When set:
- Memory files go to memory/{scope}/ subdirectory
- Fallback search covers both scoped and shared directories
Unscoped agents (scope="") use the shared memory/ root directory.
This enables agents to have private memory while still searching
shared knowledge.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cora-start
parent
86511d5a0f
commit
883fee36a3
|
|
@ -23,10 +23,21 @@ log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class MemorySystem:
|
class MemorySystem:
|
||||||
def __init__(self, config: Config, db: Database):
|
def __init__(self, config: Config, db: Database, scope: str = ""):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.db = db
|
self.db = db
|
||||||
self.memory_dir = config.memory_dir
|
self.scope = scope
|
||||||
|
|
||||||
|
# Scoped agents get their own subdirectory; shared memory stays in root
|
||||||
|
if scope:
|
||||||
|
self.memory_dir = config.memory_dir / scope
|
||||||
|
self.memory_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
else:
|
||||||
|
self.memory_dir = config.memory_dir
|
||||||
|
|
||||||
|
# Shared memory dir (for cross-scope search)
|
||||||
|
self._shared_memory_dir = config.memory_dir
|
||||||
|
|
||||||
self._embedder = None
|
self._embedder = None
|
||||||
self._embed_lock = threading.Lock()
|
self._embed_lock = threading.Lock()
|
||||||
self._embed_db_path = self.memory_dir / "embeddings.db"
|
self._embed_db_path = self.memory_dir / "embeddings.db"
|
||||||
|
|
@ -235,18 +246,28 @@ class MemorySystem:
|
||||||
self._embed_conn.commit()
|
self._embed_conn.commit()
|
||||||
|
|
||||||
def _fallback_search(self, query: str, top_k: int) -> list[dict]:
|
def _fallback_search(self, query: str, top_k: int) -> list[dict]:
|
||||||
"""Simple keyword search when embeddings are unavailable."""
|
"""Simple keyword search when embeddings are unavailable.
|
||||||
|
|
||||||
|
Searches both scoped and shared memory directories.
|
||||||
|
"""
|
||||||
results = []
|
results = []
|
||||||
query_lower = query.lower()
|
query_lower = query.lower()
|
||||||
for path in self.memory_dir.glob("*.md"):
|
|
||||||
try:
|
# Collect directories to search (avoid duplicates)
|
||||||
content = path.read_text(encoding="utf-8")
|
search_dirs = [self.memory_dir]
|
||||||
except Exception:
|
if self.scope and self._shared_memory_dir != self.memory_dir:
|
||||||
continue
|
search_dirs.append(self._shared_memory_dir)
|
||||||
for line in content.split("\n"):
|
|
||||||
stripped = line.strip().lstrip("- ")
|
for search_dir in search_dirs:
|
||||||
if len(stripped) > 10 and query_lower in stripped.lower():
|
for path in search_dir.glob("*.md"):
|
||||||
results.append({"id": path.name, "text": stripped, "score": 1.0})
|
try:
|
||||||
if len(results) >= top_k:
|
content = path.read_text(encoding="utf-8")
|
||||||
return results
|
except Exception:
|
||||||
|
continue
|
||||||
|
for line in content.split("\n"):
|
||||||
|
stripped = line.strip().lstrip("- ")
|
||||||
|
if len(stripped) > 10 and query_lower in stripped.lower():
|
||||||
|
results.append({"id": path.name, "text": stripped, "score": 1.0})
|
||||||
|
if len(results) >= top_k:
|
||||||
|
return results
|
||||||
return results
|
return results
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue