06-ai-tucanwave: W3filled15 citations

RAG System — Document Indexation and Retrieval

Audiences: developer, internal

RAG System — Document Indexation and Retrieval

User-facing RAG covers four corpora: session attachments (per-session ephemeral collections), knowledge base (curated clinical knowledge), patient documents (long-term doxing archive), and web/deep-search. Each ingestion path is owned by its own service; the consumer surface is a set of TUCAN tools under tools/rag/, tools/knowledge/, and tools/deep-search/.

Business Purpose

A clinical AI is only as useful as the documents it can read. Three real use cases drive this:

  1. Per-visit attachments: A patient walks in with a stack of PDFs from another clinic. Upload them to the session, TUCAN embeds and queries them on the fly — answer "what did the cardiologist say about her ejection fraction?" without doctor-side reading.
  2. Long-term patient archive: Documents in the patient's chart (uploads, lab reports, prior letters) live in MinIO, are extracted via OCR, and indexed into Qdrant. TUCAN can query the patient's full document history.
  3. Curated knowledge (drug data, clinical guidelines, ICD-10 catalogs): A vetted knowledge corpus separate from per-patient data. The Knowledge Curator agent maintains it as a multi-step workflow.

This is distinct from the tool-RAG ([[tucan-rag-tools]]) which embeds tool metadata for human discovery. User-facing document RAG is what the doctor experiences as "the assistant can read my documents."

