# Documentation Reverse-engineered from the code in this repository. Every claim here traces to a file, a command, or an artifact on disk — see [DOCUMENTATION_PLAN.md](DOCUMENTATION_PLAN.md) for the method and for what was not verified. ## Chọn tài liệu theo việc bạn cần làm Bộ tài liệu dùng cấu trúc Diataxis: mỗi trang ưu tiên một nhu cầu của người đọc thay vì cố dạy, hướng dẫn thao tác, liệt kê reference và giải thích kiến trúc trong cùng một trang. ### Học qua thực hành — Tutorial - [Theo một câu hỏi từ API đến trang PDF nguồn](tutorials/first-grounded-query.md) ### Hoàn thành một tác vụ — How-to - [Local development](23-local-development.md) - [Rebuild và publish corpus](how-to/rebuild-and-publish-corpus.md) - [Chạy test và evaluation](how-to/run-tests-and-evals.md) - [Deploy và rollback production](how-to/deploy-and-rollback.md) - [Lần một request từ người dùng đến evidence](how-to/trace-a-request.md) - [Production operations](24-production-operations.md) - [Troubleshooting](25-troubleshooting.md) ### Tra cứu dữ kiện — Reference - [Catalog toàn bộ tài liệu](reference/documentation-catalog.md) - [Repository structure](01-repository-structure.md) - [API contracts](12-api-architecture.md) - [Configuration](15-configuration.md) - [Observability signals](17-observability.md) - [Known limitations](26-known-limitations.md) - [Glossary và reason codes](29-glossary.md) ### Hiểu thiết kế — Explanation - [Pipeline canonical từ PDF đến chatbot](pipeline-tu-pdf-den-chatbot-production.md) - [Vì sao dùng structured RAG](explanation/why-structured-rag.md) - [System architecture](02-system-architecture.md) - [Query understanding](08-query-understanding.md) - [Retrieval pipeline](09-retrieval-pipeline.md) - [Generation and grounding](11-generation-and-grounding.md) - [Audit kiến trúc tài liệu](diataxis-audit.md) ## What this system is A Vietnamese-language question-answering system over the **Dược thư Quốc gia Việt Nam 2018** (Vietnamese National Drug Formulary), for doctors and pharmacists. A user asks a drug question in Vietnamese; the system resolves what was asked, retrieves the exact monograph section from a vector store, has an LLM restate it, verifies that restatement against the retrieved text, and returns it with printed-page citations — or refuses. Two things distinguish it from a generic RAG app, and both are enforced in code: - **Retrieval decides what is true; generation only decides how it reads.** A generated answer is discarded unless every number in it appears verbatim in the specific evidence block it cites (`rag/grounding.py`) *and* a second LLM pass confirms the cited block actually says it (`rag/answer.py`). - **Tables and formulas are quarantined, not linearised.** Content whose numbers could not be reliably reconstructed from the PDF is never embedded as prose and never restated; it is surfaced as "check the source page". Scope boundary: the corpus is **Part 2 monographs only** (printed pages 99–1496). Part 1 general chapters and Part 3 appendices are not ingested. ## Architecture at a glance ```mermaid flowchart LR U[Clinician
browser] CADDY[Caddy 2
TLS + reverse proxy] WEB["web — Next.js 14
chat UI + BFF routes
+ in-memory rate limit"] AI["ai-service — FastAPI
RagAgent orchestrator"] QD[("Qdrant
duocthu_v1
15,100 points")] PG[("PostgreSQL 16
traces · turns · feedback")] BR["AWS Bedrock
Cohere embed-v4 · Cohere rerank
Converse generation"] ING["ingestion — offline batch
PDF → chunks → vectors"] PDF[/"duoc-thu-quoc-gia-viet-nam-2018.pdf"/] U --> CADDY --> WEB --> AI AI --> QD AI --> PG AI --> BR PDF --> ING --> QD ING --> BR ``` The `api-gateway`, `auth-service`, `user-service` and `chat-service` directories in `apps/` contain **only** a `README.md` and a `package.json`. There is no gateway, no authentication and no chat-service in the request path; `web` calls `ai-service` directly. See [02-system-architecture.md](02-system-architecture.md). ## Main technology stack | Layer | Technology | Evidence | |---|---|---| | Frontend | Next.js 14 (App Router), React 18, Tailwind, framer-motion | `apps/web/package.json` | | Backend | Python 3.12, FastAPI, Pydantic Settings, uvicorn | `apps/ai-service/pyproject.toml`, `Dockerfile` | | Vector store | Qdrant (cosine, 1024-d) | `adapters/qdrant.py`, `ingestion/load/` | | Relational | PostgreSQL 16 (`psycopg` 3) | `adapters/postgres.py`, `migrations/` | | Embedding | `cohere.embed-v4:0` on AWS Bedrock | `adapters/embedding.py`, `ingestion/embed/bedrock_cohere.py` | | Generation | Bedrock Converse API (model id is config) | `adapters/bedrock_converse.py` | | Rerank | `cohere.rerank-v3-5:0` on Bedrock | `adapters/bedrock_converse.py` | | PDF parsing | PyMuPDF (`fitz`), pdfplumber for tables only | `ingestion/extract/`, `ingestion/tables/` | | Observability | Prometheus, OpenTelemetry → OTel Collector → Tempo, Grafana | `rag/telemetry.py`, `infra/docker/` | | Runtime | Docker Compose on a single EC2 host, Caddy for TLS | `infra/docker/docker-compose.prod.yml` | | Monorepo | pnpm workspaces + Turborepo (JS side only) | `pnpm-workspace.yaml`, `turbo.json` | No RAG framework is used. There is no LangChain and no LlamaIndex anywhere in the dependency set — the orchestration is hand-written in `rag/agent.py`. ## Core runtime services | Service | Language | Entrypoint | Port | |---|---|---|---| | `ai-service` | Python | `apps/ai-service/main.py` → `app` | 8000 | | `web` | TypeScript | `apps/web/app/` (Next.js) | 3000 | | `caddy` | — | `infra/docker/Caddyfile` | 80/443 | | `ingestion` | Python | `python -m ingestion.cli`, `python -m ingestion.load.run` | offline, no port | ## Main data stores | Store | Holds | Live-path role | |---|---|---| | Qdrant `duocthu_v1` | 15,100 chunk points + payload | Every retrieval | | Qdrant `duocthu_v1__manifest` | One point: corpus sha, model id, dimensions | Startup gate (`bootstrap.py`) | | PostgreSQL | `rag_retrieval_trace`, `rag_conversation_turn`, `rag_answer_feedback` | Traces + multi-turn history; both fail-open | | Local disk | `chunks.jsonl`, `monographs.jsonl`, embedding cache | Offline pipeline only | Redis appears in `infra/docker/docker-compose.yml` (local dev) and in the pre-existing architecture document. **Nothing in the codebase imports a Redis client.** It is not deployed in production and not read or written by any code. ## Main pipelines The single canonical, end-to-end explanation is [pipeline-tu-pdf-den-chatbot-production.md](pipeline-tu-pdf-den-chatbot-production.md). The numbered pages below remain the component-level reference. For a concise demonstration of the changes delivered from 31/07 to 14/08/2026, use [ke-hoach-showcase-cai-tien-2-tuan.md](ke-hoach-showcase-cai-tien-2-tuan.md). 1. **Ingestion (offline)** — PDF → spans → monographs → chunks → embeddings → Qdrant. Seven CLI subcommands plus a separate embed/load entrypoint. Has already been run; re-running the embed step costs real Bedrock spend. → [04-ingestion-pipeline.md](04-ingestion-pipeline.md) 2. **Query (live)** — HTTP → understanding LLM call → deterministic route → Qdrant retrieval → generation LLM call → deterministic grounding → entailment LLM call → citations → response. → [10-rag-orchestration.md](10-rag-orchestration.md) ## Documentation map **Start here, in order:** 1. [00-project-overview.md](00-project-overview.md) — problem, users, boundaries 2. [02-system-architecture.md](02-system-architecture.md) — components and what is *not* built 3. [03-data-flow.md](03-data-flow.md) — the two end-to-end flows in one page **For AI/RAG engineers:** [08-query-understanding.md](08-query-understanding.md) → [09-retrieval-pipeline.md](09-retrieval-pipeline.md) → [10-rag-orchestration.md](10-rag-orchestration.md) → [11-generation-and-grounding.md](11-generation-and-grounding.md) → [19-rag-evaluation.md](19-rag-evaluation.md). For the corpus itself: [04](04-ingestion-pipeline.md) → [05](05-document-parsing.md) → [06](06-document-model-and-chunking.md) → [07](07-indexing-and-storage.md). **For backend engineers:** [12-api-architecture.md](12-api-architecture.md) → [14-data-stores.md](14-data-stores.md) → [15-configuration.md](15-configuration.md) → [18-testing.md](18-testing.md) → [23-local-development.md](23-local-development.md). **For frontend engineers:** [13-frontend-architecture.md](13-frontend-architecture.md) → [12-api-architecture.md](12-api-architecture.md) (the response contract) → [16-security.md](16-security.md) (rate limiting lives in the frontend today). **For DevOps/SRE:** [20-deployment.md](20-deployment.md) → [22-ci-cd.md](22-ci-cd.md) → [17-observability.md](17-observability.md) → [24-production-operations.md](24-production-operations.md) → [25-troubleshooting.md](25-troubleshooting.md) → [21-kubernetes-and-argocd.md](21-kubernetes-and-argocd.md) (unapplied target state). **For QA:** [18-testing.md](18-testing.md) → [19-rag-evaluation.md](19-rag-evaluation.md) → [26-known-limitations.md](26-known-limitations.md). **Before planning work:** [26-known-limitations.md](26-known-limitations.md) → [27-technical-debt.md](27-technical-debt.md) → [28-roadmap-from-code.md](28-roadmap-from-code.md). Terms: [29-glossary.md](29-glossary.md). ## Historical and empirical documents `architecture.md`, `progress-log.md`, `pdf-parsing-outlier-catalog.md`, `document-profile.md`, and the ADRs predate the numbered set. They are retained only for decision history and empirical PDF measurements. Completed plans and superseded audits were removed; they are not current-state references.