Wire the guarded conversational RAG answer layer end-to-end
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
"""The answer contract given to the generator, and the schema it must fill.
|
||||
|
||||
This is domain policy, not infrastructure: it states what a grounded answer to
|
||||
a clinician is allowed to contain. It lives here so it can be read, reviewed
|
||||
and tested without an SDK, and so swapping the provider cannot silently change
|
||||
what the model was told.
|
||||
|
||||
The audience is doctors and pharmacists, so the instructions ask for the
|
||||
book's own wording and its own precision rather than a simplification.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
SYSTEM_PROMPT = """\
|
||||
Bạn trình bày lại nội dung Dược thư Quốc gia Việt Nam cho bác sĩ và dược sĩ.
|
||||
|
||||
Bạn KHÔNG phải nguồn tri thức. Toàn bộ nội dung câu trả lời phải đến từ phần
|
||||
BẰNG CHỨNG được cung cấp trong tin nhắn này.
|
||||
|
||||
Quy tắc bắt buộc:
|
||||
1. Chỉ dùng thông tin có trong BẰNG CHỨNG. Không thêm kiến thức y khoa từ
|
||||
bên ngoài, kể cả khi bạn chắc chắn nó đúng.
|
||||
2. Mọi con số — liều, nồng độ, khoảng thời gian, tuổi, cân nặng — phải được
|
||||
CHÉP NGUYÊN VĂN từ BẰNG CHỨNG, đúng từng ký tự, kể cả dấu phẩy thập phân.
|
||||
Không làm tròn, không đổi đơn vị, không quy đổi.
|
||||
3. Mỗi ý phải gắn số nguồn dạng [n], với n là số thứ tự đoạn bằng chứng.
|
||||
4. Nếu BẰNG CHỨNG không đủ để trả lời, nói rõ là không đủ. Đó là câu trả lời
|
||||
hợp lệ, không phải thất bại.
|
||||
5. 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.
|
||||
|
||||
Viết gọn. Trả lời đúng điều được hỏi, không mở rộng phạm vi."""
|
||||
|
||||
|
||||
ANSWER_SCHEMA = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"answer": {
|
||||
"type": "string",
|
||||
"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."
|
||||
),
|
||||
},
|
||||
"evidence_sufficient": {
|
||||
"type": "boolean",
|
||||
"description": "false nếu bằng chứng không đủ để trả lời câu hỏi.",
|
||||
},
|
||||
},
|
||||
"required": ["answer", "evidence_sufficient"],
|
||||
"additionalProperties": False,
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class GenerationRequest:
|
||||
system: str
|
||||
user: str
|
||||
schema: dict
|
||||
|
||||
|
||||
def build_request(question: str, evidence_texts: tuple[str, ...]) -> GenerationRequest:
|
||||
"""The prompt for one question over one ordered evidence list.
|
||||
|
||||
Evidence is numbered from 1 so the model's `[n]` markers and the citation
|
||||
list the API returns share one index space; `grounding.verify` rejects any
|
||||
marker outside it.
|
||||
"""
|
||||
if not evidence_texts:
|
||||
raise ValueError("cannot build a grounded prompt with no evidence")
|
||||
|
||||
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=SYSTEM_PROMPT, user=user, schema=ANSWER_SCHEMA)
|
||||
Reference in New Issue
Block a user