Connect a memory backend
octo can optionally connect to a self-hosted external memory service that indexes your
conversations and lets the agent search them later. This is separate from
MEMORY.md: MEMORY.md is the agent’s curated standing guidance
(preferences, rules, project decisions), frozen into the system prompt every session. A memory
backend is free-form semantic recall over raw conversation text — the backend does its own
extraction and indexing, and octo doesn’t touch or duplicate the MEMORY.md layer to support it.
Three backends are supported; pick at most one:
- hindsight — self-hosted, no auth by default; a managed Hindsight Cloud option also exists if you’d rather not run the container yourself (see below).
- mem0 — self-hosted (
server/in the mem0ai/mem0 repo), auth on by default; a managed mem0 Platform (cloud) option also exists (see below). - MemTensor/MemOS — self-hosted, no auth by default. (Not
usememos/memos, which is an unrelated note-taking app, and notagiresearch/MemOS.)
All three need an LLM (for fact extraction) and an embedding model (for search) — either your own OpenAI-compatible endpoint (DashScope/Bailian, DeepSeek, etc.) or, for hindsight, a fully local setup. The steps below are a tested, copy-pasteable quick start for each — a supplement to their own docs, not a replacement.
Running a backend locally
Section titled “Running a backend locally”Pick one. Each assumes Docker is installed and running.
hindsight
Section titled “hindsight”Simplest to start: no database to configure, no auth by default.
docker run -d --name hindsight \ -p 8888:8888 -p 9999:9999 \ -v hindsight-data:/home/hindsight/.pg0 \ -v hindsight-hf-cache:/home/hindsight/.cache \ -e HINDSIGHT_API_LLM_PROVIDER=openai \ -e HINDSIGHT_API_LLM_MODEL=<your-model> \ -e HINDSIGHT_API_LLM_BASE_URL=<your-openai-compatible-base-url> \ -e HINDSIGHT_API_LLM_API_KEY=<your-api-key> \ -e HINDSIGHT_API_EMBEDDINGS_LOCAL_MODEL=BAAI/bge-small-en-v1.5 \ ghcr.io/vectorize-io/hindsight:latestHINDSIGHT_API_LLM_* can point at any OpenAI-compatible endpoint (DashScope’s
https://dashscope.aliyuncs.com/compatible-mode/v1, DeepSeek, real OpenAI, etc.) — hindsight uses it
only for consolidating what it retains, not for embeddings (those run locally via the
HINDSIGHT_API_EMBEDDINGS_LOCAL_MODEL, no key needed). First start takes ~1-2 minutes while it
downloads the embedding/reranker models and boots an embedded Postgres — don’t be alarmed if the API
doesn’t answer immediately.
Verify it’s up:
curl http://localhost:8888/v1/default/banks# {"banks":[]} — empty is fine, a bank is created automatically on first writeNo auth is required unless you explicitly set HINDSIGHT_API_TENANT_API_KEY on the container.
Hindsight Cloud (no Docker required)
Section titled “Hindsight Cloud (no Docker required)”Vectorize also runs a managed version — Hindsight Cloud — for
anyone who’d rather not run the container themselves. It speaks the same REST API at
https://api.hindsight.vectorize.io with the same /v1/default/banks/... paths as the self-hosted
container, so pointing octo at it is a config change, not a code change:
memory_backend: type: hindsight base_url: https://api.hindsight.vectorize.io api_key: "<your Hindsight Cloud API key>" namespace: octo-agentUnlike the self-hosted default, Hindsight Cloud requires the API key — generate one from its
dashboard and set it here. octo sends it as Authorization: Bearer <api_key>, matching what the
cloud API expects.
Needs Postgres (with pgvector) — the official server/ stack bundles it via Docker Compose.
git clone https://github.com/mem0ai/mem0cd mem0/servercp .env.example .envEdit .env:
OPENAI_API_KEY=<your-api-key>POSTGRES_PASSWORD=<pick-anything>AUTH_DISABLED=true # local development only — see "Auth" below for the real thingMEM0_DEFAULT_LLM_MODEL=<your-model>MEM0_DEFAULT_EMBEDDER_MODEL=<your-embedding-model>Then:
make bootstrapIf you’re using a non-OpenAI, OpenAI-compatible provider (DashScope, DeepSeek, …), the .env
model names alone aren’t enough — mem0’s OpenAI client defaults to api.openai.com. Point it at your
provider’s base URL via the /configure endpoint, before you store anything:
curl -X POST http://localhost:8888/configure \ -H "Content-Type: application/json" \ -d '{ "llm": {"provider": "openai", "config": {"model": "<your-model>", "openai_base_url": "<your-base-url>"}}, "embedder": {"provider": "openai", "config": {"model": "<your-embedding-model>", "embedding_dims": <dims>, "openai_base_url": "<your-base-url>"}} }'(No auth header needed with AUTH_DISABLED=true.) embedding_dims matters — see
Troubleshooting if you skip this and hit a dimension error.
AUTH_DISABLED=true is fine for a local trial but skips real access control. For anything longer-lived,
drop it, run make bootstrap as-is, and it prints an admin email/password/API key on first start —
use that generated API key as api_key in octo’s config instead of leaving it blank.
mem0 Cloud (no Postgres/Docker required)
Section titled “mem0 Cloud (no Postgres/Docker required)”mem0 also runs a managed version — the mem0 Platform —
for anyone who’d rather not self-host the server/ stack. Unlike Hindsight Cloud, this isn’t
a drop-in swap: the Platform API uses different endpoint paths and a different auth header than the
self-hosted server, so octo needs mode: cloud to talk to it correctly:
memory_backend: type: mem0 mode: cloud api_key: "<your mem0 Platform API key>" namespace: octo-agentbase_url can be omitted — it defaults to https://api.mem0.ai when mode: cloud and no
base_url is set. api_key is required (the Platform has no unauthenticated mode); octo sends it
as Authorization: Token <api_key>, matching what the Platform API expects.
MemOS (MemTensor)
Section titled “MemOS (MemTensor)”The heaviest of the three — bundles Neo4j (graph store) and Qdrant (vector store) alongside the API. Their docs give a ready-made Bailian/DashScope example that already has the embedding dimension set correctly, so it’s the least fiddly of the three if you have a DashScope key:
git clone https://github.com/MemTensor/MemOScd MemOScat > .env <<'EOF'OPENAI_API_KEY=<your-bailian-api-key>OPENAI_API_BASE=https://dashscope.aliyuncs.com/compatible-mode/v1MOS_CHAT_MODEL=qwen3-max
MEMRADER_MODEL=qwen3-maxMEMRADER_API_KEY=<your-bailian-api-key>MEMRADER_API_BASE=https://dashscope.aliyuncs.com/compatible-mode/v1
MOS_EMBEDDER_MODEL=text-embedding-v4MOS_EMBEDDER_BACKEND=universal_apiMOS_EMBEDDER_API_BASE=https://dashscope.aliyuncs.com/compatible-mode/v1MOS_EMBEDDER_API_KEY=<your-bailian-api-key>EMBEDDING_DIMENSION=1024MOS_RERANKER_BACKEND=cosine_local
NEO4J_BACKEND=neo4j-communityNEO4J_URI=bolt://localhost:7687NEO4J_USER=neo4jNEO4J_PASSWORD=12345678NEO4J_DB_NAME=neo4jMOS_NEO4J_SHARED_DB=false
DEFAULT_USE_REDIS_QUEUE=falseENABLE_CHAT_API=trueCHAT_MODEL_LIST=[{"backend": "qwen", "api_base": "https://dashscope.aliyuncs.com/compatible-mode/v1", "api_key": "<your-bailian-api-key>", "model_name_or_path": "qwen3-max", "support_models": ["qwen3-max"]}]EOFcd dockerdocker compose up --build(Using a different OpenAI-compatible provider: swap OPENAI_API_BASE/MOS_EMBEDDER_API_BASE and set
EMBEDDING_DIMENSION to match your embedding model’s actual output size — same rule as mem0 above.)
Verify it’s up: http://localhost:8000/docs should load. No auth is required by default — identity
comes from an X-User-Name header instead, which octo sends automatically when api_key is blank.
How it works
Section titled “How it works”- Storing is automatic. After every turn, octo sends that turn’s content to the backend in the
background — there’s no
memory_storetool and nothing for the agent to decide. This matches how these backends are designed to be used: you feed them raw text and they do their own extraction/dedup. It’s fire-and-forget — a failed store doesn’t surface anywhere and doesn’t slow down your turn. - Recall is a tool by default. The agent calls
memory_recallwhen it suspects something relevant was discussed in a prior session or conversation. This one does block on the network round trip and its errors do surface, since it’s an explicit, visible action rather than a background side effect. Whether to call it is a model judgment call — it can miss an isolated factual question that doesn’t read as “resume a prior conversation” (seeauto_recallbelow if you’d rather not rely on that).
Configuring
Section titled “Configuring”Add a memory_backend block to ~/.octo/config.yml:
memory_backend: type: hindsight # hindsight | mem0 | memos mode: "" # only meaningful for mem0: "cloud" or "" (self-hosted, default) base_url: http://localhost:8888 api_key: "" # optional — see per-backend notes below namespace: my-project # scopes stored/recalled memories; defaults to "default" auto_recall: false # optional — see "Automatic recall" belowtypeselects the backend. Leaving it unset (or omitting the whole block) disables the feature entirely — no tool is advertised, nothing is sent anywhere.modeonly matters fortype: mem0: set it tocloudto talk to the hosted mem0 Platform instead of a self-hosted server (see “mem0 Cloud” above) — the two use different endpoint paths and auth headers, so this isn’t inferred frombase_url. Ignored by hindsight and memos.base_urlis the backend’s REST endpoint — wherever you’re running its server (http://localhost:8888for hindsight/mem0 as set up above,http://localhost:8000for MemOS). Can be omitted formem0withmode: cloud, which defaults it tohttps://api.mem0.ai.api_keyis optional and backend-dependent:- self-hosted hindsight has no auth by default; set an API key only if you’ve enabled
HINDSIGHT_API_TENANT_API_KEYon the server. Hindsight Cloud is the exception — it always requires the API key from its dashboard. - self-hosted mem0 requires auth by default — set the server’s
X-API-Key-compatible key here, or run the server withAUTH_DISABLED=truefor local development and leave this blank. mem0 Cloud (mode: cloud) always requires the API key from its dashboard. - memos (MemTensor/MemOS) has no auth by default; leaving this blank sends your
namespaceas anX-User-Nameheader instead.
- self-hosted hindsight has no auth by default; set an API key only if you’ve enabled
namespacescopes what gets stored/recalled — hindsight’sbank_id, mem0’suser_id, or memos’suser_id. Use something stable per project (or leave it as the default single bucket).auto_recall— see below. Defaults tofalse.
Restart octo (or octo serve) after changing this — it’s read once at session start, the same as
every other config-file setting.
Sanity-check the wiring: start octo, have a short exchange, then ask something that requires
recalling it (in a fresh session, or after octo restarts) — you should see it call memory_recall
and get the earlier fact back.
Automatic recall
Section titled “Automatic recall”Set auto_recall: true to call Recall with the user’s message on every turn and fold the
result into that turn’s context automatically — instead of waiting on the model to decide to call
memory_recall. The tool stays available either way, for a deeper or differently-worded search;
the injected context tells the model not to bother re-calling it for the same question.
This trades a small, bounded amount of latency (recall is synchronous, capped at a 3s timeout, and
silently skipped on error or timeout) for not depending on the model’s judgment about when to
check memory — useful if you’ve noticed it answering “I don’t know” to something that’s actually in
the backend, rather than trying memory_recall first. Leave it off if you’d rather keep every turn
free of the extra round trip and rely on the tool alone.
Troubleshooting
Section titled “Troubleshooting”- mem0:
psycopg.errors.DataException: expected 1536 dimensions, not N— mem0’s Postgres vector column size is fixed the first time anything is stored (1536, matching OpenAI’s default embedding model). If your embedder returns a different size, you must setembedding_dimsvia/configurebefore the first/memoriescall. If you already stored something with the wrong size, there’s no in-place fix — wipe and start over:docker compose down -v && docker compose up -d, then/configureagain immediately, before storing anything. - mem0/MemOS:
provider_auth_failed/ 401 fromapi.openai.com— your LLM/embedder config is still pointing at real OpenAI. For mem0, set the base URL via/configure(not just.env); for MemOS, double checkOPENAI_API_BASE/MOS_EMBEDDER_API_BASEin.envand rebuild (docker compose up -d --force-recreate). - hindsight: connection refused right after
docker run— give it a minute or two; it’s still downloading/loading the embedding and reranker models.docker logs hindsightshows progress. Once it prints its startup banner, subsequent restarts are much faster (models are cached in thehindsight-hf-cachevolume). - Using Colima instead of Docker Desktop and a bind-mount volume shows up empty in the
container — Colima only shares specific host paths into its VM (by default your home directory
and
/tmp/colima). Clone the repo somewhere under your home directory, not under a path outside Colima’s configured mounts (e.g. not a random/tmp/...or/private/tmp/...location), or the.:/app-style bind mounts these projects use will silently mount as empty. - octo never calls
memory_recall, or the backend never receives anything — confirmmemory_backend.typeis actually set (an empty/missing block disables the feature with no error), and thatbase_urlmatches the port you actually exposed.