locket
A privacy-first personal context engine that ingests personal photos and message exports, extracts cited facts, and serves a queryable profile over MCP for natural-language questions like 'when did I last see Sarah?'.
README
locket
A privacy-first personal context engine: it ingests your own photo and messaging exports (WhatsApp, Instagram DMs, SMS/MMS backups, Google Photos Takeout), extracts typed, provenance-cited facts about your life with an LLM pipeline, resolves the people and places those facts mention into stable entities, stores everything in Postgres+pgvector, and serves the resulting profile to other tools over MCP (Model Context Protocol) — so you can ask Claude Code or Claude Desktop things like "when did I last see Sarah?" and get an answer that cites the exact message it came from.
Architecture
flowchart LR
Sources["Your exports\n(WhatsApp/Instagram/SMS/Photos)"] --> Adapters["adapters/\npure parsers"]
Adapters --> RawItems[(RawItem stream)]
RawItems -- photos --> Vision["vision/\nSigLIP2 + RapidOCR + InsightFace\n(local, 100% of photos)"]
Vision -- curated subset --> VisionLLM["local Ollama qwen3-vl:8b\n(curated tail only)"]
RawItems --> Windowing["extraction/chunking.py"]
Windowing --> Extraction["extraction/graph.py\nLangGraph: Claude API\nstructured outputs"]
Extraction --> Resolution["resolution.py\ntiered entity resolution\n+ human confirm queue"]
Resolution --> Store[(Postgres + pgvector)]
Store --> Profile["profile.py\nsynthesized, cited profile"]
Profile --> Store
Store --> MCP["mcp_server.py\nsix tools, stdio"]
MCP --> Client["Claude Code /\nClaude Desktop"]
Full stage-by-stage breakdown, module boundaries, and the dual-corpus design:
docs/architecture.md. 90-second walkthrough with
scripted questions and MCP registration commands:
docs/demo.md.
Quickstart (against the committed synthetic demo corpus)
docker compose up -d db # Postgres + pgvector
uv sync # hand-edit pyproject.toml + `uv sync` to add deps — no `uv add`
uv run python -m locket.cli ingest demo_corpus/whatsapp/team.txt
uv run python -m locket.cli ingest demo_corpus/sms/backup.xml
uv run python -m locket.cli ingest demo_corpus/photos
# No ANTHROPIC_API_KEY needed — with no key set, extraction/resolution/
# profile all run against a local Ollama model by default (see "Running
# fully local" below). --skip-vision bypasses the local vision pre-pass +
# Ollama vision-LLM tail (~135s/image measured — see evals/BASELINE.md —
# worth skipping for a quick pass).
uv run python -m locket.cli pipeline run --skip-vision --corpus-dir demo_corpus
uv run python -m locket.cli profile build
claude mcp add --scope user locket -- uv run --directory "$(pwd)" python -m locket.mcp_server
Full command reference (every subcommand: ingest, pipeline run,
resolve, label-faces, eval extraction|rag, profile build, serve)
and the exact Claude Desktop registration JSON block:
docs/demo.md.
To run against your own data instead of the demo corpus, set
LOCKET_CORPUS_DIR in a local .env (never inside this repo — see
.env.example) and point ingest / pipeline run --corpus-dir at it.
Running fully local (no API key)
Every LLM call locket makes — extraction, entity resolution, profile
rendering, and the MCP server's answer_question — goes through one
backend-selection seam, locket.llm.get_chat_model. It picks between two
backends:
anthropic(ChatAnthropic, real network calls to Claude, higher quality, costs money): used automatically whenANTHROPIC_API_KEYis set, or when you force it withLOCKET_LLM_BACKEND=anthropic.ollama(ChatOllama, a local Ollama server, free, no data leaves your machine): the default when no API key is present.locket pipeline runno longer refuses to run keylessly — it just uses this backend instead.
Requirements: an Ollama server running locally (ollama serve, or the
desktop app) with the text model pulled — ollama pull gemma3:12b (the
default, ~8GB) or set LOCKET_LOCAL_MODEL=qwen2.5:3b-instruct for a
smaller, already-common model. OLLAMA_HOST is respected if you want to
point at a different machine's Ollama (e.g. over Tailscale) instead of
localhost:11434 — locket does not read or override it itself.
Honest quality/speed tradeoff, measured on this project's dev machine
(CPU-only Ollama): the local backend is markedly slower and somewhat
lower-quality than the Claude API backend. gemma3:12b took roughly 10-130s
per extraction window (vs. sub-second-to-a-few-seconds for claude-haiku-4-5)
and produced fewer, though more information-dense, facts per window than a
smaller local model (qwen2.5:3b-instruct, ~10x faster but noisier — see
src/locket/llm.py's module docstring for the side-by-side). Vision
(qwen3-vl:8b) already ran local-only regardless of this setting, at its own
separately-measured ~135s/image. Full real pipeline-run numbers (fact
counts, wall time) for the local backend are in evals/BASELINE.md's
"local backend (informal)" section — the official baseline stays the
Claude API run, pending a real key.
Privacy posture
Stated plainly, not hand-waved:
- Storage is fully local. Postgres+pgvector runs in your own Docker container. Nothing about your facts, entities, or profile is sent anywhere except the specific API calls described below.
- Text extraction uses the Claude API. Message/photo-OCR text is sent to
Anthropic under their no-training API terms to extract structured facts
(
claude-haiku-4-5, escalating toclaude-sonnet-5on repeated validation failures) and to render profile prose and answer questions. This is a real network call to a third party — disclosed honestly, not claimed as "fully private." - Real photos are processed by local models only. EXIF/GPS, SigLIP2
zero-shot tagging, RapidOCR, and InsightFace face clustering all run
locally on 100% of your photo library, for free. The one open-ended
"describe this photo" step (the vision-LLM tail) runs against a small,
curated subset using local Ollama
qwen3-vl:8b— never a cloud vision model, for real photos. - Gemini's free tier is explicitly forbidden for real photos. Google's
free-tier terms grant Google the right to train on and have humans review
submitted content — unacceptable for private photos of your life. Gemini
is permitted only as an opt-in path for generating the synthetic demo
corpus, where no privacy stakes exist (the faces are AI-generated, MIT-
licensed SFHQ portraits, and every conversation is invented). A paid
Claude-API fallback for real photos exists behind an explicit
--cloud-okflag if local Ollama is unavailable — still Anthropic's no-training terms, never Gemini free tier. - Your real exports never enter this repository. They're read from
LOCKET_CORPUS_DIR, an env var pointing outside the repo, declared in a local, gitignored.env..gitignorealso blocksreal_corpus/and*.local.*(the pattern the real self-labeled eval gold set uses:evals/gold/real_gold.local.yaml). Everything underdemo_corpus/in this repo is synthetic — five invented personas, generated conversations, and staged photos of AI-generated faces — used for every test, CI run, and the public demo. No real data of any kind ships in this repository.
Eval results
locket ships two eval suites (evals/extraction_eval.py,
evals/rag_eval.py), both runnable via locket eval extraction|rag --json
and both gated in CI (.github/workflows/eval.yml, nightly + on-demand —
kept out of the free push/PR lint+test workflow since they cost real money
per run). Full methodology, every number's provenance, and the exact
commands to reproduce or extend each measurement: evals/BASELINE.md.
| Metric | Value | Status |
|---|---|---|
Vision-LLM tail latency (qwen3-vl:8b, CPU-only, this machine) |
~135s/image mean (range 86–205s, n=6) | Measured live, Task 13 |
Entity-resolution similarity floor (arctic-embed-s) |
Same-person variants 0.57–0.90 cosine; different-person 0.42–0.47 | Measured live, Task 14 |
| Extraction P/R/F1 vs. the 60-fact synthetic gold set | — | Pending ANTHROPIC_API_KEY — harness implemented + unit-tested, live run recorded as a ready-to-run command in evals/BASELINE.md |
| Ragas faithfulness / answer-relevancy / context-precision (25 questions) | — | Pending ANTHROPIC_API_KEY — same status; starting thresholds (0.85 / 0.80 / 0.70) are asserted directly once it runs |
| Real-corpus self-labeled gold set (100–200 facts, spec §4.1) | — | Noah-gated — needs his real exports + the API key, off-repo by design (evals/gold/real_gold.local.yaml, gitignored) |
No number above is invented — where a measurement is blocked on a still-absent API key, the table says so plainly instead of filling in a plausible-looking placeholder.
License
MIT (LICENSE). Third-party model weights and assets carry their own,
narrower terms — see THIRD_PARTY_NOTICES.md before distributing or
monetizing anything built on this repo (notably: InsightFace's buffalo_l
face-analysis weights are non-commercial/research-personal use only, even
though the InsightFace code itself is MIT).
See Claude/specs/2026-07-30-locket-design.md (private planning vault, not
part of this repo) for the full design writeup.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。