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 with text-embedding-v4 into a per-patient episodic store.
  • Timely forgetting. A nightly consolidation merges episodes into semantic memories with decay classesstrength(t) = s₀·2^(−Δt/λ). Critical items (allergy-suspect, falls-risk) never decay until explicitly resolved; condition items half-life at λ=72h; routine items at λ=8h (one shift). Human confirmation bumps s₀ by +0.25 and resets the clock; items below strength 0.05 are swept by a signed expire op. 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 measuredmemory_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-memory library 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-memory to PyPI, build the Next.js timeline/feedback UI, and implement crypto-deletion (per-episode key destruction on expiry).

Built With

  • alibaba-cloud
  • batch-api
  • codeql
  • dashscope
  • ecies
  • ed25519
  • fastapi
  • fun-asr
  • function-calling
  • function-compute
  • healthcare
  • pgvector
  • pydantic
  • pynacl
  • pytest
  • python
  • qwen
  • qwen3-rerank
  • sqlite
  • structured-output
  • supabase
  • text-embedding-v4
Share this project:

Updates