-
-
What it does: the three MemoryAgent challenges (store, forget, recall) mapped to Lamplight.
-
Architecture diagram: episodic store → nightly consolidate/decay → reranked, budgeted, cited SBAR brief.
-
Offline, zero-keys, zero-network quickstart: 458 tests + socket-guarded replay.
-
The hero handover brief in one shot, with "IV site check" visibly retiring.
-
The recall bench: 0.99 vs 0.85, falls-risk 1.00 vs 0.00, 0 retired items resurfaced.
-
Ed25519 signed, hash-chained op-ledger + ECIES-at-rest: one-byte tamper fails.
-
Domain-agnostic: the same engine rebuilt as a support-team handover in ~15 lines.
-
Social/OG card.
Inspiration
A night nurse inherits 14 patients and a six-minute verbal handover. The one sentence that mattered — the rash started four hours after the new antibiotic — never makes it past shift change. At 3 AM it's anaphylaxis. Cross-shift handover failure is the human factor behind real sentinel events, and it's a memory problem: the right fact exists, but it isn't stored, ranked, and surfaced when the next clinician needs it. Lamplight is Track 1 — MemoryAgent — made literal against exactly this failure mode.
What it does
Lamplight is a cross-shift clinical memory agent built as a one-to-one implementation of the track's three named challenges:
- Efficient storage & retrieval. Every shift note and
fun-asr-diarized voice handover becomes a structured Episode (extraction + structured output), embedded withtext-embedding-v4into a per-patient episodic store. - Timely forgetting. A nightly consolidation merges episodes into semantic memories with decay classes —
strength(t) = s₀·2^(−Δt/λ). Critical items (allergy-suspect, falls-risk) never decay until explicitly resolved;conditionitems half-life at λ=72h;routineitems at λ=8h (one shift). Human confirmation bumpss₀by +0.25 and resets the clock; items below strength 0.05 are swept by a signedexpireop. Resolved items visibly retire — the demo shows "IV site check" struck through as it leaves memory. - Recall within a limited context window. The incoming nurse's brief is a hard 2,000-token budget, filled by a knapsack packer over rerank score × decay strength × criticality — and the UI shows what didn't make the cut, and why.
Two things make it defensible where a thousand chat-memory entries won't be. First, the memory is measured — memory_bench.py replays a synthetic 5-day ward (6 patients × 15 shifts) with planted cross-shift threads and prints recall curves against a naive top-k RAG baseline over the same embeddings and query:
| Metric | Lamplight | Naive RAG |
|---|---|---|
| Mean critical-item recall@5 | 0.99 | 0.85 |
| falls-risk (buried clause) recall | 1.00 | 0.00 |
| Retired items resurfaced (across 78 briefs) | 0 | 151 |
| Citation validity | 1.00 | — |
| Token compliance (≤2,000) | 100% (max 418) | — |
On engineered vocabulary-gap threads the gap is decisive (falls-risk 1.00 vs 0.00, because "unsteady to the bathroom" ≠ "falls risk"); forgetting precision is perfect (zero expired items ever surface, vs the baseline resurfacing retired items 151 times). We publish the ties too — on plainly-worded facts (penicillin-allergy) naive RAG is legitimately competitive at 1.00 vs 1.00.
Second, the memory is accountable — every operation (write, consolidate, decay, contradict, pin, expire) is an Ed25519-signed, hash-chained ledger entry, and episode payloads are ECIES-sealed at rest (plaintext NULL in the store). "Who knew what, when — and when did the system forget it" is a signed fact; flip one byte and lamplight verify-chain fails, by test.
The engine ships as a domain-agnostic library, lamplight-memory (decay policies, consolidator, contradiction resolver, budget packer), with a formal spec covering the half-life math and five tested invariants (I1–I5); the README rebuilds it as a support-team handover in ~15 lines.
Synthetic data only. The entire ward is hand-authored synthetic fixture data — no real patients, no PHI. Lamplight is a research prototype, not a medical device, and makes no clinical claims.
How we built it
Python 3.12; a lamplight CLI + FastAPI app. It is built on Qwen Cloud (DashScope) and deployed live on Alibaba Function Compute (managed python3.10 runtime — no container, no image registry) at https://lamplight-asvskcmpbg.ap-southeast-1.fcapp.run. Storage is SQLite + cosine offline (Supabase/pgvector in the deployed spec). ECIES sealing via PyNaCl SealedBox.
Qwen Cloud surfaces and their roles:
| Surface | Role |
|---|---|
fun-asr |
speaker-diarized voice-handover ingest |
qwen3.7-plus |
clinical-register episode extraction + SBAR brief prose |
text-embedding-v4 |
per-patient episodic store |
qwen3-rerank |
precision@5 two-stage retrieval |
| Structured output | Episode / BriefCard schemas |
| Function calling | typed, signed memory ops |
| Batch API (−50%) | nightly consolidation |
qwen3.7-max is used only for disclosed one-time seed-prose drafting (seed.py --llm), never at runtime. A transport switch keeps the graded path deterministic: FakeQwen (default, deterministic hash-embeddings, no key, no sockets) vs LiveQwen (real DashScope, behind DASHSCOPE_API_KEY). The deployed and graded path is the offline-deterministic FakeQwen engine — byte-for-byte replayable — and /verify reproduces the committed hero brief with a socket guard in the cloud. The live Qwen Cloud path is wired and verified with a real DashScope call (smoke); a full captured live-Qwen run is key-gated and not claimed here.
Engineering: 458 pytest tests at 100% source coverage, five tested invariants (I1–I5), a zero-network offline proof (scripts/verify_offline.py), and a 4-stage GitHub Actions pipeline (Quality → Security → Build → Deploy Gate) with CodeQL, Dependabot + pip-audit, TruffleHog, and semantic-release.
Challenges we ran into
- Forgetting on purpose, provably. A memory that only accumulates becomes noise. Encoding decay as real half-life math with per-class λ — then testing that resolved/expired items never surface in a brief (invariant I2, zero across 78 briefs) — was the hard part and the differentiator.
- Being honest about where naive RAG wins. On plainly-worded facts, naive RAG is legitimately competitive. We engineered the bench to isolate where Lamplight actually earns its keep — buried/vocabulary-gap threads and forgetting — and published the ties, not just the wins.
- Auditability at rest. Sealing every episode payload (plaintext NULL, ciphertext in
envelopes, unsealed only at brief-build) while keeping a signed, replayable op-chain meant the retrieval path had to unseal lazily and still reproduce byte-identical briefs (invariant I5).
Accomplishments that we're proud of
- Measured memory: recall@5 0.99 vs 0.85, and 1.00 vs 0.00 on the buried falls-risk thread — plus perfect forgetting precision (0 retired items resurfaced vs 151).
- A signed, hash-chained op-ledger with ECIES-at-rest — "when did the system forget it" is a cryptographic fact.
- 458 tests at 100% coverage on a fully offline, deterministic engine + bench.
- Live on Alibaba Function Compute (managed python3.10) with the invariant proof (
/verify) running in the cloud. - A reusable
lamplight-memorylibrary with a formal spec and a 15-line non-clinical re-use.
What we learned
Chat "memory" that only remembers is the easy half; the product is in the forgetting. Once decay became explicit half-life math with a signed retirement op, memory got a mortality you can audit — and the brief got shorter and better at the same time, because the budget packer was choosing from live, ranked, decayed candidates instead of an ever-growing pile.
What's next for Lamplight
- A full live keyed Qwen run and a bench re-run on real
text-embedding-v4+qwen3-rerank, so the recall numbers are Qwen results, not synthetic-embedding results. The live path is wired and verified with a real DashScope smoke call; the full captured run is pending. - Move the store to Supabase/pgvector in production.
- Publish
lamplight-memoryto PyPI, build the Next.js timeline/feedback UI, and implement crypto-deletion (per-episode key destruction on expiry).
Log in or sign up for Devpost to join the conversation.