Files
duocthu/docs/current-rag-pipeline-audit.md
T

15 KiB
Raw Blame History

Audit pipeline RAG hội thoại hiện tại

Phạm vi: worktree D:\VSF-DUOCTHU ngày 2026-08-10. Báo cáo phản ánh implementation thật đang có trong worktree, bao gồm các thay đổi chưa commit. EXISTS không có nghĩa là đã đạt chất lượng production; nó chỉ nghĩa là đã tìm thấy implementation live tương đương.

1. Request path đã xác minh

ChatPanel.handleSendMessage
  -> POST /api/chat (Next.js BFF)
  -> POST /v1/rag/query (FastAPI)
  -> RagAgent.handle
       -> history + prior QueryFrame
       -> LlmQueryUnderstander.understand
       -> RagAgent._route
       -> RetrievalService.retrieve_framed / retrieve_by_indication
       -> Qdrant metadata route hoặc bounded fallback
       -> parent hydration + dedupe + evidence policy
       -> GroundedAnswerService.answer_from_result
       -> structured claim generation
       -> deterministic number/citation grounding
       -> semantic support + completeness verifier
  -> RagQueryResponse (answer blocks + claims + source ids)
  -> /api/chat mapping sang ChatMessage
  -> ChatBubble + CitationCard/Evidence panel

Đường live không dùng QueryRoutingService để fuzzy-resolve thuốc; class này còn được giữ cho retrieval-only fallback. Live agent dùng candidate-bounded LlmQueryUnderstander rồi truyền drug_idsection_key đã resolve vào RetrievalService.retrieve_framed.

Trace live đã chạy

Query Kết quả Latency quan sát Đối chiếu raw
Levetiracetam cần tránh những điều kiện môi trường nào khi cất giữ? qua browser localhost:3000 answerable; block Bảo quản; 1 source, tr. 888 10,9 s end-to-end Đủ 2025 °C, tránh ánh sáng, dung dịch uống giữ trong bao bì ban đầu
Nêu đầy đủ tác dụng không mong muốn của Medroxyprogesteron acetat... qua API answerable; 14 nhóm ADR 14,6 s Tên ADR và điều kiện chính đủ; hierarchy tần suất kế thừa vẫn cần regression test chặt hơn
Bảo quản Levetiracetam thế nào? abstain provider_unavailable 17,9 s Không có answer để chấm; không tính pass
Medroxy completeness repair abstain incomplete_answer 23,526,8 s Cho thấy repair path có thể chạm budget/provider và làm latency xấu

Số mẫu trên chưa đủ để gọi là p50/p95. TTFB bằng gần toàn bộ latency vì response hiện là JSON nguyên khối, không có streaming.

2. Capability matrix

