Weight-based dosing and multi-drug comparison were measured live and recorded as fixtures (known_gaps.jsonl) but never showed up in the canonical docs describing chat capabilities.
4.4 KiB
Pipeline RAG và chat
Loại chính: Explanation
Đối tượng: backend/RAG engineer
Một lượt hỏi diễn ra thế nào
- Browser gửi message tới Next.js BFF
/api/chat. - BFF tạo/chuyển correlation ID và gọi
POST /v1/rag/query. - Query understanding xác định scope, intent, drug candidates, thuộc tính, route và dữ kiện lâm sàng còn thiếu.
- Deterministic guard chặn câu ngoài phạm vi hoặc yêu cầu làm rõ trước retrieval.
- Router chọn đường drug + section, overview similarity hoặc condition → drug.
- Retrieval lấy chunks, hydrate context và tạo evidence có provenance.
- Evidence policy quyết định đủ bằng chứng, cần xem PDF hay phải abstain.
- Generator tạo claims có source IDs khi generation được bật.
- Validator kiểm tra citation, con số, semantic support và completeness; có repair có giới hạn, sau đó fail closed nếu vẫn sai.
- Backend ghi trace/hội thoại, trả decision/reason/answer/citations; BFF map sang DTO UI.
Retrieval routes
Khi drug và section đã rõ, filter payload theo drug_id + section_key được ưu
tiên để tránh chunk gần nghĩa của thuốc khác. Similarity search dùng cho overview
hoặc khi section chưa rõ. Rerank mặc định tắt và chỉ áp dụng trên đường
similarity/overview, không áp dụng route section chính xác.
Câu hỏi condition → drug thử keyword trước, chỉ fallback dense khi không có candidate keyword. Candidate phải tiếp tục được kiểm tra indication và safety; retrieval match không được biến thành khuyến cáo điều trị.
Decision
| Decision | Ý nghĩa |
|---|---|
answerable |
có answer đã vượt qua các cổng kiểm tra |
clarify |
thiếu dữ kiện; có thể kèm quick replies |
verify_pdf |
đã có evidence nhưng nguồn bảng/công thức cần đối chiếu PDF |
abstain |
không phát hành nội dung chuyên môn cho lượt này |
Conversation state
Conversation turns được lưu PostgreSQL theo conversation_id và dùng lại trong
multi-turn. Store hoạt động fail-open để lỗi history không làm sập toàn bộ query.
Một phần state chống clarify loop nằm trong process; đây chưa phải state phân tán
bền vững giữa nhiều replica. Circuit breaker dừng chuỗi clarify không hội tụ.
Lịch sử truy vấn & duyệt chuyên luận
conversation_id là session id sinh phía client và giữ trong localStorage
của apps/web (không phải server session — hệ thống chưa có auth). GET /v1/rag/history liệt kê lại các truy vấn cũ của đúng session đó cho Sidebar,
để bấm lại một câu hỏi cũ; answer prose không được lưu nên đây là re-run, không
phải replay.
response_mode: "monograph" là lối đi song song với hỏi-đáp AI: bỏ qua
generation/grounding, cho người dùng tự chọn section rồi đọc verbatim qua
GET /v1/rag/sections + /section-text. Hữu ích khi cần đối chiếu nguyên văn
thay vì câu trả lời tổng hợp.
Output cho UI
Response mang business trace_id, correlation_id, otel_trace_id, decision,
reason, answer, resolved drug, citations, blocks, answer plan, candidate assessments,
quick replies và disclaimer cố định. Citation giữ chunk, printed page, physical page,
source crop/attachment và thông tin drug/section/source document.
Giới hạn đã biết
Hai năng lực dưới đây chưa có code path, đo trực tiếp qua live query (không
suy đoán) và ghi lại làm fixture tại apps/ai-service/evals/known_gaps.jsonl để
tái kiểm chứng thay vì chỉ dựa trên tuyên bố:
- Tính liều theo cân nặng.
rag/calculators.py::body_surface_area_m2()tồn tại nhưng không đượcagent.pygọi trên đường xử lý thật — hỏi liều theo cân nặng vẫn quay lại hỏi tuổi thay vì tính ra số. - So sánh nhiều thuốc. Không có
turn_typehay đường xử lý riêng cho so sánh thuộc tính giữa 2+ thuốc; đường 2-thuốc duy nhất hiện có (agent.py::_interaction) chỉ xử lý tương tác thuốc-thuốc, không tổng quát hoá được. Hệ thống trả lời trung thực "chưa đủ cơ sở" thay vì bịa bảng so sánh.