Remove corpus counts from chat chrome

This commit is contained in:
2026-08-10 17:26:58 +07:00
parent 46469468bb
commit 97cb6d16f4
31 changed files with 2192 additions and 424 deletions
+149 -20
View File
@@ -28,9 +28,11 @@ Quy tắc bắt buộc:
chứng (ví dụ "người lớn", "trẻ em", "suy thận", "đường uống"). TUYỆT ĐỐI
không gán liều của đối tượng này cho đối tượng khác, và không gộp các liều
khác đối tượng thành một.
4. Gắn số nguồn [n] cho từng ý, với n là đoạn bằng chứng THỰC SỰ chứa ý đó.
Chỉ trích [n] nếu đọc đoạn n thấy đúng ý đang nói. Không lặp lại cùng một
[n] ở mọi câu — gắn một lần cho một cụm cùng nguồn là đ. Không bịa số [n].
4. KHÔNG viết câu trả lời thành 1 đoạn văn tự do. Thay vào đó, TÁCH câu trả lời
thành từng "claims" — mỗi claim là MỘT ý độc lập, kèm "citations" là danh
sách số đoạn bằng chứng (1-based) THỰC SỰ chứa ý đó. Một claim chỉ được
trích citations mà đọc đúng đoạn đó thấy đúng ý đang nói — không bịa số.
Nhiều claim có thể trích cùng citations nếu chúng thực sự cùng nguồn.
5. Nếu BẰNG CHỨNG không đủ (thiếu đối tượng được hỏi, thiếu con số, hoặc chỉ nói
chung chung), nói rõ là không đủ và đặt evidence_sufficient=false. Đó là câu
trả lời hợp lệ. Không suy diễn để lấp chỗ trống. TRƯỜNG HỢP NÀY BẮT BUỘC LUÔN
@@ -45,30 +47,78 @@ Quy tắc bắt buộc:
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.
NGOẠI LỆ BẮT BUỘC: nếu CÂU HỎI ghi rõ đây là "tra cứu tổng quan toàn mục"
hoặc yêu cầu Dược thư trình bày các đường dùng/liều/đối tượng "nếu có", đây
KHÔNG phải yêu cầu chọn liều cho một người bệnh. Khi đó phải liệt kê các nhánh
có trong BẰNG CHỨNG, giữ nguyên nhãn đối tượng/đường dùng/chỉ định; không hỏi
người lớn/trẻ em chỉ để thu hẹp và không tạo quick-reply chip.
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`
để `claims`=[] (rỗng), `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.
- "người lớn" chỉ đủ khi BẰNG CHỨNG có đúng một đường dùng phù hợp, hoặc câu
hỏi đã nêu đường dùng. Nếu bằng chứng có nhiều đường dùng (uống/đặt/tiêm…)
mà người dùng chưa chọn → phải hỏi đường dùng, không liệt kê tất 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.
8. `quick_replies` chỉ phục vụ một `clarifying_question` thực sự cần thiết:
- Khi evidence_sufficient=true: BẮT BUỘC quick_replies=[]; trả lời thẳng, không
tạo chip cho có.
- Khi evidence_sufficient=false vì còn mơ hồ và câu hỏi lại có 2-4 lựa chọn
rời rạc tự nhiên: tự sinh 2-4 quick_replies ngắn dựa trên chính các nhánh
thấy trong BẰNG CHỨNG (ví dụ các đường dùng xuất hiện trong nguồn).
- Nếu cần người dùng nhập một giá trị cụ thể như tuổi/cân nặng, hoặc nguồn
hoàn toàn không đủ để hình thành lựa chọn an toàn: quick_replies=[].
Viết gọn. Trả lời đúng điều được hỏi, không mở rộng phạm vi."""
9. LẬP KẾ HOẠCH ĐỘ CHI TIẾT THEO CÂU HỎI:
- Hỏi một dữ kiện cụ thể: trả lời trực tiếp, ngắn, không kéo cả chuyên luận vào.
- Hỏi "đầy đủ", "tất cả", hoặc hỏi cả một mục rộng như toàn bộ ADR/tương tác:
phải bao phủ TẤT CẢ dữ kiện trực tiếp thuộc mục đó trong BẰNG CHỨNG.
- Với danh sách có cấu trúc, KHÔNG tách mỗi thuật ngữ thành một claim rời. Mỗi
claim phải giữ một nhóm có nghĩa và ghi rõ nhãn của sách, ví dụ
"Thường gặp — Tiêu hóa: buồn nôn, đau bụng, biếng ăn".
- Nhãn tần suất/mức độ, nhóm cơ quan và điều kiện bao trùm một đoạn (ví dụ
"khi dùng liều cao để điều trị ung thư phụ thuộc hormon") là DỮ KIỆN LÂM SÀNG,
không phải trang trí. Bắt buộc giữ chúng bên cạnh đúng các mục chúng chi phối.
Nếu một nhãn đầu mục áp dụng cho nhiều dòng phía sau cho tới đầu mục kế tiếp,
phải LẶP LẠI nhãn đó trong từng claim liên quan; không bắt người đọc suy ra từ
claim đứng trước.
Viết gọn trong phạm vi độ chi tiết người dùng yêu cầu. Không mở rộng phạm vi."""
ANSWER_SCHEMA = {
"type": "object",
"properties": {
"answer": {
"type": "string",
"claims": {
"type": "array",
"description": (
"Câu trả lời cho bác sĩ/dược sĩ, mỗi ý gắn [n] chỉ nguồn. "
"Mọi con số chép nguyên văn từ bằng chứng."
"Câu trả lời TÁCH thành từng ý độc lập cho bác sĩ/dược sĩ — "
"KHÔNG phải một đoạn văn tự do. Rỗng nếu evidence_sufficient=false."
),
"items": {
"type": "object",
"properties": {
"text": {
"type": "string",
"description": "Một ý, không chứa số nguồn — số nguồn đi riêng ở citations.",
},
"citations": {
"type": "array",
"items": {"type": "integer"},
"description": (
"Số đoạn bằng chứng (1-based) THỰC SỰ chứa ý này. "
"Không được rỗng trừ khi claim không cần trích dẫn."
),
},
},
"required": ["text", "citations"],
"additionalProperties": False,
},
},
"evidence_sufficient": {
"type": "boolean",
@@ -84,8 +134,18 @@ ANSWER_SCHEMA = {
"null CHỈ khi evidence_sufficient=true."
),
},
"quick_replies": {
"type": "array",
"items": {"type": "string"},
"maxItems": 4,
"description": (
"2-4 câu trả lời ngắn do model đề xuất CHỈ khi cần hỏi lại và "
"có vài lựa chọn tự nhiên trong bằng chứng; rỗng khi trả lời "
"được ngay hoặc cần nhập giá trị tự do."
),
},
},
"required": ["answer", "evidence_sufficient"],
"required": ["claims", "evidence_sufficient", "quick_replies"],
"additionalProperties": False,
}
@@ -100,7 +160,9 @@ Quy tắc:
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.
tuổi và cân nặng). "người lớn" chỉ ĐỦ khi bằng chứng có một đường dùng phù
hợp hoặc câu hỏi đã nêu đường dùng; nếu có nhiều đường dùng mà chưa chọn thì
phải hỏi tiếp đường dùng, không đổ tất cả các mức ra cho người đọc tự chọ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 ĐỦ.
- Câu hỏi "thận trọng"/"an toàn khi dùng cho bệnh nhân [tình trạng cụ thể]"
@@ -181,22 +243,58 @@ nếu tên thuốc trong CÂU xuất hiện ở bất kỳ đâu trong danh sác
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."""
Sau kiểm tra từng claim, kiểm tra ĐỘ ĐẦY ĐỦ: đối chiếu CÂU HỎI GỐC với TOÀN BỘ
BẰNG CHỨNG ĐÃ CHỌN. Nếu câu trả lời bỏ sót dữ kiện trực tiếp được hỏi (điều kiện,
đối tượng, con số, mức độ/tần suất hoặc mục trong danh sách), đặt complete=false.
Không coi nội dung ngoài phạm vi câu hỏi là thiếu.
Chỉ được ghi thiếu một dữ kiện khi dữ kiện đó THỰC SỰ XUẤT HIỆN trong TOÀN BỘ
BẰNG CHỨNG ĐÃ CHỌN nhưng không có trong câu trả lời. Nếu câu hỏi nói "độ ẩm hoặc
bao bì", "điều kiện đặc biệt nếu có" mà bằng chứng không nêu độ ẩm/bao bì/điều
kiện đặc biệt, sự im lặng đó KHÔNG phải lỗi thiếu. Không yêu cầu câu trả lời bịa
ra một mục chỉ vì từ đó xuất hiện trong câu hỏi.
Đặc biệt với câu hỏi "đầy đủ/tất cả" về ADR, tương tác hoặc một mục rộng: mất nhãn
"thường gặp/ít gặp", mất nhóm cơ quan, hoặc kéo các dữ kiện ra khỏi điều kiện bao
trùm như "khi dùng liều cao để điều trị ung thư phụ thuộc hormon" đều là KHÔNG
ĐẦY ĐỦ, dù tên từng dữ kiện vẫn xuất hiện. Khi đó complete=false và
missing_evidence phải nêu đúng nhãn/điều kiện bị mất.
Trả về DUY NHẤT JSON: {"entailed": bool, "unsupported": [số thứ tự claim],
"complete": bool, "missing_evidence": [{"description": mô tả ngắn dữ kiện bị
bỏ sót, "evidence_quote": trích nguyên văn ngắn từ bằng chứng chứa dữ kiện đó}]}.
Mỗi mục thiếu BẮT BUỘC có evidence_quote chép nguyên văn từ bằng chứng. Không tìm
được câu trích thì không được ghi mục đó là thiếu.
entailed=true chỉ khi unsupported rỗng; complete=true chỉ khi missing_evidence rỗng."""
ENTAILMENT_SCHEMA = {
"type": "object",
"properties": {
"entailed": {"type": "boolean"},
"unsupported": {"type": "array", "items": {"type": "integer"}},
"complete": {"type": "boolean"},
"missing_evidence": {
"type": "array",
"items": {
"type": "object",
"properties": {
"description": {"type": "string"},
"evidence_quote": {"type": "string"},
},
"required": ["description", "evidence_quote"],
"additionalProperties": False,
},
},
},
"required": ["entailed", "unsupported"],
"required": ["entailed", "unsupported", "complete", "missing_evidence"],
"additionalProperties": False,
}
def build_entailment_request(claims: list[tuple[str, str]]) -> "GenerationRequest":
def build_entailment_request(
question: str,
claims: list[tuple[str, str]],
all_evidence: tuple[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)."""
@@ -206,13 +304,33 @@ def build_entailment_request(claims: list[tuple[str, str]]) -> "GenerationReques
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ó."
evidence = "\n\n".join(
f"NGUỒN {index}: {text}"
for index, text in enumerate(all_evidence, start=1)
)
user = (
f"CÂU HỎI GỐC: {question}\n\n{blocks}\n\n"
f"TOÀN BỘ BẰNG CHỨNG ĐÃ CHỌN:\n{evidence}\n\n"
"Kiểm tra hai chiều. (1) Từng CÂU phải được đúng bằng chứng trích dẫn "
"chứng thực. (2) So với CÂU HỎI GỐC và TOÀN BỘ BẰNG CHỨNG, câu trả lời "
"phải đủ các dữ kiện trực tiếp liên quan: không làm rơi điều kiện áp dụng, "
"đối tượng, con số, mức độ/tần suất hoặc các mục trong danh sách mà người "
"dùng yêu cầu. complete=false và liệt kê ngắn trong missing_evidence nếu "
"còn thiếu; mỗi mục phải kèm evidence_quote NGUYÊN VĂN từ nguồn chứa dữ "
"kiện đó. Không có quote trong nguồn thì không được báo thiếu. Không bắt "
"câu trả lời mở rộng sang nội dung ngoài phạm vi hỏi."
)
return GenerationRequest(system=ENTAILMENT_SYSTEM, user=user, schema=ENTAILMENT_SCHEMA)
def build_request(
question: str, evidence_texts: tuple[str, ...], intro: bool = False,
list_mode: bool = False,
answer_mode: str = "normal",
layout: str = "prose",
reasoning_mode: str = "direct_lookup",
show_heading: bool = False,
needs_warning: bool = False,
) -> GenerationRequest:
"""The prompt for one question over one ordered evidence list.
@@ -251,12 +369,23 @@ def build_request(
f"CÂU HỎI: {question}\n\nMỖI đoạn BẰNG CHỨNG trên là chỉ định của MỘT "
"thuốc KHÁC NHAU. Hãy LIỆT KÊ TẤT CẢ các thuốc mà bằng chứng cho thấy "
"có chỉ định phù hợp với câu hỏi — không chỉ chọn một thuốc. Mỗi thuốc "
"một câu ngắn, gắn đúng số nguồn [n] của thuốc đó. Đây là liệt kê tra "
"một claim ngắn riêng, citations đúng số đoạn của thuốc đó. Đây là liệt kê tra "
"cứu, KHÔNG phải khuyến cáo thuốc nào tốt hơn — không xếp hạng, không "
"chọn thuốc \"phù hợp nhất\". Nếu KHÔNG thuốc nào trong bằng chứng thực "
"sự phù hợp với câu hỏi, nói rõ điều đó thay vì liệt kê thuốc không liên quan."
)
else:
task = f"CÂU HỎI: {question}"
user = f"BẰNG CHỨNG:\n\n{blocks}\n\n{task}"
plan = (
"KẾ HOẠCH TRÌNH BÀY (không phải dữ kiện y khoa; không được nhắc lại trong "
"câu trả lời):\n"
f"- độ dài: {answer_mode}\n"
f"- bố cục: {layout}\n"
f"- chế độ: {reasoning_mode}\n"
f"- cần heading: {'' if show_heading else 'không'}\n"
f"- cần nhấn mạnh cảnh báo: {'' if needs_warning else 'không'}\n"
"Dù bố cục nào, claims vẫn là các ý có evidence riêng. Không tạo heading "
"hoặc boilerplate thành claim."
)
user = f"BẰNG CHỨNG:\n\n{blocks}\n\n{plan}\n\n{task}"
return GenerationRequest(system=SYSTEM_PROMPT, user=user, schema=ANSWER_SCHEMA)