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
This commit is contained in:
parent
d5001ed0b8
commit
db208031ae
1 changed files with 121 additions and 0 deletions
121
docs/ops-docs-ped-ai-and-milvus.md
Normal file
121
docs/ops-docs-ped-ai-and-milvus.md
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
# 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
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
Loading…
Reference in a new issue