Capability Status Evidence implementation Quyết định
Conversation state PARTIAL rag/agent.py:RagAgent._get_history/_remember; adapters/postgres.py:PostgresConversationStore EXTEND: raw lines có window 6 turns; _last_frame chỉ in-process, không bền qua restart/multi-worker
Context resolution PARTIAL LlmQueryUnderstander.understand, _known_facts_block, _merge_with_prior_frame EXTEND: merge có code backstop chủ yếu cho clarify continuation; topic switching vẫn phụ thuộc model
Standalone query rewrite PARTIAL rag/agent.py:_synthesize_query EXTEND: đã fold population/age/weight/route/indication nhưng không lưu standalone_query first-class trong frame/trace
Active entity tracking PARTIAL QueryFrame.drugs; RagAgent._last_frame EXTEND persistence/isolation; active frame hiện mất khi process restart
Intent/facet detection EXISTS QueryFrame.turn_type, attribute, population, route, indication; closed vocab trong understanding.py REUSE; mở rộng multi-facet/reasoning mode, không thêm classifier call riêng
Metadata routing EXISTS RetrievalService.retrieve_framed; QdrantRetriever.find_by_section/find_by_drug KEEP: known entity + facet đi thẳng đúng section
Dense retrieval EXISTS QdrantRetriever.search/search_indication KEEP bounded fallback; không dùng cho mọi query
Sparse/lexical retrieval PARTIAL QdrantRetriever.search_lexical EXTEND nếu cần: term-overlap/BM25-style, không phải một sparse vector/BM25 index đầy đủ
Hybrid/RRF PARTIAL rag/fusion.py:reciprocal_rank_fusion có testable primitive nhưng live RetrievalService chưa gọi Không quảng cáo là live hybrid; chỉ wire sau eval chứng minh lợi ích
Reranker PARTIAL RetrievalService._rerank; BedrockCohereReranker trong bootstrap KEEP: chỉ overview/similarity fallback; explicit section route cố ý không rerank
Parent/sibling expansion PARTIAL RetrievalService._hydrate parent hydration; _pooled_neighbour_hits bounded cross-section KEEP bounded; không có generic sibling expansion cho mọi query
Evidence selector PARTIAL _hydrate dedupe, provenance/quarantine policy, pack_evidence token budget EXTEND: chưa có explicit selected/rejected reason trace theo population/route relevance
Evidence sufficiency PARTIAL generation evidence_sufficient; _check_sufficiency; completeness verifier EXTEND thành supported/partial/insufficient/conflicting; hiện boolean và fail toàn answer
Multi-section retrieval PARTIAL interaction gom evidence nhiều thuốc; than_trong opt-in lexical neighbor EXTEND cho multi-facet có kế hoạch; không mở cross-section pooling toàn cục
Reasoning/multi-step logic MISSING Không có premise/conclusion representation hoặc bounded decomposition path ADD sau P0P2; không dùng agent loop cho simple lookup
Structured claims EXISTS prompt.py:ANSWER_SCHEMA; answer.py:_parse_claims KEEP
Claim-to-evidence mapping EXISTS claim citation indices được map sang stable source_ids; response blocks giữ mapping KEEP; bổ sung claim id/support status khi cần inference/partial
Grounding validation EXISTS grounding.verify; _verify_entailment KEEP; completeness judge cần eval để giảm false positive/negative
Abstention EXISTS EvidenceDecision; granular reject reasons; provider/malformed/grounding guards KEEP
Answer planning PARTIAL generation instruction + _answer_mode theo claim count + _build_blocks theo section REPLACE heuristic bằng compact plan trong cùng generation call; không thêm LLM call
Adaptive verbosity PARTIAL _answer_mode chỉ dựa claim count; prompt phân biệt broad/specific EXTEND theo query complexity/answer mode, không chỉ số claim
Response composition PARTIAL AnswerBlock/AnswerClaim và BFF DTO EXTEND: hiện block granularity còn section-centric; chưa có lead/limitation/group hierarchy
SSE/streaming MISSING ChatPanel dùng await res.json(); FastAPI trả RagQueryResponse, không StreamingResponse ADD sau correctness; hiện không được nói là streaming
Source rendering EXISTS CitationCard, evidence pane, printed/physical page, raw snippet KEEP provenance; giảm chip lặp dưới từng claim
Semantic response components PARTIAL ChatBubble render AnswerBlock.kind; citation panel EXTEND nhỏ; không biến mỗi paragraph/section thành card
Follow-up handling PARTIAL history, prior frame merge, latest-clarify quick replies, clarify circuit breaker EXTEND và eval 50100 turns; history window hiện 6 turns nên long chat chưa được chứng minh
Prometheus/Grafana PARTIAL /metrics, PrometheusMetrics, provisioned Grafana dashboard KEEP aggregate counters; stack chưa được xác minh running trong audit này
Full request trace PARTIAL Postgres rag_retrieval_trace chỉ lưu query/decision/reason/resolved drug/citations EXTEND stage timing/frame/route/evidence/guard verdict; không đưa lên user UI

3. Actual pipeline so với target

Phần nên giữ:

  • candidate-bounded entity understanding;
  • structured QueryFrame và deterministic metadata route;
  • whole-section retrieval cho explicit facet;
  • parent hydration, dedupe, provenance và quarantine;
  • structured claims, deterministic numeric grounding và semantic verifier;
  • Postgres conversation/trace, Prometheus counter và evidence panel.

Khoảng trống có tác động lớn nhất:

  1. active frame không durable và standalone meaning không phải first-class output;
  2. một attribute duy nhất không biểu diễn multi-facet query;
  3. evidence selection/sufficiency chưa biểu diễn partial/conflicting;
  4. chưa có direct/synthesis/inference mode và premise mapping;
  5. answer plan chỉ là heuristic, renderer hiện quá card-heavy/source-heavy;
  6. không streaming; latency 927 s và provider availability là lỗi backend thực;
  7. trace chưa đủ stage timing để drill-down từ Grafana.

4. Failure taxonomy theo layer

Layer Failure đã thấy hoặc có code path Không được ngụy trang thành
Understanding/provider timeout/throttle/malformed frame user clarification
Context stale entity, mất constraint, clarify loop retrieval miss
Routing sai facet, single-facet collapse generator hallucination
Retrieval wrong section, dense weak neighbor, parent missing answer-style problem
Evidence duplicate, mất heading/condition, token truncation citation success
Generation unsupported/partial/incomplete claim “đã grounded”
Composition hierarchy bị làm phẳng, source chip lặp RAG correctness
Availability provider unavailable, request budget exhausted “không có trong Dược thư”
Observability thiếu stage timing/selected-rejected evidence user-facing technical trace

5. Smallest coherent change-set

Không dựng pipeline thứ hai. Mở rộng các abstraction đang có theo thứ tự:

  1. P0 evidence/response contract: giữ structured claims, thêm answer plan nhỏ trong cùng generation call; hỗ trợ lead, semantic group và limitation; verifier trả support/completeness rõ, partial không bị trình bày như full.
  2. P1 context: đưa standalone_querydepends_on_previous_turn vào QueryFrame; persist active frame cùng conversation store thay vì dict local.
  3. P2 retrieval planning: cho frame mang nhiều facets; gọi retrieve_framed theo từng facet có giới hạn rồi dùng cùng decide/provenance policy. Không bật generic RRF/cross-section pooling nếu eval chưa chứng minh.
  4. Composition/UI: prose/list là mặc định; warning/dosage/table chỉ khi plan yêu cầu; một affordance Xem căn cứ theo group/message, không chip dưới mọi dòng; bỏ dashboard chrome trong mỗi answer.
  5. Trace/latency: stage timing và call counts vào internal trace/metrics; sau khi correctness ổn mới thiết kế safe streaming commit-by-verified-claim.

