Journey 5

Memory & learning.

Everything the crew knows about your organization, and how it evolves.

The Memory page — Brand Voice tab
The Memory page — Brand Voice tab
01

Three layers of memory

The crew injects context into every agent call. There are three layers:

  1. 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).
  2. 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.
  3. 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.