Skip to main content
Glama

Konsilium

Privacy-first medical document analysis with multi-perspective AI review.

Konsilium ingests medical documents (discharge letters, lab reports, clinical findings), de-identifies them locally, maintains a longitudinal per-patient memory, and runs multi-specialist "consilium" reviews that produce structured reports: claims with evidence references, open questions for physicians, explicit disagreements between specialist perspectives, and draft letters to doctors. It is a preparation tool for talking to real physicians — not a replacement for them.

Medical disclaimer. Konsilium does not diagnose, prescribe, or provide medical advice. Every output is preparation material intended to be reviewed with a licensed physician. Do not make treatment decisions based on its reports.

Why

If you (or someone you care for) manage a complex medical history, you accumulate PDFs from different practices, labs, and hospitals. Getting value out of them with an LLM normally means uploading raw documents — names, addresses, insurance numbers and all — to a cloud provider. Konsilium is built around a different contract:

Personal identity never leaves your machine. Only de-identified medical content ever reaches a model provider.

Related MCP server: KnowS MCP Server

How privacy works

Two data planes, enforced in code — not by prompt instructions:

PDF / text document
      │
      ▼
┌───────────────────────────────┐
│ Local de-identification       │  regex (structured identifiers)
│                               │  + GLiNER NER (primary recall)
│                               │  + Ollama (second opinion)
└───────┬───────────────┬───────┘
        │               │
        ▼               ▼
  patients/<id>/    identity_vault/<id>.json
  de-identified     token → PII mapping
  Markdown memory   LOCAL ONLY, never indexed,
  [PATIENT_1]-style never sent to any API
  tokens
        │
        ▼
  LLM reasoning, literature search (PubMed, Semantic Scholar),
  consilium reviews — de-identified content only
  • Fail-closed ingest. Non-synthetic ingest is impossible without a configured, reachable local PII detector and a structuring model. A misconfigured pipeline fails loudly instead of degrading to regex-only.

  • Egress guard. Every outbound knowledge query is checked; anything containing PII tokens or raw-PII patterns is rejected before the request is built.

  • Tokens-only on disk. Patient memory and letter drafts contain only tokens. Rendering a letter with real names happens in a local command that prints to stdout and never writes rendered PII into the memory tree.

  • Dates of birth become ages in de-identified text; the original stays in the local vault.

  • Local embeddings. Memory retrieval uses a deterministic local embedding — no embedding API calls.

Features

  • Scope: German healthcare documents. De-identification and document conventions are verified against German medical material only.

  • Ingest pipeline: PDF/text → local ensemble de-ID → templated Markdown documents plus timeline, problem list, medications, and labs per patient.

  • Review-first inbox: preview-inbox de-identifies newly dropped files idempotently before an operator admits a reviewed preview.

  • Scoped identity tokens: tokens are unique per numbered patient; a local-only operator ledger lists token mappings beside the JSON vault.

  • Patient-scoped hybrid memory: embedded LanceDB index over canonical Markdown files (plain-JSON fallback), retrieval always filtered by patient. The memory is human-readable — open it in Obsidian or any editor to see exactly what the system knows.

  • Consilium reviews: each selected specialist role (Markdown profiles in roles/ — internist, endocrinologist, neurologist, add your own) gets an independent model pass; a chair-synthesis pass merges them and surfaces real disagreements instead of smoothing them over.

  • DIN 5008 doctor letters in German: tokenized paper and e-mail drafts on disk, with deterministic local-only PII rendering.

  • Literature grounding: PubMed (NCBI E-utilities) and Semantic Scholar search with the egress guard in front; AWMF guideline lookup.

  • Monitoring: periodic multi-patient review reports.

  • MCP server: drive everything from Claude Desktop or any MCP client — chat is the UI, no custom frontend to run.

  • Pluggable model providers: any OpenAI-compatible endpoint (e.g. Cloudflare AI Gateway), ChatGPT subscription (device login), or a local Claude Code CLI in headless mode.

Quickstart (Docker)

git clone <this-repo> && cd konsilium
cp config.yaml.example config.yaml   # edit: model provider, paths
docker build -t konsilium .

