Local with Docker Compose¶
A single-machine deployment from zero to a working API. The active stack is four services: the AI Engine API, the Content Engine, Qdrant, and Redis.
1. Prerequisites¶
- Docker and Docker Compose installed.
- This repository checked out.
2. Configure¶
Edit .env: set QDRANT_API_URL=http://qdrant:6333, REDIS_URL=redis://redis:6379/0, and
your LLM key (OPENROUTER_API_KEY) or a local Ollama URL. Leave QDRANT_API_KEY empty for a
local Qdrant.
3. A complete compose file¶
Save this as docker-compose.local.yml next to the API:
services:
qdrant:
image: qdrant/qdrant:latest
ports: ["6333:6333", "6334:6334"]
volumes: ["qdrant_data:/qdrant/storage"]
redis:
image: redis:7
ports: ["6379:6379"]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 10
api:
build: .
env_file: .env
environment:
QDRANT_API_URL: http://qdrant:6333
REDIS_URL: redis://redis:6379/0
depends_on:
redis: { condition: service_healthy }
qdrant: { condition: service_started }
ports: ["8000:8000"]
restart: unless-stopped
content-engine:
image: python:3.12-slim
working_dir: /app
volumes: ["../content-engine:/app"]
environment:
QDRANT_API_URL: http://qdrant:6333
COLLECTION_NAME: ${COLLECTION_NAME}
EMBEDDING_MODEL: ${EMBEDDING_MODEL}
command: bash -c "pip install -e '.[api,qdrant,embed]' && content-engine"
depends_on:
qdrant: { condition: service_started }
ports: ["8002:8002"]
volumes:
qdrant_data:
4. Bring it up¶
5. Load content¶
The Qdrant collection starts empty. Ingest documents through the Content Engine:
curl -X POST http://localhost:8002/ingest \
-H "Content-Type: application/json" \
-d '[{"id":"841","title":"Forced labour","text":"...","content_type":"text_item",
"tags":[{"facet":"theme","label":"forced_labour","weight":1.0}]}]'
Or sync from a configured Omeka instance:
curl -X POST http://localhost:8002/sync/omeka -H "Content-Type: application/json" -d '{"max_items":500}'
6. Verify¶
curl http://localhost:8000/ # {"status":"ok", ...}
curl "http://localhost:8000/api/search?q=Bergen-Belsen"
curl http://localhost:8002/health # {"status":"ok"}
Open the interactive references at AI Engine API and Content Manager API.
7. Optional: behavioral capture¶
To capture behavior, point a RudderStack source at the recsys ingest webhook
(POST /api/ingest). Events land in the Redis buffer and are folded into the user model.
See Online serving model.
Troubleshooting¶
- Empty recommendations: the collection is not populated, or
COLLECTION_NAMEdiffers between the API and the Content Engine. See the consistency rule in Configuration. - API boots but recsys is fake:
REDIS_URLorQDRANT_API_URLis unset; the engine fell back to in-memory stores. - No narratives: check the LLM variables (
OPENROUTER_API_KEYorOLLAMA_BASE_URL).