使用说明
Hindsight — local stack for macOS
Long-term memory for AI agents, running locally: the API and the ML models run in a
native Python process, docker-compose carries PostgreSQL + pgvector and nothing else.
Quick start
git clone <this-repo> && cd hindsight
cp .env.example .env
# edit three lines in the REQUIRED block: LLM_BASE_URL, LLM_API_KEY, LLM_MODEL
make start # bootstraps everything, ~3 min on first run
make bank BANK=myproject # create and configure a memory bank
That's it. make start creates the Python venv, installs the pinned version,
starts PostgreSQL, runs DB migrations and brings up the API and the UI.
| API | http://localhost:8888 |
| UI | http://localhost:9999 |
| MCP endpoint | http://localhost:8888/mcp/{bank_id}/ |
Check it works:
make health # {"status":"healthy","database":"connected",...}
make banks # list banks with fact counts
First run downloads ~2.5 GB of embedding and reranker models into ./huggingface.
Subsequent starts take a few seconds.
Requirements
Verified on the versions below. Older ones usually work, but these are what the stack is actually tested against.
| Tool | Version | Needed for |
|---|---|---|
| macOS | 26.x, Apple Silicon | MPS acceleration for local models |
| Docker Desktop | 29.x | PostgreSQL only |
| uv | 0.9.x | Python env, installed automatically by make |
| Node.js | 22.x LTS | UI via npx — optional |
| Python | 3.11 | pinned by the Makefile, uv installs it for you |
Install the two you need:
brew install --cask docker
brew install uv node
You also need an OpenAI-compatible LLM endpoint — OpenAI itself, a local LM Studio / Ollama, or any proxy in front of them.
Why not run everything in Docker
Docker Desktop on macOS gives a container no usable access to the GPU. The
embedding model (bge-m3) and the cross-encoder reranker would fall back to CPU
and become several times slower.
So the split is: models and API on the host where they reach Metal/MPS, and the database — which does not care — in a container.
┌──────────────────────────────────────────┐
│ macOS host │
│ - hindsight-api (Python 3.11 venv) │
│ - embeddings + reranker (MPS) │
│ - control-plane UI via npx (optional) │
└───────────────────────┬──────────────────┘
│ localhost:5432
┌───────────────────────▼──────────────────┐
│ docker-compose │
│ - postgres 16 + pgvector │
└──────────────────────────────────────────┘
Commands
| Command | What it does |
|---|---|
make start |
Postgres + API + UI (bootstraps on first run) |
make stop |
stop API and UI, leave Postgres running |
make stop-all |
stop everything including Postgres |
make restart |
stop + start |
make status |
what is currently running |
make health |
API health check |
make logs |
tail API and UI logs together |
make bank BANK=x |
create/configure bank x from bank-template.json |
make banks |
list banks with fact counts |
make sync-version |
align installed packages with VERSION in .env |
Setting up a memory bank
A bank is an isolated memory space — normally one per project.
make bank BANK=myproject
This applies bank-template.json to the bank, creating it if missing. The file
in the repo is a sane engineering default: it tells the extractor to keep
decisions, constraints, verified procedures and dead-ends, and to ignore session
chatter.
Edit bank-template.json to taste and re-run the command — it is idempotent.
Two rules worth knowing:
- Missions must be written in English. The server-side validator rejects other languages. This is independent of the language your facts are stored in.
- The language of stored facts is
HINDSIGHT_API_LLM_OUTPUT_LANGUAGEin.env.
The full field list is served by the API itself:
curl http://localhost:8888/v1/bank-template-schema | jq .
Configuration
Everything lives in .env, which the API reads directly at startup
(load_dotenv(override=True)) — no shell exports needed. .env is gitignored;
only .env.example is committed.
.env.example has two parts: an active block with working defaults, and a large
commented block documenting the optional settings with their current defaults.
A few settings deserve a mention because their failure mode is silent:
HINDSIGHT_API_LLM_TIMEOUT — if a Cloudflare-fronted proxy sits in front of
your endpoint, it cuts connections at 120 s. Keep the client timeout below
that, or every failing attempt burns the full two minutes.
HINDSIGHT_API_LLM_CACHE_AFFINITY — the default auto is an allowlist
covering only x.ai and openai.com. Any custom proxy silently resolves to
none. Set it explicitly if your endpoint understands prompt_cache_key. It
only pays off for multi-call operations, since the key is one per operation run.
A misspelled variable name is silently ignored. There is no validation. To check your file against the installed version:
grep -oE '"HINDSIGHT_API_[A-Z0-9_]+"' \
.venv-hs/lib/python3.11/site-packages/hindsight_api/config.py \
| tr -d '"' | sort -u > /tmp/valid.txt
grep -oE '^HINDSIGHT_API_[A-Z0-9_]+' .env | sort -u | comm -23 - /tmp/valid.txt
Anything printed does not exist and is doing nothing.
Search language
Two independent settings, easy to confuse:
| Setting | Controls |
|---|---|
HINDSIGHT_API_LLM_OUTPUT_LANGUAGE |
the language the LLM writes new facts in |
HINDSIGHT_API_TEXT_SEARCH_EXTENSION_NATIVE_LANGUAGE |
how text is stemmed for lexical search |
PostgreSQL's built-in russian configuration is already bilingual: Cyrillic
tokens go through russian_stem, ASCII tokens (phpstan, GraphQL, file paths)
through english_stem, and stopwords are dropped on both sides. There is no need
to build a custom hybrid configuration — verify with \dF+ russian.
Changing the search language on a populated database requires a backfill.
memory_units.search_vector is a plain column written at insert time, not a
GENERATED one, so existing rows keep their old configuration:
UPDATE memory_units SET search_vector = to_tsvector('<lang>'::regconfig,
COALESCE(text,'') || ' ' || COALESCE(context,'') || ' ' || COALESCE(text_signals,''));
REINDEX INDEX idx_memory_units_text_search;
This is pure SQL — no LLM calls, no re-embedding, no document re-ingest. Measured at ~27 seconds for 3,800 rows.
Upgrading
# 1. back up first
docker compose exec -T postgres pg_dump -U hindsight -Fc hindsight_db \
> backups/hindsight_pre_<version>.dump
# 2. pin the new version
# edit VERSION in .env, e.g. VERSION=v0.9.1
# 3. apply
make restart
make restart reinstalls the Python package and the UI at the pinned version, and
Alembic migrations run automatically on API start. Verify:
curl -s http://localhost:8888/version
grep -iE 'alembic|error' logs/api.log | tail
Rollback is the reverse: restore VERSION, make restart, and pg_restore the
dump if a migration changed the schema.
Troubleshooting
API will not start — make logs, then make health. Most often .env is
missing or the LLM endpoint is unreachable.
Database connection refused — docker compose ps to confirm Postgres is up,
docker compose up -d postgres to bring it back. The password in
HINDSIGHT_API_DATABASE_URL must match POSTGRES_PASSWORD in
docker-compose.yaml.
UI does not come up — install Node.js for npx, or just ignore it; the API
works standalone.
Operations stuck in processing — a worker died without releasing its tasks
(a restart will do it). Release them:
.venv-hs/bin/hindsight-admin decommission-workers -y
Checking what the background workers are doing:
SELECT operation_type, status, count(*)
FROM async_operations
WHERE created_at > now() - interval '7 days'
GROUP BY 1,2 ORDER BY 3 DESC;
Contributing / Security
- Contribution guide:
CONTRIBUTING.md - Security policy:
.github/SECURITY.md - Upstream project: vectorize-io/hindsight
