03-memory-augmented-agent / memory_agent.py
FlyingNunchucks's picture
Reset Agent 3 to sanitized public history
c63c390
Raw History Blame Contribute Delete
6.86 kB
from datetime import datetime, timezone
from typing import Dict, Iterable, List, Optional
from memory_policy import evaluate_memory_write
from memory_store import MemoryStore
from schemas import MemoryEvent, RetrievedMemory
class MemoryAugmentedAgent:
def __init__(self, memory_store: MemoryStore, audit_log: Optional[Iterable[dict]] = None):
self.memory_store = memory_store
self.audit_log = list(audit_log or [])
def compress_context(self, retrieved_memories: List[RetrievedMemory]) -> str:
if not retrieved_memories:
return "No relevant project memories were retrieved."
semantic = [item.memory.content for item in retrieved_memories if item.memory.memory_type == "semantic"]
episodic = [item.memory.content for item in retrieved_memories if item.memory.memory_type == "episodic"]
parts: List[str] = []
if semantic:
parts.append("Stable project context:")
parts.extend(f"- {memory}" for memory in semantic)
if episodic:
if parts:
parts.append("")
parts.append("Relevant prior decisions and events:")
parts.extend(f"- {memory}" for memory in episodic)
return "\n".join(parts)
def generate_response(self, query: str, retrieved_memories: List[RetrievedMemory]) -> str:
if not retrieved_memories:
return (
"I do not have enough remembered project context to answer this from memory. "
"The demo will not invent a project memory that was not retrieved."
)
lines = [
"Here is the most relevant remembered project context for this request:",
"",
]
for item in retrieved_memories[:4]:
lines.append(f"- {item.memory.content}")
lines.extend(
[
"",
"Memory boundary: this answer is derived only from the session memories shown in the inspection panel.",
]
)
return "\n".join(lines)
def log_run(self, result: Dict) -> dict:
retrieved_memory_ids = [item.memory.memory_id for item in result["retrieved_memories"]]
decision = result["write_decision"]
log_entry = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"query_length": len(result["query"]),
"retrieved_memory_ids": retrieved_memory_ids,
"write_outcome": decision.outcome,
"write_reason": decision.reason,
"policy_flags": decision.policy_flags,
"memory_count_after_run": len(self.memory_store.memories),
}
self.audit_log.append(log_entry)
return log_entry
@staticmethod
def _frame(event: MemoryEvent, result: Dict) -> Dict:
return {"event": event, "result": dict(result)}
def run_iter(self, query: str, top_k: int = 4, save_new_memory: bool = True):
clean_query = (query or "").strip()
result: Dict = {
"query": clean_query,
"retrieved_memories": [],
"compressed_context": "",
"agent_response": "",
"write_decision": None,
"saved_memory": None,
"log_entry": None,
}
yield self._frame(
MemoryEvent(stage="query_received", message="Request received. The agent is deciding which memories are relevant."),
result,
)
retrieved = self.memory_store.retrieve(clean_query, top_k=top_k)
result["retrieved_memories"] = retrieved
yield self._frame(
MemoryEvent(
stage="memory_retrieved",
message=f"Retrieved {len(retrieved)} ranked memories from this session's memory store.",
memory_ids=[item.memory.memory_id for item in retrieved],
),
result,
)
compressed_context = self.compress_context(retrieved)
result["compressed_context"] = compressed_context
yield self._frame(
MemoryEvent(stage="context_compressed", message="Separated stable context from prior events and compressed the working context."),
result,
)
write_decision = evaluate_memory_write(clean_query)
result["write_decision"] = write_decision
yield self._frame(
MemoryEvent(
stage="write_policy_evaluated",
message=f"Memory write policy returned: {write_decision.outcome.upper()}.",
decision=write_decision.outcome,
),
result,
)
saved_memory = None
if save_new_memory and write_decision.outcome == "save":
saved_memory = self.memory_store.save_memory_from_decision(write_decision)
result["saved_memory"] = saved_memory
yield self._frame(
MemoryEvent(
stage="memory_saved",
message="The approved project memory was added to this browser session only.",
memory_ids=[saved_memory.memory_id] if saved_memory else [],
decision="save",
),
result,
)
elif write_decision.outcome == "block":
yield self._frame(
MemoryEvent(
stage="memory_blocked",
message="The application blocked this content from becoming long-term memory.",
decision="block",
),
result,
)
else:
yield self._frame(
MemoryEvent(
stage="memory_skipped",
message=(
"Memory storage is disabled for this run."
if not save_new_memory and write_decision.outcome == "save"
else "The interaction was useful for this turn but was not saved as durable memory."
),
decision="skip",
),
result,
)
result["agent_response"] = self.generate_response(clean_query, retrieved)
yield self._frame(
MemoryEvent(stage="response_ready", message="Memory-grounded response is ready."),
result,
)
result["log_entry"] = self.log_run(result)
yield self._frame(
MemoryEvent(stage="audit_recorded", message="Recorded control metadata without storing the raw query in the audit log."),
result,
)
def run(self, query: str, top_k: int = 4, save_new_memory: bool = True):
last_frame = None
for frame in self.run_iter(query=query, top_k=top_k, save_new_memory=save_new_memory):
last_frame = frame
return last_frame["result"] if last_frame else None