Memory v0.6.0

Episodic Memory

EpisodicMemory is a durable log of specific past runs — what was asked, what the agent answered, which tools it used, and how well it went — with similarity + recency recall of relevant prior experience before a new run. It is the third memory concept, and the one that keeps the specifics.

TypeAPIKeeps
SemanticAgent.memoryraw key/value facts (tool results)
ProceduralProceduralMemorydistilled how-to rules (helpful / harmful bullets)
EpisodicEpisodicMemorythe actual past runs (query, answer, outcome, score)

Like ProceduralMemory, it layers on any MemoryProvider as a ring buffer of JSON episodes (episodic:{namespace}:{i} + a __meta__ key).

The automatic cycle

Set Agent.episodic_memory and both halves happen on their own.

episodic_agent.py
from deepcrew import Agent, EpisodicMemory, run_agent
from deepcrew.memory import FileMemoryProvider

episodic = EpisodicMemory(FileMemoryProvider("episodes.json"), namespace="support-bot")

agent = Agent(
    name="assistant",
    model="gemini/gemini-3.5-flash-lite",
    episodic_memory=episodic,
)

# run 1 - nothing to recall; the run is recorded afterwards
await run_agent(agent, [{"role": "user", "content": "How do I rotate an API key?"}])

# run 2 - the earlier episode is recalled into context, then this run is recorded too
r = await run_agent(agent, [{"role": "user", "content": "Is key rotation zero-downtime?"}])
print(r.episodes_recalled)   # >= 1
  • Recall happens in the runner before the LLM sees anything: relevant episodes are ranked and injected as a ## Relevant past experience system block. AgentResult.episodes_recalled reports how many.
  • Record happens after the run. In a refinement loop, one episode is recorded per iteration, with its outcome / score taken from that iteration's verifier feedback.

Episode

python
@dataclass
class Episode:
    query: str
    response: str
    outcome: Literal["success", "failure", "partial", "unknown"] = "unknown"
    score: float | None = None
    tools_used: list[str] = field(default_factory=list)
    reflection: str | None = None      # set when reflect_model is configured
    tags: list[str] = field(default_factory=list)
    timestamp: str = ""                # ISO-8601 UTC, auto-filled
    agent_id: str = ""

EpisodicMemory

backend
MemoryProvider
Any provider used as the flat key/value store.
namespace
str = "default"
Isolates one episodic log from another in a shared backend.
max_episodes
int = 100
Ring buffer; the oldest episode is overwritten once full.
recall_k
int = 3
Default number of episodes recall() returns.
reflect_model
str | None = None
Optional LiteLLM model for a one-line lesson per episode.

Methods: await record(episode), await recall(query, *, top_k=None) (Jaccard keyword overlap + recency tie-breaker; no-overlap episodes are not returned; dependency-free), render(episodes), await reflect(episode), await clear().

Optional LLM reflection

python
episodic = EpisodicMemory(backend, reflect_model="gemini/gemini-3.5-flash-lite")

Each recorded episode gets a one-sentence reflection. render() prefers the reflection over the raw response, so recall stays compact. Off by default — no extra call unless you opt in. Reflection failures are swallowed (the episode is still recorded, reflection=None). The same option exists on ProceduralMemory(reflect_model=...) for its curation step.

Events

EventType.EPISODE_RECALLED ({count}) and EPISODE_RECORDED ({outcome, score}) are emitted on the stream. Like MEMORY_STORE / MEMORY_RETRIEVE, they are not in the chat() or standard() StreamPolicy presets — use verbose() to see them.

Common pitfalls

  • Recall needs topical overlap. A totally unrelated new query recalls nothing — that is intentional. Widen recall with tags on your episodes.
  • The ring buffer overwrites. Past max_episodes, the oldest episode is gone. Raise max_episodes or prune a persistent backend deliberately.
  • reflect_model costs one call per record. In a 5-iteration loop that is 5 calls. Leave it off unless the lesson quality is worth it.
  • Namespaces are manual. Two agents sharing a backend but not a namespace keep separate logs; give them the same one to share experience.
Want an AI coding agent to wire this up for you? Click Copy prompt above and paste it into Claude, ChatGPT, or your IDE's AI assistant.