中文说明请见:README.zh-CN.md
Current repository: https://github.com/mxyhi/always-on-memory-agent
Original upstream repository: https://github.com/GoogleCloudPlatform/generative-ai
Original upstream directory: https://github.com/GoogleCloudPlatform/generative-ai/tree/main/gemini/agents/always-on-memory-agent
A deployable always-on memory service built with Google ADK, Gemini, aiohttp, and a shadcn/ui web console.
This fork no longer treats the dashboard as a separate client. server.py remains the unified application entrypoint, while container deployment separates API and web serving:
- Browser users sign in with
email + password + Bearer JWT - Programmatic clients can optionally use
X-API-Key auth.dbstoresusers,api_keys, and auth settings- The original per-user
memory.dbremains the core memory store - Local single-process mode can still serve the built
web/app fromserver.py - Docker / Compose use separate
apiandwebcontainers, with nginx keeping external access same-origin
data/
├── auth.db
└── users/
└── <user_id>/
├── inbox/
└── memory.db
auth.db: control-plane data for login, API keys, and JWT signing settingsdata/users/<user_id>/memory.db: memory facts, consolidations, processed filesdata/users/<user_id>/inbox/: uploaded files waiting to be processed
The memory pipeline accepts 27 file types:
| Category | Extensions |
|---|---|
| Text | .txt, .md, .json, .csv, .log, .xml, .yaml, .yml |
| Images | .png, .jpg, .jpeg, .gif, .webp, .bmp, .svg |
| Audio | .mp3, .wav, .ogg, .flac, .m4a, .aac |
| Video | .mp4, .webm, .mov, .avi, .mkv |
| Documents | .pdf |
Create a local .env file:
cp .env.example .envAvailable environment variables:
| Variable | Required | Description |
|---|---|---|
GOOGLE_API_KEY |
Yes | Gemini / Google AI Studio API key |
MODEL |
No | Defaults to gemini-3.1-flash-lite-preview |
MEMORY_DATA_DIR |
No | Data root, defaults to ./data |
JWT_SECRET |
No | Optional stable signing secret for multi-instance JWT deployments |
Notes:
- API keys are optional.
- API keys are created by users inside the web console, not through env vars.
auth.dbdefaults todata/auth.db.- If
JWT_SECRETis unset, the API generates one and stores it inauth.db.
pip install -r requirements.txt
pnpm --dir web installpython server.py --port 8888 --data-dir ./data --auth-db ./data/auth.dbThis starts:
- the unified API on
http://localhost:8888/api/* - the built web console on
http://localhost:8888whenweb/distexists - inbox polling
- periodic consolidation for known users
pnpm --dir web devThe Vite dev server proxies /api and /healthz to http://localhost:8888.
pnpm --dir web build
python server.py --port 8888 --data-dir ./data --auth-db ./data/auth.db --web-dist ./web/distThen open http://localhost:8888.
POST /api/auth/registerPOST /api/auth/loginPOST /api/auth/logoutGET /api/auth/me
register and login both return an access token. The web console stores that JWT and sends Authorization: Bearer ....
- Users create API keys in the web console after logging in
- External callers can send
X-API-Key: ma_... - If both Bearer token and
X-API-Keyare present, explicitX-API-Keywins
| Endpoint | Method | Description |
|---|---|---|
/healthz |
GET | Anonymous health check |
/api/healthz |
GET | Same health check under /api |
| Endpoint | Method | Description |
|---|---|---|
/api/auth/register |
POST | Create account and return a Bearer token |
/api/auth/login |
POST | Return a Bearer token |
/api/auth/logout |
POST | Client-side logout acknowledgement |
/api/auth/me |
GET | Return current authenticated user |
/api/auth/api-keys |
GET | List current user's API keys |
/api/auth/api-keys |
POST | Create one API key and return its plaintext token once |
/api/auth/api-keys/{api_key_id} |
DELETE | Revoke one API key |
| Endpoint | Method | Description |
|---|---|---|
/api/memories/status |
GET | Current user's memory statistics |
/api/memories |
GET | List current user's memories |
/api/memories/ingest |
POST | Ingest text: {"text":"...","source":"..."} |
/api/memories/ingest-file |
POST | Upload one file as multipart field file |
/api/memories/query?q=... |
GET | Query current user's memory |
/api/memories/consolidate |
POST | Trigger consolidation |
/api/memories/delete |
POST | Delete one memory: {"memory_id": 1} |
/api/memories/clear |
POST | Clear current user's memories |
Build the API image:
docker build -t always-on-memory-agent-api .Build the web image:
docker build -t always-on-memory-agent-web -f web/Dockerfile webRun the API container:
docker network create always-on-memory-agent
docker run --rm \
--env-file .env \
-e MEMORY_DATA_DIR=/app/data \
-v "$(pwd)/data:/app/data" \
--network always-on-memory-agent \
--name always-on-memory-agent-api \
always-on-memory-agent-apiRun the web container:
docker run --rm \
--network always-on-memory-agent \
-p 8888:80 \
always-on-memory-agent-webOpen http://localhost:8888.
docker compose up --buildThis starts two services:
apion the internal Docker networkwebonhttp://localhost:8888
Compose details:
./datais mounted into theapicontainer at/app/data- nginx in
webserves the SPA and proxies/api/*and/healthztoapi:8888 - external traffic stays same-origin even though containers are split
auth.dblives inside the mounteddata/directory
always-on-memory-agent/
├── agent.py
├── server.py
├── dashboard.py
├── Dockerfile
├── docker-compose.yml
├── .env.example
├── web/
│ ├── Dockerfile
│ ├── nginx.conf
│ ├── src/
│ └── dist/
├── docs/
└── data/
├── auth.db
└── users/
└── <user_id>/
├── inbox/
└── memory.db
dashboard.py remains in the repo but is no longer the primary UI path.
- Google ADK for agent orchestration
- Gemini 3.1 Flash-Lite for LLM operations
- SQLite for
auth.dband per-usermemory.db - aiohttp for the API server
- React + shadcn/ui + Tailwind CSS for the web console
- nginx for same-origin reverse proxying in split container deployments

