Memory
August 21, 2026 · View on GitHub
Languages: English (current) · Português
Memory stores task outputs so later tasks can recall what has already been
produced — even without an explicit WithContext. The short-term path is the
in-process *Memory bag (Crew.Memory = true). Long-term backends plug in via
MemoryStore (FileStore, custom), controlled by MemoryPolicy, with optional
embeddings for cosine recall.
Enabling it
crew := crewai.NewCrew(agents, tasks)
crew.Memory = true
crew.Kickoff(ctx, nil)
With Memory = true, each task with no explicit context receives, in its
prompt, a summary of the memory accumulated up to that point.
Reading the memory
mem := crew.MemorySnapshot() // *crewai.Memory (nil if Memory == false)
for _, r := range mem.Records() {
fmt.Printf("[%s] task=%q → %s\n", r.Agent, r.Task, r.Content)
}
Each record is a MemoryRecord:
type MemoryRecord struct {
Agent string // the role of the agent that produced it
Task string // the related task's name
Content string // the memorized content
}
Searching
Simple text search (substring, case-insensitive):
for _, r := range mem.Search("sales") {
fmt.Println(r.Content)
}
An empty query returns all records.
Using memory standalone
Memory can also be used in isolation:
m := crewai.NewMemory()
m.Save(crewai.MemoryRecord{Agent: "Analyst", Content: "revenue grew 12%"})
fmt.Println(m.String())
Context vs Memory
- Context (
WithContext) is explicit and directed: you say exactly which outputs feed into a task. This is the merge channel for parallel siblings (Staged peers or Async waves). - Memory is implicit and cumulative: budgeted recall of the
committed past for tasks with empty context (
InjectWhenEmptyContext).
Use context for precise dependencies; use memory for general awareness — not as a side channel to order sibling outputs. During parallel groups, AutoSave is buffered and committed at the barrier in declaration order (D-M7); inject never sees in-flight siblings. Details: Concurrency model.
Custom MemoryStore backends
The built-in *Memory is in-RAM and concurrency-safe, and already implements
MemoryStore. For durable persistence use OpenFileStore; for semantic
recall provide Crew.Embed + MemoryPolicy.AutoEmbed (see below). To plug
your own backend (SQLite, remote KV, vector DB client…), implement
MemoryStore and assign it to Crew.MemoryStore — the app owns Close.
MemoryPolicy + commit barrier (M2)
Crew.MemoryPolicy controls automatic save/inject. Nil means
NewMemoryPolicy() defaults (AutoSave=true, InjectWhenEmptyContext=true,
budgeted DefaultLimit / DefaultMaxChars). A literal MemoryPolicy{} is
not those defaults — use NewMemoryPolicy and override fields.
crew.Memory = true // ensures InMemory store when MemoryStore is nil
crew.Name = "my-crew" // default MemoryPolicy.Scope (G3)
crew.MemoryPolicy = crewai.NewMemoryPolicy()
crew.MemoryPolicy.DefaultMaxChars = 2000
// or supply an external store (app owns Close — D-M6):
// crew.MemoryStore = myStore
D-M7 visibility invariant: during a parallel wave/stage, AutoSave writes
go into a per-task buffer. They are committed to the store only at the
barrier, in declaration order. The next wave's inject/Query sees only the
committed snapshot — never in-flight sibling writes. Do not use Memory as
the merge channel for parallel siblings; use WithContext.
Failed tasks are never AutoSaved (G2). AutoSave errors are warned and captured; they do not abort Kickoff (G11).
Embeddings + cosine recall (M4)
Provide an EmbeddingFunc and set MemoryPolicy.AutoEmbed = true to
persist vectors on AutoSave. Embedding runs serially at the commit
barrier (G8) — never inside parallel workers. The core never bundles a
model; the app owns the HTTP call (same trust as LLM providers).
crew.Embed = func(ctx context.Context, texts []string) ([][]float32, error) {
// call OpenAI / Ollama / local model…
return vectors, nil
}
p := crewai.NewMemoryPolicy()
p.AutoEmbed = true
crew.MemoryPolicy = p
crew.Memory = true
Query ranking: pass a pre-computed MemoryQuery.Embedding to rank by
cosine similarity (stdlib only). Built-in stores (*Memory, FileStore)
ignore substring Text on the semantic path. Entries without a usable
vector score 0 and are trimmed when any positive match exists; if nothing
is embedded, Query falls back to latest-N of the candidate set.
hits, _ := store.Query(ctx, crewai.MemoryQuery{
Embedding: queryVec, // embed q yourself via EmbeddingFunc
Limit: 5,
})
AutoEmbed errors are soft (warn+capture, entry still saved without a
vector) — they never abort Kickoff. See examples/memory_embed.
FileStore (JSONL, M3)
OpenFileStore(dir) opens a durable stdlib-only backend under a
caller-trusted root (never pass model-controlled paths):
store, err := crewai.OpenFileStore("/var/lib/myapp/crew-memory")
if err != nil { /* … */ }
defer store.Close() // app owns lifecycle (D-M6)
crew.Name = "finance-crew" // default MemoryPolicy.Scope (G3)
crew.MemoryStore = store
crew.MemoryPolicy = crewai.NewMemoryPolicy()
Layout:
{root}/scopes/{urlsafeScope}/
entries.jsonl # append-only MemoryEntry JSON lines (+ delete tombstones)
meta.json # schema version, corrupt-skip counter
- Files
0600, directories0700. - v1 is single-writer (G7): one process per root; one
*FileStoreis mutex-safe for concurrent Puts/Queries inside that process. - Corrupt JSONL lines are skipped on Open and counted in
meta.json/CorruptSkipped()(D-M5). - Survives process restart: Put → Close → Open → Query.
See examples/memory_file.
Long-term store interface
*Memory also implements crewai.MemoryStore, the pluggable long-term
memory contract used by durable backends (FileStore) and custom stores:
var store crewai.MemoryStore = crewai.NewMemory() // or existing *Memory
e, _ := store.Put(ctx, crewai.MemoryEntry{Agent: "Analyst", Content: "revenue grew 12%"})
hits, _ := store.Query(ctx, crewai.MemoryQuery{Limit: 5})
_ = store.Delete(ctx, "", e.ID)
Putassigns a stableIDandCreatedAt; entries overMaxMemoryEntryBytesare rejected (ErrMemoryEntryTooLarge).QueryhonorsLimit(defaultDefaultMemoryQueryLimit, hard capMaxMemoryQueryLimit) andMaxChars(defaultDefaultMemoryMaxChars, negative = uncapped), searchingContent/Taskcase-insensitively. An emptyTextreturns the latest entries first.Deleteis idempotent;Closeis a no-op for the in-memory store.- Entries are partitioned by
MemoryScope; short-termSaverecords live in the default (empty) scope.
Crew.Memory remains the permanent v0.x alias that ensures an InMemory store
when MemoryStore is nil.