Remove corpus counts from chat chrome
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
# 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
|
||||
|
||||
```text
|
||||
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_id` và `section_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 | Đủ 20–25 °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,5–26,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 P0–P2; 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 50–100 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 9–27 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_query` và `depends_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:
|
||||
|
||||
- `QueryFrame` có `standalone_query`, `depends_on_previous_turn` và
|
||||
`section_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; đủ `20–25 °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 51–56 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 51–56: 6,9–18,8 giây; correctness context đạt trong kịch bản
|
||||
này nhưng tốc độ và provider/completeness availability chưa đạt.
|
||||
Reference in New Issue
Block a user