Persistent Memory¶
Each Agent can enable persistent memory — a SQLite database with FTS5 full-text search, served by a per-Agent memory server (a Deployment and Service next to the agent pods). The database lives on a PersistentVolume, so memory survives across ephemeral agent runs.
Agents interact with memory through three tools, which the agent-runner serves by calling the memory server over HTTP:
| Tool | Description |
|---|---|
memory_search(query, top_k?) |
Full-text search across stored memories. Returns the top k results (default 10). |
memory_store(content, tags?) |
Store a new memory entry with optional tags for categorisation. |
memory_list(tags?, limit?) |
List memories, optionally filtered by tags. |
How It Works¶
- Attaching the
memorySkillPack makes the controller create a memory server for the Agent: a Deployment<agent>-memory(theskill-memoryimage,cmd/memory-server/), a Service of the same name on port 8080, and a PVC<agent>-memory-dbholdingmemory.db. - Each AgentRun's agent container receives
MEMORY_SERVER_URL(http://<agent>-memory.<namespace>.svc:8080); the memory tools call it over HTTP. - SQLite FTS5 indexes all stored content for fast full-text search.
- Because the server and its PVC outlive individual runs, memories persist across runs.
graph LR
A["Agent Container"] -- "HTTP<br/>MEMORY_SERVER_URL" --> M["Memory Server<br/>(Deployment + Service)"]
M -- "reads / writes" --> DB[("SQLite + FTS5<br/>on PVC")]
Automatic Memory Storage¶
When the memory SkillPack is attached, the agent-runner automatically stores a summary of every successful run — a truncated Task: … / Response: … entry tagged auto and agent-run — so future runs have context without the agent having to call memory_store explicitly.
Opting out¶
Some teams prefer to curate memory deliberately rather than log every run. Set autoStore: false to disable the automatic write while keeping the memory skill and its memory_store tool for manual/curated entries — only the automatic per-run write stops.
On a single Agent:
apiVersion: sympozium.ai/v1alpha1
kind: Agent
metadata:
name: my-agent
spec:
skills:
- skillPackRef: memory
memory:
autoStore: false # keep the skill, stop the automatic per-run write
On an Ensemble — autoStoreMemory sets the team-wide default, overridable per member via agentConfigs[].memory.autoStore:
apiVersion: sympozium.ai/v1alpha1
kind: Ensemble
metadata:
name: research-team
spec:
autoStoreMemory: false # default for every generated agent
agentConfigs:
- name: archivist
memory:
autoStore: true # this member still auto-stores (overrides the default)
Precedence: per-agent-config memory.autoStore → ensemble autoStoreMemory → default (true). Omitting the field everywhere preserves the existing auto-store behaviour.
Tuning truncation limits¶
Auto-stored entries are truncated to keep the database small. The byte limits default to 500 bytes (task) and 1000 bytes (response) and can be overridden per pod via environment variables (set through spec.agents.default.env on the Agent, or env on an ensemble agent-config):
| Env var | Default | Effect |
|---|---|---|
MEMORY_AUTO_STORE_MAX_TASK_BYTES |
500 |
Max stored bytes of the task; a value ≤ 0 disables task truncation |
MEMORY_AUTO_STORE_MAX_RESPONSE_BYTES |
1000 |
Max stored bytes of the response; a value ≤ 0 disables response truncation |
MEMORY_AUTO_STORE |
true |
Set to false to disable auto-store directly (the controller sets this from memory.autoStore) |
Setting MEMORY_AUTO_STORE yourself through spec.agents.default.env (or an ensemble agent-config's env) also works, and an explicit env var wins over memory.autoStore — the controller skips its own injection when the pod already carries the variable. That matches every other controller-injected variable (RUN_TIMEOUT, MEMORY_SERVER_URL, and so on): spec.env is the last word. Prefer memory.autoStore for anything declarative, since the env var is per-pod and invisible to the Agent/Ensemble spec.
Enabling Memory¶
Add the memory SkillPack to your Agent's skills list:
apiVersion: sympozium.ai/v1alpha1
kind: Agent
metadata:
name: my-agent
spec:
agents:
default:
model: gpt-4o
skills:
- skillPackRef: memory
Or reference it from an Ensemble:
apiVersion: sympozium.ai/v1alpha1
kind: Ensemble
metadata:
name: sre-watchdog
spec:
agentConfigs:
- name: sre-watchdog
systemPrompt: |
You are an SRE watchdog. Monitor the cluster and track recurring issues.
skills:
- k8s-ops
- memory
memory:
enabled: true
seeds:
- "Track recurring issues for trend analysis"
- "Note any nodes that frequently report NotReady"
Seed memories are inserted into the SQLite database when the Agent is first created.
SkillPack Configuration¶
The memory SkillPack is defined at config/skills/memory.yaml. It carries Markdown instructions mounted at /skills/ and no sidecar; the controller runs the server separately:
- Skills layer: Instructions that teach the agent when and how to use
memory_search,memory_store, andmemory_list. - Server: The per-Agent
memory-serverDeployment that manages the SQLite database and answers HTTP requests. - No RBAC required: The memory server only accesses its own PVC — it does not talk to the Kubernetes API.
- Admin delete: With
memory.adminDelete.enabled(Helm, default on) the server also accepts an admin-onlyDELETE /deletewith a bearer token that is never given to agent pods.
Data Persistence¶
| Aspect | Detail |
|---|---|
| Storage | One PVC per Agent, named <agent>-memory-db |
| Database | SQLite 3 with FTS5 extension |
| Lifecycle | PVC persists until the Agent is deleted (or manually removed) |
| Backup | Standard PV backup tools apply (Velero, volume snapshots, etc.) |
| Upgradeable | The SQLite schema is designed to support a future upgrade path to vector search |
Viewing Memory¶
View an agent's stored memories through the TUI:
Or query the database directly by exec-ing into the memory server:
kubectl exec deploy/<agent>-memory -c memory-server -- sqlite3 /data/memory.db "SELECT content, tags FROM memories ORDER BY created_at DESC LIMIT 10;"
Shared Workflow Memory¶
When agents work together in a Ensemble, each persona has its own private memory by default. Shared Workflow Memory adds a pack-level memory pool that all personas can access, enabling team knowledge accumulation.
Private vs Shared Memory¶
| Aspect | Private Memory | Shared Workflow Memory |
|---|---|---|
| Scope | One Agent | All personas in an Ensemble |
| Storage | <agent>-memory-db PVC |
<pack>-shared-memory-db PVC |
| Tools | memory_search, memory_store, memory_list |
workflow_memory_search, workflow_memory_store, workflow_memory_list |
| Access | Always read-write | Per-persona: read-write or read-only |
| Attribution | N/A (single owner) | Auto-tagged with source persona name |
| Auto-context | Top 3 results injected as "Your Past Findings" | Top 3 results injected as "Team Knowledge" |
Enabling¶
Add sharedMemory to the Ensemble spec:
apiVersion: sympozium.ai/v1alpha1
kind: Ensemble
metadata:
name: research-delegation-example
spec:
# Define your personas here (researcher and reviewer in this example)
agentConfigs: []
sharedMemory:
enabled: true
storageSize: "1Gi"
accessRules:
- agentConfig: researcher
access: read-write
- agentConfig: reviewer
access: read-only
Infrastructure¶
The Ensemble controller provisions three Kubernetes resources:
graph LR
A1["Agent Pod<br/>(researcher)"] -- "WORKFLOW_MEMORY_SERVER_URL" --> SM["Shared Memory Server"]
A2["Agent Pod<br/>(writer)"] -- "WORKFLOW_MEMORY_SERVER_URL" --> SM
A3["Agent Pod<br/>(reviewer)"] -- "WORKFLOW_MEMORY_SERVER_URL<br/>(read-only)" --> SM
SM -- "reads / writes" --> DB[("SQLite + FTS5<br/>on shared PVC")]
- PVC:
<pack>-shared-memory-db—ReadWriteOnce, single replica - Deployment:
<pack>-shared-memory— sameskill-memoryimage,Recreatestrategy - Service:
<pack>-shared-memory— ClusterIP on port 8080
Agent pods receive two env vars:
- WORKFLOW_MEMORY_SERVER_URL — points to the shared memory service
- WORKFLOW_MEMORY_ACCESS — read-write or read-only (from access rules)
A wait-for-shared-memory init container ensures the server is ready before the agent starts.
Tools¶
| Tool | Description |
|---|---|
workflow_memory_search(query, top_k?) |
Full-text search across all team knowledge |
workflow_memory_store(content, tags?) |
Store findings for other personas (auto-tagged with source persona) |
workflow_memory_list(tags?, limit?) |
List entries, filterable by tag or persona |
The workflow_memory_store tool is only available to personas with read-write access. The source persona name is automatically added as a tag for attribution.
Synthetic Membrane¶
The Synthetic Membrane is an optional layer on top of Shared Workflow Memory that adds selective permeability, provenance tracking, token budgets, circuit breakers, and time decay. It transforms the flat shared memory pool into a structured medium where agents share state selectively.
Add a membrane block inside sharedMemory:
spec:
sharedMemory:
enabled: true
storageSize: "1Gi"
membrane:
defaultVisibility: public
permeability:
- agentConfig: researcher
defaultVisibility: trusted
exposeTags: ["findings"]
- agentConfig: reviewer
defaultVisibility: private
trustGroups:
- name: content-team
agentConfigs: ["researcher", "writer"]
tokenBudget:
maxTokens: 100000
action: halt
circuitBreaker:
consecutiveFailures: 3
timeDecay:
ttl: "168h"
Key capabilities:
| Feature | What it does |
|---|---|
| Permeability | Three-tier visibility (public/trusted/private) per persona with tag-level selectivity |
| Trust groups | Named groups of personas that can see each other's "trusted" entries |
| Token budget | Caps total token consumption across all runs; halts or warns on breach |
| Circuit breaker | Opens after N consecutive delegation failures, blocking further spawns |
| Time decay | Excludes old entries from search results via configurable TTL |
| Provenance | Every entry tracks its source agent and derivation chain via parent_id |
When the membrane is configured, agent pods receive additional env vars (WORKFLOW_MEMBRANE_VISIBILITY, WORKFLOW_MEMBRANE_TRUST_PEERS, WORKFLOW_MEMBRANE_ACCEPT_TAGS, WORKFLOW_MEMBRANE_MAX_AGE) that the agent runner uses to filter store and search calls automatically.
See Ensembles — Synthetic Membrane for full configuration reference.
Further Reading
The membrane design is based on the Synthetic Membrane research paper: "The Synthetic Membrane: A Shared Permeable Boundary for Multi-Agent AI Systems" (April 2026).
Viewing Shared Memory¶
Query the shared memory via the API:
curl -H "Authorization: Bearer $TOKEN" \
http://localhost:9090/api/v1/ensembles/research-delegation-example/shared-memory
Or exec into the shared memory pod:
kubectl exec deploy/research-delegation-example-shared-memory -c memory-server -- \
sqlite3 /data/memory.db "SELECT content, tags FROM memories ORDER BY created_at DESC LIMIT 10;"
Migration from ConfigMap Memory (Legacy)¶
The previous ConfigMap-based memory system (<agent>-memory ConfigMap with MEMORY.md) is preserved as a legacy fallback. If an Agent has spec.memory.enabled: true but does not include the memory SkillPack, the controller falls back to the ConfigMap approach.
To migrate:
- Add
memoryto the Agent's skills list. - Existing ConfigMap memories can be imported by storing them via
memory_storeduring the first run — the agent's skill instructions include guidance for this. - Once migrated, you can disable the legacy ConfigMap by removing
spec.memory.enabledor setting it tofalse.
Both systems can coexist during the transition period. The memory server takes precedence when both are present. Ensembles still create a seeded <agent>-memory ConfigMap alongside the memory SkillPack.