diff --git a/README.md b/README.md index 697a5fb..0256675 100644 --- a/README.md +++ b/README.md @@ -1,199 +1,55 @@ -# Dược Thư RAG — Medical Chatbot Platform +# Dược Thư RAG — Demo GitOps trên Gitea cá nhân -Medical chatbot grounded in the Vietnamese National Drug Formulary -(Dược thư quốc gia Việt Nam 2018), built as a microservices monorepo. +Chatbot y dược tra cứu Dược thư Quốc gia Việt Nam 2018, có căn cứ trích dẫn +trang thật cho từng câu trả lời. Repo này là **bản demo độc lập**, dựng lại để +chứng minh một điều cụ thể: toàn bộ hạ tầng có thể chạy qua GitOps đọc từ +Gitea cá nhân (`git.vinmec.tech/VuQuangBao`), thay vì GitHub — cùng kiến trúc, +khác nguồn Git, khác hạ tầng vật lý, không đụng gì tới sản phẩm chính hay hạ +tầng của team. -Start with the [canonical documentation set](docs/README.md). It is a compact, -code-verified set covering architecture, PDF ingestion, RAG/chat, local development, -operations, API, configuration, evaluation and documentation governance. +**Chạy thật tại:** https://finalduocthu.realvuxbaro.me -The former numbered `00–29` material and historical plans are retained in -[`docs-legacy/`](docs-legacy/) as raw input only. Architecture decisions also remain -there until reviewed. See the canonical -[documentation policy](docs/documentation-policy.md) for source precedence. +## Trạng thái -> **Status** (2026-08-24): **live in production at -> [realvuxbaro.me](https://realvuxbaro.me)**, running on **k3s + ArgoCD** -> since the 2026-08-17 cutover — a real RAG chatbot over the whole -> formulary, not a scaffold. What exists and what does not: -> -> | Part | State | -> |---|---| -> | `ingestion/` | Done — 15,100 chunks embedded and loaded into Qdrant `duocthu_v1` | -> | `apps/ai-service/` | Done — live grounded RAG (retrieval, generation, grounding, abstention, citations, traces) | -> | `apps/web/` | Done — chat UI with citation/evidence panel, optional login | -> | `apps/auth-service`, `apps/api-gateway` | **Live since 2026-08-19** (real NestJS: register/login/JWT, `/auth/*` proxy) with a `/login` page. Verified 2026-08-24: `POST /api/auth/login` with the seed `demo` account returns a session. Login stays **optional** — anonymous chat is unaffected. Seed accounts (`admin`/`demo`) still use placeholder passwords and are **not** safe to expose publicly as-is | -> | `apps/user-service`, `apps/chat-service` | **Not built** — `README.md` + `package.json` only | -> | `apps/mobile/` | **Not built** — reserved | -> | `infra/docker/` | Compose is **stopped** (was production through 2026-08-17); still used for local dev (Postgres/Qdrant/observability), not for deploying anywhere | -> | `infra/helm/medical-chatbot/` | **This is production.** Two ArgoCD Applications, `medical-chatbot-app` and `medical-chatbot-data`, both tracked in `infra/argocd/applications/` | -> | Team's Gitea + ArgoCD (ADR 0002) | **Still not started**, not abandoned — the personal k3s/ArgoCD instance above is a separate, owner-operated cluster, not the team's shared infrastructure | -> -> `apps/web`'s chat/history/feedback/sections routes still call -> `apps/ai-service` **directly** — auth-service/api-gateway exist but chat -> traffic doesn't route through the gateway yet. Login is optional and only -> gates `/admin`; anonymous chat is unaffected. See -> [canonical architecture document](docs/architecture.md) for the implemented -> topology (not yet updated for this — verify against code, not that doc, -> until it is). +| Phần | Thế nào | +|---|---| +| Corpus | 15.100 chunk, embed và nạp vào Qdrant `duocthu_v1` | +| Chat | RAG có căn cứ — chỉ định, chống chỉ định, ADR theo tần suất, tương tác thuốc, thận trọng thai kỳ, giữ ngữ cảnh nhiều lượt | +| Eval toàn sách | 673/684 (98,4%) trả lời được trong phạm vi có thể trả lời | +| ArgoCD | 4 Application: `clickhouse-operator`, `langfuse`, `medical-chatbot-app`, `medical-chatbot-data` — tất cả đọc từ repo này | +| Quan sát | Grafana (độ trễ, phân bố quyết định, lỗi provider) + Langfuse tự host (trace từng bước xử lý, chi phí thật theo token) | -## Directory map +Hai năng lực chưa có: tính liều theo cân nặng, so sánh nhiều thuốc. Đo trực +tiếp và ghi lại trung thực tại +[`apps/ai-service/evals/known_gaps.jsonl`](apps/ai-service/evals/known_gaps.jsonl), +không phải suy đoán. Xem thêm ở [`docs/rag-chat.md`](docs/rag-chat.md#giới-hạn-đã-biết). + +## Đọc gì trước + +Bắt đầu ở [`docs/README.md`](docs/README.md) — bộ tài liệu chuẩn của dự án +(kiến trúc, ingestion, RAG/chat, local dev, vận hành, API, cấu hình, eval). +Bộ tài liệu này mô tả **sản phẩm**, không riêng cho bản demo — máy demo chỉ +khác ở nguồn Git và hạ tầng chạy, hành vi chatbot thì y hệt. + +## Cấu trúc ``` apps/ - web/ Next.js frontend (also hosts the BFF route the browser calls) - ai-service/ Python FastAPI — RAG orchestration + AWS Bedrock calls - api-gateway/ NestJS — public entry point, routes to internal services - auth-service/ NestJS — signup/login/JWT - user-service/ NestJS — profile/preferences - chat-service/ NestJS — chat session + message history - mobile/ reserved for a future mobile app -packages/ - shared-types/ TS DTOs shared across Node services + web - api-client/ typed HTTP client for web - ui/ shared React components - config/ shared eslint/tsconfig presets -ingestion/ offline batch pipeline: PDF -> monographs -> chunks -> embeddings -> Qdrant -infra/ docker-compose, k8s/Helm, Terraform, CI -docs/ canonical project documentation -docs-legacy/ raw notes, historical plans and ADRs pending review + web/ Next.js — giao diện chat + BFF + ai-service/ Python FastAPI — pipeline RAG, gọi AWS Bedrock + api-gateway/ NestJS — cổng vào công khai + auth-service/ NestJS — đăng ký/đăng nhập/JWT +packages/ DTO dùng chung, HTTP client, UI components +ingestion/ pipeline offline: PDF -> chuyên luận -> chunk -> embedding -> Qdrant +infra/helm/ Helm chart 4 Application đọc bởi ArgoCD (chart giống hệt production) +docs/ tài liệu chuẩn của dự án ``` -## Prerequisites +## Vì sao repo này tồn tại -- Node.js + pnpm (JS workspace: `apps/web`, `apps/auth-service`, - `apps/api-gateway`, `packages/*`; `apps/user-service`/`apps/chat-service` - are still unbuilt placeholders) -- Python 3.11+ (`apps/ai-service`, `ingestion`) -- Docker (local Postgres + Qdrant via `infra/docker/docker-compose.yml`) -- AWS credentials with Bedrock invoke permission, for anything that generates - an answer. Without them `ai-service` still starts, but every answer abstains - rather than falling back to raw source text. - -## Running it locally - -```powershell -docker compose -f infra\docker\docker-compose.yml up -d postgres qdrant -cd apps\ai-service -python -m migrate -python -m uvicorn main:app --port 8079 # NOT --reload, see below -``` - -```powershell -pnpm install -pnpm --filter web dev # http://localhost:3000 -``` - -`ai-service` needs a populated Qdrant collection to serve answers: it verifies -a `duocthu_v1__manifest` sidecar at startup and refuses to run against a corpus -whose sha/model/dimensions do not match. A fresh machine either restores a -Qdrant snapshot or re-runs `ingestion/` (the latter costs real Bedrock spend). - -> **Prefer a plain restart over `uvicorn --reload` on Windows here.** The -> reloader has been observed serving the previous code after an edit on this -> project, which makes it hard to tell whether a change took effect. - -Tests: `cd apps/ai-service && python -m pytest -q` — 230 pass. `test_api.py` and -`test_live_datastores.py` need Postgres and Qdrant actually running; skip them -with `--ignore` when the stack is down. `apps/web` has **no test setup at all**, -so a green suite says nothing about the frontend — drive it in a browser. - -## Observability: Prometheus, Grafana and Tempo - -The observability stack is provisioned in the repository and has been deployed -to the production EC2 instance since 2026-08-11. - -- **Prometheus** scrapes `/metrics` from `ai-service`. It records request rate - and latency, latency for each RAG stage, routing decisions and reasons, - provider failures, trace-write failures and the existing domain counters. -- **Grafana** is the user interface for dashboards and metric queries. Its - datasource and the Dược Thư dashboard are provisioned automatically. -- **Tempo** stores OpenTelemetry traces. A trace contains the receive, - understanding, routing, retrieval, rerank/evidence, generation, - grounding/entailment, persistence and response stages. Correlation and trace - IDs follow the request from the Next.js BFF into FastAPI. -- **OpenTelemetry Collector** receives spans from `ai-service` and exports them - to Tempo. Grafana exemplars link aggregate latency metrics to an individual - Tempo trace. - -For answer lineage, use the three views together: - -1. The web citation/evidence panel shows which source chunks, pages and exact - evidence text were selected for the answer. -2. **Grafana -> Explore -> Tempo** shows which pipeline stages ran, their - nesting and timing, the final decision/reason, provider failures and the - persisted trace ID. -3. PostgreSQL table `rag_retrieval_trace` is the durable audit record. It stores - the query, resolved drug, decision/reason, selected citations/evidence, - correlation ID and OpenTelemetry trace ID, so a returned `trace_id` can be - joined to its Tempo trace. - -This is provenance and execution tracing, not model chain-of-thought logging. -Full prompts/responses, hidden reasoning, every rejected retrieval candidate -and every ranking score are deliberately not stored today. If deeper debugging -is needed, add bounded audit fields rather than putting sensitive prompt or -patient content into metric labels or span names. - -Start the local stack from the repository root: - -```powershell -docker compose -f infra\docker\docker-compose.yml up -d prometheus tempo otel-collector grafana -``` - -Local endpoints: - -| Service | Address | Use | -|---|---|---| -| Grafana | `http://localhost:3002` | Dashboards and Explore | -| Prometheus | `http://localhost:9090` | Raw targets, PromQL and metrics | -| Tempo | `http://localhost:3200` | Trace backend; normally queried through Grafana | -| ai-service metrics | `http://localhost:8079/metrics` | Raw OpenMetrics output when ai-service runs on port 8079 | - -Grafana is served through the same k3s ingress as the app, at -`https://realvuxbaro.me/grafana/`. Anonymous access is on but demoted to -**Viewer** (dashboards load with no login; write actions need the admin -account). Prometheus has no public URL — use **Grafana -> Explore -> -Prometheus** for PromQL, or **Explore -> Tempo** with a returned -`X-Trace-ID` to investigate a slow request. - -## Production - -Live at [realvuxbaro.me](https://realvuxbaro.me), running on **k3s + -ArgoCD** (a personal, owner-operated cluster — not the team's shared -infrastructure) since the 2026-08-17 cutover. Two ArgoCD Applications: -`medical-chatbot-app` (ai-service, web, observability) and -`medical-chatbot-data` (PostgreSQL, Qdrant — a separate release so an app -redeploy or prune can never touch persistent data). Both are tracked in -`infra/argocd/applications/`; `infra/helm/medical-chatbot/` is the chart both -render from. Bedrock is reached through an IAM instance role — no long-lived -AWS keys on the box or in any manifest. - -Two things deploy independently: - -- **App code** (`apps/ai-service/**`, `apps/web/**`, `packages/**`) — a push - to `master` triggers `.github/workflows/build-practice-images.yml`, which - builds and pushes GHCR images tagged by commit SHA, then repoints - `medical-chatbot-app` at the new tag. `ci.yml` runs in parallel and does - **not** gate this — a red test suite does not block a deploy. -- **Chart/config** (`infra/helm/**`) — `helm-chart.yml` lints and asserts - render invariants on the PR; once merged, ArgoCD's own `selfHeal` picks up - the change automatically. No CI step applies it directly. - -Rollback is `.github/workflows/rollback-k3s.yml` (`workflow_dispatch`, -`target_sha`) — see `docs/operations.md` for the full runbook, including its -current gaps (no automated rollback for a config-only change, and the -Grafana admin password still lives inline on the ArgoCD Application rather -than in a real Kubernetes Secret). - -The former EC2 Docker Compose deployment (`i-039fc8f6102467a54`) is -**stopped**, not deleted — see `docs/operations.md` if it's ever needed as a -manual DNS fallback again, though its corpus/schema will drift further out -of date the longer it stays off. - -The team's self-hosted **Gitea** + **ArgoCD** (per -`docs-legacy/adr/0002-argocd-gitops.md`) remains the longer-term target for -this project and is **still not started** — not abandoned, just a separate -decision from the personal-cluster cutover above. Until it is deliberately -started, the project stays on private GitHub, and the team's existing -`git.vinmec.tech/ai-team/gitops` repository is reference-only: never push -this project into it. +Không phải để thay thế sản phẩm chính. Team dùng `git.vinmec.tech/ai-team/*` +cho hạ tầng thật — repo này nằm trong namespace cá nhân +(`VuQuangBao/duocthu`), tách biệt hoàn toàn: EC2 riêng, cluster k3s riêng, +Postgres/Qdrant riêng, ArgoCD riêng, Langfuse riêng. Mục đích duy nhất: chứng +minh khả năng migrate GitOps sang Gitea mà không đổi gì về kiến trúc hay hành +vi hệ thống.