Journey 5
Memory & learning.
Everything the crew knows about your organization, and how it evolves.

01
Three layers of memory
The crew injects context into every agent call. There are three layers:
- Layer 1 — Explicit: what you tell the system directly. Brand voice profile (tone, vocabulary, sentence style), org facts (key/value pairs grouped by category), executive profiles (name, title, bio, communication style, sample quotes, topics of expertise).
- Layer 2 — Implicit (learned): edit signals. Every time you change a draft, the diff is sent to a background Lambda which asks Claude Haiku to classify the edit (tone shift, vocabulary swap, hard removal). After 3 similar classified signals, the brand voice profile updates and the next generation reflects the change.
- Layer 3 — Semantic: real cosine similarity over past content, website scans, and imported examples. Rows are embedded when they’re written and ranked by score at query time. The default provider is a local model (BAAI/bge-small-en-v1.5, 384 dimensions) that needs no API key; voyage-3 is available if you set one. On Postgres the vectors live in a
vector(N)column with an HNSW index and the similarity is computed in SQL; on SQLite they’re JSON and the cosine runs in Python. Same ordering, same score scale, either way.
02
Where memory lives in the UI
The /memory page has five tabs:
- Brand Voice — voice summary, preferred / avoided vocabulary, sentence style notes. Versioned: each time the implicit layer updates the profile, a new version is created.
- Org Facts — grouped by category (company, product, executive, messaging_pillar, boilerplate, competitor). Each fact is a key/value pair. Used by the Strategist when proposing angles.
- Executives — per-exec profiles used for ghostwriting. The Writer pulls the matching profile when the brief mentions an exec name.
- Past Content — pasted shipped comms and website scan text. Used as retrievable first-party examples when the crew drafts or plans.
- Learning Log — recent edit signals with proposed memory rules, before/after excerpts, and accept/reject/correction controls. The user-facing window into the implicit memory layer.
03
How memory is injected into agent prompts
Each agent call builds a structured system prompt that’s a list of text blocks. The org context block carries cache_control: ephemeral, so once it crosses 1024 tokens (the bcrypt-equivalent for prompt caching), subsequent calls in the same chat pay roughly 10% of the original input cost for that block. For ghostwriting, the executive profile is appended as a second cached block.
Note —The per-call kwargs pattern (passing executive profile as
system_extras instead of mutating instance state) prevents a concurrency hazard that would otherwise cause cross-request voice contamination under load. This was a real bug we fixed in Batch A.Honest limitations
- Semantic retrieval finds most things, not everything. Measured over a labelled 34-document, 17-query corpus at top-5: recall 0.87 and MRR 0.69, against 0.28 and 0.20 for the keyword search this replaced. One query fails outright on both — “how much did we raise and from whom?” does not retrieve the announcement that says “closed a $40M round led by Kestrel Partners”. It is kept as a known-failing case rather than relabelled.
- The similarity score is not a confidence. A query with no answer in the corpus still comes back at 0.54, against a mean of 0.47 for irrelevant documents. So the crew cannot yet decide “I don’t know” from the score alone, and nothing in the product pretends it can.
- Retrieval is O(n) in Python on SQLite. Around 2,500 memories per organization it crosses a 100 ms budget per agent, per turn. The fix past that is Postgres, where the ranking already happens in SQL against an HNSW index — not faster Python.
- Switching embedding providers hides the old memories. Vectors from two models aren’t comparable, so each row records which provider wrote it and search only reads matching rows. That is deliberate — the alternative is silently corrupted rankings — but it means a provider change requires a re-embed before anything is retrievable again.
- The 3-signal threshold for implicit updates is arbitrary. No user-facing control over how quickly the brand voice profile evolves. Could be confusing if it changes faster than expected.
- Per-executive voice profiles aren’t scored separately. The brand voice score in the Editor is org-wide, not exec-specific. So if you ghostwrite for Sarah but the org default voice is different from hers, the score may misrepresent fit.
- Memory proof is still V1. The Learning Log exposes individual rules and decisions, but there is no weekly digest or source-coverage score that tells a comms manager what the crew still needs to learn.
Next
Complete limitations list →
Everything that's stubbed, broken, or deliberately deferred. In one place.