Files
duocthu/docs-legacy/00-project-overview.md
T

6.4 KiB
Raw Blame History

00 — Project overview

Problem domain

Clinicians in Vietnam consult the Dược thư Quốc gia Việt Nam 2018 (Vietnamese National Drug Formulary), a ~1,668-page reference book. Part 2 of that book is 684 drug monographs, each split into up to 19 fixed sections (indications, contraindications, precautions, dosage, interactions, ADRs, …).

Looking something up in the paper book is slow and the answer is section-shaped: "what is the paediatric dose of paracetamol" is answered by one specific subsection of one monograph, not by a summary of the drug. This system makes that lookup conversational while keeping the answer bound to the book's own text.

Who the users are

Doctors and pharmacists. The prompts explicitly instruct the model to keep the book's professional terminology and not simplify for a lay reader (apps/ai-service/rag/prompt.py, rule 6). The UI is Vietnamese-only.

There is no authentication, so in the deployed system "user" means anyone who can reach the public URL. See 16-security.md.

What the system does

Capability Where
Understand a Vietnamese turn (possibly misspelled, abbreviated, multi-turn) into a structured frame rag/understanding.py
Resolve drug identity against a 684-drug / 10,164-alias catalog, bounded before the LLM runs rag/routing.py + rag/understanding.py
Retrieve a whole named monograph section deterministically by payload filter adapters/qdrant.py::find_by_section
Reverse lookup: a condition/indication → drugs whose chi_dinh names it adapters/qdrant.py::find_by_indication / search_indication
Two-drug interaction lookup across both monographs rag/agent.py::_interaction
Ask a clarifying question instead of dumping every dose band rag/agent.py, rag/prompt.py rule 7
Restate retrieved evidence as structured, individually-cited claims rag/prompt.py ANSWER_SCHEMA
Refuse a generation whose numbers or citations do not trace to the evidence rag/grounding.py
Refuse a generation a second LLM pass judges unsupported by its cited block rag/answer.py::_verify_entailment
Return printed-page + physical-page + bbox provenance per citation rag/answer.py::_indexed_citations
Persist a retrieval trace and per-answer thumbs feedback adapters/postgres.py, migrations/
As-you-type drug-name autocomplete with no model call rag/routing.py::complete

What the system deliberately does not do

  • Does not answer from Part 1 or Part 3 of the book. Only printed pages 991496 are ingested (ingestion/segment/detector.py, MONOGRAPH_PRINTED_PAGE_START/END). Questions about the BSA appendix, IV preparation tables, ATC index or the general chapters abstain.
  • Does not read numbers out of quarantined tables or 2-D formulas. A VERIFY_PDF decision returns a notice and the source page instead (rag/answer.py, rag/service.py::_decide).
  • Does not rank or recommend. prompt.py rule 10 forbids first-line / treatment-of-choice framing; a condition→drug answer is a factual list.
  • Does not answer for non-human subjects. A keyword scope check abstains on veterinary phrasing (rag/policy.py).
  • Does not reverse-look-up "which drug causes X" or "which drug is contraindicated in X". Both are explicitly routed to an abstain (rag/agent.py, turn_type == "condition_relation").
  • Does not fall back to a raw source dump when a configured generator fails. It abstains with the specific failure reason.
  • Does not compute doses. rag/calculators.py implements the book's DuBois BSA formula but no runtime code calls it — see 27-technical-debt.md.

System boundary

flowchart TB
    CLIN["Doctor / pharmacist<br/><i>Vietnamese, professional, no account</i>"]
    SYS["<b>Dược Thư RAG</b><br/>Grounded Q&A over the 2018 formulary<br/>web + ai-service + ingestion"]
    BR["AWS Bedrock<br/><i>Cohere embed-v4 · rerank-v3.5 · Converse</i>"]
    LE["Let's Encrypt<br/><i>ACME via Caddy</i>"]
    GH["GitHub Actions<br/><i>SSH deploy to EC2</i>"]
    PDF[/"duoc-thu-quoc-gia-viet-nam-2018.pdf<br/>37 MB, committed in-repo"/]

    CLIN -->|HTTPS chat| SYS
    SYS -->|InvokeModel / Converse| BR
    SYS <-->|certificate issuance| LE
    GH -->|git reset + compose up --build| SYS
    PDF -->|offline ingestion, already run| SYS

Runtime components

Component State Notes
apps/ai-service Implemented The whole RAG engine. ~9.2k lines Python.
apps/web Implemented Chat UI + BFF + rate limiting.
ingestion Implemented, already run ~8.4k lines. Corpus is loaded.
packages/ui, shared-types, api-client, config Implemented Shared React/TS. api-client is not imported by web's live path (see 13).
apps/api-gateway, auth-service, user-service, chat-service Not found README.md + a 4-line package.json each. No source.
apps/mobile Not found README.md + .gitkeep.

External dependencies

Dependency Required for Failure behaviour
Qdrant Every retrieval Startup fails if the manifest cannot be read; a query-time failure propagates
AWS Bedrock — embed Dense/indication fallback search only QueryEmbeddingUnavailable → abstain (rag/ports.py)
AWS Bedrock — Converse Understanding, generation, entailment AnswerGenerationUnavailable → abstain with a specific reason
AWS Bedrock — rerank Ordering on the similarity fallback RerankUnavailable → original order kept (fail-open)
PostgreSQL Traces, multi-turn history, feedback Fail-open: answer still returned, trace id becomes an unpersisted UUID
Prometheus / Tempo / Grafana Observability only Absent = no metrics/traces; service answers unchanged

Credentials for Bedrock come from the EC2 instance's IAM role — no AWS access keys appear in any committed file (infra/docker/docker-compose.prod.yml header comment; IAM policy documents in infra/aws/iam/).

Deployment target

Current: a single EC2 host running Docker Compose behind Caddy at https://realvuxbaro.me, deployed by .github/workflows/deploy.yml over SSH on push to master.

Target (written, never applied): Helm chart + ArgoCD Application manifests under infra/helm/ and infra/argocd/, with three placeholder TODOs per environment. See 21-kubernetes-and-argocd.md.