Memory¶
pkg/agent/memory/ provides the conversation- and knowledge-state
backends an agent can use. Every backend implements the same
Memory interface, so an agent's memory: block can be swapped from
a short-lived in-process buffer to a durable Redis-backed store
without touching the agent code. Eight first-party backends are
available; a Middleware family adds cross-cutting concerns
(logging, encryption, …) composably, and a SessionMemoryManager
lets one shared agent service many concurrent sessions with isolated
memory.
What it does¶
type Memory interface {
Add(ctx context.Context, message *llm.Message) error
Get(ctx context.Context) ([]*llm.Message, error)
Search(ctx context.Context, query string, limit int) ([]*llm.Message, error)
Clear(ctx context.Context) error
}
Every ReAct turn does:
mem.Get(ctx)— seed the LLM context with history.- After the user message and after the assistant turn,
mem.Add(ctx, msg)— persist the new turn. Searchis not used by the agent loop directly; it is exposed for tools / Studio / RAG-style lookups.
The choice of backend governs what Get returns: full history,
the last N messages, a summary + recent window, or a vector-search
shaped lookup. The agent itself is backend-agnostic.
Agent.SetMemory swaps the backend atomically via atomic.Pointer
— concurrent Runs never see a torn state. SessionMemoryManager
uses this to give every session its own backend while sharing one
agent instance.
Backends¶
ConversationMemory¶
In-process append-only message log — the default when no memory:
block is set. Search uses a case-insensitive substring match.
Thread-safe via sync.RWMutex. Use only when conversation length
stays small or when you intentionally want full retention.
YAML: type: conversation
SlidingWindowMemory¶
Keeps only the last windowSize messages. The first system message
is always preserved and does not count toward the window. On Add,
trim() drops the oldest non-system messages until size <= window.
YAML: type: sliding-window + window_size: N
SummaryMemory¶
When the message count exceeds maxMessages, summarises the
oldest half using a SummaryFunc and stores the summary as a
running text. Get returns the running summary as a synthetic
system message followed by the recent messages. Use
NewSummaryMemoryWithModel(model, maxMessages) for a turnkey
LLM-backed summariser. This pattern keeps context bounded without
losing earlier facts.
YAML: type: summary + max_messages: N
TokenWindowMemory¶
Like SlidingWindowMemory but the window is in TOKENS, not
messages. Uses llm.EstimateTokens (a punctuation- and
word-aware estimator, replacing the old len/4 heuristic that
systematically undercounted JSON/code by ~25%). Estimated token
count is capped at 1,000,000 for safety. System message is
always preserved.
YAML: type: token-window + max_tokens: N
SmartMemory¶
A token-budgeted memory tuned for tool-heavy agents (coding, RAG, analysis). It:
- Tracks tokens, not turns (large tool results count properly).
- Compresses old tool results to summaries when evicted.
- Keeps tool-call / tool-response pairs together (never orphans).
- Maintains a "file context map" so it remembers
path → summaryfor files it has read or written, even after eviction.
NewSmartMemory(maxTokens) — pass 0 to get a 100K-token default.
YAML: type: smart (also accepts type: code for back-compat) +
max_tokens: N
LongTermMemory¶
Persistent memory with a pluggable Store interface — backed by
InMemoryStore (for tests), or
SQLDBLongTermMemory / RedisLongTermStore / MemxLongTermMemory
for production. Messages are scoped by an agent ID (namespace);
Search is substring-match in the in-memory store, ILIKE in
SQL, and Redis-keyspace-scan in Redis.
YAML: type: long-term + store: memory | redis | postgres
SQLDBLongTermMemory¶
PostgreSQL-backed memory using the generic SQLDB interface
(so any database/sql driver works, including pgx/v5 via its
stdlib adapter). Table name defaults to agent_memory; custom
names validated against alphanumeric+underscore-only and the SQL
keyword list. EnsureTable(ctx) creates the schema if absent.
YAML: type: sql-long-term + dsn: postgres://... + (session_id:
or session_id_header: X-Session-ID)
RedisConversationMemory + RedisSlidingWindowMemory + RedisTokenWindowMemory + RedisLongTermStore¶
Redis-backed implementations of the conversation / sliding-window /
token-window / long-term shapes. Keys are namespaced
helix:memory:<namespace>:<sessionID> so a multi-tenant deployment
can share one Redis cluster. Optional TTL and max-length (LTRIM) caps.
YAML: type: conversation | sliding-window | token-window | long-term
with store: redis + redis_url: redis://... + namespace: +
(session_id: or session_id_header:).
VectorMemory¶
Semantic-search memory. Add embeds each message and Upserts
into a VectorStore; Search embeds the query and returns the
top-K nearest. Get returns an empty slice — vector memory is
search-only, NOT a sequential history. Use it as ONE backend
inside a CompositeMemory next to a sequential backend.
NewVectorMemory takes an EmbedFunc + VectorStore + agentID +
topK. NewVectorMemoryWithEmbedder is the same with an Embedder
interface. The package ships InMemoryVectorStore (brute-force
cosine for tests, capped at 100K entries) and MemxVectorStore
(memx-backed, EMBEDDING column + MATCH_DENSE).
YAML: type: vector + top_k: N + embed: { provider, model, ... }
+ a store: directive.
CompositeMemory¶
Combines multiple backends. Add writes to ALL; Get and Search
merge their results with dedup on (role, content). Use this to
pair a sliding-window for recent turns with a vector store for
historical recall, or a Redis-backed short-term with a SQL
long-term archive.
YAML: type: composite + backends: [ ... ]
FileBackedMemory¶
Wraps any Memory with JSON-file persistence. Loads on construction
if the file exists, saves on every Add and Clear. Designed for
CLI agents that need memory across restarts without Redis/Postgres.
YAML: type: <any> with store: file + file_path: .helix/memory/agent.json
Middleware¶
Middleware is func(Memory) Memory — composable wrappers,
identical to tool.Middleware and llm.ModelMiddleware.
ChainMiddleware(a, b, c) produces outermost-first composition.
Built-in:
WithLogging(logf)— logs every Add / Get / Search / Clear call (defaults to slog at debug level whenlogfis nil).
YAML names it: memory_middleware: [logging]. Custom middlewares
register their factory with the reifier.
SessionMemoryManager¶
SessionMemoryManager keeps a sync.Map keyed by session ID, each
holding its own Memory instance, created on first use via a
MemoryFactory. The agent uses Agent.SetMemory(...) per request
to swap in the session's memory. This is the standard pattern for a
single agent serving many users without leaking conversation state.
Configuration¶
YAML reference (MemoryDefinition in pkg/workflow/definition.go):
| Field | Type | Used by | Description |
|---|---|---|---|
type |
string | all | conversation / sliding-window / smart (alias code) / summary / token-window / long-term / sql-long-term / vector / composite. |
store |
string | redis/file backends | Storage backend: memory, redis, postgres, file. |
namespace |
string | redis | Key prefix in Redis. |
window_size |
int | sliding-window | Message count cap. |
max_tokens |
int | token-window, smart | Token budget. |
max_messages |
int | summary | Threshold for triggering summarisation. |
top_k |
int | vector | Default search depth. |
dsn |
string | sql-long-term | Postgres connection string. |
redis_url |
string | redis | Redis connection URL. |
file_path |
string | file-backed | Path on disk. |
session_id |
string | sql/redis | Static session scope. |
session_id_header |
string | sql/redis | Header name for per-request session ID. |
ttl |
string | redis | Key expiration (e.g. 24h, 7d). |
embed |
block | vector | { provider, model, api_key, base_url }. |
backends |
list | composite | Nested MemoryDefinitions. |
Examples¶
A sliding window of 30 messages with logging:
agents:
- id: chat
type: react
memory:
type: sliding-window
window_size: 30
memory_middleware: [logging]
A composite of recent-window + vector recall:
memory:
type: composite
backends:
- { type: sliding-window, window_size: 20 }
- { type: vector, top_k: 5,
embed: { provider: openai, model: text-embedding-3-small },
store: memory }
A multi-tenant Redis-backed conversation memory:
memory:
type: conversation
store: redis
namespace: prod-chat
session_id_header: X-Session-ID
redis_url: redis://redis:6379
ttl: 168h # 7 days
Source¶
pkg/agent/memory/memory.go— Memory interface, ConversationMemory, SlidingWindowMemory, SummaryMemory, TokenWindowMemory, LongTermMemory + InMemoryStore, SQLDBLongTermMemory, VectorMemory, CompositeMemory, InMemoryVectorStore.pkg/agent/memory/smart_memory.go— SmartMemory.pkg/agent/memory/redis_store.go— Redis variants.pkg/agent/memory/file_store.go— FileBackedMemory.pkg/agent/memory/memx.go— MemxVectorStore.pkg/agent/memory/middleware.go— Middleware framework + WithLogging.pkg/agent/memory/session.go— SessionMemoryManager.pkg/dsl/reifier.go—reifyMemory(the YAML → instance bridge).