6. Những gì chưa được gọi là pass

  • Batch 30 thuốc đã chạy xong nhưng không pass: chỉ 11/30 trả lời, 11/30 abstain và 8/30 hỏi lại. Đây là baseline trước bản sửa section_overview và evidence-quoted completeness bên dưới, không được dùng làm số sau-fix.
  • Hội thoại dài đã chạy qua BFF; xem kết quả và giới hạn encoding ở mục 7.
  • Prometheus/Grafana chưa được mở và xác minh trong phiên audit này.
  • Không có p50/p95/p99 đủ mẫu.
  • Grounded inference chưa được implement.
  • Medroxy đã tốt hơn nhưng hierarchy tần suất cần test machine-checkable và browser review sau khi answer-plan contract hoàn thiện.

7. Kết quả triển khai và kiểm chứng ngày 2026-08-10

Thay đổi nhỏ trên đúng pipeline hiện hữu, không tạo pipeline thứ hai:

  • QueryFramestandalone_query, depends_on_previous_turnsection_overview. Tra toàn mục được tách khỏi yêu cầu chọn một liều cho ca bệnh; drug + facet rõ không còn bị classifier tự ý biến thành chip thu hẹp.
  • Answer plan compact (verbosity, layout, reasoning_mode, heading/warning) được lập trước generation bằng code, không thêm model call.
  • Completeness objection phải kèm evidence_quote; code kiểm tra quote tồn tại trong raw và thật sự hỗ trợ mô tả “bị thiếu”. Judge không còn có thể loại câu bảo quản chỉ vì câu hỏi nhắc “độ ẩm” trong khi raw không nêu độ ẩm.
  • Renderer dùng prose/list mặc định, một Xem căn cứ cho group, không [1] [2] trong câu trả lời và không card cho từng claim.

Baseline random 30 trước-fix theo facet:

Facet Answer Abstain Clarify Nhận xét
Bảo quản 4 2 0 completeness false-positive
Tương tác 6 0 0 tốt nhất trong mẫu
ADR 1 5 0 incomplete/provider/grounding gây fail
Liều/cách dùng 0 2 4 ép population cho cả truy vấn toàn mục
Thận trọng 0 2 4 classifier hỏi lại dù facet đã rõ

Retest có đối chiếu raw:

  • Levetiracetam sau-fix: answerable 9,3 giây; đủ 2025 °C, tránh ánh sáng, dung dịch uống giữ bao bì ban đầu. Browser localhost sau hot path: 6,1 giây.
  • Ergotamin tartrat: answerable 6,8 giây; giữ đúng nhiệt độ riêng theo dạng dùng.
  • Isosorbid dinitrat toàn mục liều: answerable 19,9 giây thay vì chip; giữ nhãn chỉ định/đường dùng/liều, nhưng latency chưa đạt.
  • Sildenafil ADR vẫn fail ungrounded_number; đây là fail đúng của safety gate, không được đổi nhãn thành pass.
  • Ganciclovir, Glipizid, Vancomycin, Isradipin từng gặp provider_unavailable; availability/provider vẫn là blocker thực.

Validation code hiện tại:

  • Ruff: pass.
  • Pytest: 226 passed, 5 skipped.
  • TypeScript --noEmit: pass.
  • Next.js production build: pass.
  • UI browser: pass về request/render; ảnh review xác nhận hết bullet kép và câu trả lời không còn citation marker nội tuyến.

Long conversation:

  • Một conversation ID chạy 50 request liên tiếp qua localhost:3000/api/chat, không có HTTP error. Runner đầu làm mất dấu tiếng Việt trong user lines khi đi qua PowerShell nên không dùng 6 lượt cuối của lần này làm kết luận context.
  • Giữ nguyên conversation đó và chạy sạch lượt 5156 bằng chuỗi không lỗi encoding: Levetiracetam → follow-up chống chỉ định → đổi sang Isradipin → follow-up bảo quản → đổi sang Zolpidem → follow-up ADR. Cả ba follow-up đều bám đúng thuốc gần nhất; không rò Levetiracetam sang Isradipin/Zolpidem.
  • Isradipin thận trọng ở lượt 53 bị incomplete_answer, nhưng lượt 54 vẫn resolve “thuốc này” đúng Isradipin và trả bảo quản dưới 30 °C, lọ kín, tránh sáng/ẩm.
  • Latency lượt sạch 5156: 6,918,8 giây; correctness context đạt trong kịch bản này nhưng tốc độ và provider/completeness availability chưa đạt.