pediatric-ai-scribe-v3/docs/ops-docs-ped-ai-and-milvus.md
Daniel db208031ae docs: Ops Docs page on how Ped-AI and the two Milvus servers work
Written in the operator-handbook style of the Ops Docs collective (title,
last-updated line, short prose, tables, a quick-checks block, no secrets).
Ready to paste into the collective, or to publish once writing to Nextcloud
is permitted — that call was blocked by my permission layer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
2026-09-10 18:37:41 +02:00

5.3 KiB

Ped AI And Milvus

Last updated: 2026-09-11

What Ped-AI is, which Milvus is which, and where each kind of data lives. Written for the operator handbook, so it says where things are rather than how they are coded.

What Ped-AI is

A clinical documentation assistant. Two halves:

  • Workspace — encounter notes, dictation, SOAP, well and sick visits, ED, hospital course, calculators, Learning Hub. Each generates text with an LLM through the LiteLLM gateway.
  • Clinical Assistant — a chat that answers only from an indexed medical library, with numbered citations back to the source documents.

It runs as one container, pediatric-ai-scribe, on 127.0.0.1:3552 behind Caddy. Postgres and Redis are its own; everything else it talks to belongs to another stack.

The two Milvus servers

They are easy to confuse, and both listen on 19530. Connecting to the wrong one succeeds at the network level and then fails authentication, because each has its own users — so a mistake here looks like a password problem.

Which Container Database Collection Holds
Clinical nextcloud-mcp-server-milvus-1 default mcp_bge_m3_1024 The medical library Ped-AI answers from. ~1.8M chunks.
Personal assistant personal-assistant-storage-milvus-basic-milvus-1 basic personal_assistant_bge_m3_1024 Your Nextcloud mail, notes, tables and files.

Both embed with bge-m3 at 1024 dimensions, which is what the collection names now say. Neither is reachable from the internet: the personal one publishes no ports at all, the clinical one is bound to 127.0.0.1.

Repositories, renamed 2026-09-11 to match what they hold:

  • personal-assistant-storage-milvus — was ped-ai-storage
  • clinical-storage-milvus — was nextcloud-mcp-server, and runs no MCP server, only etcd, MinIO and Milvus

Container and volume prefixes still read ped-ai-storage_* on anything not yet migrated; the project name is pinned deliberately, because changing it makes Compose create empty volumes instead of finding the existing ones.

How Milvus stores its data

Milvus keeps segment files in MinIO, not on a local disk, and its metadata in etcd. That matters: COMMON_STORAGETYPE=local writes segments relative to the working directory, so recreating the container destroys them while etcd still references them, and the collection then hangs at Loading forever. Each Milvus has its own MinIO and its own etcd container.

Access is per-database and per-collection. Renaming a collection revokes its grants, because a grant names the collection — after any rename, re-run the bootstrap that provisions the roles.

Where each kind of data lives

Data Where
Accounts, notes, encounters, Learning Hub content and quizzes Postgres (pedscribe)
Learning Hub semantic search learning_content.embedding, pgvector
Generated images MinIO bucket generated-images
Voice recordings, kept 24 hours MinIO bucket audio-backups
Medical library chunks Clinical Milvus
Nextcloud mail, notes, tables, files Personal assistant Milvus

Quizzes are ordinary relational tables and use no vectors at all.

How a question is answered

  1. The browser posts to /api/clinical-assistant/chat/stream.
  2. Ped-AI calls the MCP tool clinical_semantic_search on mcp-server-mcp-1.
  3. That searches the clinical Milvus and returns passages with scores.
  4. Ped-AI sends those passages to the chat model as the only permitted source.
  5. The answer streams back with numbered citations; the Sources panel shows the passages behind them.

If retrieval returns nothing, the assistant says so rather than answering from the model's own knowledge.

Recordings

Every recording is kept for 24 hours, whether or not its transcription succeeded, gzipped and encrypted before storage. A recording holds a screen wake lock while it runs, survives moving around the app, and is saved with the module it came from if the session ends. Recordings can be downloaded from Settings → Audio backups.

Transcription is a gateway model — currently mistral-voxtral-mini-transcribe. The browser's own speech recognition is a separate, off-by-default setting; it sends audio to the browser vendor and is not HIPAA-compliant.

Indexing

Index Written by Source
Clinical mcp-server-mcp-indexer-1 The medical library. Text extraction via unstructured, PyMuPDF4LLM and Tesseract.
Personal assistant personal-assistant-storage-milvus-basic-indexer-1 Nextcloud. Mail first, then notes, deck, tables, calendar, todos, collectives, cookbook, talk, contacts, and files. PDF text via PyMuPDF4LLM.

The folder the personal indexer walks is BASIC_INDEXING_DOCUMENTS_FOLDER. It takes one folder or several separated by commas, each walked recursively:

BASIC_INDEXING_DOCUMENTS_FOLDER=Personal assistant,Clinical Notes

A complete listing is the authority for deletion, so moving that setting to a different folder removes the chunks of everything no longer under an indexed root.

Quick checks

curl -fsS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3552/api/health
docker ps --format '{{.Names}} {{.Status}}' | grep -E 'pediatric-ai-scribe|milvus|indexer'
docker logs --since 10m personal-assistant-storage-milvus-basic-indexer-1 | grep -c Indexed