# smoke checks
docker run --rm -v "$PWD/config:/config:ro" konsilium \
  --config /config/config.yaml --stage1-smoke
docker run --rm -v "$PWD/config:/config:ro" konsilium \
  --config /config/config.yaml --knowledge-smoke "metformin hba1c"

See DEPLOY.md for the full local (Docker Desktop on macOS, bind-mounted memory folders, Ollama on the host) and hardened server runbooks.

De-identification model

Install the local detector dependencies, pull an Ollama model, and configure both ensemble layers:

deidentification:
  gliner_model: "urchade/gliner_multi_pii-v1"
  ollama_url: "http://host.docker.internal:11434"
  ollama_model: "qwen3:4b"

Ingest of real documents is deliberately blocked until a de-ID model is configured and runtime.allow_real_patient_docs is set to true. Review the de-identified output on synthetic and first real documents before trusting the pipeline — you can read every file it writes.

Usage

CLI

konsilium() { docker run --rm -i \
  -v "$HOME/konsilium/config:/config:ro" \
  -v "$HOME/konsilium/memory:/memory" \
  --env-file "$HOME/konsilium/secrets/konsilium.env" \
  konsilium --config /config/config.yaml "$@"; }

konsilium preview-inbox --patient case-1
konsilium deid-preview --file /memory/inbox/befund.pdf --known-identity case-1
konsilium ingest --patient case-1 --from-preview /memory/previews/preview-befund.md
# After reviewing named non-PII residue patterns for this preview only:
konsilium ingest --patient case-1 --from-preview /memory/previews/preview-befund.md \
  --accept-residue DIGIT_RUN
konsilium review --patient case-1 --roles internist,endocrinologist \
  --question "What should the next appointment clarify?"
konsilium letter --patient case-1 --channel paper
konsilium letter-render --patient case-1 \
  --file patients/case-1/letters/doctor_letter_de_paper.md   # PII to stdout only
konsilium memory-search --patient case-1 --query "HbA1c trend"
konsilium monitor --patients case-1,case-2

MCP (chat as the interface)

Register the server in Claude Desktop (claude_desktop_config.json) or any MCP client — snippet in DEPLOY.md. Exposed tools: ingest_document, deid_preview, case_review, doctor_letter, memory_search, memory_get, monitor_review, list_patients.

Then just talk: "Ingest the lab report from the inbox for case-1 and run an internist + endocrinologist review."

By design, the MCP surface has no tool that returns vault contents or rendered PII — letter rendering stays a local CLI command.

Model providers

Provider

Config model.provider

Use case

OpenAI-compatible endpoint

custom

Cloudflare AI Gateway, any compatible API

ChatGPT subscription

codex

local use via device login (--codex-login)

Claude Code CLI (headless)

claude-cli

local use via your Claude subscription

The de-identification boundary is identical for all providers: they only ever see de-identified content.

Memory layout

memory/
  patients/<id>/
    passport.md          # summary
    documents/           # one de-identified Markdown file per source document
    timeline/events.md   # dated events
    problems.md  meds.md  labs/labs.md
    hypotheses/  consilium/  letters/   # reports & tokenized drafts
    strategy.md
  identity_vault/<id>.json   # local-only token→PII map
  identity_vault/<id>.tokens.md  # local-only operator token ledger
  inbox/  previews/          # review-first source queue and local artifacts
  lance/                     # embedded vector index

Everything canonical is plain Markdown. Point Obsidian at memory/ for a zero-code dashboard of what the system has stored. Ingest returns the stored document path. Source files use YYYY-MM-DD_Topic_Sender.md; frontmatter records when the date had to fall back to the ingest date.

Development

pip install -e ".[dev]"
python -m pytest          # suite must pass with and without lancedb installed

Design decisions and known implementation limits are recorded in docs/decisions.md and neutral engineering comments.

Status & roadmap

Early, actively developed. Working today: local ensemble de-ID, PDF OCR, review-first inbox previews, templated patient memory, consilium reviews, DIN 5008 letter channels, PubMed/Semantic Scholar/AWMF knowledge tools, CLI, MCP server, and Docker deployment. Planned: scheduled autonomous monitoring and research-agent delegation for deep literature work.

License

Apache-2.0

Related MCP Connectors

Related MCP Servers