Audiences

  • Investor: Per-session ephemeral collections + per-patient long-term archive + curated clinical knowledge are three distinct Qdrant lifecycles. Each is tenant-isolated. RAG quality is what closes "I asked the AI about Mrs. Garcia's chart and it answered" sales demos.
  • Clinical buyer: Upload PDFs to a session, ask questions, get cited answers. Or ask a clinical question and get an answer backed by the knowledge base. The patient archive is searchable in plain language.
  • Developer / partner: Three ingestion paths (tucan-attachment-extraction.service, tucan-session-rag.service, knowledge-curator workflow). Four query surfaces (rag/, deep-search/, knowledge/). All use the same Dudoxx embedder (port resolution at https://embedder.home.dudoxx.com/v1/embeddings, dudoxx-embed, dim 2560).
  • Internal: Session collections are ephemeral — deleted with the session. Patient archive and knowledge collections are long-lived; backfill / re-embed is a manual operation.

Architecture

diagram
                  Source of documents
                          │
   ┌──────────────────────┼─────────────────────────┐
   │                      │                         │
   ▼                      ▼                         ▼
Session attachments  Patient archive          Curated knowledge
(per session,        (per patient,            (global / per-tenant,
 ephemeral)           long-lived)              curator-managed)
   │                      │                         │
   ▼                      ▼                         ▼
TucanAttachmentExtractionService                      Knowledge curator
TucanSessionRagService                                workflow
   │                      │                         │
   │  embed (Dudoxx       │  embed                  │  embed
   │   embedder, 2560)    │                         │
   │                      │                         │
   ▼                      ▼                         ▼
              Qdrant (port 15333)
              ┌─────────────────────────────────────┐
              │ tucan_session_<sessionId>           │ ephemeral
              │ patient_documents_<orgSlug>         │ long-lived
              │ knowledge_<orgSlug or 'global'>     │ long-lived
              │ portfolio_<orgSlug>                 │ portfolio search
              │ diagnosis_knowledge                 │ shared curated
              └─────────────────────────────────────┘
                          ▲
                          │  (query)
                          │
┌─────────────────────────────────────────────────────┐
│  TUCAN tools (the consumer surface)                  │
│   tools/rag/                                          │
│     · query-session-attachments     (ephemeral coll) │
│     · query-medical-documents       (patient archive)│
│     · query-portfolio                                │
│     · search-knowledge-base                          │
│     · search-patient-knowledge                       │
│     · search-diagnosis-knowledge                     │
│     · search-web                                     │
│   tools/deep-search/                                  │
│     · plan / search-* / synthesize / export-report  │
│   tools/knowledge/                                    │
│     · curator-managed knowledge tools                │
└─────────────────────────────────────────────────────┘

Per-session attachments — ephemeral lifecycle

TucanSessionRagService (tucan-session-rag.service.ts:37) is the per-session RAG service. Methods (verified):

MethodPurposeLine
ensureCollection(sessionId)Create Qdrant collection for a session49
embedAndStore(sessionId, chunks, ...)Chunk-embed-upsert58
query(sessionId, prompt, ...)Search collection100
deleteCollection(sessionId)Delete on session end128
collectionExists(sessionId)Existence check135

Lifecycle: created on first attachment upload; deleted when the session is deleted. The session collection is the only ephemeral RAG corpus.

Document extraction pipeline

TucanAttachmentExtractionService (tucan-attachment-extraction.service.ts:37) handles OCR / parse:

  1. PDF / image arrives via tucan-attachments.controller.ts.
  2. Extraction service runs OCR (Docling / native parse) → text + structured metadata.
  3. Text is chunked + embedded via the Dudoxx embedder.
  4. TucanSessionRagService.embedAndStore writes points to the session's Qdrant collection.

Knowledge curator (long-lived corpus)

Knowledge curator is a Mastra workflow under tucan/agents/knowledge-curator/ with steps research.step.ts, compare.step.ts, translate.step.ts, upsert.step.ts. It is manually triggered to maintain the knowledge base — not a per-turn flow.

Deep-search composite workflow

tools/deep-search/ is a multi-tool workflow:

  • plan.tool.ts — plan search across sources
  • search-clinical-guidelines.tool.ts — guidelines lookup
  • search-medications.tool.ts — medication knowledge
  • search-icd-codes.tool.ts — ICD-10 codes
  • search-patient-records.tool.ts — patient archive
  • search-documents.tool.ts — generic document search
  • evaluate-sources.tool.ts — score retrieved sources
  • synthesize.tool.ts — synthesize answer with citations
  • follow-up.tool.ts — propose follow-up queries
  • export-report.tool.ts — export to document

Tech Stack & Choices

ComponentChoiceWhy
Vector storeQdrant 1.x at port 15333 (production)Same Qdrant deployment as tool-RAG ([[tucan-rag-tools]]); per-corpus collections.
Embedderdudoxx-embed via https://embedder.home.dudoxx.com/v1/embeddings, dim 2560Single embedding space across docs, code, attachments, knowledge. Production CUDA endpoint.
Session collection namingtucan_session_<sessionId>Trivially tenant-scoped (sessionId is unique).
Patient archive collectionpatient_documents_<orgSlug>Tenant slug as discriminator; cross-clinic isolation.
Knowledge collectionknowledge_<orgSlug> or knowledge_globalPer-tenant + shared global; curator decides.
ChunkingPer-document chunker (size + overlap)Common config for all ingestion paths.
OCRDocling-based extraction with structured outputReturns text + metadata, ready for chunking.
LifecycleSession: ephemeral (delete on session end). Patient + knowledge: long-lived.Matches the data sensitivity model.
Query surfaceTUCAN tools, not direct DBTools enforce tenant isolation + audit logging.

Alternatives rejected:

  • Single global collection with payload filters — payload-only isolation is fragile; collection-per-tenant is structural.
  • Vector DB-as-a-service (Pinecone / Weaviate) — adds an external dependency + network hop; we already run Qdrant.
  • Per-document collection — collection-count explosion; one-per-(patient,corpus) is the right granularity.

Data Flow

Business outcome: A patient brings a stack of external lab PDFs. Receptionist uploads them to the session. The doctor later asks "what was her HbA1c six months ago?" TUCAN reads the uploaded labs and answers in one turn.

Technical mechanism:

  1. Receptionist uploads via tucan-attachments.controller.tsTucanAttachmentsService.create.
  2. Background: TucanAttachmentExtractionService.extract(file) runs OCR on the PDF (ddx-api/src/ai/tucan/attachments/tucan-attachment-extraction.service.ts:37).
  3. Extracted text → chunked + embedded via TucanSessionRagService.embedAndStore(sessionId, chunks) (ddx-api/src/ai/tucan/attachments/tucan-session-rag.service.ts:58).
  4. Qdrant collection tucan_session_<sessionId> now has points.
  5. Doctor's turn: LLM picks query-session-attachments tool.
  6. Tool calls TucanSessionRagService.query(sessionId, 'HbA1c six months') (ddx-api/src/ai/tucan/attachments/tucan-session-rag.service.ts:100).
  7. Returns top-K chunks with scores; LLM synthesizes the answer with the chunk text in context.
  8. SSE metrics:toolrag event published to surface the activity in monitoring.

Business outcome (cleanup): Receptionist deletes the session at end of day. The uploaded PDFs disappear from Qdrant (GDPR).

Technical mechanism: Session delete → TucanSessionRagService.deleteCollection(sessionId) (ddx-api/src/ai/tucan/attachments/tucan-session-rag.service.ts:128).

Implicated Code

  • ddx-api/src/ai/tucan/attachments/tucan-attachments.controller.ts — upload + list + delete endpoints.
  • ddx-api/src/ai/tucan/attachments/tucan-attachments.service.ts — business logic.
  • ddx-api/src/ai/tucan/attachments/tucan-attachment-extraction.service.ts:37 — class TucanAttachmentExtractionService (OCR + parse).
  • ddx-api/src/ai/tucan/attachments/tucan-session-rag.service.ts:37 — class TucanSessionRagService.
  • ddx-api/src/ai/tucan/attachments/tucan-session-rag.service.ts:49ensureCollection(sessionId).
  • ddx-api/src/ai/tucan/attachments/tucan-session-rag.service.ts:58embedAndStore(sessionId, chunks, ...).
  • ddx-api/src/ai/tucan/attachments/tucan-session-rag.service.ts:100query(sessionId, prompt, ...).
  • ddx-api/src/ai/tucan/attachments/tucan-session-rag.service.ts:128deleteCollection(sessionId).
  • ddx-api/src/ai/tucan/attachments/tucan-session-rag.service.ts:135collectionExists(sessionId).
  • ddx-api/src/ai/tucan/tools/rag/query-session-attachments.tool.ts — LLM-facing tool for session-attachment query.
  • ddx-api/src/ai/tucan/tools/rag/query-medical-documents.tool.ts — LLM tool for patient-archive query.
  • ddx-api/src/ai/tucan/tools/rag/query-portfolio.tool.ts — portfolio search.
  • ddx-api/src/ai/tucan/tools/rag/search-knowledge-base.tool.ts — general knowledge search.
  • ddx-api/src/ai/tucan/tools/rag/search-patient-knowledge.tool.ts — patient-scoped knowledge.
  • ddx-api/src/ai/tucan/tools/rag/search-diagnosis-knowledge.tool.ts — diagnosis-engine knowledge.
  • ddx-api/src/ai/tucan/tools/rag/search-web.tool.ts — web search.
  • ddx-api/src/ai/tucan/tools/deep-search/plan.tool.ts — deep-search planner tool.
  • ddx-api/src/ai/tucan/tools/deep-search/synthesize.tool.ts — deep-search synthesizer with citations.
  • ddx-api/src/ai/tucan/tools/deep-search/evaluate-sources.tool.ts — source-quality scoring.
  • ddx-api/src/ai/tucan/tools/knowledge/_config-curator.ts — curator agent config.
  • ddx-api/src/ai/tucan/agents/knowledge-curator/workflow.ts — Mastra curator workflow.
  • ddx-api/src/ai/tucan/agents/knowledge-curator/steps/research.step.ts — research step.
  • ddx-api/src/ai/tucan/agents/knowledge-curator/steps/upsert.step.ts — upsert step.
  • ddx-api/src/ai/tucan/tool-rag/embedding.service.ts:35 — class EmbeddingService (shared with tool-RAG embedder client).
  • ddx-api/src/ai/tucan/tool-rag/qdrant.repository.ts:44 — class QdrantRepository (shared Qdrant adapter).
  • ddx-api/src/ai/tucan/context/adapters/rag-context.adapter.ts — RAG-related context slice (collection availability).

Operational Notes

  • Per-session collections must be deleted on session delete. The session controller's delete cascades into Prisma but Qdrant has its own lifecycle — call TucanSessionRagService.deleteCollection(sessionId). Missing this leaks ephemeral data into Qdrant.
  • Tenant slug, not UUID, in collection names. Patient and knowledge collections are keyed by org slug. Mirror of the SSE / pool / context-builder rule.
  • Embedder is shared with tool-RAG. Single Dudoxx embedder endpoint serves both. If the embedder is down, both tool-RAG and document RAG degrade.
  • Re-embed is a manual operation. When the embedder version changes (different dim, different model), every long-lived collection must be re-embedded. There is no automatic migration.
  • Knowledge curator is multi-step. It is not a per-turn workflow — it is run by an admin / curator to maintain the knowledge corpus; not triggered by a clinical turn.
  • Document RAG ≠ tool-RAG. [[tucan-rag-tools]] embeds tool metadata for human discovery; this page covers user-facing document/knowledge RAG. The Qdrant infra is shared; the consumer pattern is different.
  • search-web is an outbound tool. It hits the web; check the egress policy for the tenant before enabling.
  • Citations are required. The deep-search synthesizer produces answers with [source-i] citations linking to the retrieved chunks. Drift in this contract is a defect — file one if you see uncited answers.
  • OCR cost lives in the extraction service, not in the consumption pricing for the LLM turn. Don't confuse OCR cost with token cost; they are separate budget lines.
  • No SSE event for index complete (today). Frontend has no push for "your document is ready"; it polls. A rag:index:complete event is a known enhancement; not yet in the SSE inventory.
  • [[tucan-rag-tools]] — sibling RAG layer for TOOL METADATA (build-time, not per-turn).
  • [[ai-medical-tools]] — catalog containing the rag/* and deep-search/* and knowledge/* tools.
  • [[tucan-context-builder]] — rag-context.adapter.ts exposes RAG collection state to the context bundle.
  • [[tucan-orchestrator]] — deep-search workflow uses orchestrator primitives.
  • [[infra-qdrant]] (W1) — Qdrant infrastructure provider.
  • [[sse-event-engine]] (W1) — metrics:toolrag events surface tool-RAG and document-RAG activity.
  • MEMORY_DOXING shard — document OCR + summarization historical context.
    RAG System — Document Indexation and Retrieval — Dudoxx Docs | Dudoxx