Checkpoint frontend UI/UX overhaul and ingestion embed benchmark work

This commit is contained in:
2026-08-06 17:21:21 +07:00
parent 1e8cbdb586
commit a4b8e1c4db
78 changed files with 6761 additions and 654 deletions
+112
View File
@@ -36,6 +36,19 @@ Quy tắc bắt buộc:
trả lời hợp lệ. Không suy diễn để lấp chỗ trống.
6. Giữ nguyên thuật ngữ chuyên môn của sách. Không diễn giải cho người
không chuyên.
7. HỎI LẠI khi thiếu dữ kiện — ĐÂY LÀ QUY TẮC QUAN TRỌNG NHẤT, ưu tiên hơn việc
trả lời. TUYỆT ĐỐI KHÔNG liệt kê nhiều mức liều rồi để người đọc tự chọn.
Nếu là câu hỏi LIỀU/CÁCH DÙNG và bằng chứng phân mức theo điều kiện (tuổi,
cân nặng, chức năng thận/gan, chỉ định/bệnh, đường dùng, mức độ nặng…) mà
người dùng CHƯA nêu đủ (các) điều kiện để chọn ĐÚNG MỘT mức, thì BẮT BUỘC:
để `answer`="", `evidence_sufficient`=false, và đặt `clarifying_question`
hỏi NGẮN GỌN tất cả dữ kiện còn thiếu.
- "trẻ em" hay "cho trẻ" nói chung là CHƯA đủ (liều trẻ em thay đổi theo
tuổi/cân nặng) → phải hỏi lại, KHÔNG được liệt kê các nhóm tuổi.
- "người lớn" thường là đủ cho liều người lớn tiêu chuẩn → trả lời được.
Ví dụ clarifying_question: "Bé mấy tuổi, cân nặng bao nhiêu kg, dùng đường
nào (uống/đặt hậu môn/tiêm) và để hạ sốt hay giảm đau?".
Nếu đã đủ dữ kiện thì trả lời bình thường, `clarifying_question`=null.
Viết gọn. Trả lời đúng điều được hỏi, không mở rộng phạm vi."""
@@ -54,12 +67,59 @@ ANSWER_SCHEMA = {
"type": "boolean",
"description": "false nếu bằng chứng không đủ để trả lời câu hỏi.",
},
"clarifying_question": {
"type": ["string", "null"],
"description": (
"Câu hỏi lại khi người dùng chưa nêu đủ dữ kiện (vd tuổi/cân nặng "
"cho câu hỏi liều có nhiều mức). null nếu đã đủ dữ kiện để trả lời."
),
},
},
"required": ["answer", "evidence_sufficient"],
"additionalProperties": False,
}
SUFFICIENCY_SYSTEM = """\
Bạn là bộ KIỂM TRA ĐỦ DỮ KIỆN cho tra cứu Dược thư, chạy TRƯỚC khi trả lời.
Cho CÂU HỎI của người dùng và BẰNG CHỨNG, xác định câu hỏi đã đủ dữ kiện để đưa
ra ĐÚNG MỘT câu trả lời cụ thể hay chưa.
Quy tắc:
- Nếu là câu hỏi về LIỀU/CÁCH DÙNG và BẰNG CHỨNG có NHIỀU mức khác nhau theo điều
kiện (tuổi, cân nặng, chức năng thận/gan, chỉ định/bệnh, đường dùng, mức độ
nặng) mà CÂU HỎI chưa nêu đủ (các) điều kiện để chọn đúng MỘT mức → CHƯA đủ.
- "trẻ em" / "cho trẻ" / "cho bé" nói chung là CHƯA đủ (liều trẻ thay đổi theo
tuổi và cân nặng). "người lớn" thường ĐỦ cho liều người lớn tiêu chuẩn.
- Câu hỏi KHÔNG về liều (chống chỉ định, tương tác, tác dụng phụ, giới thiệu
thuốc…) thì thường ĐỦ.
Trả về DUY NHẤT JSON: {"sufficient": bool, "clarifying_question": string|null}.
Nếu CHƯA đủ: sufficient=false và clarifying_question hỏi NGẮN GỌN tất cả dữ kiện
còn thiếu (vd: "Bé mấy tuổi, cân nặng bao nhiêu kg, dùng đường nào và để hạ sốt
hay giảm đau?"). Nếu đủ: sufficient=true, clarifying_question=null."""
SUFFICIENCY_SCHEMA = {
"type": "object",
"properties": {
"sufficient": {"type": "boolean"},
"clarifying_question": {"type": ["string", "null"]},
},
"required": ["sufficient", "clarifying_question"],
"additionalProperties": False,
}
def build_sufficiency_request(
question: str, evidence_texts: tuple[str, ...]
) -> "GenerationRequest":
blocks = "\n\n".join(
f"[{index}] {text}" for index, text in enumerate(evidence_texts, start=1)
)
user = f"BẰNG CHỨNG:\n\n{blocks}\n\nCÂU HỎI: {question}"
return GenerationRequest(system=SUFFICIENCY_SYSTEM, user=user, schema=SUFFICIENCY_SCHEMA)
@dataclass(frozen=True)
class GenerationRequest:
system: str
@@ -67,6 +127,58 @@ class GenerationRequest:
schema: dict
ENTAILMENT_SYSTEM = """\
Bạn là bộ KIỂM TRA ĐỘ CHÍNH XÁC, chạy SAU khi một câu trả lời đã được sinh ra.
Với mỗi CÂU dưới đây, so sánh nó với ĐÚNG đoạn BẰNG CHỨNG ĐƯỢC TRÍCH đi kèm câu
đó (câu đã được gắn số nguồn [n] trỏ tới đúng đoạn này). Việc DUY NHẤT cần làm:
nội dung của CÂU có được đoạn BẰNG CHỨNG ĐƯỢC TRÍCH đó — và CHỈ đoạn đó — nói
tới hay không. KHÔNG dùng kiến thức y khoa của bạn, KHÔNG suy luận thêm, KHÔNG
tự hỏi liệu câu đó có hợp lý về mặt y khoa hay không.
Một CÂU là KHÔNG được chứng thực nếu nó nêu chỉ định, chống chỉ định, cơ chế,
tương tác, đối tượng áp dụng, hoặc bất kỳ quan hệ nào mà đoạn BẰNG CHỨNG ĐƯỢC
TRÍCH của nó KHÔNG nói tới — kể cả khi câu đó đúng về mặt y khoa, và kể cả khi
đúng tên thuốc nhưng sai ý (ví dụ bằng chứng nói "điều trị đái tháo đường"
câu nói "chữa ung thư").
LƯU Ý QUAN TRỌNG: bằng chứng trong lĩnh vực dược thường liệt kê nhiều tên
thuốc trong một câu/danh sách dài, phân cách bởi dấu phẩy (ví dụ: "Tác dụng
của warfarin có thể tăng lên khi dùng với: acetaminophen, allopurinol, ...,
aspirin, kháng sinh, ..."). Hãy ĐỌC KỸ TOÀN BỘ danh sách trước khi kết luận —
nếu tên thuốc trong CÂU xuất hiện ở bất kỳ đâu trong danh sách đó với đúng
quan hệ đang nói (vd "làm tăng tác dụng của X"), đó LÀ được chứng thực, dù
tên thuốc chỉ là một mục nhỏ giữa danh sách dài.
Trả về DUY NHẤT JSON: {"entailed": bool, "unsupported": [danh sách số thứ tự
1-based của các CÂU KHÔNG được chứng thực; rỗng nếu tất cả đều được chứng
thực]}. entailed=true chỉ khi unsupported rỗng."""
ENTAILMENT_SCHEMA = {
"type": "object",
"properties": {
"entailed": {"type": "boolean"},
"unsupported": {"type": "array", "items": {"type": "integer"}},
},
"required": ["entailed", "unsupported"],
"additionalProperties": False,
}
def build_entailment_request(claims: list[tuple[str, str]]) -> "GenerationRequest":
"""`claims` is a list of (claim_text, cited_evidence_text) pairs — already
filtered by the caller to the claims worth checking (substantive content,
a validly-cited evidence block to check it against)."""
if not claims:
raise ValueError("cannot build an entailment request with no claims")
blocks = "\n\n".join(
f"CÂU {index}: {claim}\nBẰNG CHỨNG ĐƯỢC TRÍCH: {evidence}"
for index, (claim, evidence) in enumerate(claims, start=1)
)
user = f"{blocks}\n\nKiểm tra từng CÂU theo đúng BẰNG CHỨNG ĐƯỢC TRÍCH của nó."
return GenerationRequest(system=ENTAILMENT_SYSTEM, user=user, schema=ENTAILMENT_SCHEMA)
def build_request(
question: str, evidence_texts: tuple[str, ...], intro: bool = False
) -> GenerationRequest: